#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 #include #include 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); // +1 ensures *pUIFile is NULL-terminated if (*pUIFile) { CopyMemory(*pUIFile, pFile, dwSize); hr = S_OK; } else { hr = E_OUTOFMEMORY; } } else { hr = ResultFromLastError(); } } else { hr = ResultFromLastError(); } } else { hr = ResultFromLastError(); } 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)) { size_t cchResult = lstrlenA(pBase) + lstrlenA(pStyle) + 1; char *pResult = (char *)LocalAlloc(LPTR, cchResult * sizeof(char)); if (pResult) { // Put the files together StringCchCopyA(pResult, cchResult, pStyle); // pResult allocated above to be sufficient size StringCchCatA(pResult, cchResult, pBase); // pResult allocated above to be sufficient size // 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 ** ppTaskList) { 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 hr = peSectionList->Add (peSection); if (SUCCEEDED(hr)) { // 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 *ppTaskList = peSection->FindDescendent (Expando::idTaskList); if (*ppTaskList) { hr = S_OK; } else { TraceMsg (TF_ERROR, "CDUIView::_BuildSection: Failed to find task list element"); hr = E_FAIL; } } else { peSection->Destroy(); if (ppeExpando) *ppeExpando = NULL; *ppTaskList = NULL; } 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); } if (SUCCEEDED(peTaskList->Add(pe))) { fShow = TRUE; } else { pe->Destroy(); } } } 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); } if (FAILED(peTaskList->Add(pe))) { pe->Destroy(); } } 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); if (FAILED(hr)) { pElement->Destroy(); } } 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; } if (!PostMessage(_hWnd, WM_NAVIGATETOPIDL, (WPARAM)wFlags, (LPARAM)pidlClone)) { ILFree(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); 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]; if (SUCCEEDED(StringCchCopy(szFile, ARRAYSIZE(szFile), pszIconDesc))) // 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(); 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(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::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; }