diff --git a/DIST/with-mdc.deploy b/DIST/with-mdc.deploy index 662fdea..ceac91f 100644 --- a/DIST/with-mdc.deploy +++ b/DIST/with-mdc.deploy @@ -4,7 +4,7 @@ faddenSoft http://www.faddensoft.com/ CiderPress http://a2ciderpress.com/ -4.0.0b1 +4.0.0b2 42016 C:\DATA\faddenSoft\fs.ico Copyright © 2015 CiderPress project authors. All rights reserved. @@ -355,7 +355,7 @@ FALSE 4095 -Setup400b1.exe +Setup400b2.exe FALSE diff --git a/app/AppleSingleArchive.cpp b/app/AppleSingleArchive.cpp index c538888..43c9bb9 100644 --- a/app/AppleSingleArchive.cpp +++ b/app/AppleSingleArchive.cpp @@ -441,7 +441,7 @@ bool AppleSingleArchive::CreateEntry() } pNewEntry->SetCompressedLen(dataLen + rsrcLen); - if (rsrcLen > 0) { + if (rsrcLen > 0) { // could do ">=" to preserve empty resource forks pNewEntry->SetRecordKind(GenericEntry::kRecordKindForkedFile); } else { pNewEntry->SetRecordKind(GenericEntry::kRecordKindFile); @@ -460,6 +460,10 @@ bool AppleSingleArchive::CreateEntry() CStringA fileNameA(fileName); pNewEntry->SetPathNameMOR(fileNameA); } + + // This doesn't matter, since we only have the file name, but it keeps + // the entry from getting a weird default. + pNewEntry->SetFssep(':'); AddEntry(pNewEntry); return true; diff --git a/app/ChooseDirDialog.cpp b/app/ChooseDirDialog.cpp deleted file mode 100644 index 7e3d08c..0000000 --- a/app/ChooseDirDialog.cpp +++ /dev/null @@ -1,153 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -#include "stdafx.h" -#include "ChooseDirDialog.h" -#include "NewFolderDialog.h" -#include "DiskFSTree.h" - -BEGIN_MESSAGE_MAP(ChooseDirDialog, CDialog) - ON_NOTIFY(TVN_SELCHANGED, IDC_CHOOSEDIR_TREE, OnSelChanged) - ON_BN_CLICKED(IDC_CHOOSEDIR_EXPAND_TREE, OnExpandTree) - ON_BN_CLICKED(IDC_CHOOSEDIR_NEW_FOLDER, OnNewFolder) - ON_WM_HELPINFO() - //ON_COMMAND(ID_HELP, OnIDHelp) - ON_BN_CLICKED(IDHELP, OnHelp) -END_MESSAGE_MAP() - - -BOOL ChooseDirDialog::OnInitDialog(void) -{ - CDialog::OnInitDialog(); - - /* set up the "new folder" button */ - fNewFolderButton.ReplaceDlgCtrl(this, IDC_CHOOSEDIR_NEW_FOLDER); - fNewFolderButton.SetBitmapID(IDB_NEW_FOLDER); - - /* replace the tree control with a ShellTree */ - if (fShellTree.ReplaceDlgCtrl(this, IDC_CHOOSEDIR_TREE) != TRUE) { - LOGI("WARNING: ShellTree replacement failed"); - ASSERT(false); - } - - //enable images - fShellTree.EnableImages(); - //populate for the with Shell Folders for the first time - fShellTree.PopulateTree(/*CSIDL_DRIVES*/); - - if (fPathName.IsEmpty()) { - // start somewhere reasonable - fShellTree.ExpandMyComputer(); - } else { - CString msg(""); - fShellTree.TunnelTree(fPathName, &msg); - if (!msg.IsEmpty()) { - /* failed */ - LOGI("TunnelTree failed on '%ls' (%ls), using MyComputer instead", - (LPCWSTR) fPathName, (LPCWSTR) msg); - fShellTree.ExpandMyComputer(); - } - } - - fShellTree.SetFocus(); - return FALSE; // leave focus on shell tree -} - -BOOL ChooseDirDialog::PreTranslateMessage(MSG* pMsg) -{ - if (pMsg->message == WM_KEYDOWN && - pMsg->wParam == VK_RETURN) - { - //LOGI("RETURN!"); - if (GetFocus() == GetDlgItem(IDC_CHOOSEDIR_PATHEDIT)) { - OnExpandTree(); - return TRUE; - } - } - - return CDialog::PreTranslateMessage(pMsg); -} - -void ChooseDirDialog::OnSelChanged(NMHDR* pnmh, LRESULT* pResult) -{ - CString path; - CWnd* pWnd = GetDlgItem(IDC_CHOOSEDIR_PATH); - ASSERT(pWnd != NULL); - - if (fShellTree.GetFolderPath(&path)) - fPathName = path; - else - fPathName = L""; - pWnd->SetWindowText(fPathName); - - // disable the "Select" button when there's no path ready - pWnd = GetDlgItem(IDOK); - ASSERT(pWnd != NULL); - pWnd->EnableWindow(!fPathName.IsEmpty()); - - // It's confusing to have two different paths showing, so wipe out the - // free entry field when the selection changes. - pWnd = GetDlgItem(IDC_CHOOSEDIR_PATHEDIT); - pWnd->SetWindowText(L""); - - *pResult = 0; -} - -void ChooseDirDialog::OnExpandTree(void) -{ - CWnd* pWnd; - CString str; - CString msg; - - pWnd = GetDlgItem(IDC_CHOOSEDIR_PATHEDIT); - ASSERT(pWnd != NULL); - pWnd->GetWindowText(str); - - if (!str.IsEmpty()) { - fShellTree.TunnelTree(str, &msg); - if (!msg.IsEmpty()) { - CString failed; - CheckedLoadString(&failed, IDS_FAILED); - MessageBox(msg, failed, MB_OK | MB_ICONERROR); - } - } -} - -void ChooseDirDialog::OnNewFolder(void) -{ - if (fPathName.IsEmpty()) { - MessageBox(L"You can't create a folder in this part of the tree.", - L"Bad Location", MB_OK | MB_ICONERROR); - return; - } - - NewFolderDialog newFolderDlg; - - newFolderDlg.fCurrentFolder = fPathName; - if (newFolderDlg.DoModal() == IDOK) { - if (newFolderDlg.GetFolderCreated()) { - /* - * They created a new folder. We want to add it to the tree - * and then select it. This is not too hard because we know - * that the folder was created under the currently-selected - * tree node. - */ - if (fShellTree.AddFolderAtSelection(newFolderDlg.fNewFolder)) { - CString msg; - LOGI("Success, tunneling to '%ls'", - (LPCWSTR) newFolderDlg.fNewFullPath); - fShellTree.TunnelTree(newFolderDlg.fNewFullPath, &msg); - if (!msg.IsEmpty()) { - LOGI("TunnelTree failed: %ls", (LPCWSTR) msg); - } - } else { - LOGI("AddFolderAtSelection FAILED"); - ASSERT(false); - } - } else { - LOGI("NewFolderDialog returned IDOK but no create"); - } - } -} diff --git a/app/ChooseDirDialog.h b/app/ChooseDirDialog.h index 6377490..1e603f1 100644 --- a/app/ChooseDirDialog.h +++ b/app/ChooseDirDialog.h @@ -11,65 +11,47 @@ #include "../util/UtilLib.h" #include "resource.h" +#include + /* * Choose a directory. This is distinctly different from what the standard * "Open" and "Save As" dialogs do, because those want to choose normal files * only, while this wants to select a folder. * - * TODO: Vista-style dialogs support folder selection. Consider switching - * based on OS version. + * Win2K added the shell "browse for folder" dialog, which does exactly + * what we want. */ -class ChooseDirDialog : public CDialog { +class ChooseDirDialog { public: - ChooseDirDialog(CWnd* pParent = NULL, int dialogID = IDD_CHOOSEDIR) : - CDialog(dialogID, pParent) - { - fPathName = L""; + ChooseDirDialog(CWnd* pParent = NULL) { + fpParent = pParent; } - virtual ~ChooseDirDialog(void) {} + ~ChooseDirDialog() {} - const WCHAR* GetPathName(void) const { return fPathName; } - - // set the pathname; when DoModal is called this will tunnel in - void SetPathName(const WCHAR* str) { fPathName = str; } - -protected: - virtual BOOL OnInitDialog(void) override; - - // Special handling for "return" key. - virtual BOOL PreTranslateMessage(MSG* pMsg) override; - - /* - * Replace the ShellTree's default SELCHANGED handler with this so we can - * track changes to the edit control. - */ - afx_msg void OnSelChanged(NMHDR* pnmh, LRESULT* pResult); - - // User pressed "Expand Tree" button. - afx_msg void OnExpandTree(void); - - // User pressed "New Folder" button. - afx_msg void OnNewFolder(void); - - // User pressed "Help" button. - afx_msg void OnHelp(void) { - MyApp::HandleHelp(this, HELP_TOPIC_CHOOSE_FOLDER); + // Gets the pathname. Call this after DoModal has updated it. + const CString& GetPathName(void) const { + return fPathName; } - // F1 key hit, or '?' button in title bar used to select help for an - // item in the dialog. For ON_WM_HELPINFO. - afx_msg BOOL OnHelpInfo(HELPINFO* lpHelpInfo) { - return MyApp::HandleHelpInfo(lpHelpInfo); + // Sets the pathname. Call before DoModal(). + void SetPathName(const CString& str) { + fPathName = str; + } + + // Returns false if nothing was selected (e.g. the dialog was canceled). + BOOL DoModal() { + CShellManager* pMan = gMyApp.GetShellManager(); + CString outFolder; + BOOL result = pMan->BrowseForFolder(outFolder, fpParent, fPathName, + L"Select folder:", BIF_RETURNONLYFSDIRS | BIF_USENEWUI); + fPathName = outFolder; + return result; } private: - CString fPathName; - - ShellTree fShellTree; - MyBitmapButton fNewFolderButton; - - DECLARE_MESSAGE_MAP() + CWnd* fpParent; + CString fPathName; }; #endif /*APP_CHOOSEDIRDIALOG*/ diff --git a/app/CiderPress.rc b/app/CiderPress.rc index 9a86ac2..7759002 100644 --- a/app/CiderPress.rc +++ b/app/CiderPress.rc @@ -483,19 +483,6 @@ BEGIN PUSHBUTTON "Expand Tree",IDC_CHOOSEDIR_EXPAND_TREE,198,208,50,14 END -IDD_NEWFOLDER DIALOG 0, 0, 251, 69 -STYLE DS_SETFONT | DS_MODALFRAME | DS_CONTEXTHELP | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU -CAPTION "Create New Folder" -FONT 8, "MS Sans Serif" -BEGIN - EDITTEXT IDC_NEWFOLDER_NAME,6,47,177,14,ES_AUTOHSCROLL - DEFPUSHBUTTON "OK",IDOK,193,7,50,14 - PUSHBUTTON "Cancel",IDCANCEL,193,24,50,14 - LTEXT "Current folder:",IDC_STATIC,6,7,177,8 - EDITTEXT IDC_NEWFOLDER_CURDIR,6,16,177,14,ES_AUTOHSCROLL | ES_READONLY - LTEXT "New folder name (will be created in current folder):",IDC_STATIC,6,37,177,8 -END - IDD_EXTRACT_FILES DIALOG 0, 0, 299, 242 STYLE DS_SETFONT | DS_MODALFRAME | DS_CONTEXTHELP | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU CAPTION "Extract Files" @@ -1439,14 +1426,6 @@ BEGIN BOTTOMMARGIN, 246 END - IDD_NEWFOLDER, DIALOG - BEGIN - LEFTMARGIN, 6 - RIGHTMARGIN, 243 - TOPMARGIN, 7 - BOTTOMMARGIN, 61 - END - IDD_EXTRACT_FILES, DIALOG BEGIN LEFTMARGIN, 7 diff --git a/app/MyApp.cpp b/app/MyApp.cpp index d75ea44..4f7e053 100644 --- a/app/MyApp.cpp +++ b/app/MyApp.cpp @@ -26,7 +26,7 @@ DebugLog* gDebugLog; * This is the closest thing to "main" that we have, but we * should wait for InitInstance for most things. */ -MyApp::MyApp(LPCTSTR lpszAppName) : CWinApp(lpszAppName) +MyApp::MyApp(LPCTSTR lpszAppName) : CWinAppEx(true /*lpszAppName*/) { gDebugLog = new DebugLog(L"C:\\src\\cplog.txt"); @@ -262,8 +262,8 @@ BOOL MyApp::OnIdle(LONG lCount) IDC_FVIEW_FONT, IDH_FVIEW_FONT, IDC_FVIEW_NEXT, IDH_FVIEW_NEXT, IDC_FVIEW_PREV, IDH_FVIEW_PREV, - IDC_NEWFOLDER_CURDIR, IDH_NEWFOLDER_CURDIR, - IDC_NEWFOLDER_NAME, IDH_NEWFOLDER_NAME, + //IDC_NEWFOLDER_CURDIR, IDH_NEWFOLDER_CURDIR, // dialog removed + //IDC_NEWFOLDER_NAME, IDH_NEWFOLDER_NAME, // dialog removed IDC_EXT_PATH, IDH_EXT_PATH, IDC_EXT_CONVEOLTEXT, IDH_EXT_CONVEOLTEXT, IDC_EXT_CONVEOLALL, IDH_EXT_CONVEOLALL, diff --git a/app/MyApp.h b/app/MyApp.h index dff7416..ff22e5e 100644 --- a/app/MyApp.h +++ b/app/MyApp.h @@ -20,7 +20,7 @@ /* * Windows application object. */ -class MyApp: public CWinApp +class MyApp: public CWinAppEx { public: MyApp(LPCTSTR lpszAppName = NULL); diff --git a/app/NewFolderDialog.cpp b/app/NewFolderDialog.cpp deleted file mode 100644 index 9523d17..0000000 --- a/app/NewFolderDialog.cpp +++ /dev/null @@ -1,68 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -#include "stdafx.h" -#include "NewFolderDialog.h" - -BEGIN_MESSAGE_MAP(NewFolderDialog, CDialog) - ON_WM_HELPINFO() -END_MESSAGE_MAP() - -void NewFolderDialog::DoDataExchange(CDataExchange* pDX) -{ - /* - * It is very important to keep '\\' out of the folder path, because it allows - * for all sorts of behavior (like "..\foo" or "D:\ack") that the caller - * might not be expecting. For example, if it's displaying a tree, it - * might assume that the folder goes under the currently selected node. - * - * Under WinNT, '/' is regarded as equivalent to '\', so we have to block - * that as well. - * - * Other characters (':') are also dangerous, but so long as we start with - * a valid path, Windows will prevent them from being used where they are - * inappropriate. - */ - - if (!pDX->m_bSaveAndValidate) - DDX_Text(pDX, IDC_NEWFOLDER_CURDIR, fCurrentFolder); - - DDX_Text(pDX, IDC_NEWFOLDER_NAME, fNewFolder); - - /* validate the new folder by creating it */ - if (pDX->m_bSaveAndValidate) { - if (fNewFolder.IsEmpty()) { - MessageBox(L"No name entered, not creating new folder.", - L"CiderPress", MB_OK); - // fall out of DoModal with fFolderCreated==false - } else if (fNewFolder.Find('\\') >= 0 || - fNewFolder.Find('/') >= 0) - { - MessageBox(L"Folder names may not contain '/' or '\\'.", - L"CiderPress", MB_OK); - pDX->Fail(); - } else { - fNewFullPath = fCurrentFolder; - if (fNewFullPath.Right(1) != "\\") - fNewFullPath += "\\"; - fNewFullPath += fNewFolder; - LOGI("CREATING '%ls'", (LPCWSTR) fNewFullPath); - if (!::CreateDirectory(fNewFullPath, NULL)) { - /* show the sometimes-bizarre Windows error string */ - CString msg, errStr, failed; - DWORD dwerr = ::GetLastError(); - GetWin32ErrorString(dwerr, &errStr); - msg.Format(L"Unable to create folder '%ls': %ls", - (LPCWSTR) fNewFolder, (LPCWSTR) errStr); - CheckedLoadString(&failed, IDS_FAILED); - MessageBox(msg, failed, MB_OK | MB_ICONERROR); - pDX->Fail(); - } else { - /* success! */ - fFolderCreated = true; - } - } - } -} diff --git a/app/NewFolderDialog.h b/app/NewFolderDialog.h deleted file mode 100644 index c5575bd..0000000 --- a/app/NewFolderDialog.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Allow the user to create a new folder. - */ -#ifndef APP_NEWFOLDERDIALOG_H -#define APP_NEWFOLDERDIALOG_H - -#include "resource.h" - -/* - * Create a new folder in an existing location. - * - * Expects, but does not verify, that "fCurrentFolder" is set to a valid - * path before DoModal is called. - */ -class NewFolderDialog : public CDialog { -public: - NewFolderDialog(CWnd* pParent = NULL) : CDialog(IDD_NEWFOLDER, pParent) { - fCurrentFolder = L""; - fNewFolder = L""; - fFolderCreated = false; - } - virtual ~NewFolderDialog(void) {} - - bool GetFolderCreated(void) const { return fFolderCreated; } - - // set to CWD before calling DoModal - CString fCurrentFolder; - - // filename (NOT pathname) of new folder (DDXed in edit ctrl) - CString fNewFolder; - - // full pathname of new folder, valid if fFolderCreated is true - CString fNewFullPath; - -protected: - void DoDataExchange(CDataExchange* pDX) override; - - afx_msg BOOL OnHelpInfo(HELPINFO* lpHelpInfo) { - return MyApp::HandleHelpInfo(lpHelpInfo); - } - - // on exit, set to "true" if we created the folder in "fNewFolder" - bool fFolderCreated; - - DECLARE_MESSAGE_MAP() -}; - -#endif /*APP_NEWFOLDERDIALOG_H*/ diff --git a/app/StdAfx.h b/app/StdAfx.h index 6b12869..f833e50 100644 --- a/app/StdAfx.h +++ b/app/StdAfx.h @@ -24,6 +24,7 @@ #include "targetver.h" #include +#include #include #include #include diff --git a/app/app.vcxproj b/app/app.vcxproj index 30d75e1..e65ec11 100644 --- a/app/app.vcxproj +++ b/app/app.vcxproj @@ -194,7 +194,6 @@ - @@ -266,7 +265,6 @@ - @@ -292,7 +290,6 @@ - diff --git a/app/app.vcxproj.filters b/app/app.vcxproj.filters index 7d9a972..7617344 100644 --- a/app/app.vcxproj.filters +++ b/app/app.vcxproj.filters @@ -134,9 +134,6 @@ Header Files - - Header Files - Header Files @@ -282,9 +279,6 @@ Source Files - - Source Files - Source Files @@ -360,9 +354,6 @@ Source Files - - Source Files - Source Files diff --git a/app/resource.h b/app/resource.h index 49d3bc5..ec32bc0 100644 --- a/app/resource.h +++ b/app/resource.h @@ -25,7 +25,6 @@ #define IDD_CHOOSEDIR 135 #define IDB_NEW_FOLDER 139 #define IDB_CHOOSE_FOLDER 140 -#define IDD_NEWFOLDER 141 #define IDD_EXTRACT_FILES 142 #define IDD_ACTION_PROGRESS 143 #define IDD_CONFIRM_OVERWRITE 144 @@ -157,8 +156,6 @@ #define IDC_FVIEW_FONT 1129 #define IDC_FVIEW_NEXT 1130 #define IDC_FVIEW_PREV 1131 -#define IDC_NEWFOLDER_CURDIR 1132 -#define IDC_NEWFOLDER_NAME 1133 #define IDC_EXT_PATH 1136 #define IDC_EXT_CONVEOLTEXT 1137 #define IDC_EXT_CONVEOLALL 1138 diff --git a/reformat/StdAfx.h b/reformat/StdAfx.h index f108905..e5c34c3 100644 --- a/reformat/StdAfx.h +++ b/reformat/StdAfx.h @@ -24,8 +24,10 @@ #include #include #include -#include -#include // for some stuff in util lib +#include // for some stuff in util lib +#include +#include +#include #include "../util/UtilLib.h" diff --git a/util/ShellTree.cpp b/util/ShellTree.cpp deleted file mode 100644 index ae4a7d8..0000000 --- a/util/ShellTree.cpp +++ /dev/null @@ -1,1413 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * ShellTree, a TreeCtrl derivative for displaying the Windows shell namespace. - */ -#include "StdAfx.h" -#include "ShellTree.h" -#include "Pidl.h" -#include "PathName.h" - - -/* - * ========================================================================== - * ShellTree - * ========================================================================== - */ - -BEGIN_MESSAGE_MAP(ShellTree, CTreeCtrl) - ON_NOTIFY_REFLECT(TVN_ITEMEXPANDING, OnFolderExpanding) - ON_NOTIFY_REFLECT(TVN_DELETEITEM, OnDeleteShellItem) - ON_NOTIFY_REFLECT_EX(TVN_SELCHANGED, OnSelectionChange) -END_MESSAGE_MAP() - - -BOOL ShellTree::ReplaceDlgCtrl(CDialog* pDialog, int treeID) -{ - CWnd* pWnd = pDialog->GetDlgItem(treeID); - if (pWnd == NULL) - return FALSE; - -#if 0 - DWORD styles = pWnd->GetStyle(); - DWORD stylesEx = pWnd->GetExStyle(); - CRect rect; - pWnd->GetWindowRect(&rect); - pDialog->ScreenToClient(&rect); - - pWnd->DestroyWindow(); - CreateEx(stylesEx, WC_TREEVIEW, NULL, styles, rect, pDialog, treeID); -#endif - - /* latch on to their window handle */ - Attach(pWnd->m_hWnd); - - return TRUE; -} - -BOOL ShellTree::PopulateTree(int nFolder) -{ - LPSHELLFOLDER lpsf = NULL, lpsf2 = NULL; - LPITEMIDLIST lpi = NULL; - TV_SORTCB tvscb; - LPMALLOC lpMalloc = NULL; - HRESULT hr; - BOOL retval = FALSE; - - // Grab a malloc handle. - hr = ::SHGetMalloc(&lpMalloc); - if (FAILED(hr)) - return FALSE; - - // Get a pointer to the desktop folder. - hr = SHGetDesktopFolder(&lpsf); - if (FAILED(hr)) - goto bail; - - // Initialize the tree view to be empty. - DeleteAllItems(); - - if (nFolder == CSIDL_DESKTOP) { - // already done - lpsf2 = lpsf; - lpsf = NULL; - ASSERT(lpi == NULL); - } else { - // find the desired special folder - hr = SHGetSpecialFolderLocation(m_hWnd, nFolder, &lpi); - if (FAILED(hr)) { - LOGI("BUG: could not find requested special folder"); - goto bail; - } - - // bind a ShellFolder to the PIDL. - hr = lpsf->BindToObject(lpi, 0, IID_IShellFolder, (LPVOID *)&lpsf2); - if (FAILED(hr)) - goto bail; - } - - // fill in the tree starting from this point - FillTreeView(lpsf2, lpi, TVI_ROOT); - - // Sort the items in the tree view - tvscb.hParent = TVI_ROOT; - tvscb.lParam = 0; - tvscb.lpfnCompare = TreeViewCompareProc; - SortChildrenCB(&tvscb); - -bail: - if (lpsf != NULL) - lpsf->Release(); - if (lpsf != NULL) - lpsf2->Release(); - lpMalloc->Free(lpi); - - return retval; -} - -void ShellTree::ExpandMyComputer(void) -{ - HTREEITEM hItem; - hItem = FindMyComputer(); - if (hItem == NULL) - hItem = GetRootItem(); - Expand(hItem, TVE_EXPAND); - Select(hItem, TVGN_CARET); -} - -void ShellTree::FillTreeView(LPSHELLFOLDER lpsf, LPITEMIDLIST lpifq, - HTREEITEM hParent) -{ - CWaitCursor wait; - HTREEITEM hPrev = NULL; // Previous Item Added. - LPENUMIDLIST lpe=NULL; - LPITEMIDLIST lpi=NULL; - LPMALLOC lpMalloc=NULL; - ULONG ulFetched; - HRESULT hr; - HWND hwnd=::GetParent(m_hWnd); - bool gotOne = false; - - // Allocate a shell memory object. - hr = ::SHGetMalloc(&lpMalloc); - if (FAILED(hr)) - return; - - // Get the IEnumIDList object for the given folder. - hr = lpsf->EnumObjects(hwnd, - SHCONTF_FOLDERS | SHCONTF_NONFOLDERS | SHCONTF_INCLUDEHIDDEN, - &lpe); - - if (SUCCEEDED(hr)) - { - // Enumerate throught the list of folder and non-folder objects. - while (S_OK == lpe->Next(1, &lpi, &ulFetched)) - { - //Create a fully qualified path to the current item - //The SH* shell api's take a fully qualified path pidl, - //(see GetIcon above where I call SHGetFileInfo) whereas the - //interface methods take a relative path pidl. - ULONG ulAttrs = SFGAO_HASSUBFOLDER | SFGAO_FOLDER | - SFGAO_FILESYSANCESTOR | SFGAO_DROPTARGET | SFGAO_HIDDEN; - bool goodOne; - - // Determine what type of object we have. - lpsf->GetAttributesOf(1, (const struct _ITEMIDLIST **)&lpi, &ulAttrs); - -#if 1 - { /* DEBUG */ - CString name; - if (Pidl::GetName(lpsf, lpi, SHGDN_NORMAL, &name)) { - LOGD(" Checking '%ls' %08lx", (LPCWSTR) name, ulAttrs); - } else { - LOGD(" Checking 0x%08lx", ulAttrs); - } - } -#endif - - /* - * (This should be converted to a table and automatically - * scanned when assertions are enabled.) - * - * Win2K folders to show: - * 'My Computer' 0xb0000100 [at root] - * 'My Network Places' 0xb0000100 [at root] - * 'My Documents' 0xb0000100 [at root] - * 'WIN (C:)' 0xb0000100 - * 'Compact Disc (H:)' 0xb0000100 <-- SFGAO_REMOVABLE not set - * 'Removable Disk (L:)' 0xb0000100 - * 'Documents and Settings' 0xf0400177 - * 'Entire Network' 0xb0000000 - * 'Microsoft Windows Network' 0xb0000000 - * 'Computers Near Me' 0xa0000100 - * 'ftp.apple.asimov.net' 0xa0000100 - * 'QA-C-Recv on QA' 0xa0000100 - * 'BACKUP' 0xf0400177 - * 'dudley' 0x70400177 - * - * Win2K files, folders, etc. to hide: - * 'gVim 6.1' 0x00000100 - * 'Internet Explorer' 0x20000000 - * 'Recycle Bin' 0x20000100 (folder + droptarget) - * 'Control Panel' 0xa0000000 - * 'Scheduled Tasks on Shiny' 0x20000000 - * 'Add Network Place' 0x00000000 - * 'ColorBars.jpg' 0x40400177 - * - * Win98 folders to show: - * 'My Computer' 0xb0000100 [at root] - * 'My Documents' 0xb0000100 [at root] - * 'Network Neighborhood' 0xb0000100 [at root] - * 'WIN98 (C:)' 0xb0000100 - * [C:\]'My Documents' 0xf8000177 - * 'Entire Network' 0xb0000000 - * 'cpt' 0xe0000177 - * 'My eBooks' 0x60000177 - * - * Win98 folders and stuff to hide: - * 'Control Panel' 0x20000000 - * 'Printers' 0x20000100 (folder + droptarget) - * 'Web Folders' 0xa0000000 - * 'BOOTLOG.TXT' 0x40080177 - * - * Note that Win98 folders on disk don't have FILESYSANCESTOR - * set. If we check FOLDER && FILESYSTEM (0x60000000), we get - * anything starting with 6/7/E/F, which appears safe. We - * need to do additional tests to pick up some of the A/B items - * that we want while hiding the A items we don't want. - * - * FILESYSANCESTOR is 0x10000000, so that plus FOLDER allows - * 3/7/b/f. These appear to be entirely okay. - * - * DROPTARGET is 0x00000100, and HASSUBFOLDER is 0x80000000. - * Combining with FOLDER yields 0xa00000100, allowing A/B/E/F. - * The only at-risk is A, but combined with DROPTARGET we - * seem to screen out all the bad ones. - */ - - if (lpifq == NULL) { - /* dealing with stuff at the root level */ - goodOne = ( (ulAttrs & SFGAO_FOLDER) && - (ulAttrs & SFGAO_HASSUBFOLDER) && - (ulAttrs & SFGAO_FILESYSANCESTOR) ); - } else { - /* deeper down, we're picky in different ways */ - bool isFolder = (ulAttrs & SFGAO_FOLDER) != 0; - bool fileSys = (ulAttrs & SFGAO_FILESYSTEM) != 0; - bool hasFSAncestor = (ulAttrs & SFGAO_FILESYSANCESTOR) != 0; - bool dropAndSub = (ulAttrs & SFGAO_DROPTARGET) != 0 && - (ulAttrs & SFGAO_HASSUBFOLDER) != 0; - - goodOne = isFolder && - (fileSys || hasFSAncestor || dropAndSub); - } - - if (goodOne) { - gotOne = true; - - if (!AddNode(lpsf, lpi, lpifq, ulAttrs, hParent, &hPrev)) { - LOGI("AddNode failed!"); - goto Done; - } - } - - lpMalloc->Free(lpi); //Free the pidl that the shell gave us. - lpi=0; - } - } - - // Sometimes SFGAO_HASSUBFOLDERS lies, notably in Network Neighborhood. - // When we actually scan the directory we can update the parent node - // if it turns out there's nothing underneath. - if (!gotOne) { - TVITEM tvi; - CString name = GetItemText(hParent); - tvi.hItem = hParent; - tvi.mask = TVIF_CHILDREN; - if (!GetItem(&tvi)) { - LOGW("Could not get TV '%ls'", (LPCWSTR) name); - ASSERT(false); - } else if (tvi.cChildren) { - LOGI("Removing child count (%d) from '%ls'", - tvi.cChildren, (LPCWSTR) name); - tvi.cChildren = 0; - if (!SetItem(&tvi)) { - LOGW("Could not set TV '%ls'", (LPCWSTR) name); - ASSERT(false); - } - } - } - -Done: - if (lpe) - lpe->Release(); - - //The following 2 if statements will only be TRUE if we got here on an - //error condition from the "goto" statement. Otherwise, we free this memory - //at the end of the while loop above. - if (lpi && lpMalloc) - lpMalloc->Free(lpi); - if (lpMalloc) - lpMalloc->Release(); - - //LOGI("FillTreeView DONE"); -} - -BOOL ShellTree::AddNode(LPSHELLFOLDER lpsf, LPITEMIDLIST lpi, LPITEMIDLIST lpifq, - unsigned long ulAttrs, HTREEITEM hParent, HTREEITEM* phPrev) -{ - TVITEM tvi; - TVINSERTSTRUCT tvins; - LPITEMIDLIST lpifqThisItem = NULL; - TVItemData* lptvid = NULL; - WCHAR szBuff[MAX_PATH]; - CString name; - LPMALLOC lpMalloc = NULL; - HRESULT hr; - BOOL result = FALSE; - - hr = ::SHGetMalloc(&lpMalloc); - if (FAILED(hr)) - return FALSE; - - //Now get the friendly name that we'll put in the treeview. - if (!Pidl::GetName(lpsf, lpi, SHGDN_NORMAL, &name)) { - LOGI("HEY: failed getting friendly name"); - goto bail; // Error - could not get friendly name. - } - wcscpy_s(szBuff, name); - //LOGI("AddNode '%ls' ATTR=0x%08lx", szBuff, ulAttrs); - - lptvid = (TVItemData*)lpMalloc->Alloc(sizeof(TVItemData)); - if (!lptvid) - goto bail; - - tvi.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | - TVIF_PARAM; - - if (ulAttrs & SFGAO_HASSUBFOLDER) { - //This item has sub-folders, so let's put the + in the TreeView. - //The first time the user clicks on the item, we'll populate the - //sub-folders. - tvi.mask |= TVIF_CHILDREN; - tvi.cChildren = 1; - } - - tvi.pszText = szBuff; - tvi.cchTextMax = MAX_PATH; // (not needed for InsertItem) - - // Allocate a fully-qualified PIDL and stuff it in. - lpifqThisItem = Pidl::ConcatPidls(lpifq, lpi); - - // Add the icons. - GetNormalAndSelectedIcons(lpifqThisItem, &tvi); - - // Done with lipfqThisItem. - lptvid->lpifq = lpifqThisItem; - lpifqThisItem = NULL; - - // Put in a copy of the relative PIDL. - lptvid->lpi = Pidl::CopyITEMID(lpMalloc, lpi); - - // Stuff the parent folder's lpsf in. - lptvid->lpsfParent = lpsf; - lpsf->AddRef(); - - // Done with lptvid. - tvi.lParam = (LPARAM)lptvid; - lptvid = NULL; - - // Populate the TreeView Insert Struct - // The item is the one filled above. - // Insert it after the last item inserted at this level. - // And indicate this is a root entry. - tvins.item = tvi; - tvins.hInsertAfter = *phPrev; - tvins.hParent = hParent; - - // Add the item to the tree - *phPrev = InsertItem(&tvins); - - result = TRUE; - -bail: - lpMalloc->Release(); - return result; -} - -void ShellTree::GetNormalAndSelectedIcons(LPITEMIDLIST lpifq, LPTV_ITEM lptvitem) -{ - //Note that we don't check the return value here because if GetIcon() - //fails, then we're in big trouble... - - lptvitem->iImage = Pidl::GetItemIcon(lpifq, - SHGFI_SYSICONINDEX | - SHGFI_SMALLICON); - - lptvitem->iSelectedImage = Pidl::GetItemIcon(lpifq, - SHGFI_SYSICONINDEX | - SHGFI_SMALLICON | - SHGFI_OPENICON); - - return; -} - -/*static*/ int CALLBACK ShellTree::TreeViewCompareProc(LPARAM lparam1, - LPARAM lparam2, LPARAM) -{ - TVItemData* lptvid1 = (TVItemData*)lparam1; - TVItemData* lptvid2 = (TVItemData*)lparam2; - HRESULT hr; - - hr = lptvid1->lpsfParent->CompareIDs(0, lptvid1->lpi, lptvid2->lpi); - - if (FAILED(hr)) { - ASSERT(false); - return 0; - } - -#if 0 - if (lptvid1->alphaSort && lptvid2->alphaSort) { - char buf1[MAX_PATH], buf2[MAX_PATH]; - if (Pidl::GetName(lptvid1->lpsfParent, lptvid1->lpi, SHGDN_NORMAL, buf1) && - Pidl::GetName(lptvid2->lpsfParent, lptvid2->lpi, SHGDN_NORMAL, buf2)) - { - LOGI("COMPARING '%s' to '%s' (res=%d)", buf1, buf2, - (short) HRESULT_CODE(hr)); - return stricmp(buf1, buf2); - } else { - ASSERT(false); - return 0; - } - } -#endif - - return (short) HRESULT_CODE(hr); -} - -BOOL ShellTree::AddFolderAtSelection(const CString& name) -{ - LPSHELLFOLDER lpsf = NULL; - LPITEMIDLIST lpi = NULL; - HTREEITEM hParent; - LPMALLOC lpMalloc = NULL; - LPENUMIDLIST lpe = NULL; - const TVItemData* parentTvid; - TVItemData* newTvid = NULL; - HWND hwnd = ::GetParent(m_hWnd); - HTREEITEM hPrev = NULL; - BOOL result = false; - CString debugName; - HRESULT hr; - - LOGI("AddFolderAtSelection '%ls'", (LPCWSTR) name); - - // Allocate a shell memory object. - hr = ::SHGetMalloc(&lpMalloc); - if (FAILED(hr)) - return FALSE; - - hParent = GetSelectedItem(); - if (hParent == NULL) { - LOGI("Nothing selected!"); - goto bail; - } - - /* - * Now we either need to create a new node in an existing tree, or if - * we haven't expanded the current node yet then we can just let the - * usual FillTree mechanism do it for us. - * - * If the current node is marked as having children, but has no - * child structures, then it's a folder with sub-folders that hasn't - * been filled in yet. We don't need to do anything. - * - * If the current node doesn't have children, then this is a leaf - * node that has just become a branch. We update its "#of kids" state - * and again let the usual mechanisms do their work. - * - * If the current node has expanded children, then we need to do the - * work ourselves. (It's that, or invalidate the entire subtree, - * which has some UI consequences.) - */ - TVITEM tvi; - debugName = GetItemText(hParent); - tvi.hItem = hParent; - tvi.mask = TVIF_CHILDREN; - if (!GetItem(&tvi)) { - LOGW("Could not get TV '%ls'", (LPCWSTR) debugName); - ASSERT(false); - } else { - HTREEITEM child = GetChildItem(hParent); - if (child == NULL && tvi.cChildren) { - LOGD(" Found unexpanded node, not adding %ls", (LPCWSTR) name); - result = TRUE; - goto bail; - } else if (child == NULL && !tvi.cChildren) { - LOGD(" Found former leaf node, updating kids in %ls", - (LPCWSTR) debugName); - tvi.cChildren = 1; - if (!SetItem(&tvi)) { - LOGW("Could not set TV '%ls'", (LPCWSTR) debugName); - ASSERT(false); - } - result = TRUE; - goto bail; - } else { - ASSERT(child != NULL && tvi.cChildren != 0); - LOGD(" Found expanded branch node '%ls', adding new '%ls'", - (LPCWSTR) debugName, (LPCWSTR) name); - } - } - - - parentTvid = (TVItemData*)GetItemData(hParent); - ASSERT(parentTvid != NULL); - - // Get a handle to the ShellFolder for the currently selected node. - hr = parentTvid->lpsfParent->BindToObject(parentTvid->lpi, - 0, IID_IShellFolder, (LPVOID *)&lpsf); - if (FAILED(hr)) { - LOGW("Glitch: unable to get ShellFolder for selected folder"); - goto bail; - } - - // Get an enumerator for the selected node. - hr = lpsf->EnumObjects(hwnd, SHCONTF_FOLDERS | SHCONTF_INCLUDEHIDDEN, - &lpe); - if (FAILED(hr)) { - LOGW("Glitch: unable to get enumerator for selected folder"); - goto bail; - } - - // Enumerate throught the list of folder and non-folder objects. - while (S_OK == lpe->Next(1, &lpi, NULL)) { - CString pidlName; - if (Pidl::GetName(lpsf, lpi, SHGDN_NORMAL, &pidlName)) { - if (name.CompareNoCase(pidlName) == 0) { - /* match! */ - if (!AddNode(lpsf, lpi, parentTvid->lpifq, 0, hParent, &hPrev)) { - LOGI("AddNode failed!"); - goto bail; - } - result = TRUE; - break; - } - } - - lpMalloc->Free(lpi); //Free the pidl that the shell gave us. - lpi = NULL; - } - -bail: - if (lpi != NULL) - lpMalloc->Free(lpi); - if (lpsf != NULL) - lpsf->Release(); - if (lpe != NULL) - lpe->Release(); - lpMalloc->Release(); - return result; -} - -void ShellTree::OnFolderExpanding(NMHDR* pNMHDR, LRESULT* pResult) -{ - TVItemData* lptvid; //Long pointer to TreeView item data - HRESULT hr; - LPSHELLFOLDER lpsf2=NULL; - TV_SORTCB tvscb; - - NM_TREEVIEW* pnmtv = (NM_TREEVIEW*)pNMHDR; - if (pnmtv->itemNew.state & TVIS_EXPANDEDONCE) { - LOGI("Already expanded!"); - return; - } - - lptvid = (TVItemData*)pnmtv->itemNew.lParam; - if (lptvid) { - hr = lptvid->lpsfParent->BindToObject(lptvid->lpi, - 0, IID_IShellFolder,(LPVOID *)&lpsf2); - - if (SUCCEEDED(hr)) - { - FillTreeView(lpsf2, - lptvid->lpifq, - pnmtv->itemNew.hItem); - } - - tvscb.hParent = pnmtv->itemNew.hItem; - tvscb.lParam = 0; - tvscb.lpfnCompare = TreeViewCompareProc; - - SortChildrenCB(&tvscb); - } - - *pResult = 0; -} - -#if 0 -/**************************************************************************** -* -* FUNCTION: GetContextMenu(NMHDR* pNMHDR, LRESULT* pResult) -* -* PURPOSE: Diplays a popup menu for the folder selected. Pass the -* parameters from Rclick() to this function. -* -* MESSAGEMAP: NM_RCLICK; -* -****************************************************************************/ -void ShellTree::GetContextMenu(NMHDR*, LRESULT* pResult) -{ - POINT pt; - TVItemData* lptvid; //Long pointer to TreeView item data - TV_HITTESTINFO tvhti; - TV_ITEM tvi; - - ::GetCursorPos((LPPOINT)&pt); - ScreenToClient(&pt); - tvhti.pt=pt; - HitTest(&tvhti); - SelectItem(tvhti.hItem); - if (tvhti.flags & (TVHT_ONITEMLABEL|TVHT_ONITEMICON)) - { - ClientToScreen(&pt); - tvi.mask=TVIF_PARAM; - tvi.hItem=tvhti.hItem; - - if (!GetItem(&tvi)){ - return; - } - - lptvid = (TVItemData*)tvi.lParam; - - Pidl::DoTheMenuThing(::GetParent(m_hWnd), - lptvid->lpsfParent, lptvid->lpi, &pt); - } - - *pResult = 0; -} -#endif - -BOOL ShellTree::OnSelectionChange(NMHDR* pnmh, LRESULT* pResult) -{ - fFolderPathValid = OnFolderSelected(pnmh, pResult, fFolderPath); - *pResult = 0; - return FALSE; // allow window parent to handle notification -} - -BOOL ShellTree::OnFolderSelected(NMHDR* pNMHDR, LRESULT* pResult, - CString &szFolderPath) -{ - TVItemData* lptvid; - LPSHELLFOLDER lpsf2=NULL; - WCHAR szBuff[MAX_PATH]; - HRESULT hr; - BOOL bRet=false; - HTREEITEM hItem=NULL; - - hItem = GetSelectedItem(); - if (hItem) { - lptvid = (TVItemData*)GetItemData(hItem); - - if (lptvid && lptvid->lpsfParent && lptvid->lpi) - { - hr = lptvid->lpsfParent->BindToObject(lptvid->lpi, - 0, IID_IShellFolder, (LPVOID *)&lpsf2); - - if (SUCCEEDED(hr)) { - ULONG ulAttrs = SFGAO_FILESYSTEM; - - // Determine what type of object we have. - lptvid->lpsfParent->GetAttributesOf(1, - (const struct _ITEMIDLIST **)&lptvid->lpi, &ulAttrs); - - if (ulAttrs & (SFGAO_FILESYSTEM)) { - if (SHGetPathFromIDList(lptvid->lpifq, szBuff)){ - szFolderPath = szBuff; - bRet = true; - } - } - - if (bRet) { - LOGI("Now selected: '%ls'", szBuff); - } else { - LOGI("Now selected: "); - } - -#if 0 - // If we're expanding into new territory, load the - // sub-tree. [This makes it expand things that aren't - // necessarily going to be opened, which is very bad for - // empty floppy and CD-ROM drives. Makes little sense.] - TV_SORTCB tvscb; - NM_TREEVIEW* pnmtv = (NM_TREEVIEW*)pNMHDR; - if ((pnmtv->itemNew.cChildren == 1) && - !(pnmtv->itemNew.state & TVIS_EXPANDEDONCE)) - { - FillTreeView(lpsf2, lptvid->lpifq, pnmtv->itemNew.hItem); - - tvscb.hParent = pnmtv->itemNew.hItem; - tvscb.lParam = 0; - tvscb.lpfnCompare = TreeViewCompareProc; - SortChildrenCB(&tvscb); - - pnmtv->itemNew.state |= TVIS_EXPANDEDONCE; - pnmtv->itemNew.stateMask |= TVIS_EXPANDEDONCE; - pnmtv->itemNew.mask |= TVIF_STATE; - SetItem(&pnmtv->itemNew); - } -#endif - } - } - if(lpsf2) - lpsf2->Release(); - - } - *pResult = 0; - return bRet; -} - -void ShellTree::OnDeleteShellItem(NMHDR* pNMHDR, LRESULT* pResult) -{ - TVItemData* lptvid=NULL; - HRESULT hr; - LPMALLOC lpMalloc; - - //LOGI("TVN_DELETEITEM"); - - NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR; - - //Let's free the memory for the TreeView item data... - hr = SHGetMalloc(&lpMalloc); - if (FAILED(hr)) - return; - - lptvid = (TVItemData*)pNMTreeView->itemOld.lParam; - lptvid->lpsfParent->Release(); - lpMalloc->Free(lptvid->lpi); - lpMalloc->Free(lptvid->lpifq); - lpMalloc->Free(lptvid); - lpMalloc->Release(); - - *pResult = 0; -} - -void ShellTree::EnableImages() -{ - // Get the handle to the system image list, for our icons - HIMAGELIST hImageList; - SHFILEINFO sfi = { 0 }; - - hImageList = (HIMAGELIST)SHGetFileInfo(L"C:\\", - 0, &sfi, sizeof(SHFILEINFO), - SHGFI_SYSICONINDEX | SHGFI_SMALLICON); - - // Attach ImageList to TreeView - if (hImageList) - ::SendMessage(m_hWnd, TVM_SETIMAGELIST, (WPARAM) TVSIL_NORMAL, - (LPARAM)hImageList); -} - -BOOL ShellTree::GetSelectedFolderPath(CString &szFolderPath) -{ - TVItemData* lptvid; //Long pointer to TreeView item data - LPSHELLFOLDER lpsf2=NULL; - WCHAR szBuff[MAX_PATH]; - HTREEITEM hItem=NULL; - HRESULT hr; - BOOL bRet=false; - - hItem = GetSelectedItem(); - if(hItem) - { - lptvid = (TVItemData*)GetItemData(hItem); - - if (lptvid && lptvid->lpsfParent && lptvid->lpi) - { - hr = lptvid->lpsfParent->BindToObject(lptvid->lpi, - 0, IID_IShellFolder,(LPVOID *)&lpsf2); - - if (SUCCEEDED(hr)) - { - ULONG ulAttrs = SFGAO_FILESYSTEM; - - // Determine what type of object we have. - lptvid->lpsfParent->GetAttributesOf(1, - (const struct _ITEMIDLIST **)&lptvid->lpi, &ulAttrs); - - if (ulAttrs & (SFGAO_FILESYSTEM)) - { - if(SHGetPathFromIDList(lptvid->lpifq, szBuff)){ - szFolderPath = szBuff; - bRet = true; - } - } - } - - } - if(lpsf2) - lpsf2->Release(); - } - return bRet; -} - -LPSHELLFOLDER ShellTree::GetParentShellFolder(HTREEITEM folderNode) -{ - TVItemData* lptvid; //Long pointer to TreeView item data - - lptvid = (TVItemData*)GetItemData(folderNode); - if (lptvid) - return lptvid->lpsfParent; - else - return NULL; -} - -LPITEMIDLIST ShellTree::GetRelativeIDLIST(HTREEITEM folderNode) -{ - TVItemData* lptvid; //Long pointer to TreeView item data - - lptvid = (TVItemData*)GetItemData(folderNode); - if (lptvid) - return lptvid->lpifq; - else - return NULL; -} - -LPITEMIDLIST ShellTree::GetFullyQualifiedID(HTREEITEM folderNode) -{ - TVItemData* lptvid; //Long pointer to TreeView item data - - lptvid = (TVItemData*)GetItemData(folderNode); - if (lptvid) - return lptvid->lpifq; - else - return NULL; -} - -void ShellTree::TunnelTree(CString path, CString* pResultStr) -{ - const WCHAR* str = path; - int len; - - if (str[0] == '\\' && str[1] == '\\') { - *pResultStr = L"Can't expand network locations directly."; - return; - } - len = path.GetLength(); - if (len < 1) { - *pResultStr = L"You must enter a folder name."; - return; - } - - /* make sure it ends in \ so splitpath knows it's a directory */ - if (path[len-1] != '\\') - path += '\\'; - - /* if it doesn't exist, there's not much point in searching for it */ - PathName pathName(path); - if (!pathName.Exists()) { - *pResultStr = L"Folder not found."; - return; - } - - /* - * Find the folder that corresponds to "My Computer", and then scan - * it for the drive letter. - */ - HTREEITEM myComputer = FindMyComputer(); - if (myComputer == NULL) { - *pResultStr = L"Unable to locate My Computer in tree."; - return; - } - - CString drive = pathName.GetDriveOnly(); - LOGI("Searching for drive='%ls'", (LPCWSTR) drive); - - HTREEITEM node = FindDrive(myComputer, drive); - if (node == NULL) { - /* unexpected -- couldn't find the drive */ - pResultStr->Format(L"Unable to find drive %ls.", (LPCWSTR) drive); - return; - } - - /* - * We've got the node for the drive. Now we just need to walk - * through the tree one level at a time, comparing the name in - * the tree against our pathname component. - */ - node = SearchTree(node, pathName.GetPathOnly()); - - if (node == NULL) { - /* unexpected -- file doesn't exist */ - pResultStr->Format(L"Unable to find file '%ls'.", - (LPCWSTR) pathName.GetPathOnly()); - } else { - Select(node, TVGN_CARET); - EnsureVisible(node); - } -} - -HTREEITEM ShellTree::FindMyComputer(void) -{ - /* - * Find the tree entry that corresponds to "My Computer". - * - * This is hampered somewhat by the absence of a way to compare two - * shell folders for equality. The PIDL compare function is meant for - * sorting only (at least as far as it has been documented), and the My - * Computer "folder" has no path to examine. - * - * It helps greatly to assume that My Computer is right under Desktop. - * If it moved, or if we started the tree somewhere other than right at - * the desktop, we'd have to recursively search the tree. - */ - - LPSHELLFOLDER desktop = NULL; - LPITEMIDLIST myComputerPidl = NULL; - LPMALLOC lpMalloc = NULL; - HTREEITEM node; - HTREEITEM result = NULL; - HRESULT hr; - - hr = ::SHGetMalloc(&lpMalloc); - if (FAILED(hr)) - return NULL; - - hr = SHGetDesktopFolder(&desktop); - if (FAILED(hr)) - goto bail; - - hr = SHGetSpecialFolderLocation(NULL, CSIDL_DRIVES, &myComputerPidl); - if (FAILED(hr)) - goto bail; - - node = GetRootItem(); - while (node != NULL) { - CString itemText = GetItemText(node); - TVItemData* pData = (TVItemData*) GetItemData(node); - ASSERT(pData != NULL); - - hr = desktop->CompareIDs(0, myComputerPidl, pData->lpi); - if (SUCCEEDED(hr) && HRESULT_CODE(hr) == 0) { - LOGD("MATCHED on '%ls'", (LPCWSTR) itemText); - result = node; - break; - } - node = GetNextSiblingItem(node); - } - - if (result != NULL && !ItemHasChildren(result)) { - LOGW("Glitch: My Computer has no children"); - result = NULL; - } - -bail: - if (desktop != NULL) - desktop->Release(); - lpMalloc->Free(myComputerPidl); - lpMalloc->Release(); - return result; -} - -HTREEITEM ShellTree::FindDrive(HTREEITEM myComputer, const CString& drive) -{ - CString udrive; - - /* expand & scan */ - Expand(myComputer, TVE_EXPAND); - - HTREEITEM node; - node = GetChildItem(myComputer); - if (node == NULL) { - ASSERT(false); // we verified My Computer has kids earlier - return NULL; - } - - /* - * Look for the drive letter. It's buried amongst other fluff, so - * we have to rely on Windows preventing the use of a ":" anywhere - * else in the string to avoid false-positives. - * - * We *might* be able to assume it looks like "(C:)", but that's - * probably unwise. - */ - udrive = drive; - udrive.MakeUpper(); - while (node != NULL) { - CString itemText = GetItemText(node); - itemText.MakeUpper(); - - //LOGI("COMPARING '%ls' vs '%ls'", (LPCWSTR) udrive, (LPCWSTR) itemText); - if (itemText.Find(udrive) != -1) { - LOGI("MATCHED '%ls' in '%ls'", (LPCWSTR) udrive, (LPCWSTR) itemText); - break; - } - node = GetNextSiblingItem(node); - } - - return node; -} - -HTREEITEM ShellTree::SearchTree(HTREEITEM treeNode, const CString& path) -{ - LOGD("SearchTree node=0x%p path='%ls'", treeNode, (LPCWSTR) path); - - HTREEITEM node; - CString mangle(path); - WCHAR* start; - WCHAR* end; - - /* make a copy of "path" that we can mess with */ - start = mangle.GetBuffer(0); - if (start == NULL || *start != '\\' || *(start + wcslen(start)-1) != '\\') - return NULL; - start++; - - node = treeNode; - while (*start != '\0') { - /* grab first node in next level down */ - Expand(node, TVE_EXPAND); // need to fill in the tree - node = GetChildItem(node); - - end = wcschr(start, '\\'); - if (end == NULL) { - ASSERT(false); - return NULL; - } - *end = '\0'; - - while (node != NULL) { - CString itemText = GetItemText(node); - - //LOGI("COMPARE '%s' '%s'", start, itemText); - if (itemText.CompareNoCase(start) == 0) { - //LOGI("MATCHED '%s' '%s'", itemText, start); - break; - } - - node = GetNextSiblingItem(node); - } - if (node == NULL) { - LOGI("NOT FOUND '%ls' '%ls'", (LPCTSTR) path, start); - break; - } - - start = end+1; - } - - return node; -} - - -#ifdef USE_OLD - -/**************************************************************************** -* -* FUNCTION: SearchTree( HTREEITEM treeNode, -* CString szSearchName ) -* -* PURPOSE: Too crude to explain, just use it -* -* WARNING: Only works if you use the default PopulateTree() -* Not guaranteed to work on any future or existing -* version of windows. Use with caution. Pretty much -* ok if you're using on local drives -* -****************************************************************************/ -bool ShellTree::SearchTree(HTREEITEM treeNode, - CString szSearchName, - FindAttribs attr) -{ - TVItemData* lptvid; //Long pointer to TreeView item data - LPSHELLFOLDER lpsf2=NULL; - char drive[_MAX_DRIVE]; - char dir[_MAX_DIR]; - char fname[_MAX_FNAME]; - char ext[_MAX_EXT]; - bool bRet=false; - HRESULT hr; - CString szCompare; - - szSearchName.MakeUpper(); - while(treeNode && bRet==false) - { - lptvid=(TVItemData*)GetItemData(treeNode); - if (lptvid && lptvid->lpsfParent && lptvid->lpi) - { - hr=lptvid->lpsfParent->BindToObject(lptvid->lpi, - 0,IID_IShellFolder,(LPVOID *)&lpsf2); - if (SUCCEEDED(hr)) - { - ULONG ulAttrs = SFGAO_FILESYSTEM; - lptvid->lpsfParent->GetAttributesOf(1, - (const struct _ITEMIDLIST **)&lptvid->lpi, &ulAttrs); - if (ulAttrs & (SFGAO_FILESYSTEM)) - { - if(SHGetPathFromIDList(lptvid->lpifq, - szCompare.GetBuffer(MAX_PATH))) - { - switch(attr) - { - case type_drive: - _splitpath(szCompare,drive,dir,fname,ext); - szCompare=drive; - break; - case type_folder: - szCompare = GetItemText(treeNode); - break; - } - szCompare.MakeUpper(); - if(szCompare == szSearchName) - { - EnsureVisible(treeNode); - SelectItem(treeNode); - bRet=true; - } - } - } - lpsf2->Release(); - } - } - treeNode = GetNextSiblingItem(treeNode); - } - return bRet; -} - -/**************************************************************************** -* -* FUNCTION: TunnelTree(CString szFindPath) -* -* PURPOSE: Too crude to explain, just use it -* -* WARNING: Only works if you use the default PopulateTree() -* Not guaranteed to work on any future or existing -* version of windows. Use with caution. Pretty much -* ok if you're using on local drives -* -****************************************************************************/ -void ShellTree::TunnelTree(CString szFindPath) -{ - HTREEITEM subNode = GetRootItem(); - CString szPathHop; - char drive[_MAX_DRIVE]; - char dir[_MAX_DIR]; - char fname[_MAX_FNAME]; - char ext[_MAX_EXT]; - char delimiter[]="\\"; - - PathName checkPath(szFindPath); - if(!checkPath.Exists()) - { - MessageBox(szFindPath,"Folder not found",MB_ICONERROR); - return; - } - - if(szFindPath.ReverseFind('\\') != szFindPath.GetLength()-1) - { - szFindPath += "\\"; - } - - _splitpath(szFindPath,drive,dir,fname,ext); - - //search the drive first - szPathHop=drive; - subNode=GetChildItem(subNode); - if(subNode) - { - if(SearchTree(subNode,szPathHop, ShellTree::type_drive)) - { - //break down subfolders and search - char *p=strtok(dir,delimiter); - while(p) - { - subNode = GetSelectedItem(); - subNode = GetChildItem(subNode); - if(SearchTree(subNode,p,ShellTree::type_folder)) - p=strtok(NULL,delimiter); - else - p=NULL; - } - } - } -} - - -#endif -#ifdef USE_NEW -// new version for Win2K - -/**************************************************************************** -* -* FUNCTION: SearchTree( HTREEITEM treeNode, -* CString szSearchName ) -* -* PURPOSE: Too crude to explain, just use it -* -* WARNING: Only works if you use the default PopulateTree() -* Not guaranteed to work on any future or existing -* version of windows. Use with caution. Pretty much -* ok if you're using on local drives -* -****************************************************************************/ -bool ShellTree::SearchTree(HTREEITEM treeNode, - CString szSearchName, - FindAttribs attr) -{ - TVItemData* lptvid; //Long pointer to TreeView item data - LPSHELLFOLDER lpsf2=NULL; - char drive[_MAX_DRIVE]; - char dir[_MAX_DIR]; - char fname[_MAX_FNAME]; - char ext[_MAX_EXT]; - bool bRet=false; - HRESULT hr; - CString szCompare; - - szSearchName.MakeUpper(); - while(treeNode && bRet==false) - { - lptvid=(TVItemData*)GetItemData(treeNode); - if (lptvid && lptvid->lpsfParent && lptvid->lpi) - { - hr=lptvid->lpsfParent->BindToObject(lptvid->lpi, - 0,IID_IShellFolder,(LPVOID *)&lpsf2); - if (SUCCEEDED(hr)) - { - ULONG ulAttrs = SFGAO_FILESYSTEM; - lptvid->lpsfParent->GetAttributesOf(1, (const struct _ITEMIDLIST **)&lptvid->lpi, &ulAttrs); - if (ulAttrs & (SFGAO_FILESYSTEM)) - { - if(SHGetPathFromIDList(lptvid->lpifq, - szCompare.GetBuffer(MAX_PATH))) - { - CString folder; - - SHGetSpecialFolderPath(NULL, - folder.GetBuffer(MAX_PATH), - CSIDL_COMMON_DESKTOPDIRECTORY, FALSE); - if( szCompare.Find( folder ) != -1 ) - if( szSearchName.Find( szCompare ) == -1 ) { - LOGI("Magic match on '%s'", szCompare); - return false; - } - - SHGetSpecialFolderPath(NULL, - folder.GetBuffer(MAX_PATH), - CSIDL_DESKTOPDIRECTORY, FALSE ); - if( szCompare.Find( folder ) != -1 ) - if( szSearchName.Find( szCompare ) == -1 ) { - LOGI("MAGIC '%s'='%s' and '%s'='%s'", - szCompare, folder, szSearchName, szCompare); - return false; - } - - SHGetSpecialFolderPath(NULL, - folder.GetBuffer(MAX_PATH), - CSIDL_PERSONAL, FALSE ); - if( szCompare.Find( folder ) != -1 ) - if( szSearchName.Find( szCompare ) == -1 ) { - LOGI("Magic match on '%s'", szCompare); - return false; - } - - switch(attr) { - case type_drive: - _splitpath(szCompare,drive,dir,fname,ext); - szCompare = drive; - break; - case type_folder: - szCompare = GetItemText(treeNode); - break; - } - szCompare.MakeUpper(); - if(szCompare == szSearchName) - { - EnsureVisible(treeNode); - SelectItem(treeNode); - bRet=true; - } - } - } - lpsf2->Release(); - } - } - treeNode = GetNextSiblingItem(treeNode); - } - return bRet; -} - -/**************************************************************************** -* -* FUNCTION: TunnelTree(CString szFindPath) -* -* PURPOSE: Too crude to explain, just use it -* -* WARNING: Only works if you use the default PopulateTree() -* Not guaranteed to work on any future or existing -* version of windows. Use with caution. Pretty much -* ok if you're using on local drives -* -****************************************************************************/ -void ShellTree::TunnelTree(CString szFindPath) -{ - HTREEITEM subNode = GetRootItem(); - CString szPathHop; - char drive[_MAX_DRIVE]; - char dir[_MAX_DIR]; - char fname[_MAX_FNAME]; - char ext[_MAX_EXT]; - char delimiter[]="\\"; - - PathName checkPath(szFindPath); - if(!checkPath.Exists()) { - MessageBox(szFindPath,"Folder not found",MB_ICONERROR); - return; - } - - if(szFindPath.ReverseFind('\\') != szFindPath.GetLength()-1) { - szFindPath += "\\"; - } - - _splitpath(szFindPath, drive, dir, fname, ext); - - HTREEITEM root = subNode; - //search the drive first - szPathHop=drive; - do { - CString currItem = GetItemText( root ); - LOGI("Scanning '%s' for drive '%s'", currItem, szPathHop); - if (ItemHasChildren(root)) - { - Expand(root, TVE_EXPAND); - subNode = GetChildItem(root); - if(subNode) - { - if(SearchTree(subNode, szPathHop, ShellTree::type_drive)) - { - // we have a match on the drive; SearchTree will have - // left it as the selected item - LOGI("Tunnel match '%s' in subnode", szPathHop); - - // break down subfolders and search - char* p = strtok(dir, delimiter); - while (p) { - subNode = GetSelectedItem(); - subNode = GetChildItem(subNode); - if(SearchTree(subNode, p, ShellTree::type_folder)) - p=strtok(NULL,delimiter); - else - p=NULL; - } - return; - } - } - Expand(root, TVE_COLLAPSE); - } - - root = GetNextSiblingItem( root ); - } while( root ); -} -#endif - - - -#if 0 // quick test -LPMALLOC g_pMalloc = NULL; - -// Main_OnBrowse - browses for a program folder. -// hwnd - handle to the application's main window. -// -// Uses the global variable g_pMalloc, which is assumed to point -// to the shell's IMalloc interface. -void Main_OnBrowse(HWND hwnd) -{ - BROWSEINFO bi; - LPSTR lpBuffer; - LPITEMIDLIST pidlPrograms; // PIDL for Programs folder - LPITEMIDLIST pidlBrowse; // PIDL selected by user - - if (g_pMalloc == NULL) - ::SHGetMalloc(&g_pMalloc); - - // Allocate a buffer to receive browse information. - if ((lpBuffer = (LPSTR) g_pMalloc->Alloc( - MAX_PATH)) == NULL) - return; - - // Get the PIDL for the Programs folder. - if (!SUCCEEDED(SHGetSpecialFolderLocation( - hwnd, CSIDL_PROGRAMS, &pidlPrograms))) { - g_pMalloc->Free(lpBuffer); - return; - } - - // Fill in the BROWSEINFO structure. - bi.hwndOwner = hwnd; - bi.pidlRoot = pidlPrograms; - bi.pszDisplayName = lpBuffer; - bi.lpszTitle = "Choose a Program Group"; - bi.ulFlags = 0; - bi.lpfn = NULL; - bi.lParam = 0; - - // Browse for a folder and return its PIDL. - pidlBrowse = SHBrowseForFolder(&bi); - if (pidlBrowse != NULL) { - - // Show the display name, title, and file system path. - MessageBox(hwnd, lpBuffer, "Display name", MB_OK); - if (SHGetPathFromIDList(pidlBrowse, lpBuffer)) - SetWindowText(hwnd, lpBuffer); - - // Free the PIDL returned by SHBrowseForFolder. - g_pMalloc->Free(pidlBrowse); - } - - // Clean up. - g_pMalloc->Free(pidlPrograms); - g_pMalloc->Free(lpBuffer); -} -#endif diff --git a/util/ShellTree.h b/util/ShellTree.h deleted file mode 100644 index 81bd5c8..0000000 --- a/util/ShellTree.h +++ /dev/null @@ -1,216 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * TreeView control containing Windows shell folders. - * - * Originally based on MFCENUM from "Programming the Windows 95 User - * interface". Enhanced by Selom Ofori as "ShellTree" class. Modified - * extensively. - */ -#ifndef UTIL_SHELLTREE_H -#define UTIL_SHELLTREE_H - - -/* - * ShellTree class. - */ -class ShellTree : public CTreeCtrl { -public: - //enum FindAttribs {type_drive, type_folder}; - - ShellTree(void) { - fFolderPathValid = false; - } - virtual ~ShellTree(void) { - Detach(); // we don't own the window handle - } - - /* - * Replace a CTreeCtrl in a dialog box with us. All of the styles are - * copied from the original dialog window. - * - * Returns TRUE on success, FALSE on failure. - */ - BOOL ReplaceDlgCtrl(CDialog* pDialog, int treeID); - - /* - * Populate the tree, starting from "nFolder". - * - * Returns TRUE on success, FALSE on failure. - */ - BOOL PopulateTree(int nFolder = CSIDL_DESKTOP); - - /* - * Open up and select My Computer. - */ - void ExpandMyComputer(void); - - /* - * Add a new folder to the tree at the currently-selected node. This may - * not actually add a folder if the new folder is at a point in the tree - * below where we have already expanded. - * - * Returns TRUE on success, or FALSE on failure. - */ - BOOL AddFolderAtSelection(const CString& name); - - void GetContextMenu(NMHDR* pNMHDR, LRESULT* pResult); - - /* - * Gets a handle to the system image list (by just grabbing whatever is - * in place for C:\) and makes it available to the tree control. - * - * The image list should NOT be deleted. - */ - void EnableImages(); - - /* - * Retrieves the path of the currently selected string. - * Pass a CString object that will hold the folder path. - * If the path is not in the filesystem(eg MyComputer) - * or none is selected it returns false. - */ - BOOL GetSelectedFolderPath(CString &szFolderPath); - - /* - * Retrieves the pointer to the ISHELLFOLDER interface - * of the tree node passed as the parameter. - */ - LPSHELLFOLDER GetParentShellFolder(HTREEITEM folderNode); - - /* - * Retrieves the Pointer to an ITEMIDLIST structure that - * identifies the subfolder relative to its parent folder. - * see GetParentShellFolder(); - */ - LPITEMIDLIST GetRelativeIDLIST(HTREEITEM folderNode); - - /* - * Retrieves the Pointer to an ITEMIDLIST - * structure that identifies the subfolder relative to the - * desktop. This is a fully qualified Item Identifier - */ - LPITEMIDLIST GetFullyQualifiedID(HTREEITEM folderNode); - - /* - * Tunnel into the tree, finding the node that corresponds to the - * requested pathname. - * - * Sets "resultMsg" to a non-empty string on error. - */ - void TunnelTree(CString path, CString* pResultStr); - - // Get the most-recently-set folder path. This will be updated on - // every TVN_SELCHANGED, so add an ON_NOTIFY handler to the parent. - BOOL GetFolderPath(CString* pStr) { - *pStr = fFolderPath; - return fFolderPathValid; - } - -protected: - /* - * Respond to TVN_ITEMEXPANDING message. - * - * If the subtree hasn't been expanded yet, dig in. - */ - void OnFolderExpanding(NMHDR* pNMHDR, LRESULT* pResult); - - /* - * Handle TVN_DELETEITEM notification by cleaning up our stuff. - */ - void OnDeleteShellItem(NMHDR* pNMHDR, LRESULT* pResult); - - /* - * Respond to TVN_SELCHANGED notification. - */ - BOOL OnSelectionChange(NMHDR* pNMHDR, LRESULT* pResult); - - /* - * This does the bulk of the work when the selection changes. - * - * The filesystem path (if any) to the object is placed in "szFolderPath". - */ - BOOL OnFolderSelected(NMHDR* pNMHDR, LRESULT* pResult, - CString& szFolderPath); - - /* - * Fills a branch of the TreeView control. Given the shell folder (both as - * a shell folder and the fully-qualified item ID list to it) and the parent - * item in the tree (TVI_ROOT to start off), add all the kids to the tree. - * - * Does not try to add the current entry, as a result of which we don't - * have a root "Desktop" node that everything is a child of. This is okay. - */ - void FillTreeView(LPSHELLFOLDER lpsf,LPITEMIDLIST lpifq, HTREEITEM hParent); - - /* - * Add a node to the tree. - * - * Returns TRUE on success, FALSE on failure. - */ - BOOL AddNode(LPSHELLFOLDER lpsf, LPITEMIDLIST lpi, LPITEMIDLIST lpifq, - unsigned long ulAttrs, HTREEITEM hParent, HTREEITEM* phPrev); - - /* - * Sort function callback for TreeView SortChildrenCB. - */ - static int CALLBACK TreeViewCompareProc(LPARAM, LPARAM, LPARAM); - - /* - * Set the TreeView normal and selected icons for the specified entry. - * - * "lpifq" is the fully-qualified PIDL, LPTV_ITEM is an item in the tree. - */ - void GetNormalAndSelectedIcons(LPITEMIDLIST lpifq, LPTV_ITEM lptvitem); - - /* - * Find the tree entry that corresponds to "My Computer". - * - * Returns a handle to the tree item, or NULL if My Computer wasn't found - * or didn't have any children. - */ - HTREEITEM FindMyComputer(void); - - /* - * Given a pointer to the My Computer node in the tree, find the node - * corresponding to the requested drive (which should be of the form - * "C:"). - * - * Returns a pointer to the drive's node on success, or NULL on failure. - */ - HTREEITEM FindDrive(HTREEITEM myComputer, const CString& drive); - - /* - * Given a path, search a subtree following the components. - * - * Pass in the tree's root (it's children will be searched for a - * match with the first path component) and the path to look for - * (which must start and end with '\\'). - */ - HTREEITEM SearchTree(HTREEITEM treeNode, const CString& path); - - /* - * Tree view element. Each one holds a pointer to the ShellFolder - * object, a pointer to the ItemIDList for the item within the folder, - * and a pointer to the fully-qualified ItemIDList for the item. - */ - typedef struct TVItemData { - LPSHELLFOLDER lpsfParent; - LPITEMIDLIST lpi; - LPITEMIDLIST lpifq; - //bool alphaSort; - } TVItemData; - - CString fFolderPath; - BOOL fFolderPathValid; - - DECLARE_MESSAGE_MAP() - -private: - DECLARE_COPY_AND_OPEQ(ShellTree) -}; - -#endif /*UTIL_SHELLTREE_H*/ diff --git a/util/UtilLib.h b/util/UtilLib.h index 2d04ee2..550ea08 100644 --- a/util/UtilLib.h +++ b/util/UtilLib.h @@ -21,7 +21,6 @@ #include "PathName.h" #include "Pidl.h" #include "SelectFilesDialog.h" -#include "ShellTree.h" #include "SoundFile.h" #include "Modeless.h" diff --git a/util/util.vcxproj b/util/util.vcxproj index d0cd176..7943988 100644 --- a/util/util.vcxproj +++ b/util/util.vcxproj @@ -115,7 +115,6 @@ - @@ -131,7 +130,6 @@ - Create diff --git a/util/util.vcxproj.filters b/util/util.vcxproj.filters index c3aa577..cc1ff9d 100644 --- a/util/util.vcxproj.filters +++ b/util/util.vcxproj.filters @@ -50,9 +50,6 @@ Header Files - - Header Files - Header Files @@ -91,9 +88,6 @@ Source Files - - Source Files - Source Files