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
45 #include <shlobj_undoc.h>
47 #include <shlguid_undoc.h>
55 public CComObjectRootEx
<CComMultiThreadModelNoCS
>,
59 CTravelEntry
*fNextEntry
;
60 CTravelEntry
*fPreviousEntry
;
63 HGLOBAL fPersistState
;
67 HRESULT
GetToolTipText(IUnknown
*punk
, LPWSTR pwzText
) const;
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
);
75 BEGIN_COM_MAP(CTravelEntry
)
76 COM_INTERFACE_ENTRY_IID(IID_ITravelEntry
, ITravelEntry
)
81 public CComObjectRootEx
<CComMultiThreadModelNoCS
>,
85 CTravelEntry
*fListHead
;
86 CTravelEntry
*fListTail
;
87 CTravelEntry
*fCurrentEntry
;
90 unsigned long fEntryCount
;
95 HRESULT
FindRelativeEntry(int offset
, CTravelEntry
**foundEntry
);
96 void DeleteChain(CTravelEntry
*startHere
);
97 void AppendEntry(CTravelEntry
*afterEntry
, CTravelEntry
*newEntry
);
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();
113 BEGIN_COM_MAP(CTravelLog
)
114 COM_INTERFACE_ENTRY_IID(IID_ITravelLog
, ITravelLog
)
118 CTravelEntry::CTravelEntry()
121 fPreviousEntry
= NULL
;
123 fPersistState
= NULL
;
126 CTravelEntry::~CTravelEntry()
129 GlobalFree(fPersistState
);
132 HRESULT
CTravelEntry::GetToolTipText(IUnknown
*punk
, LPWSTR pwzText
) const
136 hResult
= ILGetDisplayNameEx(NULL
, fPIDL
, pwzText
, ILGDN_NORMAL
);
142 long CTravelEntry::GetSize() const
147 HRESULT STDMETHODCALLTYPE
CTravelEntry::Invoke(IUnknown
*punk
)
149 CComPtr
<IPersistHistory
> persistHistory
;
150 CComPtr
<IStream
> globalStream
;
153 hResult
= punk
->QueryInterface(IID_IPersistHistory
, (void **)&persistHistory
);
156 hResult
= CreateStreamOnHGlobal(fPersistState
, FALSE
, &globalStream
);
159 hResult
= persistHistory
->LoadHistory(globalStream
, NULL
);
165 HRESULT STDMETHODCALLTYPE
CTravelEntry::Update(IUnknown
*punk
, BOOL fIsLocalAnchor
)
167 CComPtr
<ITravelLogClient
> travelLogClient
;
168 CComPtr
<IPersistHistory
> persistHistory
;
169 CComPtr
<IStream
> globalStream
;
170 WINDOWDATA windowData
;
171 HGLOBAL globalStorage
;
176 GlobalFree(fPersistState
);
177 fPersistState
= NULL
;
178 hResult
= punk
->QueryInterface(IID_ITravelLogClient
, (void **)&travelLogClient
);
181 hResult
= travelLogClient
->GetWindowData(&windowData
);
184 fPIDL
= windowData
.pidl
;
185 // TODO: Properly free the windowData
186 hResult
= punk
->QueryInterface(IID_IPersistHistory
, (void **)&persistHistory
);
189 globalStorage
= GlobalAlloc(GMEM_FIXED
, 0);
190 hResult
= CreateStreamOnHGlobal(globalStorage
, FALSE
, &globalStream
);
193 hResult
= persistHistory
->SaveHistory(globalStream
);
196 hResult
= GetHGlobalFromStream(globalStream
, &fPersistState
);
202 HRESULT STDMETHODCALLTYPE
CTravelEntry::GetPidl(LPITEMIDLIST
*ppidl
)
206 *ppidl
= ILClone(fPIDL
);
208 return E_OUTOFMEMORY
;
212 CTravelLog::CTravelLog()
216 fCurrentEntry
= NULL
;
222 CTravelLog::~CTravelLog()
224 CTravelEntry
*anEntry
;
228 while (anEntry
!= NULL
)
230 next
= anEntry
->fNextEntry
;
236 HRESULT
CTravelLog::Initialize()
238 fMaximumSize
= 1024 * 1024; // TODO: change to read this from registry
239 // Software\Microsoft\Windows\CurrentVersion\Explorer\TravelLog
244 HRESULT
CTravelLog::FindRelativeEntry(int offset
, CTravelEntry
**foundEntry
)
246 CTravelEntry
*curEntry
;
249 curEntry
= fCurrentEntry
;
252 while (offset
< 0 && curEntry
!= NULL
)
254 curEntry
= curEntry
->fPreviousEntry
;
260 while (offset
> 0 && curEntry
!= NULL
)
262 curEntry
= curEntry
->fNextEntry
;
266 if (curEntry
== NULL
)
268 *foundEntry
= curEntry
;
272 void CTravelLog::DeleteChain(CTravelEntry
*startHere
)
274 CTravelEntry
*saveNext
;
277 if (startHere
->fPreviousEntry
!= NULL
)
279 startHere
->fPreviousEntry
->fNextEntry
= NULL
;
280 fListTail
= startHere
->fPreviousEntry
;
287 while (startHere
!= NULL
)
289 saveNext
= startHere
->fNextEntry
;
290 itemSize
= startHere
->GetSize();
291 fCurrentSize
-= itemSize
;
292 startHere
->Release();
293 startHere
= saveNext
;
298 void CTravelLog::AppendEntry(CTravelEntry
*afterEntry
, CTravelEntry
*newEntry
)
300 if (afterEntry
== NULL
)
302 fListHead
= newEntry
;
303 fListTail
= newEntry
;
307 newEntry
->fNextEntry
= afterEntry
->fNextEntry
;
308 afterEntry
->fNextEntry
= newEntry
;
309 newEntry
->fPreviousEntry
= afterEntry
;
310 if (newEntry
->fNextEntry
== NULL
)
311 fListTail
= newEntry
;
313 newEntry
->fNextEntry
->fPreviousEntry
= newEntry
;
318 HRESULT STDMETHODCALLTYPE
CTravelLog::AddEntry(IUnknown
*punk
, BOOL fIsLocalAnchor
)
320 CComObject
<CTravelEntry
> *newEntry
;
325 ATLTRY (newEntry
= new CComObject
<CTravelEntry
>);
326 if (newEntry
== NULL
)
327 return E_OUTOFMEMORY
;
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
;
338 HRESULT STDMETHODCALLTYPE
CTravelLog::UpdateEntry(IUnknown
*punk
, BOOL fIsLocalAnchor
)
342 if (fCurrentEntry
== NULL
)
344 return fCurrentEntry
->Update(punk
, fIsLocalAnchor
);
347 HRESULT STDMETHODCALLTYPE
CTravelLog::UpdateExternal(IUnknown
*punk
, IUnknown
*punkHLBrowseContext
)
352 HRESULT STDMETHODCALLTYPE
CTravelLog::Travel(IUnknown
*punk
, int iOffset
)
354 CTravelEntry
*destinationEntry
;
357 hResult
= FindRelativeEntry(iOffset
, &destinationEntry
);
360 fCurrentEntry
= destinationEntry
;
361 hResult
= destinationEntry
->Invoke(punk
);
367 HRESULT STDMETHODCALLTYPE
CTravelLog::GetTravelEntry(IUnknown
*punk
, int iOffset
, ITravelEntry
**ppte
)
369 CTravelEntry
*destinationEntry
;
372 hResult
= FindRelativeEntry(iOffset
, &destinationEntry
);
375 return destinationEntry
->QueryInterface(IID_ITravelEntry
, (void **)ppte
);
378 HRESULT STDMETHODCALLTYPE
CTravelLog::FindTravelEntry(IUnknown
*punk
, LPCITEMIDLIST pidl
, ITravelEntry
**ppte
)
382 if (punk
== NULL
|| pidl
== NULL
)
387 HRESULT STDMETHODCALLTYPE
CTravelLog::GetToolTipText(IUnknown
*punk
, int iOffset
, int idsTemplate
, LPWSTR pwzText
, DWORD cchText
)
389 CTravelEntry
*destinationEntry
;
390 wchar_t tempString
[MAX_PATH
];
391 wchar_t templateString
[200];
396 if (punk
== NULL
|| cchText
== 0)
398 hResult
= FindRelativeEntry(iOffset
, &destinationEntry
);
401 hResult
= destinationEntry
->GetToolTipText(punk
, tempString
);
406 wcscpy(templateString
, L
"Back to %s");
410 wcscpy(templateString
, L
"Forward to %s");
412 _snwprintf(pwzText
, cchText
, templateString
, tempString
);
416 static void FixAmpersands(wchar_t *buffer
)
418 wchar_t tempBuffer
[MAX_PATH
* 2];
433 wcscpy(buffer
, tempBuffer
);
436 HRESULT STDMETHODCALLTYPE
CTravelLog::InsertMenuEntries(IUnknown
*punk
, HMENU hmenu
, int nPos
, int idFirst
, int idLast
, DWORD dwFlags
)
438 CTravelEntry
*currentItem
;
439 MENUITEMINFO menuItemInfo
;
440 wchar_t itemTextBuffer
[MAX_PATH
* 2];
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
450 // don't forget to patch ampersands before adding to menu
453 if (idLast
<= idFirst
)
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)
463 if ((dwFlags
& TLMENUF_FORE
) != 0)
465 currentItem
= fCurrentEntry
;
466 if (currentItem
!= NULL
)
468 while (currentItem
->fPreviousEntry
!= NULL
)
469 currentItem
= currentItem
->fPreviousEntry
;
471 while (currentItem
!= fCurrentEntry
)
473 hResult
= currentItem
->GetToolTipText(punk
, itemTextBuffer
);
474 if (SUCCEEDED(hResult
))
476 FixAmpersands(itemTextBuffer
);
477 if (InsertMenuItem(hmenu
, nPos
, TRUE
, &menuItemInfo
))
483 currentItem
= currentItem
->fNextEntry
;
488 currentItem
= fCurrentEntry
;
489 if (currentItem
!= NULL
)
490 currentItem
= currentItem
->fPreviousEntry
;
491 while (currentItem
!= NULL
)
493 hResult
= currentItem
->GetToolTipText(punk
, itemTextBuffer
);
494 if (SUCCEEDED(hResult
))
496 FixAmpersands(itemTextBuffer
);
497 if (InsertMenuItem(hmenu
, nPos
, TRUE
, &menuItemInfo
))
503 currentItem
= currentItem
->fPreviousEntry
;
507 if ((dwFlags
& TLMENUF_INCLUDECURRENT
) != 0)
509 if (fCurrentEntry
!= NULL
)
511 hResult
= fCurrentEntry
->GetToolTipText(punk
, itemTextBuffer
);
512 if (SUCCEEDED(hResult
))
514 FixAmpersands(itemTextBuffer
);
515 if ((dwFlags
& TLMENUF_CHECKCURRENT
) != 0)
516 menuItemInfo
.fState
|= MFS_CHECKED
;
517 if (InsertMenuItem(hmenu
, nPos
, TRUE
, &menuItemInfo
))
522 menuItemInfo
.fState
&= ~MFS_CHECKED
;
526 if ((dwFlags
& TLMENUF_FORE
) != 0)
528 currentItem
= fCurrentEntry
;
529 if (currentItem
!= NULL
)
530 currentItem
= currentItem
->fNextEntry
;
531 while (currentItem
!= NULL
)
533 hResult
= currentItem
->GetToolTipText(punk
, itemTextBuffer
);
534 if (SUCCEEDED(hResult
))
536 FixAmpersands(itemTextBuffer
);
537 if (InsertMenuItem(hmenu
, nPos
, TRUE
, &menuItemInfo
))
543 currentItem
= currentItem
->fNextEntry
;
549 HRESULT STDMETHODCALLTYPE
CTravelLog::Clone(ITravelLog
**pptl
)
558 DWORD STDMETHODCALLTYPE
CTravelLog::CountEntries(IUnknown
*punk
)
565 HRESULT STDMETHODCALLTYPE
CTravelLog::Revert()
567 // remove the current entry?
571 HRESULT
CreateTravelLog(REFIID riid
, void **ppv
)
573 CComObject
<CTravelLog
> *theTravelLog
;
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
))
588 hResult
= theTravelLog
->Initialize();