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 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
);
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];
435 if (punk
== NULL
|| cchText
== 0)
437 hResult
= FindRelativeEntry(iOffset
, &destinationEntry
);
438 if (FAILED_UNEXPECTEDLY(hResult
))
440 hResult
= destinationEntry
->GetToolTipText(punk
, tempString
);
441 if (FAILED_UNEXPECTEDLY(hResult
))
445 if(LoadStringW(_AtlBaseModule
.GetResourceInstance(),
446 IDS_BACK
, templateString
, sizeof(templateString
) / sizeof(wchar_t)) == 0)
447 return HRESULT_FROM_WIN32(GetLastError());
451 if(LoadStringW(_AtlBaseModule
.GetResourceInstance(),
452 IDS_FORWARD
, templateString
, sizeof(templateString
) / sizeof(wchar_t)) == 0)
453 return HRESULT_FROM_WIN32(GetLastError());
455 _snwprintf(pwzText
, cchText
, templateString
, tempString
);
457 TRACE("CTravelLog::GetToolTipText for IUnknown punk=%p at offset=%d returning L\"%S\"\n", punk
, iOffset
, pwzText
);
462 static void FixAmpersands(wchar_t *buffer
)
464 wchar_t tempBuffer
[MAX_PATH
* 2];
479 wcscpy(buffer
, tempBuffer
);
482 HRESULT STDMETHODCALLTYPE
CTravelLog::InsertMenuEntries(IUnknown
*punk
, HMENU hmenu
,
483 int nPos
, int idFirst
, int idLast
, DWORD dwFlags
)
485 CTravelEntry
*currentItem
;
486 MENUITEMINFO menuItemInfo
;
487 wchar_t itemTextBuffer
[MAX_PATH
* 2];
490 TRACE("CTravelLog::InsertMenuEntries for IUnknown punk=%p, nPos=%d, idFirst=%d, idLast=%d\n", punk
);
492 // TLMENUF_BACK - include back entries
493 // TLMENUF_INCLUDECURRENT - include current entry, if TLMENUF_CHECKCURRENT then check the entry
494 // TLMENUF_FORE - include next entries
495 // if fore+back, list from oldest to newest
496 // if back, list from newest to oldest
497 // if fore, list from newest to oldest
499 // don't forget to patch ampersands before adding to menu
502 if (idLast
<= idFirst
)
504 menuItemInfo
.cbSize
= sizeof(menuItemInfo
);
505 menuItemInfo
.fMask
= MIIM_FTYPE
| MIIM_ID
| MIIM_STATE
| MIIM_STRING
;
506 menuItemInfo
.fType
= MFT_STRING
;
507 menuItemInfo
.wID
= idFirst
;
508 menuItemInfo
.fState
= MFS_ENABLED
;
509 menuItemInfo
.dwTypeData
= itemTextBuffer
;
510 if ((dwFlags
& TLMENUF_BACK
) != 0)
512 if ((dwFlags
& TLMENUF_FORE
) != 0)
514 currentItem
= fCurrentEntry
;
515 if (currentItem
!= NULL
)
517 while (currentItem
->fPreviousEntry
!= NULL
)
518 currentItem
= currentItem
->fPreviousEntry
;
520 while (currentItem
!= fCurrentEntry
)
522 hResult
= currentItem
->GetToolTipText(punk
, itemTextBuffer
);
523 if (SUCCEEDED(hResult
))
525 FixAmpersands(itemTextBuffer
);
526 TRACE("CTravelLog::InsertMenuEntries adding entry L\"%S\"/L\"%S\" with id %d\n", itemTextBuffer
, menuItemInfo
.dwTypeData
, menuItemInfo
.wID
);
527 if (InsertMenuItem(hmenu
, nPos
, TRUE
, &menuItemInfo
))
533 currentItem
= currentItem
->fNextEntry
;
538 currentItem
= fCurrentEntry
;
539 if (currentItem
!= NULL
)
540 currentItem
= currentItem
->fPreviousEntry
;
541 while (currentItem
!= NULL
)
543 hResult
= currentItem
->GetToolTipText(punk
, itemTextBuffer
);
544 if (SUCCEEDED(hResult
))
546 FixAmpersands(itemTextBuffer
);
547 TRACE("CTravelLog::InsertMenuEntries adding entry L\"%S\"/L\"%S\" with id %d\n", itemTextBuffer
, menuItemInfo
.dwTypeData
, menuItemInfo
.wID
);
548 if (InsertMenuItem(hmenu
, nPos
, TRUE
, &menuItemInfo
))
554 currentItem
= currentItem
->fPreviousEntry
;
558 if ((dwFlags
& TLMENUF_INCLUDECURRENT
) != 0)
560 if (fCurrentEntry
!= NULL
)
562 hResult
= fCurrentEntry
->GetToolTipText(punk
, itemTextBuffer
);
563 if (SUCCEEDED(hResult
))
565 FixAmpersands(itemTextBuffer
);
566 if ((dwFlags
& TLMENUF_CHECKCURRENT
) != 0)
567 menuItemInfo
.fState
|= MFS_CHECKED
;
568 TRACE("CTravelLog::InsertMenuEntries adding entry L\"%S\"/L\"%S\" with id %d\n", itemTextBuffer
, menuItemInfo
.dwTypeData
, menuItemInfo
.wID
);
569 if (InsertMenuItem(hmenu
, nPos
, TRUE
, &menuItemInfo
))
574 menuItemInfo
.fState
&= ~MFS_CHECKED
;
578 if ((dwFlags
& TLMENUF_FORE
) != 0)
580 currentItem
= fCurrentEntry
;
581 if (currentItem
!= NULL
)
582 currentItem
= currentItem
->fNextEntry
;
583 while (currentItem
!= NULL
)
585 hResult
= currentItem
->GetToolTipText(punk
, itemTextBuffer
);
586 if (SUCCEEDED(hResult
))
588 FixAmpersands(itemTextBuffer
);
589 TRACE("CTravelLog::InsertMenuEntries adding entry L\"%S\"/L\"%S\" with id %d\n", itemTextBuffer
, menuItemInfo
.dwTypeData
, menuItemInfo
.wID
);
590 if (InsertMenuItem(hmenu
, nPos
, TRUE
, &menuItemInfo
))
596 currentItem
= currentItem
->fNextEntry
;
602 HRESULT STDMETHODCALLTYPE
CTravelLog::Clone(ITravelLog
**pptl
)
612 DWORD STDMETHODCALLTYPE
CTravelLog::CountEntries(IUnknown
*punk
)
619 HRESULT STDMETHODCALLTYPE
CTravelLog::Revert()
621 // remove the current entry?
626 HRESULT
CTravelLog_CreateInstance(REFIID riid
, void **ppv
)
628 return ShellObjectCreatorInit
<CTravelLog
>(riid
, ppv
);