|
|
#include "shellprv.h"
#include "duiview.h"
#include "duilist.h"
#include "duisec.h"
#include "duitask.h"
#include "duiinfo.h"
#include "duihost.h"
#include "duidrag.h"
#include "defviewp.h"
#include "ids.h"
#include "dvtasks.h"
#include <shimgvw.h>
#include <uxtheme.h>
#include <shstyle.h>
UsingDUIClass(DUIListView); UsingDUIClass(DUIAxHost); UsingDUIClass(Expando); UsingDUIClass(ActionTask); UsingDUIClass(DestinationTask);
//
// These are private window messages posted to the host window
// so they will be processed async. There are issues with
// trying to destroy a handler while inside of the handler
//
#define WM_NAVIGATETOPIDL (WM_USER + 42)
#define WM_REFRESHVIEW (WM_USER + 43)
#define DUI_HOST_WINDOW_CLASS_NAME TEXT("DUIViewWndClassName")
//
// Default attributes associated with our DUI sections.
//
struct DUISEC_ATTRIBUTES { DUISEC _eDUISecID; BOOL _bExpandedDefault; LPCWSTR _pszExpandedPropName; } const c_DUISectionAttributes[] = { DUISEC_SPECIALTASKS, TRUE, L"ExpandSpecialTasks", DUISEC_FILETASKS, TRUE, L"ExpandFileTasks", DUISEC_OTHERPLACESTASKS, TRUE, L"ExpandOtherPlacesTasks", DUISEC_DETAILSTASKS, FALSE, L"ExpandDetailsTasks" };
// pDefView - pointer to the defview class
CDUIView* Create_CDUIView(CDefView * pDefView) { CDUIView* p = new CDUIView(pDefView); if (p) { if (FAILED(p->Initialize())) { delete p; p = NULL; } } return p; }
CDUIView::CDUIView(CDefView * pDefView) { // We have a zero-init constructor. Be paranoid and check a couple:
ASSERT(NULL ==_hWnd); ASSERT(NULL == _pshlItems); ASSERT(NULL == _ppbShellFolders);
_cRef = 1; _pDefView = pDefView; _pDefView->AddRef(); }
HRESULT CDUIView::Initialize() { // Initialize DirectUI process (InitProcess) and register classes
_hrInit = InitializeDirectUI(); if (FAILED(_hrInit)) goto Failure;
// Initialize DirectUI thread
_hrInit = InitThread(); if (FAILED(_hrInit)) goto Failure; ManageAnimations(FALSE);
_pDT = new CDUIDropTarget ();
Failure:
return _hrInit; }
CDUIView::~CDUIView() { IUnknown_SetSite(_spThumbnailExtractor2, NULL); if (_hwndMsgThumbExtract) // May have been (likely) created by CMiniPreviewer
{ DestroyWindow(_hwndMsgThumbExtract); } if (_hwndMsgInfoExtract) // May have been (likely) created by CNameSpaceItemInfoList
{ DestroyWindow(_hwndMsgInfoExtract); } ATOMICRELEASE(_pshlItems); if (_bstrIntroText) { SysFreeString(_bstrIntroText); }
if (_hinstTheme) { FreeLibrary(_hinstTheme); _hinstTheme = NULL; _fLoadedTheme = FALSE; }
if (_hinstScrollbarTheme) { CloseThemeData(_hinstScrollbarTheme); _hinstScrollbarTheme = NULL; }
UnInitializeDirectUI();
if (_ppbShellFolders) _ppbShellFolders->Release();
_pDefView->Release(); }
//
// This DUI uninitialization code was broken out from the destructor
// because we need to call it from defview in response to WM_NCDESTROY
// before the CDUIView object is destroyed. This is required to properly
// initiate the shutdown of DUser on the thread. Since we don't own the
// browser thread message pump we must ensure all DUser processing is
// complete before our defview instance goes away.
// Therefore, since it can be called twice, once from defview and once
// from CDUIView::~CDUIView, all processing must tolerate multiple calls
// for the same instance.
//
void CDUIView::UnInitializeDirectUI(void) { ATOMICRELEASE(_pvSpecialTaskSheet); ATOMICRELEASE(_pvFolderTaskSheet); ATOMICRELEASE(_pvDetailsSheet); _ClearNonStdTaskSections();
if (_pDT) { _pDT->Release(); _pDT = NULL; }
ManageAnimations(TRUE);
if (SUCCEEDED(_hrInit)) { UnInitThread(); _hrInit = E_FAIL; // UnInit thread only once.
} }
// Right now our themeing information is hard-coded due to limitations of DirectUI (only one resource)
// so we'll ask the namespace for a hardcoded name that we can look up in the below table. Add new
// names/entries to this list as we add theme parts to our shellstyle.dll.
//
// These theme elements come from shellstyle.dll.
const WVTHEME c_wvTheme[] = { { L"music", IDB_MUSIC_ICON_BMP, IDB_MUSIC_TASKS_BMP, IDB_MUSIC_LISTVIEW_BMP }, { L"picture", IDB_PICTURES_ICON_BMP, IDB_PICTURES_TASKS_BMP, IDB_PICTURES_LISTVIEW_BMP }, { L"video", IDB_VIDEO_ICON_BMP, IDB_VIDEO_TASKS_BMP, IDB_VIDEO_LISTVIEW_BMP }, { L"search", IDB_SEARCH_ICON_BMP, IDB_SEARCH_TASKS_BMP, IDB_SEARCH_LISTVIEW_BMP }, };
const WVTHEME* CDUIView::GetThemeInfo() { for (UINT i = 0 ; i < ARRAYSIZE(c_wvTheme) ; i++) { if (0 == lstrcmp(_pDefView->_wvTheme.pszThemeID, c_wvTheme[i].pszThemeName)) return &(c_wvTheme[i]); }
return NULL; }
// Main intialization point for DUI view
//
// bDisplayBarrier - Display soft barrier over top of listview
HRESULT CDUIView::Initialize(BOOL bDisplayBarrier, IUnknown * punkPreview) { DisableAnimations(); Element::StartDefer();
// Create the host window for the DUI elements
HRESULT hr = _CreateHostWindow(); if (SUCCEEDED(hr)) { // Dynamically build the .ui file for this view
int iCharCount; char *pUIFile = NULL; hr = _BuildUIFile(&pUIFile, &iCharCount); if (SUCCEEDED(hr)) { // Parse the .ui file and initialize the elements
hr = _InitializeElements(pUIFile, iCharCount, bDisplayBarrier, punkPreview); if (SUCCEEDED(hr)) { BuildDropTarget(_phe->GetDisplayNode(), _phe->GetHWND());
// Set visible for host element
_phe->SetVisible(true); } LocalFree(pUIFile); } }
// Note:
// EndDefer() here so layout coordinates are calculated before executing
// the next snippit of code which depends on them being set properly.
// The one thing to be aware of in future is that if this isn't the
// outermost BeginDefer()/EndDefer() pair in the codepath, we're in
// trouble because then DUI won't calculate its layout coordinates.
Element::EndDefer();
if (SUCCEEDED(hr)) { Value* pv; if (_peTaskPane->GetExtent(&pv)) { const SIZE * pSize = pv->GetSize();
_iOriginalTaskPaneWidth = pSize->cx; _iTaskPaneWidth = pSize->cx;
pv->Release();
// REVIEW: Why are we doing this based on a resource string, instead
// of simply having the localizers localize the size in the theme???
// It kind of sucks because we're forcing two layouts all the time.
_iTaskPaneWidth = ScaleSizeBasedUponLocalization(_iOriginalTaskPaneWidth);
if (_iTaskPaneWidth != _iOriginalTaskPaneWidth) { Element::StartDefer();
// Increase the width of the scroller if the localizers have
// bumped up the size defined in the resources
_peTaskPane->SetWidth(_iTaskPaneWidth);
Element::EndDefer(); } }
if (_fHideTasklist || (_phe->GetWidth() / 2) < _iTaskPaneWidth) { Element::StartDefer();
_peTaskPane->SetWidth(0);
Element::EndDefer(); } _bInitialized = true; } else { if (_hWnd) { DestroyWindow (_hWnd); _hWnd = NULL; } }
// Note:
// We don't re-enable animations until after we're completely finished
// with all our crazy resizing and stuff. This prevents some nasty
// issues with DUI panes only partly painting (i.e. RAID 422057).
EnableAnimations();
return hr; }
void CDUIView::DetachListview() { if (_peListView) _peListView->DetachListview();
if (_hWnd) { DestroyWindow(_hWnd); _hWnd = NULL; } }
// Creates the host window for the DUI elements to
// be associated with. This child window
// will also be passed back to defview to be used
// as the result pane host.
HRESULT CDUIView::_CreateHostWindow (void) { WNDCLASS wc = {0}; wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = _DUIHostWndProc; wc.hInstance = HINST_THISDLL; wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)GetStockObject(HOLLOW_BRUSH); wc.lpszClassName = DUI_HOST_WINDOW_CLASS_NAME; RegisterClass(&wc); // Query for the size of defview's client window so we can size this window
// to match
RECT rc; GetClientRect(_pDefView->_hwndView, &rc); _hWnd = CreateWindowEx(0, DUI_HOST_WINDOW_CLASS_NAME, NULL, WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN, rc.left, rc.top, rc.right, rc.bottom, _pDefView->_hwndView, NULL, HINST_THISDLL, (void *)this); if (!_hWnd) { TraceMsg(TF_ERROR, "CDUIView::_CreateHostWindow: CreateWindowEx failed with %d", GetLastError()); return HRESULT_FROM_WIN32(GetLastError()); }
// Temporary work-around for DUI mirroring bug 259158
SHSetWindowBits(_hWnd, GWL_EXSTYLE, WS_EX_LAYOUTRTL, 0);
return S_OK; }
void CDUIView::ManageAnimations(BOOL bExiting) { if (bExiting) { if (_bAnimationsDisabled) { DirectUI::EnableAnimations(); _bAnimationsDisabled = FALSE; } } else { BOOL bAnimate = TRUE; SystemParametersInfo(SPI_GETMENUANIMATION, 0, &bAnimate, 0);
if (bAnimate) { if (_bAnimationsDisabled) { DirectUI::EnableAnimations(); _bAnimationsDisabled = FALSE; } } else { if (!_bAnimationsDisabled) { DirectUI::DisableAnimations(); _bAnimationsDisabled = TRUE; } } } }
HINSTANCE CDUIView::_GetThemeHinst() { if (!_fLoadedTheme) { _fLoadedTheme = TRUE; if (_hinstTheme) { FreeLibrary(_hinstTheme); }
_hinstTheme = SHGetShellStyleHInstance();
if (_hinstScrollbarTheme) { CloseThemeData (_hinstScrollbarTheme); }
_hinstScrollbarTheme = OpenThemeData(_hWnd, L"Scrollbar"); }
return _hinstTheme ? _hinstTheme : HINST_THISDLL; }
// Loads the requested UI file from shell32's resources
//
// iID - UI file id
// pUIFile - receives a pointer to the UI file
HRESULT CDUIView::_LoadUIFileFromResources(HINSTANCE hinst, int iID, char **pUIFile) { HRESULT hr;
HRSRC hFile = FindResource(hinst, MAKEINTRESOURCE(iID), TEXT("UIFILE")); if (hFile) { HGLOBAL hFileHandle = LoadResource(hinst, hFile); if (hFileHandle) { char *pFile = (char *)LockResource(hFileHandle); if (pFile) { DWORD dwSize = SizeofResource(hinst, hFile);
*pUIFile = (char *)LocalAlloc(LPTR, dwSize + 1); if (*pUIFile) { CopyMemory(*pUIFile, pFile, dwSize); hr = S_OK; } else { hr = E_OUTOFMEMORY; } } else { hr = HRESULT_FROM_WIN32(GetLastError()); } } else { hr = HRESULT_FROM_WIN32(GetLastError()); } } else { hr = HRESULT_FROM_WIN32(GetLastError()); } return hr; }
// Builds the UI file for this view from the
// appropriate base template + style sheet
//
// pUIFile receives a pointer to the ui file in memory
// piCharCount receives the size of the file
HRESULT CDUIView::_BuildUIFile(char **pUIFile, int *piCharCount) { // Load the base UI file
char * pBase; HRESULT hr = _LoadUIFileFromResources(HINST_THISDLL, IDR_DUI_FOLDER, &pBase); if (SUCCEEDED(hr)) { // Load the style sheet. First, check if the current theme has a style sheet,
// if not, use the default style sheet in the resources.
char *pStyle; hr = _LoadUIFileFromResources(_GetThemeHinst(), IDR_DUI_STYLESHEET, &pStyle); if (SUCCEEDED(hr)) { char *pResult = (char *)LocalAlloc(LPTR, lstrlenA(pBase) + lstrlenA(pStyle) + 1); if (pResult) { // Put the files together
lstrcpyA(pResult, pStyle); lstrcatA(pResult, pBase);
// Store the final results
*pUIFile = pResult; *piCharCount = lstrlenA(pResult); } else { hr = E_OUTOFMEMORY; } LocalFree(pStyle); } LocalFree(pBase); } return hr; }
// Callback function used by the ui file parser
//
// pszError - Error text
// pszToken - Token text
// iLine - Line number
void CALLBACK UIFileParseError(LPCWSTR pszError, LPCWSTR pszToken, int iLine) { TraceMsg (TF_ERROR, "UIFileParseError: %s '%s' at line %d", pszError, pszToken, iLine); }
// Builds a section which holds tasks
//
// peSectionList - parent of the section
// bMain - Main section or normal section
// pTitleUI - interface describing the title, may be NULL if pTitleDesc provided
// pBitmapDesc - Description of the bitmap
// pWatermarkDesc - Description of the watermark
// pvSectionSheet - Style sheet to be used
// pParser - Parser instance pointer
// fExpanded - Expanded or closed
// ppeExpando - [out,optional] Receives the section just created
// pTaskList - [out] Receives the task list area element pointer within the pExpando
HRESULT CDUIView::_BuildSection(Element* peSectionList, BOOL bMain, IUIElement* pTitleUI, int idBitmapDesc, int idWatermarkDesc, Value* pvSectionSheet, Parser* pParser, DUISEC eDUISecID, Expando** ppeExpando, Element ** pTaskList) { Expando* peSection = NULL; Value* pv = NULL; Element* pe = NULL; HBITMAP hBitmap;
// Create a section using the definition in the ui file
HRESULT hr = pParser->CreateElement (bMain ? L"mainsection" : L"section", NULL, &pe);
if (FAILED(hr)) { TraceMsg (TF_ERROR, "CDUIView::_BuildSection: CreateElement failed with 0x%x", hr); return hr; }
ASSERTMSG(pe->GetClassInfo() == Expando::Class, "CDUIView::_BuildSection: didn't get an Expando::Class object (%s)", pe->GetClassInfo()->GetName()); peSection = (Expando*)pe;
pe->SetWidth(ScaleSizeBasedUponLocalization(pe->GetWidth()));
peSection->Initialize(eDUISecID, pTitleUI, this, _pDefView); if (ppeExpando) *ppeExpando = peSection;
// Add the section to the list
peSectionList->Add (peSection);
// Set the title
peSection->UpdateTitleUI(NULL); // nothing is selected when the folder starts up
// Set the bitmap on the left side
if (idBitmapDesc) { pe = peSection->FindDescendent (Expando::idIcon);
if (pe) { hBitmap = DUILoadBitmap(_GetThemeHinst(), idBitmapDesc, LR_CREATEDIBSECTION); if (hBitmap) { pv = Value::CreateGraphic(hBitmap, GRAPHIC_AlphaConstPerPix);
if (pv) { pe->SetValue (Element::ContentProp, PI_Local, pv); pv->Release (); } else { DeleteObject(hBitmap);
TraceMsg (TF_ERROR, "CDUIView::_BuildSection: CreateGraphic for the bitmap failed."); } } else { TraceMsg (TF_ERROR, "CDUIView::_BuildSection: DUILoadBitmap failed."); } } else { TraceMsg (TF_ERROR, "CDUIView::_BuildSection: FindDescendent for the bitmap failed."); } }
if (idWatermarkDesc) { HINSTANCE hinstTheme = _GetThemeHinst(); pe = peSection->FindDescendent (Expando::idWatermark); if (pe) { // Note: in Classic mode, we don't want the watermarks, so this function
// will return NULL
hBitmap = DUILoadBitmap(hinstTheme, idWatermarkDesc, LR_CREATEDIBSECTION); if (hBitmap) { pv = Value::CreateGraphic(hBitmap, GRAPHIC_NoBlend); if (pv) { pe->SetValue (Element::ContentProp, PI_Local, pv); pv->Release (); } else { DeleteObject(hBitmap);
TraceMsg (TF_ERROR, "CDUIView::_BuildSection: CreateGraphic for the watermark failed."); } } } else { TraceMsg (TF_ERROR, "CDUIView::_BuildSection: FindDescendent for the watermark failed."); } }
// Set the style sheet if specified
if (pvSectionSheet) { peSection->SetValue (Element::SheetProp, PI_Local, pvSectionSheet); }
// Set the expanded state. By default, it is expanded.
if (!_ShowSectionExpanded(eDUISecID)) { peSection->SetSelected(FALSE); }
// Add padding for the icon if appropriate. Note, this has to happen
// after the style sheet is applied
if (idBitmapDesc) { Element* pe = peSection->FindDescendent(StrToID(L"header"));
if (pe) { Value* pvValue; const RECT * prect;
prect = pe->GetPadding (&pvValue);
if (prect) { pe->SetPadding ((prect->left + 20), prect->top, prect->right, prect->bottom); pvValue->Release(); } } }
// Return the task list element pointer
*pTaskList = peSection->FindDescendent (Expando::idTaskList);
if (*pTaskList) { hr = S_OK; } else { TraceMsg (TF_ERROR, "CDUIView::_BuildSection: Failed to find task list element"); hr = E_FAIL; }
return hr; }
// Adds the action tasks to the task list
//
// peTaskList - Parent element
// penum - enumeration interface
// pvTaskSheet - Style sheet
HRESULT CDUIView::_AddActionTasks(Expando* peExpando, Element* peTaskList, IEnumUICommand* penum, Value* pvTaskSheet, BOOL bIntroAdded) { IUICommand* puiCommand; BOOL fShow = bIntroAdded;
while (S_OK==penum->Next(1, &puiCommand, NULL)) { UISTATE uis; HRESULT hr = puiCommand->get_State(_pshlItems, FALSE, &uis); // Don't do it if it's going to take long, instead, returns E_PENDING
if (SUCCEEDED(hr) && (uis==UIS_ENABLED)) { Element *pe; HRESULT hr = ActionTask::Create(0, puiCommand, _pshlItems, this, _pDefView, &pe); if (SUCCEEDED(hr)) { if (pvTaskSheet) { pe->SetValue(Element::SheetProp, PI_Local, pvTaskSheet); }
peTaskList->Add(pe); fShow = TRUE; } } else if (hr == E_PENDING) { IRunnableTask *pTask; if (SUCCEEDED(CGetCommandStateTask_Create(_pDefView, puiCommand, _pshlItems, &pTask))) { _pDefView->_AddTask(pTask, TOID_DVGetCommandState, 0, TASK_PRIORITY_GETSTATE, ADDTASK_ATEND); pTask->Release(); } }
puiCommand->Release(); } penum->Reset();
peExpando->ShowExpando(fShow);
return S_OK; }
// Adds the destination tasks to the task list
//
// peTaskList - Parent element
// penum - enumerator of pidls to display
// pvTaskSheet - Style sheet
HRESULT CDUIView::_AddDestinationTasks(Element* peTaskList, IEnumIDList* penum, Value* pvTaskSheet) { HRESULT hr = S_OK; LPITEMIDLIST pidl;
while (S_OK==penum->Next(1, &pidl, NULL)) { Element *pe; hr = DestinationTask::Create (0, pidl, this, _pDefView, &pe); if (SUCCEEDED(hr)) { if (pvTaskSheet) { pe->SetValue(Element::SheetProp, PI_Local, pvTaskSheet); }
peTaskList->Add(pe); } ILFree(pidl); }
penum->Reset();
return hr; }
//
// Purpose: Adds the DetailsSectionInfo
//
HRESULT CDUIView::_AddDetailsSectionInfo() { IShellItemArray *psiShellItems = _pshlItems;
if (!psiShellItems && _pDefView) { psiShellItems = _pDefView->_GetFolderAsShellItemArray(); } //TODO: background thread!
Element* pElement; HRESULT hr = CNameSpaceItemInfoList::Create(this, _pvDetailsSheet,psiShellItems, &pElement); if (pElement) { hr = _peDetailsInfoArea->Add(pElement); } return hr; }
// Navigates to the destination pidl
//
// pidl - destination
HRESULT CDUIView::NavigateToDestination(LPCITEMIDLIST pidl) { LPITEMIDLIST pidlClone = ILClone(pidl);
if (pidlClone) { UINT wFlags = (SBSP_DEFBROWSER | SBSP_ABSOLUTE);
// mimic "new window" behavior
if (0 > GetKeyState(VK_SHIFT)) { wFlags |= SBSP_NEWBROWSER; }
PostMessage(_hWnd, WM_NAVIGATETOPIDL, (WPARAM)wFlags, (LPARAM)pidlClone); } return S_OK; }
// Sends a delay navigation command to the view window. This delay allows
// double-clicks to be interpretted as a single click. This prevents
// double navigations usually causing the user to end up with two "things"
// instead of just one.
//
// Also by doing this, the issue of the 2nd click causing the old window to
// get activation is handled. The user is expecting the new window to pop
// up in front of the old window. But, because the user double-clicked,
// the old window would get reactivated and the new window would end up
// behind the current window. See WM_USER_DELAY_NAVIGATION in HWNDView (below)
// for more details.
//
// psiItemArray - the shell item to navigate. Can be NULL.
// puiCommand - the command object to send the navigation to.
HRESULT CDUIView::DelayedNavigation(IShellItemArray *psiItemArray, IUICommand *puiCommand) { SendMessage(_phe->GetHWND(), WM_USER_DELAY_NAVIGATION, (WPARAM) psiItemArray, (LPARAM) puiCommand); return S_OK; }
// Builds the task list area
//
// pParser - Parsing instance
HRESULT CDUIView::_BuildTaskList(Parser* pParser) { HRESULT hr = S_OK;
// Locate section list element
Element* peSectionList = _phe->FindDescendent (StrToID(L"sectionlist"));
if (!peSectionList) { TraceMsg (TF_ERROR, "CDUIView::_BuildTaskList: Failed to find section list element"); return E_FAIL; }
if (SFVMWVF_ENUMTASKS & _pDefView->_wvContent.dwFlags) { if (_bInitialized) { //
// The 'non-standard' task list is the type who's contents
// are dynamically enumerated by the folder view.
// In the case of Control Panel, items in this content appear
// conditionally based upon many factors, one being the categorization
// of applets. In order for the content to be correct, categorization
// must be correct which means that all folder items are known.
// To avoid multiple repaints of the task lists, we defer creation
// of the task lists until AFTER the initial creation of the view.
// Once all folder items have been enumerated, the webview content
// is refreshed in response to a 'contents changed' notification from
// defview. It is during this update that we pass through this code
// section and build the task list.
//
_ClearNonStdTaskSections(); hr = _GetNonStdTaskSectionsFromViewCB(); if (SUCCEEDED(hr) && NULL != _hdsaNonStdTaskSections) { hr = _BuildNonStandardTaskList(pParser, peSectionList, _hdsaNonStdTaskSections); } } } else { hr = _BuildStandardTaskList(pParser, peSectionList); } return THR(hr); }
//
// Builds the task list by requesting task section information
// from the view callback using an enumeration mechanism.
//
//
// ISSUE-2001/01/03-BrianAu Review
//
// Review this with MikeSh and EricFlo.
// I think we should build this generic mechanism then implement the
// 'standard' webview code in terms of this generic mechanism.
// Would be best to replace the SFVM_ENUMWEBVIEWTASKS callback message
// with a message that receives a COM enumerator.
//
// I like the idea. We replace SFVMWVF_SPECIALTASK with an LPCSTR to the theme identifier.
// We can put a SFVM_GETWEBVIEWTASKS to SFVM_ENUMWEBVIEWTASKS layer in for us too.
//
HRESULT CDUIView::_BuildNonStandardTaskList(Parser *pParser, Element *peSectionList, HDSA hdsaSections) { Value* pvMainSectionSheet = NULL; Value *pvMainTaskSheet = NULL; Value* pvStdSectionSheet = NULL; Value* pvStdTaskSheet = NULL;
HRESULT hr = S_OK;
ASSERT(NULL != hdsaSections); ASSERT(NULL != pParser); ASSERT(NULL != peSectionList);
const int cSections = DSA_GetItemCount(hdsaSections); for (int i = 0; i < cSections; i++) { SFVM_WEBVIEW_ENUMTASKSECTION_DATA *pSection = (SFVM_WEBVIEW_ENUMTASKSECTION_DATA *)DSA_GetItemPtr(hdsaSections, i); ASSERT(NULL != pSection);
const BOOL bMainSection = (0 != (SFVMWVF_SPECIALTASK & pSection->dwFlags));
Value *pvSectionSheet = NULL; Value *pvTaskSheet = NULL; DUISEC eDUISecID;
if (bMainSection) { if (NULL == pvMainSectionSheet) { pvMainSectionSheet = pParser->GetSheet(L"mainsectionss"); } if (NULL == pvMainTaskSheet) { pvMainTaskSheet = pParser->GetSheet(L"mainsectiontaskss"); }
pvSectionSheet = pvMainSectionSheet; pvTaskSheet = pvMainTaskSheet; eDUISecID = DUISEC_SPECIALTASKS; } else { if (NULL == pvStdSectionSheet) { pvStdSectionSheet = pParser->GetSheet(L"sectionss"); } if (NULL == pvStdTaskSheet) { pvStdTaskSheet = pParser->GetSheet(L"sectiontaskss"); }
pvSectionSheet = pvStdSectionSheet; pvTaskSheet = pvStdTaskSheet; eDUISecID = DUISEC_FILETASKS; }
ASSERT(NULL != pvSectionSheet); Expando *peSection; Element *peTaskList; hr = _BuildSection(peSectionList, bMainSection, pSection->pHeader, pSection->idBitmap, pSection->idWatermark, pvSectionSheet, pParser, eDUISecID, &peSection, &peTaskList); if (SUCCEEDED(hr)) { hr = _AddActionTasks(peSection, peTaskList, pSection->penumTasks, pvTaskSheet, FALSE); } }
if (pvMainSectionSheet) { pvMainSectionSheet->Release(); } if (pvMainTaskSheet) { pvMainTaskSheet->Release(); } if (pvStdSectionSheet) { pvStdSectionSheet->Release(); } if (pvStdTaskSheet) { pvStdTaskSheet->Release(); }
return THR(hr); }
HRESULT CDUIView::_GetIntroTextElement(Element** ppeIntroText) { if (SHRegGetBoolUSValue(REGSTR_PATH_EXPLORER, TEXT("ShowWebViewIntroText"), FALSE, FALSE)) { if (!_bstrIntroText) { WCHAR wszIntroText[INFOTIPSIZE]; if (_bBarrierShown) { LoadString(HINST_THISDLL, IDS_INTRO_BARRICADED, wszIntroText, ARRAYSIZE(wszIntroText)); } else if (!_pDefView->_pshf2Parent || FAILED(GetStringProperty(_pDefView->_pshf2Parent, _pDefView->_pidlRelative, &SCID_FolderIntroText, wszIntroText, ARRAYSIZE(wszIntroText)))) { wszIntroText[0] = L'\0'; }
_bstrIntroText = SysAllocString(wszIntroText); } } HRESULT hr = E_FAIL; if (_bstrIntroText && _bstrIntroText[0]) { hr = CNameSpaceItemInfo::Create(_bstrIntroText, ppeIntroText); if (SUCCEEDED(hr)) { if (_pvDetailsSheet) { (*ppeIntroText)->SetValue(Element::SheetProp, PI_Local, _pvDetailsSheet); } } } return hr; }
HRESULT CDUIView::_BuildStandardTaskList(Parser *pParser, Element *peSectionList) { Element* peTaskList; Value* pvSectionSheet = NULL; Value* pvTaskSheet = NULL; Value* pvDetailsSheet = NULL;
HRESULT hr = S_OK;
Element* peIntroText; if (FAILED(_GetIntroTextElement(&peIntroText))) { peIntroText = NULL; }
//
// Special Tasks section is optional (main section)
//
if (_pDefView->_wvContent.pSpecialTaskHeader) { pvSectionSheet = pParser->GetSheet(L"mainsectionss");
int idBitmap = 0; int idWatermark = 0; const WVTHEME* pThemeInfo = GetThemeInfo(); if (pThemeInfo) { idBitmap = pThemeInfo->idSpecialSectionIcon; idWatermark = pThemeInfo->idSpecialSectionWatermark; }
// TODO: get special section open/closed state from the per-user-per-pidl property bag
hr = _BuildSection( peSectionList, TRUE, _pDefView->_wvContent.pSpecialTaskHeader, idBitmap, idWatermark, pvSectionSheet, pParser, DUISEC_SPECIALTASKS, &_peSpecialSection, &peTaskList);
if (SUCCEEDED(hr)) { BOOL bIntroTextAdded = FALSE;
_peSpecialTaskList = peTaskList;
// Add the tasks + style sheet
_pvSpecialTaskSheet = pParser->GetSheet(L"mainsectiontaskss");
if (peIntroText) { if (SUCCEEDED(_peSpecialTaskList->Add(peIntroText))) { bIntroTextAdded = TRUE; peIntroText = NULL; } } _AddActionTasks(_peSpecialSection, _peSpecialTaskList, _pDefView->_wvTasks.penumSpecialTasks, _pvSpecialTaskSheet, bIntroTextAdded); }
if (pvSectionSheet) pvSectionSheet->Release(); }
// Get the style sheets for remaining standard sections
pvSectionSheet = pParser->GetSheet (L"sectionss"); pvTaskSheet = pParser->GetSheet (L"sectiontaskss");
// File tasks section (standard section) Not shown if the barricade is shown.
if (!_bBarrierShown) { if (_pDefView->_wvContent.pFolderTaskHeader) { // TODO: get folder section open/closed state from the per-user-per-pidl property bag
hr = _BuildSection( peSectionList, FALSE, _pDefView->_wvContent.pFolderTaskHeader, 0, 0, pvSectionSheet, pParser, DUISEC_FILETASKS, &_peFolderSection, &peTaskList); if (SUCCEEDED(hr)) { BOOL bIntroTextAdded = FALSE;
_peFolderTaskList = peTaskList;
_pvFolderTaskSheet = pvTaskSheet; if (_pvFolderTaskSheet) _pvFolderTaskSheet->AddRef();
if (peIntroText) { if (SUCCEEDED(_peFolderTaskList->Add(peIntroText))) { bIntroTextAdded = TRUE; peIntroText = NULL; } } _AddActionTasks(_peFolderSection, _peFolderTaskList, _pDefView->_wvTasks.penumFolderTasks, _pvFolderTaskSheet, bIntroTextAdded); } } }
// Other places tasks section (standard section)
if (_pDefView->_pOtherPlacesHeader) { // TODO: get OtherPlaces section open/closed state from the per-user-per-pidl property bag
hr = _BuildSection( peSectionList, FALSE, _pDefView->_pOtherPlacesHeader, 0, 0, pvSectionSheet, pParser, DUISEC_OTHERPLACESTASKS, NULL, &peTaskList); if (SUCCEEDED(hr)) { _AddDestinationTasks(peTaskList, _pDefView->_wvContent.penumOtherPlaces, pvTaskSheet); } }
// Details tasks section (standard section)
if (_pDefView->_pDetailsHeader) { // TODO: get Details section open/closed state from the per-user-per-pidl property bag
hr = _BuildSection( peSectionList, FALSE, _pDefView->_pDetailsHeader, 0, 0, pvSectionSheet, pParser, DUISEC_DETAILSTASKS, &_peDetailsSection, &_peDetailsInfoArea); if (SUCCEEDED(hr)) { _AddDetailsSectionInfo(); } }
if (peIntroText) { peIntroText->Destroy(); } if (pvTaskSheet) { pvTaskSheet->Release(); }
if (pvSectionSheet) { pvSectionSheet->Release(); }
return hr; }
BOOL CDUIView::_ShowSectionExpanded(DUISEC eDUISecID) { const struct DUISEC_ATTRIBUTES *pAttrib = _GetSectionAttributes(eDUISecID); BOOL bDefault; BOOL bShow;
if (eDUISecID == DUISEC_DETAILSTASKS) bDefault = ((_pDefView->_wvLayout.dwLayout & SFVMWVL_ORDINAL_MASK) == SFVMWVL_DETAILS); else bDefault = pAttrib->_bExpandedDefault;
if (_ppbShellFolders) bShow = SHPropertyBag_ReadBOOLDefRet(_ppbShellFolders, pAttrib->_pszExpandedPropName, bDefault); else bShow = bDefault;
return bShow; }
const struct DUISEC_ATTRIBUTES *CDUIView::_GetSectionAttributes(DUISEC eDUISecID) { static const size_t nSections = ARRAYSIZE(c_DUISectionAttributes); size_t iSection;
// Determine attributes of DUISEC we're interested in.
for (iSection = 0; iSection < nSections; iSection++) if (c_DUISectionAttributes[iSection]._eDUISecID == eDUISecID) return &c_DUISectionAttributes[iSection];
ASSERT(FALSE); // Game over -- insert quarters!
return NULL; // AV!
}
HRESULT SetDescendentString(Element* pe, LPWSTR pszID, UINT idString) { HRESULT hr; Element* peChild = pe->FindDescendent(StrToID(pszID)); if (peChild) { TCHAR szString [INFOTIPSIZE]; LoadString(HINST_THISDLL, idString, szString, ARRAYSIZE(szString));
hr = peChild->SetContentString(szString); } else { hr = E_FAIL; }
return hr; }
// Parses the .ui file and initializes the DUI elements
//
// pUIFile - Pointer to the UI file in memory
// iCharCount - Number of characters in the ui file
// bDisplayBarrier - Display soft barrier over listview
// punkPreview - IUnknown interface for the preview control
HRESULT CDUIView::_InitializeElements (char * pUIFile, int iCharCount, BOOL bDisplayBarrier, IUnknown * punkPreview) { Parser* pParser; Element* pe; RECT rc; HANDLE arH[2];
// Parse the UI file
arH[0] = _GetThemeHinst(); arH[1] = _hinstScrollbarTheme;
HRESULT hr = Parser::Create(pUIFile, iCharCount, arH, UIFileParseError, &pParser);
if (FAILED(hr)) { TraceMsg (TF_ERROR, "CDUIView::_InitializeElements: Parser::Create failed with 0x%x", hr); return hr; }
if (pParser->WasParseError()) { TraceMsg (TF_ERROR, "CDUIView::_InitializeElements: WasParseError is TRUE"); pParser->Destroy(); return E_FAIL; }
// Create the host element
hr = HWNDView::Create(_hWnd, false, 0, this, _pDefView, (Element**)&_phe); // _phe is owned by _hWnd
if (FAILED(hr)) { TraceMsg (TF_ERROR, "CDUIView::_InitializeElements: HWNDElement::Create failed with 0x%x", hr); pParser->Destroy(); return hr; }
// We need to ensure that the root item will not paint on WM_ERASEBCKGRND, so here we remove the default brush
// - Turn off the (default) background fill
HGADGET hgadRoot = _phe->GetDisplayNode(); ASSERTMSG(hgadRoot != NULL, "Must have a peer Gadget"); SetGadgetFillI(hgadRoot, NULL, BLEND_OPAQUE, 0, 0);
// We need to ensure that the root item will not paint on WM_ERASEBCKGRND, so make it transparent
_phe->SetBackgroundColor(ARGB(0, 0, 0, 0));
// Size the host element to match the size of the host window
GetClientRect (_hWnd, &rc); _phe->SetWidth(rc.right - rc.left); _phe->SetHeight(rc.bottom - rc.top);
// Create the main element in the ui file
hr = pParser->CreateElement(L"main", _phe, &pe);
if (FAILED(hr)) { TraceMsg (TF_ERROR, "CDUIView::_InitializeElements: pParser->CreateElement failed with 0x%x", hr); pParser->Destroy(); return hr; }
// Cache the element pointers to the 3 main areas: taskpane, clientviewhost, blockade
_peTaskPane = _phe->FindDescendent(StrToID(L"scroller")); _peClientViewHost = _phe->FindDescendent(StrToID(L"clientviewhost")); _peBarrier = _phe->FindDescendent(StrToID(L"blockade"));
// Cache style sheets for the items we create directly (that don't inherit from their immediate parents)
_pvDetailsSheet = pParser->GetSheet(L"NameSpaceItemInfoList"); if (_peTaskPane && _peClientViewHost && _peBarrier && _pvDetailsSheet) { // Double buffered items need to be opaque
_peTaskPane->SetBackgroundColor(ARGB(255, 0, 0, 0)); _peTaskPane->DoubleBuffered(true);
// Create the real listview element
hr = DUIListView::Create(AE_MouseAndKeyboard, _pDefView->_hwndListview, (Element **)&_peListView); if (SUCCEEDED(hr)) { _peListView->SetLayoutPos(BLP_Client); _peListView->SetID(L"listview");
hr = _peClientViewHost->Add(_peListView); if (SUCCEEDED(hr)) { _pDefView->_AutoAutoArrange(0); } else { TraceMsg(TF_ERROR, "CDUIView::_InitializeElements: DUIListView::Could not add listview with 0x%x", hr);
_peListView->Destroy(); _peListView = NULL; } } else { TraceMsg(TF_ERROR, "CDUIView::_InitializeElements: Could not create listview element"); } } else { TraceMsg(TF_ERROR, "CDUIView::_InitializeElements: Could not find main element");
hr = E_FAIL; } if (FAILED(hr)) { // we gotta have the listview or you get no webview...
pParser->Destroy(); return hr; }
// Build the preview control if appropriate
_ManagePreview(punkPreview);
_BuildSoftBarrier();
_SwitchToBarrier(bDisplayBarrier);
// Create an interface to the property bag for this class of IShellFolder.
_InitializeShellFolderPropertyBag();
// Build the task list area
hr = _BuildTaskList (pParser);
_fHideTasklist = (S_OK == IUnknown_Exec(_pDefView->_psb, &CGID_ShellDocView, SHDVID_ISEXPLORERBARVISIBLE, 0, NULL, NULL));
pParser->Destroy();
return hr; }
void CDUIView::_InitializeShellFolderPropertyBag() { CLSID clsid; if (SUCCEEDED(IUnknown_GetClassID(_pDefView->_pshf, &clsid))) { WCHAR szSubKey[] = L"DUIBags\\ShellFolders\\{00000000-0000-0000-0000-000000000000}"; if (SHStringFromGUID(clsid, &szSubKey[lstrlen(szSubKey) + 1 - GUIDSTR_MAX], GUIDSTR_MAX) == GUIDSTR_MAX) { HKEY hk = SHGetShellKey(SKPATH_SHELLNOROAM, szSubKey, TRUE); if (hk) { SHCreatePropertyBagOnRegKey(hk, NULL, STGM_READWRITE | STGM_SHARE_DENY_NONE, IID_PPV_ARG(IPropertyBag, &_ppbShellFolders)); RegCloseKey(hk); } } } }
HRESULT CDUIView::_BuildSoftBarrier(void) { HRESULT hr = S_OK; // Build the soft barrier if the view wants one
if (_pDefView->_wvContent.dwFlags & SFVMWVF_BARRICADE) { // Allow the view to give us a barrier implementation
Element* peBarricade = NULL; _pDefView->CallCB(SFVM_GETWEBVIEWBARRICADE, 0, (LPARAM)&peBarricade); if (peBarricade) { Element *pe = _peBarrier->GetParent(); hr = pe->Add(peBarricade); if (SUCCEEDED(hr)) { _peBarrier->Destroy(); _peBarrier = peBarricade; } else { peBarricade->Destroy(); } } else { // Load the bitmap
Element *peClient = _peBarrier->FindDescendent(StrToID(L"blockadeclient")); if (peClient) { HBITMAP hBitmap = DUILoadBitmap(_GetThemeHinst(), IDB_BLOCKADE_WATERMARK, LR_CREATEDIBSECTION);
if (hBitmap) { BITMAP bmp;
if (GetObject (hBitmap, sizeof(bmp), &bmp)) { BYTE dBlendMode = GRAPHIC_TransColor;
if (bmp.bmBitsPixel == 32) { dBlendMode = GRAPHIC_AlphaConstPerPix; }
Value *pVal = Value::CreateGraphic(hBitmap, dBlendMode, 255);
if (pVal) { peClient->SetValue(Element::ContentProp, PI_Local, pVal); pVal->Release(); } } } }
// Give the view the standard barrier
hr = SetDescendentString(_peBarrier, L"blockadetitle", IDS_BLOCKADETITLE); if (SUCCEEDED(hr)) { hr = SetDescendentString(_peBarrier, L"blockademessage", IDS_BLOCKADEMESSAGE);
// "clear barrier" button (failure of "clear barrier" button setup is not fatal)
Element *peButton = _peBarrier->FindDescendent(StrToID(L"blockadeclearbutton")); if (peButton) { Element *peButtonText = peButton->FindDescendent(StrToID(L"blockadecleartext")); if (peButtonText) { WCHAR wsz[INFOTIPSIZE]; if (LoadString(HINST_THISDLL, IDS_TASK_DEFVIEW_VIEWCONTENTS_FOLDER, wsz, ARRAYSIZE(wsz))) { Value *pv = Value::CreateString(wsz, NULL); if (pv) { if (SUCCEEDED(peButtonText->SetValue(Element::ContentProp, PI_Local, pv))) {
peButton->SetAccessible(true); peButton->SetAccName(wsz); peButton->SetAccRole(ROLE_SYSTEM_PUSHBUTTON); if (LoadString(HINST_THISDLL, IDS_LINKWINDOW_DEFAULTACTION, wsz, ARRAYSIZE(wsz))) { peButton->SetAccDefAction(wsz); } } pv->Release(); } } } } } } // Double buffered items need to be opaque
_phe->SetBackgroundColor(ARGB(255, 0, 0, 0)); _phe->DoubleBuffered(true);
// We couldn't create the barrier? don't use it then...
if (FAILED(hr)) { _peBarrier->Destroy(); _peBarrier = NULL; } } return hr; }
// Switches to / from the soft barrier and the listview
HRESULT CDUIView::_SwitchToBarrier (BOOL bDisplayBarrier) { if (bDisplayBarrier && !_peBarrier) bDisplayBarrier = FALSE;
Element *peClearButton = _peBarrier ? _peBarrier->FindDescendent(StrToID(L"blockadeclearbutton")) : NULL; if (peClearButton) { // Note:
// This is required to prevent the "clear barrier" button from being
// accessed via our accessibility interface when the barrier is hidden.
peClearButton->SetAccessible(bDisplayBarrier == TRUE); }
if (bDisplayBarrier) { _peClientViewHost->SetVisible(FALSE); _peBarrier->SetVisible(TRUE); } else { if (_peBarrier) { _peBarrier->SetVisible(FALSE); }
_peClientViewHost->SetVisible(TRUE); _pDefView->_AutoAutoArrange(0); }
_bBarrierShown = bDisplayBarrier;
return S_OK; }
// Controls the display of the soft barrier
HRESULT CDUIView::EnableBarrier (BOOL bDisplayBarrier) { if (_bBarrierShown != bDisplayBarrier) { DisableAnimations(); Element::StartDefer ();
_SwitchToBarrier (bDisplayBarrier); PostMessage (_hWnd, WM_REFRESHVIEW, 0, 0);
Element::EndDefer (); EnableAnimations(); }
return S_OK; }
// Creates / destroys the preview control
HRESULT CDUIView::_ManagePreview (IUnknown * punkPreview) { HRESULT hr = S_OK;
if ((_pePreview && punkPreview) || (!_pePreview && !punkPreview)) { return S_OK; }
if (punkPreview) { // Create the DUI element that can host an active x control
hr = DUIAxHost::Create (&_pePreview); if (SUCCEEDED(hr)) { _pePreview->SetLayoutPos (BLP_Top); _pePreview->SetID (L"preview"); _pePreview->SetHeight(_phe->GetHeight()); _pePreview->SetAccessible(TRUE);
// The order of the next 4 calls is very important!
//
// Initialize atl so the window class is registered.
// Then call the Add method. This will cause CreateHWND to be
// called. Then site it so when we call AttachControl to
// put the preview control in (this requires the hwnd to exist already)
// it will be parented properly
AtlAxWinInit();
hr = _peClientViewHost->Add (_pePreview);
if (SUCCEEDED(hr)) { _pePreview->SetSite(SAFECAST(_pDefView, IShellView2*));
hr = _pePreview->AttachControl(punkPreview);
if (SUCCEEDED(hr)) { // Double buffered items need to be opaque
_phe->SetBackgroundColor(ARGB(255, 0, 0, 0)); _phe->DoubleBuffered(true);
if (_peListView) { // Since the preview control is displayed, the listview
// will be sized to 1 row in height. Determine the height
// of the listview now so we can size the preview control
// appropriate plus take care of the sizing in the SetSize
// method later
DWORD dwItemSpace = ListView_GetItemSpacing (_peListView->GetHWND(), FALSE); _iListViewHeight = (int)HIWORD(dwItemSpace) + GetSystemMetrics (SM_CYHSCROLL) + 4;
if (_phe->GetHeight() > _iListViewHeight) { _pePreview->SetHeight(_phe->GetHeight() - _iListViewHeight); } else { _pePreview->SetHeight(0); } } } }
if (FAILED(hr)) { _pePreview->Destroy(); _pePreview = NULL; } } else { TraceMsg (TF_ERROR, "CDUIView::_ManagePreview: DUIAxHost::Create failed with 0x%x", hr); } } else { _pePreview->Destroy(); _pePreview = NULL; }
return S_OK; }
// Controls the display of the preview control
HRESULT CDUIView::EnablePreview(IUnknown * punkPreview) { DisableAnimations(); Element::StartDefer ();
_ManagePreview (punkPreview);
Element::EndDefer (); EnableAnimations();
return S_OK; }
// Refreshes the view
HRESULT CDUIView::Refresh(void) { Element *pe; Parser* pParser = NULL; Value* pvSheet = NULL; HANDLE arH[2];
ManageAnimations(FALSE); DisableAnimations(); Element::StartDefer();
_fLoadedTheme = FALSE; // try to re-load the theme file
_iTaskPaneWidth = ScaleSizeBasedUponLocalization(_iOriginalTaskPaneWidth);
// Setting the task pane visibility to the current state will
// cause it to re-initialize the task pane width appropriately
SetTaskPaneVisibility(!_bHideTaskPaneAlways);
// Dynamically build the .ui file for this view
int iCharCount; char *pUIFile = NULL; HRESULT hr = _BuildUIFile(&pUIFile, &iCharCount); if (FAILED(hr)) { TraceMsg (TF_ERROR, "CDUIView::Refresh: _BuildUIFile failed with 0x%x", hr); goto Exit; }
// Parse the UI file
arH[0] = _GetThemeHinst(); arH[1] = _hinstScrollbarTheme;
hr = Parser::Create(pUIFile, iCharCount, arH, UIFileParseError, &pParser);
if (FAILED(hr)) { TraceMsg (TF_ERROR, "CDUIView::Refresh: Parser::Create failed with 0x%x", hr); goto Exit; }
if (pParser->WasParseError()) { TraceMsg (TF_ERROR, "CDUIView::Refresh: WasParseError is TRUE"); hr = E_FAIL; goto Exit; }
// Find the section list element
pe = _phe->FindDescendent (StrToID(L"sectionlist"));
if (!pe) { TraceMsg (TF_ERROR, "CDUIView::Refresh: Failed to find section list element"); hr = E_FAIL; goto Exit; }
// Free all the pointers we have to elements inside of the sectionlist
ATOMICRELEASE(_pshlItems); ATOMICRELEASE(_pvSpecialTaskSheet); ATOMICRELEASE(_pvFolderTaskSheet); ATOMICRELEASE(_peDetailsInfoArea); ATOMICRELEASE(_pvDetailsSheet);
_peSpecialSection = NULL; _peSpecialTaskList = NULL; _peFolderSection = NULL; _peFolderTaskList = NULL; _peDetailsSection = NULL;
// Destroy the section list
pe->DestroyAll();
// Take the style sheets from the new .UI file and put them on the running objects...
//
pe = _phe->FindDescendent (StrToID(L"main")); if (pe) { // Query for the main style sheet and set it
pvSheet = pParser->GetSheet (L"main"); if (pvSheet) { pe->SetValue(Element::SheetProp, PI_Local, pvSheet); pvSheet->Release(); pvSheet = NULL; } }
pe = _phe->FindDescendent (StrToID(L"scroller")); if (pe) { // Query for the taskpane style sheet and set it
pvSheet = pParser->GetSheet (L"taskpane"); if (pvSheet) { pe->SetValue(Element::SheetProp, PI_Local, pvSheet); pvSheet->Release(); pvSheet = NULL; } }
_pvDetailsSheet = pParser->GetSheet(L"NameSpaceItemInfoList");
// Rebuild the soft barrier if one exists.
_BuildSoftBarrier(); // Build the task list area again
_BuildTaskList (pParser);
Exit:
Element::EndDefer(); EnableAnimations();
// When turning off the barricade the icons in listview
// are arranged as if duiview isn't present. Call _AutoAutoArrange
// to reposition the icons correctly.
_pDefView->_AutoAutoArrange(0);
if (pParser) { pParser->Destroy(); } if (pUIFile) { LocalFree(pUIFile); }
return hr; }
// Resizes the host element when the frame size changes
//
// rc - size of frame
//
HRESULT CDUIView::SetSize(RECT * rc) { _fHideTasklist = (S_OK == IUnknown_Exec(_pDefView->_psb, &CGID_ShellDocView, SHDVID_ISEXPLORERBARVISIBLE, 0, NULL, NULL));
SetWindowPos(_hWnd, NULL, rc->left, rc->top, rc->right - rc->left, rc->bottom - rc->top, SWP_NOZORDER | SWP_NOACTIVATE);
return S_OK; }
HRESULT CDUIView::_OnResize(long lWidth, long lHeight) { DisableAnimations(); Element::StartDefer();
_phe->SetWidth(lWidth); _phe->SetHeight(lHeight);
if (_pePreview) { if (_phe->GetHeight() > _iListViewHeight) { _pePreview->SetHeight(_phe->GetHeight() - _iListViewHeight); } else { _pePreview->SetHeight(0); } }
// Hide task pane if task area is greater than 50% of the window size
// The show/hide state of the tasklist pane can change for:
// 1) we're told to always hide
// 2) an explorer bar is showing
// 3) the window is too narrow.
//
if (_peTaskPane) { if (_bHideTaskPaneAlways || _fHideTasklist || ((lWidth / 2) < _iTaskPaneWidth)) { _peTaskPane->SetWidth(0); } else if (_peTaskPane->GetWidth() == 0) { _peTaskPane->SetWidth(_iTaskPaneWidth); } }
Element::EndDefer(); EnableAnimations();
return S_OK; }
HRESULT CDUIView::SetTaskPaneVisibility(BOOL bShow) { _bHideTaskPaneAlways = !bShow; return _OnResize(_phe->GetWidth(), _phe->GetHeight()); }
// Description:
// Calculates the bounding rectangle of the infotip hotspot for
// the specified element. The bounding rectangle's coordinates
// are relative to the specified element's root element.
//
void CDUIView::CalculateInfotipRect(Element *pe, RECT *pRect) { ASSERT(pe); ASSERT(pRect);
// Calculate location.
const POINT ptLocation = { 0, 0 }; POINT ptLocationRelativeToRoot; pe->GetRoot()->MapElementPoint(pe, &ptLocation, &ptLocationRelativeToRoot); pRect->left = ptLocationRelativeToRoot.x; pRect->top = ptLocationRelativeToRoot.y;
// Calculate size.
Value *pvExtent; const SIZE *psizeExtent = pe->GetExtent(&pvExtent); pRect->right = pRect->left + psizeExtent->cx; pRect->bottom = pRect->top + psizeExtent->cy; pvExtent->Release();
// Sanity check.
ASSERT(pRect->right > pRect->left); ASSERT(pRect->bottom > pRect->top); }
HRESULT CDUIView::InitializeThumbnail(WNDPROC pfnWndProc) { HRESULT hr = E_FAIL; if (!_spThumbnailExtractor2) { if (SUCCEEDED(CoCreateInstance(CLSID_Thumbnail, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IThumbnail2, &_spThumbnailExtractor2)))) { _hwndMsgThumbExtract = SHCreateWorkerWindowW(pfnWndProc, NULL, 0, WS_POPUP, NULL, this); if (_hwndMsgThumbExtract) { // Set defview as the site for the thumbnail extractor so that
// it can QueryService defview for IShellTaskScheduler
IUnknown_SetSite(_spThumbnailExtractor2, SAFECAST(_pDefView, IShellView2*)); // Tell the image extractor to post WM_HTML_BITMAP to _hwndMsgThumbExtract
// The lParam will be the HBITMAP of the extracted image.
_spThumbnailExtractor2->Init(_hwndMsgThumbExtract, WM_HTML_BITMAP); } } } return (_spThumbnailExtractor2 && _hwndMsgThumbExtract) ? S_OK : E_FAIL; }
// if pCheck != NULL, check if the current window ptr == pCheck before setting it to p
HRESULT CDUIView::SetThumbnailMsgWindowPtr(void* p, void* pCheck) { if (_hwndMsgThumbExtract) { if (pCheck) { void* pCurrent = GetWindowPtr(_hwndMsgThumbExtract, 0); if (pCurrent == pCheck) { SetWindowPtr(_hwndMsgThumbExtract, 0, p); } } else { SetWindowPtr(_hwndMsgThumbExtract, 0, p); } } return S_OK; }
HRESULT CDUIView::StartBitmapExtraction(LPCITEMIDLIST pidl) { _dwThumbnailID++; // We are looking for a new thumbnail
return _spThumbnailExtractor2 ? _spThumbnailExtractor2->GetBitmapFromIDList(pidl, _dwThumbnailID, 150, 100) : E_FAIL; }
HRESULT CDUIView::InitializeDetailsInfo(WNDPROC pfnWndProc) { if (!_hwndMsgInfoExtract) { _hwndMsgInfoExtract = SHCreateWorkerWindowW(pfnWndProc, NULL, 0, WS_POPUP, NULL, this); } return _hwndMsgInfoExtract ? S_OK : E_FAIL; }
// if pCheck != NULL, check if the current window ptr == pCheck before setting it to p
HRESULT CDUIView::SetDetailsInfoMsgWindowPtr(void* p, void* pCheck) { if (_hwndMsgInfoExtract) { if (pCheck) { void* pCurrent = GetWindowPtr(_hwndMsgInfoExtract, 0); if (pCurrent == pCheck) { SetWindowPtr(_hwndMsgInfoExtract, 0, p); } } else { SetWindowPtr(_hwndMsgInfoExtract, 0, p); } } return S_OK; }
HRESULT CDUIView::StartInfoExtraction(LPCITEMIDLIST pidl) { _dwDetailsInfoID++; // We are looking for a new Details section info
CDetailsSectionInfoTask *pTask; HRESULT hr = CDetailsSectionInfoTask_CreateInstance( _pDefView->_pshf, pidl, _hwndMsgInfoExtract, WM_DETAILS_INFO, _dwDetailsInfoID, &pTask); if (SUCCEEDED(hr)) { if (_pDefView->_pScheduler) { // Make sure there are no other background DetailsSectionInfo
// extraction going on...
_pDefView->_pScheduler->RemoveTasks(TOID_DVBackgroundDetailsSectionInfo, ITSAT_DEFAULT_LPARAM, FALSE); }
hr = _pDefView->_AddTask(pTask, TOID_DVBackgroundDetailsSectionInfo, 0, TASK_PRIORITY_INFOTIP, ADDTASK_ATEND); pTask->Release(); } return hr; }
VOID CDUIView::ShowDetails (BOOL fShow) { if (_peDetailsSection) { _peDetailsSection->ShowExpando (fShow); } }
BOOL CDUIView::ShouldShowMiniPreview() { return !_pDefView->_IsImageMode(); }
// Window procedure for host window
LRESULT CALLBACK CDUIView::_DUIHostWndProc(HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam) { CDUIView *pThis = (CDUIView*)GetWindowLongPtr(hWnd, GWLP_USERDATA);
switch (uMessage) { case WM_NCCREATE: { LPCREATESTRUCT lpcs = (LPCREATESTRUCT)lParam; pThis = (CDUIView*)(lpcs->lpCreateParams); SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR)pThis); } break;
case WM_SIZE: if (pThis && pThis->_phe) { pThis->_OnResize(LOWORD(lParam), HIWORD(lParam)); } break;
case WM_SETFOCUS: // Push focus to HWNDElement (won't set gadget focus to the HWNDElement, but
// will push focus to the previous gadget with focus)
if (pThis) {
if (pThis->_phe && pThis->_phe->GetHWND()) SetFocus(pThis->_phe->GetHWND()); } break;
case WM_PALETTECHANGED: case WM_QUERYNEWPALETTE: case WM_DISPLAYCHANGE: if (pThis && pThis->_phe) { return SendMessageW(pThis->_phe->GetHWND(), uMessage, wParam, lParam); } break;
case WM_DESTROY: // clear posted messages
MSG msg;
while (PeekMessage(&msg, hWnd, WM_NAVIGATETOPIDL, WM_NAVIGATETOPIDL, PM_REMOVE)) { // PeekMessage(hwnd) can return messages posted to CHILDREN of this hwnd...
// Verify that the message was really for us.
if (msg.hwnd == hWnd) { LPITEMIDLIST pidl = (LPITEMIDLIST)msg.lParam; ILFree(pidl); } } break;
case WM_NAVIGATETOPIDL: { LPITEMIDLIST pidl = (LPITEMIDLIST)lParam; UINT wFlags = (UINT)wParam;
pThis->_pDefView->_psb->BrowseObject(pidl, wFlags);
ILFree(pidl); } break;
case WM_REFRESHVIEW: { pThis->Refresh(); } break;
case WM_MOUSEACTIVATE: if (pThis->_bBarrierShown) { return MA_ACTIVATE; } break;
default: break; }
return DefWindowProc(hWnd, uMessage, wParam, lParam); }
// Updates all selection-parameterized UI
//
// pdo - data object representing the selection
void CDUIView::_Refresh(IShellItemArray *psiItemArray, DWORD dwRefreshFlags) { //DirectUI::DisableAnimations();
Element::StartDefer();
IUnknown_Set((IUnknown **)&_pshlItems,psiItemArray);
if (SFVMWVF_ENUMTASKS & _pDefView->_wvContent.dwFlags) { if (0 == (REFRESH_SELCHG & dwRefreshFlags)) { //
// Only refresh if it's not a selection change.
// If we refresh here, Control Panel's left-pane menus
// are constantly rebuilt as the folder items selection
// changes. That's really ugly.
// Will this affect other folders? No, Control Panel
// is currently the only folder that sets this SFVMWVF_ENUMTASKS
// flag. Post WinXP if we decide to keep this webview
// content in the left pane, we need to re-think how better
// to handle Control Panel's special needs.
//
Refresh(); } } else { if (REFRESH_CONTENT & dwRefreshFlags) { _BuildSoftBarrier(); }
if (REFRESH_TASKS & dwRefreshFlags) { Element* peIntroText; if (FAILED(_GetIntroTextElement(&peIntroText))) { peIntroText = NULL; } if (_peSpecialSection) { BOOL bIntroTextAdded = FALSE;
_peSpecialSection->UpdateTitleUI(_pshlItems); _peSpecialTaskList->DestroyAll();
if (peIntroText) { if (SUCCEEDED(_peSpecialTaskList->Add(peIntroText))) { bIntroTextAdded = TRUE; peIntroText = NULL; } } _AddActionTasks(_peSpecialSection, _peSpecialTaskList, _pDefView->_wvTasks.penumSpecialTasks, _pvSpecialTaskSheet, bIntroTextAdded); }
if (_peFolderSection) { BOOL bIntroTextAdded = FALSE;
_peFolderSection->UpdateTitleUI(_pshlItems); _peFolderTaskList->DestroyAll();
if (peIntroText) { if (SUCCEEDED(_peFolderTaskList->Add(peIntroText))) { bIntroTextAdded = TRUE; peIntroText = NULL; } } _AddActionTasks(_peFolderSection, _peFolderTaskList, _pDefView->_wvTasks.penumFolderTasks, _pvFolderTaskSheet, bIntroTextAdded); }
if (_peDetailsInfoArea) { const SIZE *pSize; LONG lHeight = 0; Value * pv;
pSize = _peDetailsInfoArea->GetExtent(&pv);
if (pSize) { _peDetailsInfoArea->SetHeight(pSize->cy); pv->Release(); }
_peDetailsInfoArea->DestroyAll();
_AddDetailsSectionInfo(); } if (peIntroText) { peIntroText->Destroy(); } } } Element::EndDefer(); //DirectUI::EnableAnimations();
}
void CDUIView::OnSelectionChange(IShellItemArray *psiItemArray) { _Refresh(psiItemArray, REFRESH_ALL | REFRESH_SELCHG); }
void CDUIView::OnContentsChange(IShellItemArray *psiItemArray) { DWORD dwRefreshFlags = 0; if (_pDefView->_wvTasks.dwUpdateFlags & SFVMWVTSDF_CONTENTSCHANGE) { dwRefreshFlags |= REFRESH_TASKS; } if (_pDefView->_wvContent.dwFlags & SFVMWVF_CONTENTSCHANGE) { dwRefreshFlags |= REFRESH_CONTENT; } if (0 != dwRefreshFlags) { _Refresh(psiItemArray, dwRefreshFlags); } }
void CDUIView::OnExpandSection(DUISEC eDUISecID, BOOL bExpanded) { if (_ppbShellFolders) { SHPropertyBag_WriteDWORD(_ppbShellFolders, _GetSectionAttributes(eDUISecID)->_pszExpandedPropName, bExpanded); } }
//
// ISSUE-2001/01/02-BrianAu Review
//
// This webview task section code may be reworked soon.
// I created it to address the webview needs of Control Panel.
// Following this first checkin, the webview guys (EricFlo
// and MikeSh) and I will look at consolidating the generic
// needs of Control Panel with the existing webview code.
//
//
// Add a WebView task section to the list of task sections.
//
HRESULT CDUIView::_AddNonStdTaskSection(const SFVM_WEBVIEW_ENUMTASKSECTION_DATA *pData) { ASSERT(NULL != pData); ASSERT(!IsBadReadPtr(pData, sizeof(*pData)));
HRESULT hr = E_OUTOFMEMORY; if (NULL == _hdsaNonStdTaskSections) { _hdsaNonStdTaskSections = DSA_Create(sizeof(*pData), 5); } if (NULL != _hdsaNonStdTaskSections) { if (-1 != DSA_AppendItem(_hdsaNonStdTaskSections, (void *)pData)) { ASSERT(NULL != pData->pHeader); ASSERT(NULL != pData->penumTasks); //
// The list now owns a ref count on the referenced objects.
//
pData->pHeader->AddRef(); pData->penumTasks->AddRef(); hr = S_OK; } } return THR(hr); }
void CDUIView::_ClearNonStdTaskSections(void) { if (NULL != _hdsaNonStdTaskSections) { HDSA hdsa = _hdsaNonStdTaskSections; _hdsaNonStdTaskSections = NULL;
const int cItems = DSA_GetItemCount(hdsa); for (int i = 0; i < cItems; i++) { SFVM_WEBVIEW_ENUMTASKSECTION_DATA *pData = (SFVM_WEBVIEW_ENUMTASKSECTION_DATA *)DSA_GetItemPtr(hdsa, i); if (NULL != pData) { ATOMICRELEASE(pData->pHeader); ATOMICRELEASE(pData->penumTasks); } } DSA_Destroy(hdsa); } }
//
// Enumerate the non-standard webview task sections
// from the view callback.
//
// ISSUE-2001/01/03-BrianAu Review
//
// This SFVM_ENUMWEBVIEWTASKS mechanism may be replaced
// with a COM enumerator. I'll be revisiting this with
// the webview guys soon.
//
HRESULT CDUIView::_GetNonStdTaskSectionsFromViewCB(void) { SFVM_WEBVIEW_ENUMTASKSECTION_DATA data;
HRESULT hr = S_OK; do { //
// Continue requesting task section information from
// the callback until it sets the SFVMWVF_NOMORETASKS
// flag in the data. The record with this flag set
// should not contain any valid data.
//
ZeroMemory(&data, sizeof(data)); hr = _pDefView->CallCB(SFVM_ENUMWEBVIEWTASKS, 0, (LPARAM)&data); if (SUCCEEDED(hr)) { if (0 == (SFVMWVF_NOMORETASKS & data.dwFlags)) { hr = _AddNonStdTaskSection(&data); ASSERT(S_FALSE != hr);
data.pHeader->Release(); data.penumTasks->Release(); } else { ASSERT(NULL == data.pHeader); ASSERT(NULL == data.penumTasks); hr = S_FALSE; } } } while(S_OK == hr);
return THR(hr); }
// Loads a bitmap based upon:
//
// lpBitmapID - contains the bitmap description
// hInstTheme - instance handle of theme dll
HBITMAP DUILoadBitmap(HINSTANCE hInstTheme, int idBitmapID, UINT uiLoadFlags) { return (HBITMAP)LoadImage(hInstTheme, MAKEINTRESOURCE(idBitmapID), IMAGE_BITMAP, 0, 0, uiLoadFlags); }
// Loads an icon based upon the description.
// Example: shell32,-42
//
// pszIconDesc - contains the icon description
// bSmall - small icon vs large icon
HICON DUILoadIcon(LPCWSTR pszIconDesc, BOOL bSmall) { HICON hIcon = NULL; TCHAR szFile[MAX_PATH];
StrCpyN(szFile, pszIconDesc, ARRAYSIZE(szFile)); // the below writes this buffer
int iIconID = PathParseIconLocation(szFile);
if (bSmall) { PrivateExtractIcons(szFile, iIconID, 16, 16, &hIcon, NULL, 1, 0); } else { PrivateExtractIcons(szFile, iIconID, 32, 32, &hIcon, NULL, 1, 0); }
return hIcon; }
BOOL CDUIView::Navigate(BOOL fForward) { if (!_phe) return FALSE;
return _phe->Navigate(fForward); }
HRESULT CDUIView::InitializeDropTarget (LPITEMIDLIST pidl, HWND hWnd, IDropTarget **pdt) { HRESULT hr = E_FAIL;
if (_pDT) { hr = _pDT->Initialize(pidl, hWnd, pdt); }
return hr; }
////////////////////////////////////////////////////////
// HWNDView class
////////////////////////////////////////////////////////
HWNDView::HWNDView(void) : _fFocus(TRUE), _fDelayedNavigation(false), _puiDelayNavCmd(NULL), _psiDelayNavArray(NULL), _pDefView(NULL), _pDUIView(NULL) {
}
HWNDView::~HWNDView(void) { ATOMICRELEASE(_puiDelayNavCmd); ATOMICRELEASE(_psiDelayNavArray); ATOMICRELEASE(_pDefView); ATOMICRELEASE(_pDUIView); }
HRESULT HWNDView::Create(OUT Element** ppElement) { UNREFERENCED_PARAMETER(ppElement); DUIAssertForce("Cannot instantiate an HWND host derived Element via parser. Must use substitution."); return E_NOTIMPL; }
HRESULT HWNDView::Create(HWND hParent, bool fDblBuffer, UINT nCreate, CDUIView * pDUIView, CDefView *pDefView, OUT Element** ppElement) { *ppElement = NULL;
HWNDView* phv = HNewAndZero<HWNDView>(); if (!phv) return E_OUTOFMEMORY;
HRESULT hr = phv->Initialize(hParent, fDblBuffer, nCreate); if (FAILED(hr)) { phv->Destroy(); return hr; }
phv->SetWrapKeyboardNavigate(false); phv->SetAccessible(true); phv->SetAccRole(ROLE_SYSTEM_PANE); phv->SetAccName(L"WebView Pane"); phv->SetViewPtrs(pDUIView, pDefView); *ppElement = phv;
return S_OK; }
void HWNDView::SetViewPtrs (CDUIView * pDUIView, CDefView *pDefView) { pDUIView->AddRef(); _pDUIView = pDUIView; pDefView->AddRef(); _pDefView = pDefView; }
#define DELAYED_NAVIGATION_TIMER_ID 1236 // random - can be moved
LRESULT HWNDView::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch ( uMsg ) { case WM_TIMER: if (wParam == DELAYED_NAVIGATION_TIMER_ID) { KillTimer(hWnd, DELAYED_NAVIGATION_TIMER_ID);
//
// We have encountered some rare scenarios where _puiDelayNavCmd
// can be NULL.
//
if (_puiDelayNavCmd) { HRESULT hr = _puiDelayNavCmd->Invoke(_psiDelayNavArray, NULL); if (FAILED(hr)) { MessageBeep(0); } } ATOMICRELEASE(_puiDelayNavCmd); ATOMICRELEASE(_psiDelayNavArray);
_fDelayedNavigation = false; } break;
case WM_USER_DELAY_NAVIGATION: ATOMICRELEASE(_puiDelayNavCmd); ATOMICRELEASE(_psiDelayNavArray);
_puiDelayNavCmd = (IUICommand *) lParam; _puiDelayNavCmd->AddRef();
_psiDelayNavArray = (IShellItemArray *) wParam; if (NULL != _psiDelayNavArray) { _psiDelayNavArray->AddRef(); }
_fDelayedNavigation = true;
::SetTimer(hWnd, DELAYED_NAVIGATION_TIMER_ID, GetDoubleClickTime(), NULL); break;
case WM_MOUSEACTIVATE: if ( _fDelayedNavigation ) { //
// KB: gpease 05-APR-2001 Fix for WinBug #338552
//
// This prevents the re-activation of the view window after
// the user clicks on a link that launches another application,
// window, or CPL Applet.
//
return MA_NOACTIVATE; } break; // do the default wndproc
case WM_MOUSEMOVE: case WM_LBUTTONDOWN: case WM_LBUTTONUP: case WM_MBUTTONDOWN: case WM_MBUTTONUP: case WM_RBUTTONDOWN: case WM_RBUTTONUP: if (_pDefView) { // Relay relevant messages to CDefView's infotip control so any
// infotip tools created in the DUI view will appear/function.
_pDefView->RelayInfotipMessage(hWnd, uMsg, wParam, lParam); } break; }
return HWNDElement::WndProc(hWnd, uMsg, wParam, lParam); }
BOOL HWNDView::Navigate(BOOL fForward) { KeyboardNavigateEvent kne; kne.uidType = Element::KeyboardNavigate; kne.iNavDir = fForward ? NAV_NEXT : NAV_PREV;
if (_fFocus) // remove this check after SetGadgetFocus(NULL) is fixed.
{ kne.peTarget = GetKeyFocusedElement(); } else { kne.peTarget = NULL; }
if (kne.peTarget) { kne.peTarget->FireEvent(&kne); _fFocus = !kne.peTarget->GetKeyFocused(); // If this is the last element in the duiview focus cycle clear focus so if
// no one else grabs focus and we come back to duiview we'll restart at the
// first element.
//
//
//if (!fFocus)
//{
// SetGadgetFocus(NULL); Doesn't like NULL!!!
//}
} else { bool fWrap; if(!fForward) { fWrap = GetWrapKeyboardNavigate(); SetWrapKeyboardNavigate(true); }
FireEvent(&kne); _fFocus = (GetKeyFocusedElement() != NULL);
if(!fForward) { SetWrapKeyboardNavigate(fWrap); } }
return _fFocus; }
UINT HWNDView::MessageCallback(GMSG* pGMsg) { EventMsg * pmsg = static_cast<EventMsg *>(pGMsg);
switch (GET_EVENT_DEST(pmsg)) { case GMF_DIRECT: case GMF_BUBBLED:
if (pGMsg->nMsg == GM_QUERY) { GMSG_QUERYDROPTARGET * pTemp = (GMSG_QUERYDROPTARGET *)pGMsg;
if (pTemp->nCode == GQUERY_DROPTARGET) { if (SUCCEEDED(_pDUIView->InitializeDropTarget(NULL, NULL, &pTemp->pdt))) { pTemp->hgadDrop = pTemp->hgadMsg; return DU_S_COMPLETE; } } } break; }
return Element::MessageCallback(pGMsg); }
void HWNDView::OnEvent(Event* pev) { if (pev->uidType == Button::Click) { if (pev->peTarget == FindDescendent(StrToID(L"blockadeclearbutton"))) { if (NULL != _pDefView) { _pDefView->RemoveBarricade(); } pev->fHandled = true; } } HWNDElement::OnEvent(pev); }
////////////////////////////////////////////////////////
// ClassInfo (must appear after property definitions)
// Define class info with type and base type, set static class pointer
IClassInfo* HWNDView::Class = NULL; HRESULT HWNDView::Register() { return ClassInfo<HWNDView,HWNDElement>::Register(L"HWNDView", NULL, 0); }
HRESULT InitializeDUIViewClasses(void) { HRESULT hr;
hr = DUIAxHost::Register(); if (FAILED(hr)) goto Failure;
hr = CNameSpaceItemInfoList::Register(); if (FAILED(hr)) goto Failure;
hr = CNameSpaceItemInfo::Register(); if (FAILED(hr)) goto Failure;
hr = CMiniPreviewer::Register(); if (FAILED(hr)) goto Failure;
hr = CBitmapElement::Register(); if (FAILED(hr)) goto Failure;
hr = DUIListView::Register(); if (FAILED(hr)) goto Failure;
hr = Expando::Register(); if (FAILED(hr)) goto Failure;
hr = Clipper::Register(); if (FAILED(hr)) goto Failure;
hr = TaskList::Register(); if (FAILED(hr)) goto Failure;
hr = ActionTask::Register(); if (FAILED(hr)) goto Failure;
hr = DestinationTask::Register(); if (FAILED(hr)) goto Failure;
hr = HWNDView::Register(); if (FAILED(hr)) goto Failure;
return S_OK;
Failure:
return hr; }
|