#include "priv.h" #include #include "favorite.h" #include "iehelpid.h" #include "webcheck.h" #include "chanmgr.h" #include "chanmgrp.h" #include "resource.h" #include #include #include #include UINT IE_ErrorMsgBox(IShellBrowser* psb, HWND hwndOwner, HRESULT hrError, LPCWSTR szError, LPCTSTR pszURLparam, UINT idResource, UINT wFlags); void ReplaceTransplacedControls (HWND hDlgMaster, HWND hDlgTemplate); /////////////////////////////////////////////////////////////////////// // helper function for DoOrganizeFavDlgEx // the org favs dialog returns a list of null terminated strings containing // all the urls to update. void OrgFavSynchronize(HWND hwnd, VARIANT *pvarUrlsToSynch) { #ifndef DISABLE_SUBSCRIPTIONS ASSERT(pvarUrlsToSynch); //if there are no urls to update, it's an empty string so bail if ( (pvarUrlsToSynch->vt == VT_BSTR) && (pvarUrlsToSynch->bstrVal) && *(pvarUrlsToSynch->bstrVal) ) { PWSTR pwzUrls = pvarUrlsToSynch->bstrVal; ISubscriptionMgr *psm; if (SUCCEEDED(JITCoCreateInstance(CLSID_SubscriptionMgr, NULL, CLSCTX_INPROC_SERVER, IID_ISubscriptionMgr, (void**)&psm, hwnd, FIEF_FLAG_FORCE_JITUI))) { //SysStringLen doesn't look at the string contents, just the cb of the alloc while (pwzUrls < (pvarUrlsToSynch->bstrVal + SysStringLen(pvarUrlsToSynch->bstrVal))) { psm->UpdateSubscription(pwzUrls); pwzUrls += lstrlenW(pwzUrls) + 1; } psm->Release(); } } #endif /* !DISABLE_SUBSCRIPTIONS */ } /* * DoOrganizeFavDlgEx * * HWND hwnd Owner window for the dialog. * LPWSTR pszInitDir Dir to use as root. if null, the user's favorites dir is used. * * Returns: * BOOL. TRUE if succeeds. FALSE otherwise. * */ BOOL WINAPI DoOrganizeFavDlgEx(HWND hwnd, LPWSTR pszInitDir) { // The easy answer would be to add an about:OrganizeFavorites that // gets registered in our selfreg.inx file. Unfortunately, multilanguage // support requires us to generate the URL on the fly. WCHAR wszUrl[6 + MAX_PATH + 11 + 1]; // "res://MAX_PATH/orgfav.dlg" StringCchCopy(wszUrl, ARRAYSIZE(wszUrl), L"res://"); if(SUCCEEDED(GetModuleFileNameWrapW(MLGetHinst(), wszUrl + 6, MAX_PATH))) { if(SUCCEEDED(StringCchCat(wszUrl, ARRAYSIZE(wszUrl), L"/orgfav.dlg"))) { IMoniker *pmk; if (SUCCEEDED(CreateURLMoniker(NULL, wszUrl, &pmk))) { ASSERT(pmk); VARIANT varUrlsToSynch, varInitialDir; BSTR bstrInitDir; VariantInit(&varUrlsToSynch); VariantInit(&varInitialDir); if (pszInitDir) { bstrInitDir = SysAllocString(pszInitDir); if (bstrInitDir) { varInitialDir.vt = VT_BSTR; varInitialDir.bstrVal = bstrInitDir; } } ShowHTMLDialog(hwnd, pmk, &varInitialDir, L"Resizable=1", &varUrlsToSynch); OrgFavSynchronize(hwnd, &varUrlsToSynch); if (pszInitDir && bstrInitDir) SysFreeString(bstrInitDir); VariantClear(&varUrlsToSynch); pmk->Release(); return TRUE; } else return FALSE; } } return FALSE; } /* * DoOrganizeFavDlg * * This API is exported so that it may be called by explorer and mshtml in * addition to being called internally by shdocvw. * * HWND hwndOwner Owner window for the dialog. * LPWSTR pszInitDir Dir to use as root. if null, the user's favorites dir is used. * * Returns: * BOOL. TRUE if succeeds. FALSE otherwise. * */ BOOL WINAPI DoOrganizeFavDlg(HWND hwnd, LPSTR pszInitDir) { BOOL fRet; WCHAR szInitDir[MAX_PATH]; if (pszInitDir) { SHAnsiToUnicode(pszInitDir, szInitDir, ARRAYSIZE(szInitDir)); fRet = DoOrganizeFavDlgEx(hwnd, szInitDir); } else { fRet = DoOrganizeFavDlgEx(hwnd, NULL); } return fRet; } BOOL WINAPI DoOrganizeFavDlgW(HWND hwnd, LPWSTR pszInitDir) { return DoOrganizeFavDlgEx(hwnd, pszInitDir); } #define ADDTOFAVPROP TEXT("SHDOC_ATFPROP") typedef enum { ATF_FAVORITE, ATF_CHANNEL, ATF_CHANNEL_MODIFY, ATF_CHANNEL_SOFTDIST } FAVDLGTYPE; typedef struct _ADDTOFAV { PTSTR pszInitDir; UINT cchInitDir; PTSTR pszFile; UINT cchFile; LPITEMIDLIST pidl; LPITEMIDLIST pidlSelected; LPCITEMIDLIST pidlFavorite; FAVDLGTYPE iDlgType; SUBSCRIPTIONINFO siSubsInProg; SUBSCRIPTIONTYPE subsType; BOOL bIsSoftdist; BOOL bStartSubscribed; BOOL bSubscribed; } ADDTOFAV; BOOL IsSubscribed(ADDTOFAV *patf); typedef struct _BFFFavSubStruct { WNDPROC lpfnOldWndProc; HWND hwndNew; HWND hwndTV; HWND hwndSave; HWND hTemplateWnd; ADDTOFAV * patf; RECT rcRestored; } BFFFAVSUBSTRUCT; BOOL_PTR CALLBACK NewFavDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_INITDIALOG: { HWND hwnd; ASSERT(lParam); SetWindowLongPtr(hDlg, DWLP_USER, lParam); EnableWindow(GetDlgItem(hDlg, IDOK), FALSE); // cross-lang platform support SHSetDefaultDialogFont(hDlg, IDD_NAME); hwnd = GetDlgItem(hDlg, IDD_NAME); SendMessage(hwnd, EM_LIMITTEXT, MAX_PATH - 1, 0); EnableOKButtonFromID(hDlg, IDD_NAME); break; } case WM_DESTROY: SHRemoveDefaultDialogFont(hDlg); return FALSE; case WM_COMMAND: switch (GET_WM_COMMAND_ID(wParam, lParam)) { case IDD_NAME: { if (GET_WM_COMMAND_CMD(wParam, lParam) == EN_UPDATE) { LPTSTR lpstrName = (LPTSTR) GetWindowLongPtr(hDlg, DWLP_USER); EnableOKButtonFromID(hDlg, IDD_NAME); GetDlgItemText(hDlg, IDD_NAME, lpstrName, MAX_PATH); } break; } case IDOK: { TCHAR szTmp[MAX_PATH]; HRESULT hr = StringCchCopy(szTmp, ARRAYSIZE(szTmp), (LPTSTR)GetWindowLongPtr(hDlg, DWLP_USER)); if(FAILED(hr) || PathCleanupSpec(NULL,szTmp)) { HWND hwnd; MLShellMessageBox( hDlg, MAKEINTRESOURCE(IDS_FAVS_INVALIDFN), MAKEINTRESOURCE(IDS_FAVS_ADDTOFAVORITES), MB_OK | MB_ICONHAND); hwnd = GetDlgItem(hDlg, IDD_NAME); SetWindowText(hwnd, TEXT('\0')); EnableWindow(GetDlgItem(hDlg, IDOK), FALSE); SetFocus(hwnd); break; } } // fall through case IDCANCEL: EndDialog(hDlg, GET_WM_COMMAND_ID(wParam, lParam)); break; default: return FALSE; } break; default: return FALSE; } return TRUE; } // BOGUS - these id's stolen from SHBrowseForFolder implementation #define IDD_FOLDERLIST 0x3741 #define IDD_BROWSETITLE 0x3742 #define IDD_BROWSESTATUS 0x3743 const static DWORD aAddToFavHelpIDs[] = { // Context Help IDs IDC_FAVORITE_DESC, NO_HELP, IDD_BROWSETITLE, NO_HELP, IDD_BROWSESTATUS, NO_HELP, IDC_FAVORITE_ICON, NO_HELP, IDC_NAMESTATIC, IDH_NAMEEDIT, IDC_FOLDERLISTSTATIC, IDH_BROWSELIST, IDC_SUBSCRIBE_FOLDERLIST_PLACEHOLDER, IDH_BROWSELIST, IDC_FAVORITE_NEWFOLDER, IDH_CREATEIN, IDC_SUBSCRIBE_CUSTOMIZE, IDH_CHANNEL_SUBSCR_CUST_BUTTON, IDC_FAVORITE_CREATEIN, IDH_NEWFOLDER, IDC_FAVORITE_NAME, IDH_NAMEEDIT, IDC_MAKE_OFFLINE, IDH_MAKE_AVAIL_OFFLINE, 0, 0 }; const static DWORD aAddToChanHelpIDs[] = { // Context Help IDs IDC_FAVORITE_DESC, NO_HELP, IDD_BROWSETITLE, NO_HELP, IDD_BROWSESTATUS, NO_HELP, IDC_FAVORITE_ICON, NO_HELP, IDC_NAMESTATIC, IDH_NAMEEDIT, IDC_FOLDERLISTSTATIC, IDH_BROWSELIST, IDC_SUBSCRIBE_FOLDERLIST_PLACEHOLDER, IDH_BROWSELIST, IDC_FAVORITE_NEWFOLDER, IDH_CREATEIN, IDC_SUBSCRIBE_CUSTOMIZE, IDH_CHANNEL_SUBSCR_CUST_BUTTON, IDC_FAVORITE_CREATEIN, IDH_NEWFOLDER, IDC_FAVORITE_NAME, IDH_NAMEEDIT, IDC_MAKE_OFFLINE, IDH_MAKE_AVAIL_OFFLINE, 0, 0 }; /* * Makes sure the item being added to favorites doesn't already exist. If it does, * puts up a message box to have the user confirm whether they want to overwrite * the old favorite or not. */ BOOL ConfirmAddToFavorites(HWND hwndOwner, ADDTOFAV * patf) { BOOL fRet = FALSE; BOOL fExists; int iPromptString = 0; if (patf->subsType == SUBSTYPE_CHANNEL) { //patf->pszInitDir now contains the path with a .url on the end; the channel //will be stored in a directory of that name without .url. Strip it. TCHAR szPath[MAX_PATH]; if(FAILED(StringCchCopy(szPath, ARRAYSIZE(szPath), patf->pszInitDir))) { MLShellMessageBox( hwndOwner, MAKEINTRESOURCE(IDS_FAVS_INVALIDFN), MAKEINTRESOURCE(IDS_FAVS_ADDTOFAVORITES), MB_OK | MB_ICONHAND); return FALSE; } PathRemoveExtension (szPath); fExists = PathFileExists(szPath); iPromptString = IDS_CHANNELS_FILEEXISTS; } else { fExists = PathFileExists(patf->pszInitDir); iPromptString = IDS_FAVS_FILEEXISTS; } fRet = ! fExists || (MLShellMessageBox( hwndOwner, MAKEINTRESOURCE(iPromptString), NULL, //use owner's title MB_ICONQUESTION | MB_YESNO) == IDYES); return fRet; } // // Get the localized date and time // typedef HRESULT (*PFVARIANTTIMETOSYSTEMTIME)(DOUBLE, LPSYSTEMTIME); // // Subscribe to the current site. // HRESULT SubscribeToSite(HWND hwnd, LPCTSTR pszFile, LPCITEMIDLIST pidl, DWORD dwFlags, SUBSCRIPTIONINFO* pSubs, SUBSCRIPTIONTYPE subsType) { #ifndef DISABLE_SUBSCRIPTIONS TCHAR szURL[MAX_URL_STRING]; ISubscriptionMgr *pISubscriptionMgr; // // Get a displayable URL. // IEGetDisplayName(pidl, szURL, SHGDN_FORPARSING); // // Get a pointer to the subscription manager. // HRESULT hr = JITCoCreateInstance(CLSID_SubscriptionMgr, NULL, CLSCTX_INPROC_SERVER, IID_ISubscriptionMgr, (void**)&pISubscriptionMgr, hwnd, FIEF_FLAG_FORCE_JITUI); if (SUCCEEDED(hr)) { // // Create a default subscription. // BSTR bstrURL = SysAllocStringT(szURL); if (bstrURL) { BSTR bstrName = SysAllocStringT(pszFile); if (bstrName) { hr = pISubscriptionMgr->CreateSubscription(hwnd, bstrURL, bstrName, dwFlags, subsType, pSubs); SysFreeString(bstrName); } SysFreeString(bstrURL); } // // Clean up. // pISubscriptionMgr->Release(); } return hr; #else /* !DISABLE_SUBSCRIPTIONS */ return E_FAIL; #endif /* !DISABLE_SUBSCRIPTIONS */ } // // Create in-memory subscription, but only optionally save it to subscription manager // BOOL StartSiteSubscription (HWND hwnd, ADDTOFAV* patf, BOOL bFinalize) { #ifndef DISABLE_SUBCRIPTIONS //update the changes-only flag (radio buttons here are, effectively, direct access to this flag) if (patf->subsType == SUBSTYPE_CHANNEL || patf->subsType == SUBSTYPE_DESKTOPCHANNEL) { //if set, leave alone; otherwise, put to full download if (!(patf->siSubsInProg.fChannelFlags & CHANNEL_AGENT_PRECACHE_SOME)) patf->siSubsInProg.fChannelFlags |= CHANNEL_AGENT_PRECACHE_ALL; patf->siSubsInProg.fUpdateFlags |= SUBSINFO_CHANNELFLAGS | SUBSINFO_SCHEDULE; } if (S_OK != SubscribeToSite(hwnd, patf->pszFile, patf->pidlFavorite, bFinalize ? CREATESUBS_NOUI | CREATESUBS_FROMFAVORITES : CREATESUBS_NOSAVE, &patf->siSubsInProg, patf->subsType)) { return FALSE; } return TRUE; #else /* !DISABLE_SUBSCRIPTIONS */ return FALSE; #endif /* !DISABLE_SUBSCRIPTIONS */ } /* Combines the path and the filename of the favorite and puts it into patf->pszInitDir, so that it has the fully qualified pathname. */ #define SZ_URLEXT TEXT(".url") #define CCH_URLEXT SIZECHARS(SZ_URLEXT) BOOL QualifyFileName(ADDTOFAV *patf) { TCHAR szTemp[MAX_PATH]; BOOL fRet = FALSE; LPTSTR pstr; // Can we safely add the extension to this? if (lstrlen(patf->pszFile) < (int)(patf->cchFile - CCH_URLEXT)) { //Add extension .url if its not already there //This is to prevent strings like "com" in "www.microsoft.com" from being interpreted as extensions pstr = PathFindExtension(patf->pszFile); if (!pstr || (pstr && StrCmpI(pstr, SZ_URLEXT)))// && StrCmpI(pstr, SZ_CDFEXT))) { if(FAILED(StringCchCat(patf->pszFile, patf->cchFile, SZ_URLEXT))) return FALSE; } // Is there a folder associated with the filename? if (patf->pidlSelected && SHGetPathFromIDList(patf->pidlSelected, szTemp)) { // Yes if (PathCombine(szTemp, szTemp, patf->pszFile)) { if ((UINT)lstrlen(szTemp) < patf->cchInitDir) { if(SUCCEEDED(StringCchCopy(patf->pszInitDir, patf->cchInitDir, szTemp))) fRet = TRUE; } } } } return fRet; } BOOL SubscriptionFailsChannelAuthentication (HWND hDlg, SUBSCRIPTIONINFO* psi) { #ifndef DISABLE_SUBSCRIPTIONS if (psi->bNeedPassword && !(psi->bstrPassword && psi->bstrPassword[0] && psi->bstrUserName && psi->bstrUserName[0])) { //password would be required if (IsDlgButtonChecked (hDlg, IDC_MAKE_OFFLINE)) { //they're trying to subscribe... WRONG! MLShellMessageBox( hDlg, MAKEINTRESOURCE(IDS_NEED_CHANNEL_PASSWORD), NULL, MB_ICONINFORMATION | MB_OK); return TRUE; } } return FALSE; #else /* !DISABLE_SUBSCRIPTIONS */ return FALSE; #endif /* !DISABLE_SUBSCRIPTIONS */ } LRESULT CALLBACK BFFFavSubclass(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { BFFFAVSUBSTRUCT * pbffFS = (BFFFAVSUBSTRUCT *)GetProp(hwnd, ADDTOFAVPROP); WNDPROC lpfnOldWndProc = pbffFS->lpfnOldWndProc; RECT rc; switch (uMsg) { case WM_COMMAND: // Intercept the command for the New Folder button we hacked into // the SHBrowseForFolder dialog. switch (GET_WM_COMMAND_ID(wParam, lParam)) { case IDC_FAVORITE_NAME: { HWND hwndedit; if (GET_WM_COMMAND_CMD(wParam, lParam) == EN_CHANGE) EnableOKButtonFromID(hwnd, IDC_FAVORITE_NAME); hwndedit = GetDlgItem(hwnd, IDC_FAVORITE_NAME); SendMessage(hwndedit, EM_LIMITTEXT, MAX_PATH - 1, 0); break; } case IDC_MAKE_OFFLINE: EnableWindow(GetDlgItem(hwnd, IDC_SUBSCRIBE_CUSTOMIZE), IsDlgButtonChecked(hwnd, IDC_MAKE_OFFLINE)); break; case IDC_SUBSCRIBE_CUSTOMIZE: //need to create -- but not store -- subscription if (StartSiteSubscription (hwnd, pbffFS->patf, FALSE)) SendMessage (hwnd, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(hwnd, IDOK), TRUE); break; case IDC_FAVORITE_NEWFOLDER: TCHAR szPath[MAX_PATH]; TCHAR szName[MAX_PATH]; HWND hwndTV; TV_ITEM tv_item; // Bring up the Create New Folder dialog if ((DialogBoxParam(MLGetHinst(), MAKEINTRESOURCE(DLG_NEWFOLDER), hwnd, NewFavDlgProc, (LPARAM)szName) == IDOK) && (SHGetPathFromIDList(pbffFS->patf->pidlSelected, szPath)) && ((lstrlen(szPath) + lstrlen(szName) + 1) < MAX_PATH)) { PathCombine(szPath, szPath, szName); BOOL bSuccess = FALSE; #ifdef CREATEFOLDERSINCHANNELSDIR if (pbffFS->patf->subsType == SUBSTYPE_CHANNEL) { ASSERT(0); //should not be possible in this release //(I removed this button in the .rc dialogs for channels) //Note: to make this work in a future release, reenable this code -- it's //functional. But the folders created here show up ugly in the channel bar //(just a default folder icon) and if you click on them, you get a shell //Explorer window instead of a theater-mode browser window. The reason //for this second happening is that the desktop.ini file created in the new //folder has no URL=. To remedy this: AddCategory() has to be fixed so it //doesn't interpret the pszURL argument as a UNC name (I was using a resouce moniker //pointing into cdfview.dll for the html target), and the OC hosted by the default //html pages has to learn how to be hosted from a html page without a path -- or we //actually have to create an html page in the new directory, which is messy. IChannelMgr* pChanMgr; HRESULT hr; hr = JITCoCreateInstance(CLSID_ChannelMgr, NULL, CLSCTX_INPROC_SERVER, IID_IChannelMgr, (void**)&pChanMgr, hwnd, FIEF_FLAG_FORCE_JITUI); if (SUCCEEDED(hr)) { IChannelMgrPriv* pChanMgrPriv; hr = pChanMgr->QueryInterface (IID_IChannelMgrPriv, (void**)&pChanMgrPriv); if (SUCCEEDED(hr)) { char szCFPath[MAX_PATH]; WCHAR wszFolder[MAX_PATH]; IChannelMgrPriv::CHANNELFOLDERLOCATION cflLocation = (pbffFS->patf->iDlgType == ATF_CHANNEL_SOFTDIST ? IChannelMgrPriv::CF_SOFTWAREUPDATE : IChannelMgrPriv::CF_CHANNEL); hr = pChanMgrPriv->GetChannelFolderPath (szCFPath, ARRAYSIZE(szCFPath), cflLocation); int cchCommon = PathCommonPrefix (szPath, szCFPath, NULL); AnsiToUnicode (szPath + cchCommon, wszFolder, ARRAYSIZE(wszFolder)); CHANNELCATEGORYINFO info = {0}; info.cbSize = sizeof(info); info.pszTitle = wszFolder; bSuccess = SUCCEEDED (pChanMgr->AddCategory (&info)); pChanMgrPriv->Release(); } pChanMgr->Release(); } } else #endif { bSuccess = CreateDirectory(szPath, NULL); } if (bSuccess) { // This code assumes the layout of SHBrowseForFolder! // directory successfully created, must notify registered shell components. SHChangeNotify(SHCNE_MKDIR, SHCNF_PATH, szPath, NULL); // Get the TreeView control hwndTV = GetDlgItem(hwnd, IDD_FOLDERLIST); if (hwndTV) { HTREEITEM hti = TreeView_GetSelection(hwndTV); // Take the selected item and reset it, then reexpand it so // that it shows the new directory we just created. tv_item.mask = TVIF_CHILDREN; tv_item.hItem = hti; tv_item.cChildren = -1; TreeView_SetItem(hwndTV, &tv_item); TreeView_Expand(hwndTV, hti, TVE_COLLAPSE | TVE_COLLAPSERESET); TreeView_Expand(hwndTV, hti, TVE_EXPAND); // Find the new directory we just created and select it by // walking the tree from the selected item down. if (hti = TreeView_GetChild(hwndTV, hti)) { tv_item.mask = TVIF_TEXT; tv_item.pszText = szPath; tv_item.cchTextMax = MAX_PATH; do { tv_item.hItem = hti; TreeView_GetItem(hwndTV, &tv_item); if (StrCmp(szName, szPath) == 0) { TreeView_Select(hwndTV, hti, TVGN_CARET); break; } } while (hti = TreeView_GetNextSibling(hwndTV, hti)); } SetFocus(hwndTV); } } else { LPVOID lpMsgBuf; if(FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language (LPTSTR) &lpMsgBuf, 0, NULL )) { MLShellMessageBox( hwnd, (LPCTSTR)lpMsgBuf, MAKEINTRESOURCE(IDS_FAVS_ADDTOFAVORITES), MB_ICONINFORMATION | MB_OK); // Free the buffer. LocalFree( lpMsgBuf ); } lpMsgBuf = NULL; } } break; case IDOK: // first, make sure they're not trying to subscribe to an authenticated // channel without entering a password. if (SubscriptionFailsChannelAuthentication (hwnd, &pbffFS->patf->siSubsInProg)) return FALSE; // Retrieve the text from the Name edit control. GetDlgItemText(hwnd, IDC_FAVORITE_NAME, pbffFS->patf->pszFile, pbffFS->patf->cchFile); { // Just a block to declare variables BOOL fTooBig = TRUE; // assume failure TCHAR szTmp[MAX_PATH]; if (lstrlen(pbffFS->patf->pszFile) < MAX_PATH) { if(FAILED((StringCchCopy(szTmp, ARRAYSIZE(szTmp), pbffFS->patf->pszFile)))) return FALSE; // PathCleanupSpec deals with MAX_PATH buffers, so we should be fine if (PathCleanupSpec(NULL, szTmp)) { MLShellMessageBox( hwnd, MAKEINTRESOURCE(IDS_FAVS_INVALIDFN), MAKEINTRESOURCE(IDS_FAVS_ADDTOFAVORITES), MB_OK | MB_ICONHAND); return FALSE; } // Make sure the name is unique and if not, that the user has // specified that it is OK to override. if (QualifyFileName(pbffFS->patf)) { if (!ConfirmAddToFavorites(hwnd, pbffFS->patf)) return FALSE; // Bogus hack since the ATF stuff is only half done // Depending on which dlg is shown, look for the appropriate // check. if (IsDlgButtonChecked (hwnd, IDC_MAKE_OFFLINE)) { //they want to subscribe! save subscription we already have in memory //trouble is, pbffFS->patf->pszFile ends in a bogus .url TCHAR* pszTemp = pbffFS->patf->pszFile; TCHAR szNoExt[MAX_PATH]; if(FAILED((StringCchCopy(szNoExt, ARRAYSIZE(szNoExt), pbffFS->patf->pszFile)))) return FALSE; pbffFS->patf->pszFile = szNoExt; PathRemoveExtension (szNoExt); pbffFS->patf->bSubscribed = StartSiteSubscription (hwnd, pbffFS->patf, TRUE); pbffFS->patf->pszFile = pszTemp; } else if (pbffFS->patf->bStartSubscribed) { // If we started subscribed and they unchecked make available // offline, then delete the subscription. ISubscriptionMgr* pSubsMgr; if (SUCCEEDED (CoCreateInstance(CLSID_SubscriptionMgr, NULL, CLSCTX_INPROC_SERVER, IID_ISubscriptionMgr, (void**)&pSubsMgr))) { //url is in patf->pidlFavorite WCHAR wszURL[MAX_URL_STRING]; IEGetDisplayName(pbffFS->patf->pidlFavorite, wszURL, SHGDN_FORPARSING); pSubsMgr->DeleteSubscription(wszURL, NULL); pSubsMgr->Release(); } } // Enable and set focus to the tree view so that it is sure to // be selected so that SHBrowseForFolder will return a pidl. EnableWindow(pbffFS->hwndTV, TRUE); SetFocus(pbffFS->hwndTV); fTooBig = FALSE; } } if (fTooBig) { MLShellMessageBox( hwnd, MAKEINTRESOURCE(IDS_FAVS_FNTOOLONG), MAKEINTRESOURCE(IDS_FAVS_ADDTOFAVORITES), MB_OK | MB_ICONHAND); return FALSE; } } break; case IDC_FAVORITE_CREATEIN: // The advanced button has been clicked. Enable/disable the tree view // and New button, set focus to the tree view or ok button, disable the advanced // button and then resize the dialog. { BOOL fExpanding = !IsWindowEnabled(GetDlgItem(hwnd, IDC_SUBSCRIBE_FOLDERLIST_PLACEHOLDER)); //random control that gets enabled when dialog expanded TCHAR szBuffer[100]; EnableWindow(pbffFS->hwndTV, fExpanding); //don't show New Folder button for channels in the channels folder, // see code for case IDC_FAVORITE_NEWFOLDER for why if (fExpanding && pbffFS->patf->subsType == SUBSTYPE_CHANNEL) { LPITEMIDLIST pidlFavs = NULL; TCHAR tzFavsPath[MAX_PATH]; if (SUCCEEDED(SHGetSpecialFolderLocation(hwnd, CSIDL_FAVORITES, &pidlFavs)) && SUCCEEDED(SHGetNameAndFlags(pidlFavs, SHGDN_FORPARSING | SHGDN_FORADDRESSBAR, tzFavsPath, SIZECHARS(tzFavsPath), NULL)) && StrCmpNI(tzFavsPath, pbffFS->patf->pszInitDir, ARRAYSIZE(tzFavsPath))==0) { EnableWindow(pbffFS->hwndNew, TRUE); } if(pidlFavs) ILFree(pidlFavs); } else EnableWindow(pbffFS->hwndNew, fExpanding); GetWindowRect(hwnd, &rc); if (fExpanding) { int lRet = MLLoadString(IDS_FAVS_ADVANCED_COLLAPSE, szBuffer, ARRAYSIZE(szBuffer)); ASSERT(lRet); SetFocus(pbffFS->hwndTV); MoveWindow(hwnd, rc.left, rc.top, pbffFS->rcRestored.right - pbffFS->rcRestored.left, pbffFS->rcRestored.bottom - pbffFS->rcRestored.top, TRUE); } else { int lRet = MLLoadString(IDS_FAVS_ADVANCED_EXPAND, szBuffer, ARRAYSIZE(szBuffer)); ASSERT(lRet); SetFocus(GetDlgItem(hwnd, IDC_FAVORITE_NAME)); MoveWindow(hwnd, rc.left, rc.top, pbffFS->rcRestored.right - pbffFS->rcRestored.left, pbffFS->rcRestored.bottom - pbffFS->rcRestored.top, TRUE); // hide the bottom part of the dialog int cx, cy; RECT rc; GetWindowRect (GetDlgItem (hwnd, IDC_SUBSCRIBE_FOLDERLIST_PLACEHOLDER), &rc); cy = rc.top; GetWindowRect (hwnd, &rc); cx = rc.right - rc.left; cy = cy /*top of ctrl*/ - rc.top; /*top of window*/ SetWindowPos (hwnd, NULL, 0, 0, cx, cy, SWP_NOMOVE | SWP_NOZORDER); } SetWindowText(GetDlgItem(hwnd, IDC_FAVORITE_CREATEIN), szBuffer); break; } } break; case WM_DESTROY: { DWORD dwValue = IsWindowEnabled(GetDlgItem(hwnd, IDC_FAVORITE_NEWFOLDER)); //random control that gets enabled when dialog expanded SHRegSetUSValue(TEXT("Software\\Microsoft\\Internet Explorer\\Main"), TEXT("AddToFavoritesExpanded"), REG_DWORD, &dwValue, 4, SHREGSET_HKCU | SHREGSET_FORCE_HKCU); ReplaceTransplacedControls (hwnd, pbffFS->hTemplateWnd); DestroyWindow (pbffFS->hTemplateWnd); SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR) lpfnOldWndProc); RemoveProp(hwnd, ADDTOFAVPROP); SHRemoveDefaultDialogFont(hwnd); ILFree(pbffFS->patf->pidlSelected); LocalFree((HLOCAL)pbffFS); pbffFS = NULL; break; } case WM_HELP: SHWinHelpOnDemandWrap((HWND)((LPHELPINFO) lParam)->hItemHandle, c_szHelpFile, HELP_WM_HELP, (DWORD_PTR)(LPTSTR) (pbffFS->patf->iDlgType == ATF_FAVORITE ? aAddToFavHelpIDs : aAddToChanHelpIDs)); return TRUE; break; case WM_CONTEXTMENU: SHWinHelpOnDemandWrap((HWND) wParam, c_szHelpFile, HELP_CONTEXTMENU, (DWORD_PTR)(LPVOID) (pbffFS->patf->iDlgType == ATF_FAVORITE ? aAddToFavHelpIDs : aAddToChanHelpIDs)); return TRUE; break; } return CallWindowProc(lpfnOldWndProc, hwnd, uMsg, wParam, lParam); } static const TCHAR szTransplacedProp[] = TEXT("tp"); void ReplaceTransplacedControls (HWND hDlgMaster, HWND hDlgTemplate) { /* * This function moves the controls that we moved from our temporary * dialog over to SHBrowseForFolder's dialog, back to their original * home, before they get destroyed. This is because otherwise we have * problems when destroying the template dialog -- specifically, we get * a GP fault in user.exe when destroying the edit control, because it * looks to its parent window to figure out where its data segment is. * * Solution: (for safety) -- put everything back where it came from. * Other possibilities: just move the edit control (by ID) back, or * move all edit controls back, or use DS_LOCALEDIT for edit controls * (but this is documented only for use with multiline edits.) * Or modify SHBrowseForFolder to allow other dialog templates... * but that's over in shell32. */ HWND hCtrl = GetWindow (hDlgMaster, GW_CHILD); while (hCtrl) { HWND hNext = GetWindow (hCtrl, GW_HWNDNEXT); if (GetProp (hCtrl, szTransplacedProp) != NULL) { RemoveProp (hCtrl, szTransplacedProp); SetParent (hCtrl, hDlgTemplate); } hCtrl = hNext; } } #define szOriginalWND TEXT("WorkaroundOrigWndProc") INT_PTR CALLBACK MergeFavoritesDialogControls(HWND hDlgTemplate, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_INITDIALOG: { HWND hDlgMaster = (HWND)lParam; ASSERT (IsWindow (hDlgMaster)); TCHAR szT[200]; RECT rc; //resize master like us GetWindowText (hDlgTemplate, szT, ARRAYSIZE(szT)); SetWindowText (hDlgMaster, szT); GetClientRect (hDlgTemplate, &rc); AdjustWindowRect (&rc, GetWindowLong (hDlgMaster, GWL_STYLE), FALSE); SetWindowPos (hDlgMaster, NULL, 0, 0, rc.right - rc.left, rc.bottom - rc.top, SWP_NOMOVE | SWP_NOZORDER); // a-msadek; If the owned window is mirrored, a dialog with specifed // coordinates, the dialog get moved to the worng direction HWND hWndOwner = GetWindow(hDlgMaster, GW_OWNER); if(IS_WINDOW_RTL_MIRRORED(hWndOwner)) { RECT rcOwner, rcDlg; GetWindowRect(hWndOwner, &rcOwner); GetWindowRect(hDlgMaster, &rcDlg); SetWindowPos(hDlgMaster, NULL, rcDlg.left - (rcDlg.right - rcOwner.right), rcDlg.top, 0 ,0, SWP_NOSIZE | SWP_NOZORDER); } #if 0 //now we do this as part of the "move controls from template to master" process, //if we notice that a ctrl with that id already exists. This way we pick up the //tab order too. If someone decides my hack (SetParent) to change tab order is //broken, then that code can be nuked and this reenabled. //position already-existing controls in master like us int ID_PREEXIST_CTRLS[] = { IDOK_PLACEHOLDER, IDCANCEL_PLACEHOLDER, IDC_SUBSCRIBE_FOLDERLIST_PLACEHOLDER }; for (int iCtrl = 0; iCtrl < ARRAYSIZE(ID_PREEXIST_CTRLS); iCtrl++) { GetWindowRect (GetDlgItem (hDlgTemplate, ID_PREEXIST_CTRLS[iCtrl]), &rc); MapWindowPoints (NULL, hDlgTemplate, (LPPOINT)&rc, 2); MoveWindow (GetDlgItem (hDlgMaster, ID_PREEXIST_CTRLS[iCtrl]), rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, TRUE); DestroyWindow (GetDlgItem (hDlgTemplate, ID_PREEXIST_CTRLS[iCtrl])); } #endif //copy other controls from us to master //find last child HWND hCtrlTemplate = NULL; HWND hNextCtrl = GetWindow (hDlgTemplate, GW_CHILD); if (hNextCtrl) //can't see how this would fail, but... hCtrlTemplate = GetWindow (hNextCtrl, GW_HWNDLAST); //have last window in hCtrlTemplate //now move controls over in reverse order -- they'll end up stacking up in original order from template while (hCtrlTemplate) { hNextCtrl = GetWindow (hCtrlTemplate, GW_HWNDPREV); DWORD id = GetWindowLong (hCtrlTemplate, GWL_ID); HWND hCtrlExisting; if (id != (USHORT)IDC_STATIC && NULL != (hCtrlExisting = GetDlgItem (hDlgMaster, id))) //it's one of the controls pre-created by SHBrowseForFolder { //so don't move this one over -- adjust existing control for size, position, tab order RECT rc; GetWindowRect (hCtrlTemplate, &rc); MapWindowPoints (NULL, hDlgTemplate, (LPPOINT)&rc, 2); SetWindowPos (hCtrlExisting, NULL, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, SWP_NOACTIVATE | SWP_NOZORDER); DestroyWindow (hCtrlTemplate); //REVIEW //hack -- send control to end of tab order SetParent (hCtrlExisting, hDlgTemplate); SetParent (hCtrlExisting, hDlgMaster); } else //we should move this control from template to master { SetProp (hCtrlTemplate, szTransplacedProp, (HANDLE)TRUE); //anything -- it's the existence of the prop that we check for SetParent (hCtrlTemplate, hDlgMaster); //to know to move this control back later } hCtrlTemplate = hNextCtrl; } // Let Template know about the child so that it can forward WM_COMMAND notifications to it // to work around the fact that edit controls cache their parent pointers and ignore SetParents // when it comes to sending parent notifications SetProp(hDlgTemplate, szOriginalWND, hDlgMaster); } break; case WM_COMMAND: // Workaround for above bug SendMessage((HWND)GetProp(hDlgTemplate, szOriginalWND), uMsg, wParam, lParam); break; } return FALSE; } int CALLBACK BFFFavCallback(HWND hwnd, UINT uMsg, LPARAM lParam, LPARAM lpData) { switch (uMsg) { case BFFM_INITIALIZED: { ADDTOFAV* patf = (ADDTOFAV*)lpData; ASSERT (patf); HWND hDlgTemp = CreateDialogParam(MLGetHinst(), MAKEINTRESOURCE(IDD_ADDTOFAVORITES_TEMPLATE), NULL, MergeFavoritesDialogControls, (LPARAM)hwnd); //this results in all the controls being copied over //if successful, make our other modifications BFFFAVSUBSTRUCT * pbffFavSubStruct; if ((IsWindow(GetDlgItem(hwnd, IDC_SUBSCRIBE_CUSTOMIZE))) //verify existence of randomly-selected control && (pbffFavSubStruct = (BFFFAVSUBSTRUCT *) LocalAlloc(LPTR, sizeof(BFFFAVSUBSTRUCT)))) { //done with template, but don't destroy it: //see MSKB Q84190, owner/owned vs parent/child -- the children // of template are now children of master, but still OWNED // by template, and are destroyed when template is destroyed... // but we'll just keep template around // invisibly. //we'll take care of it when we go away //Do we need to do SetDefaultDialogFont stuff for localization still, since it all comes from the .rc? //set up window stuff for subclass: // Get the TreeView control so we can muck with the style bits and move it down HWND hwndT; if (hwndT = GetDlgItem(hwnd, IDD_FOLDERLIST)) { DWORD dwStyle = GetWindowLong(hwndT, GWL_STYLE); dwStyle |= TVS_SHOWSELALWAYS; dwStyle &= ~TVS_LINESATROOT; SetWindowLong(hwndT, GWL_STYLE, dwStyle); } // don't allow subscriptions if the URL is not "http:" protocol, or if already subscribed TCHAR szURL[MAX_URL_STRING]; if (!patf->pidlFavorite || FAILED(IEGetDisplayName(patf->pidlFavorite, szURL, SHGDN_FORPARSING)) || SHRestricted2(REST_NoAddingSubscriptions, szURL, 0) || !IsSubscribable(szURL) || !IsFeaturePotentiallyAvailable(CLSID_SubscriptionMgr) || !IsBrowserFrameOptionsPidlSet(patf->pidlFavorite, BFO_USE_IE_OFFLINE_SUPPORT)) { CheckDlgButton(hwnd, IDC_MAKE_OFFLINE, 0); EnableWindow(GetDlgItem (hwnd, IDC_MAKE_OFFLINE), FALSE); EnableWindow(GetDlgItem (hwnd, IDC_SUBSCRIBE_CUSTOMIZE), FALSE); } else if (IsSubscribed(patf)) { patf->bStartSubscribed = TRUE; CheckDlgButton(hwnd, IDC_MAKE_OFFLINE, 1); } else if (patf->bIsSoftdist) { CheckDlgButton(hwnd, IDC_MAKE_OFFLINE, 1); } EnableWindow(GetDlgItem(hwnd, IDC_SUBSCRIBE_CUSTOMIZE), IsDlgButtonChecked(hwnd, IDC_MAKE_OFFLINE)); //set the name Edit_LimitText(GetDlgItem(hwnd, IDC_FAVORITE_NAME), MAX_PATH - 1); // Use URL if title string is not displayable if (SHIsDisplayable(patf->pszFile, g_fRunOnFE, g_bRunOnNT5)) { SetDlgItemText(hwnd, IDC_FAVORITE_NAME, patf->pszFile); } else { TCHAR szUrlTemp[MAX_URL_STRING]; IEGetDisplayName(patf->pidlFavorite, szUrlTemp, SHGDN_FORPARSING); SetDlgItemText(hwnd, IDC_FAVORITE_NAME, szUrlTemp); } EnableOKButtonFromID(hwnd, IDC_FAVORITE_NAME); // hide the (empty) SHBrowseForFolder prompt control ShowWindow(GetDlgItem (hwnd, IDD_BROWSETITLE), SW_HIDE); // Fill out the structure and set it as a property so that our subclass // proc can get to this data. pbffFavSubStruct->lpfnOldWndProc = (WNDPROC) SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)BFFFavSubclass); pbffFavSubStruct->hwndNew = GetDlgItem(hwnd, IDC_FAVORITE_NEWFOLDER); pbffFavSubStruct->patf = patf; pbffFavSubStruct->hwndTV = GetDlgItem(hwnd, IDC_SUBSCRIBE_FOLDERLIST_PLACEHOLDER); pbffFavSubStruct->hwndSave = GetDlgItem(hwnd, IDC_FAVORITE_CREATEIN); pbffFavSubStruct->hTemplateWnd = hDlgTemp; //save for explicit destruction later GetWindowRect(hwnd, &(pbffFavSubStruct->rcRestored)); SetProp(hwnd, ADDTOFAVPROP, (HANDLE)pbffFavSubStruct); patf->pidlSelected = ILClone(patf->pidl); DWORD dwType, dwValue = 0, dwcData = sizeof(dwValue); TCHAR szBuffer[100]; SHRegGetUSValue(TEXT("Software\\Microsoft\\Internet Explorer\\Main"), TEXT("AddToFavoritesExpanded"), &dwType, &dwValue, &dwcData, 0, NULL, sizeof(dwValue)); if (dwValue == 0) { int lRet = MLLoadString(IDS_FAVS_ADVANCED_EXPAND, szBuffer, ARRAYSIZE(szBuffer)); ASSERT(lRet); // Disable the tree view and new button so that we can't tab to them. EnableWindow(GetDlgItem (hwnd, IDC_SUBSCRIBE_FOLDERLIST_PLACEHOLDER), FALSE); EnableWindow(GetDlgItem (hwnd, IDC_FAVORITE_NEWFOLDER), FALSE); // hide the bottom part of the dialog int cx, cy; RECT rc; GetWindowRect (GetDlgItem (hwnd, IDC_SUBSCRIBE_FOLDERLIST_PLACEHOLDER), &rc); cy = rc.top; GetWindowRect (hwnd, &rc); cx = rc.right - rc.left; cy = cy /*top of ctrl*/ - rc.top; /*top of window*/ SetWindowPos (hwnd, NULL, 0, 0, cx, cy, SWP_NOMOVE | SWP_NOZORDER); } else { //don't show New Folder button for channels in the channels folder, // see code for case IDC_FAVORITE_NEWFOLDER for why if (patf->subsType == SUBSTYPE_CHANNEL) { LPITEMIDLIST pidlFavs = NULL; TCHAR tzFavsPath[MAX_PATH]; if (SUCCEEDED(SHGetSpecialFolderLocation(hwnd, CSIDL_FAVORITES, &pidlFavs)) && SUCCEEDED(SHGetNameAndFlags(pidlFavs, SHGDN_FORPARSING | SHGDN_FORADDRESSBAR, tzFavsPath, SIZECHARS(tzFavsPath), NULL)) && 0 == StrCmpNI(tzFavsPath, patf->pszInitDir, ARRAYSIZE(tzFavsPath))) { EnableWindow(pbffFavSubStruct->hwndNew, TRUE); } else EnableWindow(pbffFavSubStruct->hwndNew, FALSE); if(pidlFavs) ILFree(pidlFavs); } else EnableWindow(pbffFavSubStruct->hwndNew, TRUE); int lRet = MLLoadString(IDS_FAVS_ADVANCED_COLLAPSE, szBuffer, ARRAYSIZE(szBuffer)); ASSERT(lRet); } SetWindowText(GetDlgItem(hwnd, IDC_FAVORITE_CREATEIN), szBuffer); } else { EndDialog(hwnd, IDCANCEL); } break; } case BFFM_SELCHANGED: { //the first of these comes during BFFM_INITIALIZED, so ignore it if (((ADDTOFAV *)lpData)->pidlSelected != NULL) { ILFree(((ADDTOFAV *)lpData)->pidlSelected); ((ADDTOFAV *)lpData)->pidlSelected = ILClone((LPITEMIDLIST)lParam); } break; } } return 0; } // This API is not exported. See below (DoAddToFavDlg) for the exported version // // hwnd parent window for the dialog. // pszInitDir input: initial path // output: fully qualified path and filename // chInitDir Length of pszInitDir buffer // pszFile initial (default) filename for shortcut // cchFile Length of pszFile buffer // pidlBrowse associated with pszInitDir // // Returns: // TRUE if a directory and filename were selected by user, and no error // occurs. In this case pszInitDir contains the new destination directory // and filename, pszFile contains the new file name. // // FALSE if an error occurs or the user selects CANCEL. STDAPI_(BOOL) DoAddToFavDlgEx(HWND hwnd, TCHAR *pszInitDir, UINT cchInitDir, TCHAR *pszFile, UINT cchFile, LPITEMIDLIST pidlBrowse, LPCITEMIDLIST pidlFavorite, FAVDLGTYPE atfDlgType, SUBSCRIPTIONINFO* pInfo) { ADDTOFAV atf = {pszInitDir, cchInitDir - 1, pszFile, cchFile - 1, pidlBrowse, NULL, pidlFavorite, atfDlgType, {sizeof(SUBSCRIPTIONINFO), 0}, SUBSTYPE_URL }; TCHAR szTemp[1]; //NOTE: we're not using SHBrowseForFolder's prompt string (see below) TCHAR szDisplayName[MAX_PATH]; BROWSEINFO bi = { hwnd, pidlBrowse, szDisplayName, szTemp, BIF_RETURNONLYFSDIRS, // (BFFCALLBACK) BFFFavCallback, (LPARAM)&atf, 0 }; LPITEMIDLIST pidl; if (pInfo) atf.siSubsInProg = *pInfo; switch (atfDlgType) { case ATF_CHANNEL_SOFTDIST: atf.bIsSoftdist = TRUE; // fall through case ATF_CHANNEL: atf.subsType = SUBSTYPE_CHANNEL; break; //default: // set in initialize to SUBSTYPE_URL } //this string is now in the template dialog in the .rc //REVIEW -- do we want to do it this way (we're hiding SHBrowse...'s control)? then the template dialog looks more like the finished product... szTemp[0] = 0; //init native font control, otherwise dialog may fail to initialize { INITCOMMONCONTROLSEX icc; icc.dwSize = sizeof(INITCOMMONCONTROLSEX); icc.dwICC = ICC_NATIVEFNTCTL_CLASS; InitCommonControlsEx(&icc); } pidl = SHBrowseForFolder(&bi); if (pidl) { ILFree(pidl); } // If the user created a new subscription, start a download. if (atf.bSubscribed && !atf.bStartSubscribed) { ISubscriptionMgr* pSubsMgr; if (SUCCEEDED (CoCreateInstance(CLSID_SubscriptionMgr, NULL, CLSCTX_INPROC_SERVER, IID_ISubscriptionMgr, (void**)&pSubsMgr))) { WCHAR wszURL[MAX_URL_STRING]; IEGetDisplayName(atf.pidlFavorite, wszURL, SHGDN_FORPARSING); pSubsMgr->UpdateSubscription(wszURL); pSubsMgr->Release(); } } return (pidl != NULL); } STDAPI_(BOOL) DoSafeAddToFavDlgEx(HWND hwnd, TCHAR *pszInitDir, UINT cchInitDir, TCHAR *pszFile, UINT cchFile, LPITEMIDLIST pidlBrowse, LPCITEMIDLIST pidlFavorite, FAVDLGTYPE atfDlgType, SUBSCRIPTIONINFO* pInfo) { BOOL fRet; if (IEIsLinkSafe(hwnd, pidlFavorite, ILS_ADDTOFAV)) { fRet = DoAddToFavDlgEx(hwnd, pszInitDir, cchInitDir, pszFile, cchFile, pidlBrowse, pidlFavorite, atfDlgType, pInfo); } else { fRet = FALSE; } return fRet; } // This API is exported so that it may be called by explorer and mshtml (and MSNVIEWR.EXE) // in addition to being called internally by shdocvw. // THEREFORE YOU MUST NOT CHANGE THE SIGNATURE OF THIS API // STDAPI_(BOOL) DoAddToFavDlg(HWND hwnd, CHAR *pszInitDir, UINT cchInitDir, CHAR *pszFile, UINT cchFile, LPITEMIDLIST pidlBrowse) { BOOL fRet; WCHAR szInitDir[MAX_PATH]; WCHAR szFile[MAX_PATH]; SHAnsiToUnicode(pszInitDir, szInitDir, ARRAYSIZE(szInitDir)); SHAnsiToUnicode(pszFile, szFile, ARRAYSIZE(szFile)); fRet = DoSafeAddToFavDlgEx(hwnd, szInitDir, ARRAYSIZE(szInitDir), szFile, ARRAYSIZE(szFile), pidlBrowse, NULL, ATF_FAVORITE, NULL); SHUnicodeToAnsi(szInitDir, pszInitDir, cchInitDir); SHUnicodeToAnsi(szFile, pszFile, cchFile); return fRet; } STDAPI_(BOOL) DoAddToFavDlgW(HWND hwnd, WCHAR *pszInitDir, UINT cchInitDir, WCHAR *pszFile, UINT cchFile, LPITEMIDLIST pidlBrowse) { return DoSafeAddToFavDlgEx(hwnd, pszInitDir, cchInitDir, pszFile, cchFile, pidlBrowse, NULL, ATF_FAVORITE, NULL); } STDAPI AddToFavoritesEx(HWND hwnd, LPCITEMIDLIST pidlCur, LPCTSTR pszTitle, DWORD dwFlags, SUBSCRIPTIONINFO *pInfo, IOleCommandTarget *pCommandTarget, IHTMLDocument2 *pDoc); STDAPI AddToChannelsEx (HWND hwnd, LPCITEMIDLIST pidlUrl, LPTSTR pszName, LPCWSTR pwszURL, DWORD dwFlags, SUBSCRIPTIONINFO* pInfo); STDAPI SubscribeFromFavorites (HWND hwnd, LPCITEMIDLIST pidlUrl, LPTSTR pszName, DWORD dwFlags, SUBSCRIPTIONTYPE subsType, SUBSCRIPTIONINFO *pInfo); // This API is exported privately, and is called by ISubscriptionMgr::CreateSubscription. // shuioc uses it too. STDAPI SHAddSubscribeFavoriteEx ( HWND hwnd, LPCWSTR pwszURL, LPCWSTR pwszName, DWORD dwFlags, SUBSCRIPTIONTYPE subsType, SUBSCRIPTIONINFO* pInfo, IOleCommandTarget *pcmdt, IHTMLDocument2 *pDoc) { TCHAR szName[MAX_PATH]; LPITEMIDLIST pidl = NULL; HRESULT hr; if (pwszURL==NULL || pwszName == NULL) return E_INVALIDARG; // // Need to put pwszName into a buffer because it comes in const // but gets modified in SubscribeFromFavorites. // hr = StringCchCopy(szName, ARRAYSIZE(szName), pwszName); if(SUCCEEDED(hr)) { hr = IECreateFromPath(pwszURL, &pidl); } if (SUCCEEDED(hr)) { ASSERT (pidl); if (dwFlags & CREATESUBS_FROMFAVORITES) { if (subsType != SUBSTYPE_URL && subsType != SUBSTYPE_CHANNEL) { ASSERT(0); hr = E_INVALIDARG; } else { hr = SubscribeFromFavorites (hwnd, pidl, szName, dwFlags, subsType, pInfo); } } else { if (subsType == SUBSTYPE_URL) { hr = AddToFavoritesEx (hwnd, pidl, szName, dwFlags, pInfo, pcmdt, pDoc); } else if (subsType == SUBSTYPE_CHANNEL && !SHIsRestricted2W(hwnd, REST_NoChannelUI, NULL, 0)) { hr = AddToChannelsEx (hwnd, pidl, szName, pwszURL, dwFlags, pInfo); } else { ASSERT (0); hr = E_INVALIDARG; } } ILFree(pidl); } return hr; } STDAPI SHAddSubscribeFavorite (HWND hwnd, LPCWSTR pwszURL, LPCWSTR pwszName, DWORD dwFlags, SUBSCRIPTIONTYPE subsType, SUBSCRIPTIONINFO* pInfo) { return SHAddSubscribeFavoriteEx ( hwnd, pwszURL, pwszName, dwFlags, subsType, pInfo, NULL, NULL); } // this API is also exported via the .def // Use for backward compatibility only -- note that it is only for URL's (not channels) // and doesn't know how to subscribe. STDAPI AddUrlToFavorites(HWND hwnd, LPWSTR pszUrlW, LPWSTR pszTitleW, BOOL fDisplayUI) { return SHAddSubscribeFavorite (hwnd, pszUrlW, pszTitleW, fDisplayUI ? CREATESUBS_NOUI : 0, SUBSTYPE_URL, NULL); } // this API is in the .h and is used elsewhere in shdocvw, but is not exported // Backward compatibility only -- only for URL's (not channels) and can subscribe, but can't // pass in subscriptioninfo starter. STDAPI AddToFavorites( HWND hwnd, LPCITEMIDLIST pidlCur, LPCTSTR pszTitle, BOOL fDisplayUI, IOleCommandTarget *pCommandTarget, IHTMLDocument2 *pDoc) { return AddToFavoritesEx (hwnd, pidlCur, pszTitle, fDisplayUI ? 0 : CREATESUBS_NOUI, NULL, pCommandTarget, pDoc); } //helper function to create one column in a ListView control, add one item to that column, //size the column to the width of the control, and color the control like a static... //basically, like SetWindowText for a ListView. Because we use a lot of ListViews to display //urls that would otherwise be truncated... the ListView gives us automatic ellipsis and ToolTip. void SetListViewToString (HWND hLV, LPCTSTR pszString) { ASSERT(hLV); LV_COLUMN lvc = {0}; RECT lvRect; GetClientRect (hLV, &lvRect); lvc.mask = LVCF_WIDTH; lvc.cx = lvRect.right - lvRect.left; if (-1 == ListView_InsertColumn(hLV, 0, &lvc)) { ASSERT(0); } SendMessage(hLV, LVM_SETEXTENDEDLISTVIEWSTYLE, LVS_EX_INFOTIP, LVS_EX_INFOTIP); LV_ITEM lvi = {0}; lvi.iSubItem = 0; lvi.pszText = (LPTSTR)pszString; lvi.mask = LVIF_TEXT; ListView_InsertItem(hLV, &lvi); ListView_EnsureVisible(hLV, 0, TRUE); ListView_SetBkColor(hLV, GetSysColor(COLOR_BTNFACE)); ListView_SetTextBkColor(hLV, GetSysColor(COLOR_BTNFACE)); } INT_PTR CALLBACK SubscribeFavoriteDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { ADDTOFAV * patf = (ADDTOFAV*)GetProp(hDlg, ADDTOFAVPROP); switch (uMsg) { case WM_INITDIALOG: { TCHAR szURL[MAX_URL_STRING]; patf = (ADDTOFAV*)lParam; SetProp(hDlg, ADDTOFAVPROP, (HANDLE)patf); //set up name and url displays SetDlgItemText (hDlg, IDC_CHANNEL_NAME, patf->pszFile); //url is in patf->pidlFavorite IEGetDisplayName(patf->pidlFavorite, szURL, SHGDN_FORPARSING); SetListViewToString (GetDlgItem (hDlg, IDC_CHANNEL_URL), szURL); //now the tricky part... this is for modifying the subscription associated with //an existing ChannelBar shortcut. We need to find out if they are subscribed -- //if so, load the existing subscription into memory so it can be modified in the //wizard. If not, leave the information that was passed up because it's got the //schedule extracted from the CDF. In either case, check the radio button that //corresponds to their current subscription level. ISubscriptionMgr* pSubsMgr; BOOL bSubs = FALSE; HRESULT hr = JITCoCreateInstance(CLSID_SubscriptionMgr, NULL, CLSCTX_INPROC_SERVER, IID_ISubscriptionMgr, (void**)&pSubsMgr, hDlg, FIEF_FLAG_FORCE_JITUI | FIEF_FLAG_PEEK); if (SUCCEEDED(hr)) { pSubsMgr->IsSubscribed(szURL, &bSubs); patf->bStartSubscribed = bSubs; pSubsMgr->Release(); } else if ((E_ACCESSDENIED == hr) || !IsBrowserFrameOptionsPidlSet(patf->pidlFavorite, BFO_USE_IE_OFFLINE_SUPPORT)) { EnableWindow(GetDlgItem(hDlg, IDC_MAKE_OFFLINE), FALSE); } if (!bSubs && patf->bIsSoftdist) { bSubs = TRUE; } CheckDlgButton(hDlg, IDC_MAKE_OFFLINE, bSubs ? 1 : 0); EnableWindow(GetDlgItem (hDlg, IDC_SUBSCRIBE_CUSTOMIZE), bSubs); } break; case WM_DESTROY: RemoveProp (hDlg, ADDTOFAVPROP); break; case WM_HELP: SHWinHelpOnDemandWrap((HWND)((LPHELPINFO) lParam)->hItemHandle, c_szHelpFile, HELP_WM_HELP, (DWORD_PTR)(LPTSTR) (patf->iDlgType == ATF_FAVORITE ? aAddToFavHelpIDs : aAddToChanHelpIDs)); return TRUE; break; case WM_CONTEXTMENU: SHWinHelpOnDemandWrap((HWND) wParam, c_szHelpFile, HELP_CONTEXTMENU, (DWORD_PTR)(LPVOID) (patf->iDlgType == ATF_FAVORITE ? aAddToFavHelpIDs : aAddToChanHelpIDs)); return TRUE; break; case WM_COMMAND: ASSERT (patf); switch (GET_WM_COMMAND_ID(wParam, lParam)) { case IDCANCEL: EndDialog(hDlg, IDCANCEL); break; case IDOK: // first, make sure they're not trying to subscribe to an authenticated // channel without entering a password. if (SubscriptionFailsChannelAuthentication (hDlg, &patf->siSubsInProg)) return FALSE; //find out whether they WERE subscribed, so if they click OK and they //were already subscribed, we delete that subscription -- and either leave it //deleted if "No subs" was the choice, or create the new one. ISubscriptionMgr* pSubsMgr; if (SUCCEEDED (JITCoCreateInstance(CLSID_SubscriptionMgr, NULL, CLSCTX_INPROC_SERVER, IID_ISubscriptionMgr, (void**)&pSubsMgr, hDlg, FIEF_FLAG_FORCE_JITUI))) { //url is in patf->pidlFavorite TCHAR szURL[MAX_URL_STRING]; IEGetDisplayName(patf->pidlFavorite, szURL, SHGDN_FORPARSING); BOOL bAlreadySubs; if (SUCCEEDED (pSubsMgr->IsSubscribed (szURL, &bAlreadySubs)) && bAlreadySubs) { pSubsMgr->DeleteSubscription(szURL, NULL); } pSubsMgr->Release(); } if (IsDlgButtonChecked (hDlg, IDC_MAKE_OFFLINE)) { //they want to subscribe! save subscription we already have in memory patf->bSubscribed = StartSiteSubscription (hDlg, patf, TRUE); } EndDialog(hDlg, IDOK); break; // common code with ATF dialog case IDC_SUBSCRIBE_CUSTOMIZE: //need to create -- but not store -- subscription //need to (temporarily) trash patf->pidlFavorite so that we can go through the //wizard without colliding with an existing subscription. When we actually create //the subscription, we'll use the real name. LPCITEMIDLIST pidlSave = patf->pidlFavorite; TCHAR szUrlTemp[MAX_URL_STRING+1]; IEGetDisplayName(patf->pidlFavorite, szUrlTemp, SHGDN_FORPARSING); if(SUCCEEDED(StringCchCat(szUrlTemp, ARRAYSIZE(szUrlTemp), TEXT(".")))) //just put something nearly invisible on the end { if (SUCCEEDED (IECreateFromPath(szUrlTemp, (LPITEMIDLIST*)&patf->pidlFavorite))) { if (StartSiteSubscription (hDlg, patf, FALSE)) SendMessage (hDlg, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(hDlg, IDOK), TRUE); ILFree ((LPITEMIDLIST)patf->pidlFavorite); } } patf->pidlFavorite = pidlSave; break; } break; } return FALSE; } static const int CREATESUBS_ACTIVATE = 0x8000; //hidden flag meaning channel is already on system STDAPI SubscribeFromFavorites (HWND hwnd, LPCITEMIDLIST pidlUrl, LPTSTR pszName, DWORD dwFlags, SUBSCRIPTIONTYPE subsType, SUBSCRIPTIONINFO *pInfo) { //used to subscribe to a channel that's already in the Favorites //or a URL that's already a Favorite //flags are same as ISubscriptionMgr::CreateSubscription //display our part of the fav's dialog -- no need to go through SHBrowseForFolder //or any of that, just our radio buttons in a fixed-size dialog with our own DlgProc INT_PTR iDlgResult; HRESULT hr = S_OK; ADDTOFAV atf = {0}; atf.pszFile = pszName; atf.siSubsInProg.cbSize = sizeof(SUBSCRIPTIONINFO); if (pInfo && pInfo->cbSize == sizeof(SUBSCRIPTIONINFO)) atf.siSubsInProg = *pInfo; atf.subsType = subsType; //figure out what dialog to use atf.iDlgType = (subsType == SUBSTYPE_URL ? ATF_FAVORITE : (dwFlags & CREATESUBS_ACTIVATE ? ATF_CHANNEL_MODIFY : ATF_CHANNEL)); // Do we potentially need ANOTHER dialog type for softdist channels? if (dwFlags & CREATESUBS_SOFTWAREUPDATE) { atf.bIsSoftdist = TRUE; } atf.pidlFavorite = pidlUrl; #ifdef OLD_FAVORITES int iTemplate; switch (atf.iDlgType) { case ATF_CHANNEL_SOFTDIST: // Inappropriate, but it doesn't currently get used case ATF_CHANNEL: iTemplate = IDD_SUBSCRIBE_FAV_CHANNEL; break; case ATF_CHANNEL_MODIFY: iTemplate = IDD_ACTIVATE_PLATINUM_CHANNEL; break; case ATF_FAVORITE: iTemplate = IDD_SUBSCRIBE_FAVORITE; break; } iDlgResult = DialogBoxParam(MLGetHinst(), MAKEINTRESOURCE(iTemplate), hwnd, SubscribeFavoriteDlgProc, (LPARAM)&atf); #endif iDlgResult = DialogBoxParam(MLGetHinst(), MAKEINTRESOURCE(IDD_ADDTOFAVORITES_TEMPLATE), hwnd, SubscribeFavoriteDlgProc, (LPARAM)&atf); switch (iDlgResult) { case -1: hr = E_FAIL; break; case IDCANCEL: hr = S_FALSE; break; default: if (pInfo && (pInfo->cbSize == sizeof(SUBSCRIPTIONINFO)) && (dwFlags & CREATESUBS_NOSAVE)) *pInfo = atf.siSubsInProg; hr = S_OK; break; } return hr; } STDAPI AddToChannelsEx (HWND hwnd, LPCITEMIDLIST pidlUrl, LPTSTR pszName, LPCWSTR pwszURL, DWORD dwFlags, SUBSCRIPTIONINFO* pInfo) { HRESULT hr = S_OK; IChannelMgrPriv* pIChannelMgrPriv; hr = JITCoCreateInstance(CLSID_ChannelMgr, NULL, CLSCTX_INPROC_SERVER, IID_IChannelMgrPriv, (void**)&pIChannelMgrPriv, hwnd, FIEF_FLAG_FORCE_JITUI); if (SUCCEEDED(hr)) { if (S_OK == pIChannelMgrPriv->IsChannelInstalled (pwszURL)) { hr = SubscribeFromFavorites (hwnd, pidlUrl, pszName, dwFlags | CREATESUBS_ACTIVATE, SUBSTYPE_CHANNEL, pInfo); } else { LPITEMIDLIST pidlChannelFolder; TCHAR szPath[MAX_PATH]; TCHAR szCFPath[MAX_PATH]; ASSERT(pIChannelMgrPriv); IChannelMgrPriv::CHANNELFOLDERLOCATION cflLocation = ((dwFlags & CREATESUBS_SOFTWAREUPDATE) ? IChannelMgrPriv::CF_SOFTWAREUPDATE : IChannelMgrPriv::CF_CHANNEL); hr = pIChannelMgrPriv->GetChannelFolder(&pidlChannelFolder, cflLocation); if (SUCCEEDED (hr)) { // // Change IChannelMgrPriv to unicode! This has to get fixed to // support a unicode "Channels" name. (edwardp) // CHAR szBuff[MAX_PATH]; hr = pIChannelMgrPriv->GetChannelFolderPath (szBuff, ARRAYSIZE(szBuff), cflLocation); if (SUCCEEDED(hr)) SHAnsiToUnicode(szBuff, szCFPath, ARRAYSIZE(szCFPath)); if (SUCCEEDED (hr)) { TCHAR szDspName[MAX_URL_STRING]; DWORD cchDspName = ARRAYSIZE(szDspName); hr = StringCchCopy(szPath, ARRAYSIZE(szPath), szCFPath); if(SUCCEEDED(hr)) { // When we create a short cut for the URL, we have to make sure it's readable for // the end user. PrepareURLForDisplay() will unescape the string if it's escaped. if (!UrlIs(pszName, URLIS_URL) || !PrepareURLForDisplay(pszName, szDspName, &cchDspName)) { // Unescaping wasn't wanted or didn't work. hr = StringCchCopy(szDspName, ARRAYSIZE(szDspName), pszName); } } if(SUCCEEDED(hr)) { PathCleanupSpec(szPath, szDspName); FAVDLGTYPE iDlgType = (dwFlags & CREATESUBS_SOFTWAREUPDATE ? ATF_CHANNEL_SOFTDIST : ATF_CHANNEL); if ((dwFlags & CREATESUBS_NOUI) || DoSafeAddToFavDlgEx(hwnd, szPath, ARRAYSIZE(szPath), szDspName, ARRAYSIZE(szDspName), pidlChannelFolder, pidlUrl, iDlgType, pInfo)) { //we create the channelbar entry here, instead of cdfview, because here //we know where in the channels folder the user wants it to go. IChannelMgr* pChannelMgr = NULL; hr = pIChannelMgrPriv->QueryInterface (IID_IChannelMgr, (void**)&pChannelMgr); if (SUCCEEDED (hr)) { //prepare strings PathRemoveExtension(szPath); //strip off absolute part of folder path, and convert to Unicode int cchCommon = PathCommonPrefix (szPath, szCFPath, NULL); //pack in the info we have CHANNELSHORTCUTINFO csiChannel = {0}; csiChannel.cbSize = sizeof(csiChannel); csiChannel.pszTitle = szPath + cchCommon; csiChannel.pszURL = (LPWSTR)pwszURL; csiChannel.bIsSoftware = (dwFlags & CREATESUBS_SOFTWAREUPDATE) ? TRUE : FALSE; //and tell the channel mgr to add the channel hr = pChannelMgr->AddChannelShortcut (&csiChannel); pChannelMgr->Release(); } } else { hr = S_FALSE; //no failure, but no add } } } ILFree (pidlChannelFolder); } } pIChannelMgrPriv->Release(); } return hr; } STDAPI AddToFavoritesEx( HWND hwnd, LPCITEMIDLIST pidlCur, LPCTSTR pszTitle, DWORD dwFlags, SUBSCRIPTIONINFO *pInfo, IOleCommandTarget *pCommandTarget, IHTMLDocument2 *pDoc) { HRESULT hres = S_FALSE; HRESULT hr; HCURSOR hCursorOld = SetCursor(LoadCursor(NULL, IDC_WAIT)); if (pidlCur) { TCHAR szName[MAX_URL_STRING]; TCHAR szPath[MAX_PATH]; if (pszTitle) { hr = StringCchCopy(szName, ARRAYSIZE(szName), pszTitle); if(FAILED (hr)) { SetCursor(hCursorOld); return hr; } } else { szName[0] = 0; IEGetNameAndFlags(pidlCur, SHGDN_INFOLDER | SHGDN_NORMAL, szName, SIZECHARS(szName), NULL); } LPITEMIDLIST pidlFavorites; if (SHGetSpecialFolderPath(NULL, szPath, CSIDL_FAVORITES, TRUE) && (pidlFavorites = SHCloneSpecialIDList(NULL, CSIDL_FAVORITES, TRUE))) { TCHAR szDspName[MAX_PATH]; DWORD cchDspName = ARRAYSIZE(szDspName); // When we create a short cut for the URL, we have to make sure it's readable for // the end user. PrepareURLForDisplay() will unescape the string if it's escaped. if (!UrlIs(szName, URLIS_URL) || !PrepareURLForDisplay(szName, szDspName, &cchDspName)) { // Unescaping wasn't wanted or didn't work. hr = StringCchCopy(szDspName, ARRAYSIZE(szDspName), szName); if(FAILED(hr)) { ILFree(pidlFavorites); SetCursor(hCursorOld); return hr; } } PathCleanupSpec(szPath, szDspName); // if left with spaces only, use the filename friendly version of the url instead. StrTrim(szDspName, L" "); if (szDspName[0] == 0) { if (SUCCEEDED(IEGetNameAndFlags(pidlCur, SHGDN_FORPARSING, szDspName, ARRAYSIZE(szDspName), NULL))) PathCleanupSpec(szPath, szDspName); } BOOL fDisplayUI = (dwFlags & CREATESUBS_NOUI) ? FALSE : TRUE; if (!fDisplayUI || DoSafeAddToFavDlgEx(hwnd, szPath, ARRAYSIZE(szPath), szDspName, ARRAYSIZE(szDspName), pidlFavorites, pidlCur, ATF_FAVORITE, NULL)) { if (fDisplayUI) PathRemoveFileSpec(szPath); ISHCUT_PARAMS ShCutParams = {0}; PathRemoveExtension(szDspName); ShCutParams.pidlTarget = pidlCur; ShCutParams.pszTitle = PathFindFileName(szDspName); ShCutParams.pszDir = szPath; ShCutParams.pszOut = NULL; ShCutParams.bUpdateProperties = FALSE; ShCutParams.bUniqueName = FALSE; ShCutParams.bUpdateIcon = TRUE; ShCutParams.pCommand = pCommandTarget; ShCutParams.pDoc = pDoc; hres = CreateShortcutInDirEx(&ShCutParams); if (fDisplayUI && FAILED(hres)) { IE_ErrorMsgBox(NULL, hwnd, GetLastError(), NULL, szDspName, IDS_FAV_UNABLETOCREATE, MB_OK| MB_ICONSTOP); } } else { hres = S_FALSE; } ILFree(pidlFavorites); } } SetCursor(hCursorOld); return hres; } BOOL IsSubscribed(ADDTOFAV *patf) { BOOL bSubscribed = FALSE; TCHAR szURL[MAX_URL_STRING]; if (SUCCEEDED(IEGetDisplayName(patf->pidlFavorite, szURL, SHGDN_FORPARSING))) { ISubscriptionMgr *pSubscriptionMgr; if (SUCCEEDED(CoCreateInstance(CLSID_SubscriptionMgr, NULL, CLSCTX_INPROC_SERVER, IID_ISubscriptionMgr, (void**)&pSubscriptionMgr))) { BSTR bstrURL = SysAllocStringT(szURL); if (bstrURL) { if (SUCCEEDED(pSubscriptionMgr->IsSubscribed(bstrURL, &bSubscribed)) && bSubscribed) { patf->siSubsInProg.fUpdateFlags = SUBSINFO_ALLFLAGS; pSubscriptionMgr->GetSubscriptionInfo(bstrURL, &patf->siSubsInProg); } SysFreeString(bstrURL); } pSubscriptionMgr->Release(); } } return bSubscribed; } BOOL IsSubscribed(LPWSTR pwzUrl) { #ifndef DISABLE_SUBSCRIPTIONS BOOL bSubscribed = FALSE; ISubscriptionMgr * pSubscriptionMgr; if (FAILED(CoCreateInstance(CLSID_SubscriptionMgr, NULL, CLSCTX_INPROC_SERVER, IID_ISubscriptionMgr, (void**)&pSubscriptionMgr))) { return FALSE; } pSubscriptionMgr->IsSubscribed(pwzUrl, &bSubscribed); pSubscriptionMgr->Release(); return bSubscribed; #else /* !DISABLE_SUBSCRIPTIONS */ return FALSE; #endif /* !DISABLE_SUBSCRIPTIONS */ }