From fd37bfd261a76d237ec63865a4846aaaa4bae619 Mon Sep 17 00:00:00 2001 From: Andy McFadden Date: Tue, 13 Jan 2015 13:21:43 -0800 Subject: [PATCH] Reimplement ChooseDirDialog The previous version was written to work on Win98+, and used the rather gnarly ShellTree class. Since we no longer support Win98, we can now use CShellManager::BrowseForFolder(), which does exactly what we want without all the ugly code (and it looks nicer, and it integrates better with the rest of the system). We can also get rid of NewFolderDialog, which only existed to allow the user to create a folder when trudging through ShellTree. This required "upgrading" the main app object from CWinApp to CWinAppEx, but that appears to be benign. Tested on WinXP and it all seems fine. --- DIST/with-mdc.deploy | 4 +- app/AppleSingleArchive.cpp | 6 +- app/ChooseDirDialog.cpp | 153 ---- app/ChooseDirDialog.h | 70 +- app/CiderPress.rc | 21 - app/MyApp.cpp | 6 +- app/MyApp.h | 2 +- app/NewFolderDialog.cpp | 68 -- app/NewFolderDialog.h | 53 -- app/StdAfx.h | 1 + app/app.vcxproj | 3 - app/app.vcxproj.filters | 9 - app/resource.h | 3 - reformat/StdAfx.h | 6 +- util/ShellTree.cpp | 1413 ------------------------------------ util/ShellTree.h | 216 ------ util/UtilLib.h | 1 - util/util.vcxproj | 2 - util/util.vcxproj.filters | 6 - 19 files changed, 42 insertions(+), 2001 deletions(-) delete mode 100644 app/ChooseDirDialog.cpp delete mode 100644 app/NewFolderDialog.cpp delete mode 100644 app/NewFolderDialog.h delete mode 100644 util/ShellTree.cpp delete mode 100644 util/ShellTree.h 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