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
46 public CComObjectRootEx
<CComMultiThreadModelNoCS
>,
50 CTravelEntry
*fNextEntry
;
51 CTravelEntry
*fPreviousEntry
;
54 HGLOBAL fPersistState
;
58 HRESULT
GetToolTipText(IUnknown
*punk
, LPWSTR pwzText
) const;
61 // *** ITravelEntry methods ***
62 virtual HRESULT STDMETHODCALLTYPE
Invoke(IUnknown
*punk
);
63 virtual HRESULT STDMETHODCALLTYPE
Update(IUnknown
*punk
, BOOL fIsLocalAnchor
);
64 virtual HRESULT STDMETHODCALLTYPE
GetPidl(LPITEMIDLIST
*ppidl
);
66 BEGIN_COM_MAP(CTravelEntry
)
67 COM_INTERFACE_ENTRY_IID(IID_ITravelEntry
, ITravelEntry
)
72 public CComObjectRootEx
<CComMultiThreadModelNoCS
>,
76 CTravelEntry
*fListHead
;
77 CTravelEntry
*fListTail
;
78 CTravelEntry
*fCurrentEntry
;
81 unsigned long fEntryCount
;
86 HRESULT
FindRelativeEntry(int offset
, CTravelEntry
**foundEntry
);
87 void DeleteChain(CTravelEntry
*startHere
);
88 void AppendEntry(CTravelEntry
*afterEntry
, CTravelEntry
*newEntry
);
91 // *** ITravelLog methods ***
92 virtual HRESULT STDMETHODCALLTYPE
AddEntry(IUnknown
*punk
, BOOL fIsLocalAnchor
);
93 virtual HRESULT STDMETHODCALLTYPE
UpdateEntry(IUnknown
*punk
, BOOL fIsLocalAnchor
);
94 virtual HRESULT STDMETHODCALLTYPE
UpdateExternal(IUnknown
*punk
, IUnknown
*punkHLBrowseContext
);
95 virtual HRESULT STDMETHODCALLTYPE
Travel(IUnknown
*punk
, int iOffset
);
96 virtual HRESULT STDMETHODCALLTYPE
GetTravelEntry(IUnknown
*punk
, int iOffset
, ITravelEntry
**ppte
);
97 virtual HRESULT STDMETHODCALLTYPE
FindTravelEntry(IUnknown
*punk
, LPCITEMIDLIST pidl
, ITravelEntry
**ppte
);
98 virtual HRESULT STDMETHODCALLTYPE
GetToolTipText(IUnknown
*punk
, int iOffset
, int idsTemplate
, LPWSTR pwzText
, DWORD cchText
);
99 virtual HRESULT STDMETHODCALLTYPE
InsertMenuEntries(IUnknown
*punk
, HMENU hmenu
, int nPos
, int idFirst
, int idLast
, DWORD dwFlags
);
100 virtual HRESULT STDMETHODCALLTYPE
Clone(ITravelLog
**pptl
);
101 virtual DWORD STDMETHODCALLTYPE
CountEntries(IUnknown
*punk
);
102 virtual HRESULT STDMETHODCALLTYPE
Revert();
104 BEGIN_COM_MAP(CTravelLog
)
105 COM_INTERFACE_ENTRY_IID(IID_ITravelLog
, ITravelLog
)
109 CTravelEntry::CTravelEntry()
112 fPreviousEntry
= NULL
;
114 fPersistState
= NULL
;
117 CTravelEntry::~CTravelEntry()
120 GlobalFree(fPersistState
);
123 HRESULT
CTravelEntry::GetToolTipText(IUnknown
*punk
, LPWSTR pwzText
) const
127 hResult
= ILGetDisplayNameEx(NULL
, fPIDL
, pwzText
, ILGDN_NORMAL
);
133 long CTravelEntry::GetSize() const
138 HRESULT STDMETHODCALLTYPE
CTravelEntry::Invoke(IUnknown
*punk
)
140 CComPtr
<IPersistHistory
> persistHistory
;
141 CComPtr
<IStream
> globalStream
;
144 hResult
= punk
->QueryInterface(IID_IPersistHistory
, reinterpret_cast<void **>(&persistHistory
));
147 hResult
= CreateStreamOnHGlobal(fPersistState
, FALSE
, &globalStream
);
150 hResult
= persistHistory
->LoadHistory(globalStream
, NULL
);
156 HRESULT STDMETHODCALLTYPE
CTravelEntry::Update(IUnknown
*punk
, BOOL fIsLocalAnchor
)
158 CComPtr
<ITravelLogClient
> travelLogClient
;
159 CComPtr
<IPersistHistory
> persistHistory
;
160 CComPtr
<IStream
> globalStream
;
161 WINDOWDATA windowData
;
162 HGLOBAL globalStorage
;
167 GlobalFree(fPersistState
);
168 fPersistState
= NULL
;
169 hResult
= punk
->QueryInterface(IID_ITravelLogClient
, reinterpret_cast<void **>(&travelLogClient
));
172 hResult
= travelLogClient
->GetWindowData(&windowData
);
175 fPIDL
= windowData
.pidl
;
176 // TODO: Properly free the windowData
177 hResult
= punk
->QueryInterface(IID_IPersistHistory
, reinterpret_cast<void **>(&persistHistory
));
180 globalStorage
= GlobalAlloc(GMEM_FIXED
, 0);
181 hResult
= CreateStreamOnHGlobal(globalStorage
, FALSE
, &globalStream
);
184 hResult
= persistHistory
->SaveHistory(globalStream
);
187 hResult
= GetHGlobalFromStream(globalStream
, &fPersistState
);
193 HRESULT STDMETHODCALLTYPE
CTravelEntry::GetPidl(LPITEMIDLIST
*ppidl
)
197 *ppidl
= ILClone(fPIDL
);
199 return E_OUTOFMEMORY
;
203 CTravelLog::CTravelLog()
207 fCurrentEntry
= NULL
;
213 CTravelLog::~CTravelLog()
215 CTravelEntry
*anEntry
;
219 while (anEntry
!= NULL
)
221 next
= anEntry
->fNextEntry
;
227 HRESULT
CTravelLog::Initialize()
229 fMaximumSize
= 1024 * 1024; // TODO: change to read this from registry
230 // Software\Microsoft\Windows\CurrentVersion\Explorer\TravelLog
235 HRESULT
CTravelLog::FindRelativeEntry(int offset
, CTravelEntry
**foundEntry
)
237 CTravelEntry
*curEntry
;
240 curEntry
= fCurrentEntry
;
243 while (offset
< 0 && curEntry
!= NULL
)
245 curEntry
= curEntry
->fPreviousEntry
;
251 while (offset
> 0 && curEntry
!= NULL
)
253 curEntry
= curEntry
->fNextEntry
;
257 if (curEntry
== NULL
)
259 *foundEntry
= curEntry
;
263 void CTravelLog::DeleteChain(CTravelEntry
*startHere
)
265 CTravelEntry
*saveNext
;
268 if (startHere
->fPreviousEntry
!= NULL
)
270 startHere
->fPreviousEntry
->fNextEntry
= NULL
;
271 fListTail
= startHere
->fPreviousEntry
;
278 while (startHere
!= NULL
)
280 saveNext
= startHere
->fNextEntry
;
281 itemSize
= startHere
->GetSize();
282 fCurrentSize
-= itemSize
;
283 startHere
->Release();
284 startHere
= saveNext
;
289 void CTravelLog::AppendEntry(CTravelEntry
*afterEntry
, CTravelEntry
*newEntry
)
291 if (afterEntry
== NULL
)
293 fListHead
= newEntry
;
294 fListTail
= newEntry
;
298 newEntry
->fNextEntry
= afterEntry
->fNextEntry
;
299 afterEntry
->fNextEntry
= newEntry
;
300 newEntry
->fPreviousEntry
= afterEntry
;
301 if (newEntry
->fNextEntry
== NULL
)
302 fListTail
= newEntry
;
304 newEntry
->fNextEntry
->fPreviousEntry
= newEntry
;
309 HRESULT STDMETHODCALLTYPE
CTravelLog::AddEntry(IUnknown
*punk
, BOOL fIsLocalAnchor
)
311 CComObject
<CTravelEntry
> *newEntry
;
316 ATLTRY (newEntry
= new CComObject
<CTravelEntry
>);
317 if (newEntry
== NULL
)
318 return E_OUTOFMEMORY
;
320 if (fCurrentEntry
!= NULL
&& fCurrentEntry
->fNextEntry
!= NULL
)
321 DeleteChain(fCurrentEntry
->fNextEntry
);
322 AppendEntry(fCurrentEntry
, newEntry
);
323 itemSize
= newEntry
->GetSize();
324 fCurrentSize
+= itemSize
;
325 fCurrentEntry
= newEntry
;
329 HRESULT STDMETHODCALLTYPE
CTravelLog::UpdateEntry(IUnknown
*punk
, BOOL fIsLocalAnchor
)
333 if (fCurrentEntry
== NULL
)
335 return fCurrentEntry
->Update(punk
, fIsLocalAnchor
);
338 HRESULT STDMETHODCALLTYPE
CTravelLog::UpdateExternal(IUnknown
*punk
, IUnknown
*punkHLBrowseContext
)
343 HRESULT STDMETHODCALLTYPE
CTravelLog::Travel(IUnknown
*punk
, int iOffset
)
345 CTravelEntry
*destinationEntry
;
348 hResult
= FindRelativeEntry(iOffset
, &destinationEntry
);
351 fCurrentEntry
= destinationEntry
;
352 hResult
= destinationEntry
->Invoke(punk
);
358 HRESULT STDMETHODCALLTYPE
CTravelLog::GetTravelEntry(IUnknown
*punk
, int iOffset
, ITravelEntry
**ppte
)
360 CTravelEntry
*destinationEntry
;
363 hResult
= FindRelativeEntry(iOffset
, &destinationEntry
);
366 return destinationEntry
->QueryInterface(IID_ITravelEntry
, reinterpret_cast<void **>(ppte
));
369 HRESULT STDMETHODCALLTYPE
CTravelLog::FindTravelEntry(IUnknown
*punk
, LPCITEMIDLIST pidl
, ITravelEntry
**ppte
)
373 if (punk
== NULL
|| pidl
== NULL
)
378 HRESULT STDMETHODCALLTYPE
CTravelLog::GetToolTipText(IUnknown
*punk
, int iOffset
, int idsTemplate
, LPWSTR pwzText
, DWORD cchText
)
380 CTravelEntry
*destinationEntry
;
381 wchar_t tempString
[MAX_PATH
];
382 wchar_t templateString
[200];
387 if (punk
== NULL
|| cchText
== 0)
389 hResult
= FindRelativeEntry(iOffset
, &destinationEntry
);
392 hResult
= destinationEntry
->GetToolTipText(punk
, tempString
);
397 wcscpy(templateString
, L
"Back to %s");
401 wcscpy(templateString
, L
"Forward to %s");
403 _snwprintf(pwzText
, cchText
, templateString
, tempString
);
407 static void FixAmpersands(wchar_t *buffer
)
409 wchar_t tempBuffer
[MAX_PATH
* 2];
424 wcscpy(buffer
, tempBuffer
);
427 HRESULT STDMETHODCALLTYPE
CTravelLog::InsertMenuEntries(IUnknown
*punk
, HMENU hmenu
,
428 int nPos
, int idFirst
, int idLast
, DWORD dwFlags
)
430 CTravelEntry
*currentItem
;
431 MENUITEMINFO menuItemInfo
;
432 wchar_t itemTextBuffer
[MAX_PATH
* 2];
435 // TLMENUF_BACK - include back entries
436 // TLMENUF_INCLUDECURRENT - include current entry, if TLMENUF_CHECKCURRENT then check the entry
437 // TLMENUF_FORE - include next entries
438 // if fore+back, list from oldest to newest
439 // if back, list from newest to oldest
440 // if fore, list from newest to oldest
442 // don't forget to patch ampersands before adding to menu
445 if (idLast
<= idFirst
)
447 menuItemInfo
.cbSize
= sizeof(menuItemInfo
);
448 menuItemInfo
.fMask
= MIIM_FTYPE
| MIIM_ID
| MIIM_STATE
| MIIM_STRING
;
449 menuItemInfo
.fType
= MFT_STRING
;
450 menuItemInfo
.wID
= idFirst
;
451 menuItemInfo
.fState
= MFS_ENABLED
;
452 menuItemInfo
.dwTypeData
= itemTextBuffer
;
453 if ((dwFlags
& TLMENUF_BACK
) != 0)
455 if ((dwFlags
& TLMENUF_FORE
) != 0)
457 currentItem
= fCurrentEntry
;
458 if (currentItem
!= NULL
)
460 while (currentItem
->fPreviousEntry
!= NULL
)
461 currentItem
= currentItem
->fPreviousEntry
;
463 while (currentItem
!= fCurrentEntry
)
465 hResult
= currentItem
->GetToolTipText(punk
, itemTextBuffer
);
466 if (SUCCEEDED(hResult
))
468 FixAmpersands(itemTextBuffer
);
469 if (InsertMenuItem(hmenu
, nPos
, TRUE
, &menuItemInfo
))
475 currentItem
= currentItem
->fNextEntry
;
480 currentItem
= fCurrentEntry
;
481 if (currentItem
!= NULL
)
482 currentItem
= currentItem
->fPreviousEntry
;
483 while (currentItem
!= NULL
)
485 hResult
= currentItem
->GetToolTipText(punk
, itemTextBuffer
);
486 if (SUCCEEDED(hResult
))
488 FixAmpersands(itemTextBuffer
);
489 if (InsertMenuItem(hmenu
, nPos
, TRUE
, &menuItemInfo
))
495 currentItem
= currentItem
->fPreviousEntry
;
499 if ((dwFlags
& TLMENUF_INCLUDECURRENT
) != 0)
501 if (fCurrentEntry
!= NULL
)
503 hResult
= fCurrentEntry
->GetToolTipText(punk
, itemTextBuffer
);
504 if (SUCCEEDED(hResult
))
506 FixAmpersands(itemTextBuffer
);
507 if ((dwFlags
& TLMENUF_CHECKCURRENT
) != 0)
508 menuItemInfo
.fState
|= MFS_CHECKED
;
509 if (InsertMenuItem(hmenu
, nPos
, TRUE
, &menuItemInfo
))
514 menuItemInfo
.fState
&= ~MFS_CHECKED
;
518 if ((dwFlags
& TLMENUF_FORE
) != 0)
520 currentItem
= fCurrentEntry
;
521 if (currentItem
!= NULL
)
522 currentItem
= currentItem
->fNextEntry
;
523 while (currentItem
!= NULL
)
525 hResult
= currentItem
->GetToolTipText(punk
, itemTextBuffer
);
526 if (SUCCEEDED(hResult
))
528 FixAmpersands(itemTextBuffer
);
529 if (InsertMenuItem(hmenu
, nPos
, TRUE
, &menuItemInfo
))
535 currentItem
= currentItem
->fNextEntry
;
541 HRESULT STDMETHODCALLTYPE
CTravelLog::Clone(ITravelLog
**pptl
)
550 DWORD STDMETHODCALLTYPE
CTravelLog::CountEntries(IUnknown
*punk
)
557 HRESULT STDMETHODCALLTYPE
CTravelLog::Revert()
559 // remove the current entry?
563 HRESULT
CreateTravelLog(REFIID riid
, void **ppv
)
565 CComObject
<CTravelLog
> *theTravelLog
;
571 ATLTRY (theTravelLog
= new CComObject
<CTravelLog
>);
572 if (theTravelLog
== NULL
)
573 return E_OUTOFMEMORY
;
574 hResult
= theTravelLog
->QueryInterface(riid
, reinterpret_cast<void **>(ppv
));
580 hResult
= theTravelLog
->Initialize();