diff --git a/app/Actions.cpp b/app/Actions.cpp index 0dfe2b1..9f9360e 100644 --- a/app/Actions.cpp +++ b/app/Actions.cpp @@ -189,8 +189,8 @@ void MainWindow::OnActionsAddFiles(void) addFiles.m_ofn.lpstrInitialDir = fPreferences.GetPrefString(kPrAddFileFolder); - addFiles.DoModal(); - if (addFiles.GetExitStatus() == IDOK) { + LRESULT addResult = addFiles.DoModal(); + if (addResult == IDOK) { fPreferences.SetPrefBool(kPrAddIncludeSubFolders, addFiles.fIncludeSubfolders != 0); if (addFiles.fStripFolderNamesEnable) { @@ -206,8 +206,7 @@ void MainWindow::OnActionsAddFiles(void) fPreferences.SetPrefLong(kPrAddConvEOL, addFiles.fConvEOL); - CString saveFolder = addFiles.GetFileNames(); - saveFolder = saveFolder.Left(addFiles.GetFileNameOffset()); + CString saveFolder = addFiles.GetDirectory(); fPreferences.SetPrefString(kPrAddFileFolder, saveFolder); /* @@ -401,15 +400,14 @@ void MainWindow::OnActionsAddDisks(void) /* * Set up an AddFilesDialog, but don't actually use it as a dialog. * Instead, we just configure the various options appropriately. - * - * To conform to multi-file-selection semantics, we need to drop a - * null byte in right after the pathname. */ ASSERT(dlg.m_ofn.nFileOffset > 0); - int len; - len = wcslen(dlg.m_ofn.lpstrFile) + 2; - dlg.m_ofn.lpstrFile[dlg.m_ofn.nFileOffset-1] = '\0'; - addOpts.SetFileNames(dlg.m_ofn.lpstrFile, len, dlg.m_ofn.nFileOffset); + { + CString directory(dlg.m_ofn.lpstrFile, dlg.m_ofn.nFileOffset - 1); + CString file(dlg.m_ofn.lpstrFile + dlg.m_ofn.nFileOffset); + LOGD("Stuffing '%ls' '%ls'", (LPCWSTR) directory, (LPCWSTR) file); + addOpts.StuffSingleFilename(directory, file); + } addOpts.fStoragePrefix = ""; addOpts.fIncludeSubfolders = false; addOpts.fStripFolderNames = false; diff --git a/app/AddFilesDialog.cpp b/app/AddFilesDialog.cpp index 8ed8662..72baaf6 100644 --- a/app/AddFilesDialog.cpp +++ b/app/AddFilesDialog.cpp @@ -24,6 +24,7 @@ bool AddFilesDialog::MyDataExchange(bool saveAndValidate) { CWnd* pWnd; + LOGD("AddFilesDialog MyDataExchange(%d)", saveAndValidate); if (saveAndValidate) { if (GetDlgButtonCheck(this, IDC_ADDFILES_NOPRESERVE) == BST_CHECKED) fTypePreservation = kPreserveNone; @@ -126,56 +127,9 @@ bool AddFilesDialog::ValidateStoragePrefix(void) return true; } - -UINT AddFilesDialog::MyOnCommand(WPARAM wParam, LPARAM lParam) -{ - switch (wParam) { - case IDHELP: - OnIDHelp(); - return 1; - default: - return SelectFilesDialog::MyOnCommand(wParam, lParam); - } -} - -void AddFilesDialog::ShiftControls(int deltaX, int deltaY) -{ - /* - * These only need to be here so that the initial move puts them - * where they belong. Once the dialog has been created, the - * CFileDialog will move things where they need to go. - */ - MoveControl(this, IDC_ADDFILES_STATIC1, 0, deltaY, false); - MoveControl(this, IDC_ADDFILES_NOPRESERVE, 0, deltaY, false); - MoveControl(this, IDC_ADDFILES_PRESERVE, 0, deltaY, false); - MoveControl(this, IDC_ADDFILES_PRESERVEPLUS, 0, deltaY, false); - MoveControl(this, IDC_ADDFILES_STATIC2, 0, deltaY, false); - MoveControl(this, IDC_ADDFILES_STRIP_FOLDER, 0, deltaY, false); - MoveControl(this, IDC_ADDFILES_INCLUDE_SUBFOLDERS, 0, deltaY, false); - MoveControl(this, IDC_ADDFILES_OVERWRITE, 0, deltaY, false); - MoveControl(this, IDC_ADDFILES_STATIC3, 0, deltaY, false); - MoveControl(this, IDC_ADDFILES_PREFIX, 0, deltaY, false); - MoveControl(this, IDC_ADDFILES_STATIC4, 0, deltaY, false); - MoveControl(this, IDC_ADDFILES_CONVEOLNONE, 0, deltaY, false); - MoveControl(this, IDC_ADDFILES_CONVEOLTYPE, 0, deltaY, false); - MoveControl(this, IDC_ADDFILES_CONVEOLTEXT, 0, deltaY, false); - MoveControl(this, IDC_ADDFILES_CONVEOLALL, 0, deltaY, false); - - /* - * These actively move. - */ - MoveControl(this, IDHELP, deltaX, deltaY, false); - StretchControl(this, IDC_ADDFILES_PREFIX, deltaX, 0, false); - - /* - * It's important that the base class be called last, because it calls - * Invalidate to redraw the dialog. - */ - SelectFilesDialog::ShiftControls(deltaX, deltaY); -} - -void AddFilesDialog::OnIDHelp(void) +void AddFilesDialog::HandleHelp() { + LOGD("AddFilesDialog HandleHelp"); CWnd* pWndMain = ::AfxGetMainWnd(); CWinApp* pAppMain = ::AfxGetApp(); diff --git a/app/AddFilesDialog.h b/app/AddFilesDialog.h index b0615de..85cb9ff 100644 --- a/app/AddFilesDialog.h +++ b/app/AddFilesDialog.h @@ -23,7 +23,7 @@ class AddFilesDialog : public SelectFilesDialog { public: AddFilesDialog(CWnd* pParentWnd = NULL) : - SelectFilesDialog(L"IDD_ADD_FILES", pParentWnd) + SelectFilesDialog(L"IDD_ADD_FILES", true, pParentWnd) { SetWindowTitle(L"Add Files..."); fStoragePrefix = ""; @@ -36,8 +36,6 @@ public: fConvEOL = 0; fConvEOLEnable = true; - fAcceptButtonID = IDC_SELECT_ACCEPT; - fpTargetDiskFS = NULL; //fpTargetSubdir = NULL; fpDiskImg = NULL; @@ -69,21 +67,11 @@ public: private: virtual bool MyDataExchange(bool saveAndValidate) override; - /* - * Overrides base class version so we can move our stuff around. - */ - virtual void ShiftControls(int deltaX, int deltaY) override; - - // Grabs OnIDHelp; otherwise forwards to base class. - virtual UINT MyOnCommand(WPARAM wParam, LPARAM lParam) override; - // User hit the Help button. - void OnIDHelp(void); + virtual void HandleHelp() override; - /* - * Make sure the storage prefix they entered is valid. - */ - bool ValidateStoragePrefix(void); + // Make sure the storage prefix they entered is valid. + bool ValidateStoragePrefix(); //DECLARE_MESSAGE_MAP() }; diff --git a/app/CiderPress.rc b/app/CiderPress.rc index 9f2d2d9..104bdd0 100644 --- a/app/CiderPress.rc +++ b/app/CiderPress.rc @@ -589,10 +589,7 @@ IDD_ADD_FILES DIALOGEX 0, 0, 292, 231 STYLE DS_SETFONT | DS_3DLOOK | DS_CONTROL | DS_CONTEXTHELP | WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS FONT 8, "MS Sans Serif", 0, 0, 0x1 BEGIN - LTEXT "",1119,0,0,291,87,NOT WS_GROUP,WS_EX_STATICEDGE - PUSHBUTTON "&Accept",IDC_SELECT_ACCEPT,241,88,50,14 - PUSHBUTTON "Cancel",IDCANCEL,241,106,50,14 - PUSHBUTTON "Help",IDHELP,241,124,50,14 + LTEXT "",1119,0,0,291,105,NOT WS_GROUP GROUPBOX "File attribute preservation",IDC_ADDFILES_STATIC1,4,112,169,47 CONTROL "&Ignore file attribute preservation tags",IDC_ADDFILES_NOPRESERVE, "Button",BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,8,122,157,10 @@ -608,14 +605,14 @@ BEGIN CONTROL "Auto-detect && &convert files with text",IDC_ADDFILES_CONVEOLTEXT, "Button",BS_AUTORADIOBUTTON | WS_TABSTOP,8,200,148,10 CONTROL "Convert &ALL files",IDC_ADDFILES_CONVEOLALL,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,8,211,141,10 - GROUPBOX "Miscellaneous",IDC_ADDFILES_STATIC2,176,145,115,47 + GROUPBOX "Miscellaneous",IDC_ADDFILES_STATIC2,175,112,116,47 CONTROL "&Include subfolders",IDC_ADDFILES_INCLUDE_SUBFOLDERS, - "Button",BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP,180,155,88,10 - CONTROL "&Strip folder names",IDC_ADDFILES_STRIP_FOLDER,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,180,166,88,10 + "Button",BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP,179,122,88,10 + CONTROL "&Strip folder names",IDC_ADDFILES_STRIP_FOLDER,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,179,133,88,10 CONTROL "&Overwrite existing files",IDC_ADDFILES_OVERWRITE, - "Button",BS_AUTOCHECKBOX | WS_TABSTOP,180,177,85,10 - LTEXT "Storage prefix (optional):",IDC_ADDFILES_STATIC3,177,198,77,8 - EDITTEXT IDC_ADDFILES_PREFIX,176,210,102,14,ES_AUTOHSCROLL + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,179,144,85,10 + LTEXT "Storage prefix (optional):",IDC_ADDFILES_STATIC3,178,168,77,8 + EDITTEXT IDC_ADDFILES_PREFIX,176,179,116,14,ES_AUTOHSCROLL END IDD_USE_SELECTION DIALOG 0, 0, 139, 79 @@ -1473,6 +1470,8 @@ BEGIN BEGIN VERTGUIDE, 8 BOTTOMMARGIN, 224 + HORZGUIDE, 112 + HORZGUIDE, 168 END IDD_USE_SELECTION, DIALOG diff --git a/app/DiskArchive.cpp b/app/DiskArchive.cpp index e68b45e..fae406f 100644 --- a/app/DiskArchive.cpp +++ b/app/DiskArchive.cpp @@ -1180,38 +1180,37 @@ bool DiskArchive::BulkAdd(ActionProgressDialog* pActionProgress, /* * Save the current directory and change to the one from the file dialog. */ - const WCHAR* buf = pAddOpts->GetFileNames(); - LOGI("Selected path = '%ls' (offset=%d)", buf, - pAddOpts->GetFileNameOffset()); + const CString& directory = pAddOpts->GetDirectory(); + LOGI("Selected path = '%ls'", (LPCWSTR) directory); if (GetCurrentDirectory(NELEM(curDir), curDir) == 0) { errMsg = L"Unable to get current directory.\n"; ShowFailureMsg(pActionProgress, errMsg, IDS_FAILED); goto bail; } - if (SetCurrentDirectory(buf) == false) { - errMsg.Format(L"Unable to set current directory to '%ls'.\n", buf); + if (SetCurrentDirectory(directory) == false) { + errMsg.Format(L"Unable to set current directory to '%ls'.\n", + (LPCWSTR) directory); ShowFailureMsg(pActionProgress, errMsg, IDS_FAILED); goto bail; } - buf += pAddOpts->GetFileNameOffset(); - while (*buf != '\0') { - LOGI(" file '%ls'", buf); + const CStringArray& fileNames = pAddOpts->GetFileNames(); + for (int i = 0; i < fileNames.GetCount(); i++) { + const CString& name = fileNames.GetAt(i); + LOGI(" file '%ls'", (LPCWSTR) name); /* add the file, calling DoAddFile via the generic AddFile */ - nerr = AddFile(pAddOpts, buf, &errMsg); + nerr = AddFile(pAddOpts, name, &errMsg); if (nerr != kNuErrNone) { if (errMsg.IsEmpty()) errMsg.Format(L"Failed while adding file '%ls': %hs.", - (LPCWSTR) buf, NuStrError(nerr)); + (LPCWSTR) name, NuStrError(nerr)); if (nerr != kNuErrAborted) { ShowFailureMsg(pActionProgress, errMsg, IDS_FAILED); } goto bail; } - - buf += wcslen(buf)+1; } if (fpAddDataHead == NULL) { @@ -1239,7 +1238,7 @@ bool DiskArchive::BulkAdd(ActionProgressDialog* pActionProgress, bail: FreeAddDataList(); if (SetCurrentDirectory(curDir) == false) { - errMsg.Format(L"Unable to reset current directory to '%ls'.\n", buf); + errMsg.Format(L"Unable to reset current directory to '%ls'.\n", curDir); ShowFailureMsg(pActionProgress, errMsg, IDS_FAILED); // bummer, but don't signal failure } diff --git a/app/NufxArchive.cpp b/app/NufxArchive.cpp index 5197713..0fb5fbb 100644 --- a/app/NufxArchive.cpp +++ b/app/NufxArchive.cpp @@ -908,38 +908,37 @@ bool NufxArchive::BulkAdd(ActionProgressDialog* pActionProgress, /* initialize count */ fNumAdded = 0; - const WCHAR* buf = pAddOpts->GetFileNames(); - LOGI("Selected path = '%ls' (offset=%d)", buf, - pAddOpts->GetFileNameOffset()); + const CString& directory = pAddOpts->GetDirectory(); + LOGI("Selected path = '%ls'", (LPCWSTR) directory); if (GetCurrentDirectory(NELEM(curDir), curDir) == 0) { errMsg = L"Unable to get current directory.\n"; ShowFailureMsg(fpMsgWnd, errMsg, IDS_FAILED); goto bail; } - if (SetCurrentDirectory(buf) == false) { - errMsg.Format(L"Unable to set current directory to '%ls'.\n", buf); + if (SetCurrentDirectory(directory) == false) { + errMsg.Format(L"Unable to set current directory to '%ls'.\n", + (LPCWSTR) directory); ShowFailureMsg(fpMsgWnd, errMsg, IDS_FAILED); goto bail; } - buf += pAddOpts->GetFileNameOffset(); - while (*buf != '\0') { - LOGI(" file '%ls'", buf); + const CStringArray& fileNames = pAddOpts->GetFileNames(); + for (int i = 0; i < fileNames.GetCount(); i++) { + const CString& name = fileNames.GetAt(i); + LOGI(" file '%ls'", (LPCWSTR) name); /* this just provides the list of files to NufxLib */ - nerr = AddFile(pAddOpts, buf, &errMsg); + nerr = AddFile(pAddOpts, name, &errMsg); if (nerr != kNuErrNone) { if (errMsg.IsEmpty()) errMsg.Format(L"Failed while adding file '%ls': %hs.", - buf, NuStrError(nerr)); + name, NuStrError(nerr)); if (nerr != kNuErrAborted) { ShowFailureMsg(fpMsgWnd, errMsg, IDS_FAILED); } goto bail; } - - buf += wcslen(buf)+1; } /* actually do the work */ @@ -970,7 +969,7 @@ bool NufxArchive::BulkAdd(ActionProgressDialog* pActionProgress, bail: NuAbort(fpArchive); // abort anything that didn't get flushed if (SetCurrentDirectory(curDir) == false) { - errMsg.Format(L"Unable to reset current directory to '%ls'.\n", buf); + errMsg.Format(L"Unable to reset current directory to '%ls'.\n", curDir); ShowFailureMsg(fpMsgWnd, errMsg, IDS_FAILED); // bummer, but don't signal failure } @@ -993,14 +992,20 @@ bool NufxArchive::AddDisk(ActionProgressDialog* pActionProgress, bool retVal = false; CStringA storageNameA, origNameA; - LOGI("AddDisk: '%ls' %d", pAddOpts->GetFileNames(), - pAddOpts->GetFileNameOffset()); - LOGI("Opts: '%ls' type=%d", (LPCWSTR) pAddOpts->fStoragePrefix, + LOGI("AddDisk: '%ls' (count=%d)", (LPCWSTR) pAddOpts->GetDirectory(), + pAddOpts->GetFileNames().GetCount()); + LOGI("Opts: stpfx='%ls' pres=%d", (LPCWSTR) pAddOpts->fStoragePrefix, pAddOpts->fTypePreservation); LOGI(" sub=%d strip=%d ovwr=%d", pAddOpts->fIncludeSubfolders, pAddOpts->fStripFolderNames, pAddOpts->fOverwriteExisting); + if (pAddOpts->GetFileNames().GetCount() != 1) { + LOGW("GLITCH: expected only one filename, found %d", + pAddOpts->GetFileNames().GetCount()); + goto bail; + } + pDiskImg = pAddOpts->fpDiskImg; ASSERT(pDiskImg != NULL); @@ -1016,27 +1021,27 @@ bool NufxArchive::AddDisk(ActionProgressDialog* pActionProgress, /* prepare to add */ AddPrep(pActionProgress, pAddOpts); - const WCHAR* buf; - buf = pAddOpts->GetFileNames(); - LOGI("Selected path = '%ls' (offset=%d)", buf, - pAddOpts->GetFileNameOffset()); + const CString& directory = pAddOpts->GetDirectory(); + const CStringArray& fileNames = pAddOpts->GetFileNames(); + LOGD("Selected path = '%ls'", (LPCWSTR) directory); if (GetCurrentDirectory(NELEM(curDir), curDir) == 0) { errMsg = L"Unable to get current directory.\n"; ShowFailureMsg(fpMsgWnd, errMsg, IDS_FAILED); goto bail; } - if (SetCurrentDirectory(buf) == false) { - errMsg.Format(L"Unable to set current directory to '%ls'.\n", buf); + if (SetCurrentDirectory(directory) == false) { + errMsg.Format(L"Unable to set current directory to '%ls'.\n", + (LPCWSTR) directory); ShowFailureMsg(fpMsgWnd, errMsg, IDS_FAILED); goto bail; } - buf += pAddOpts->GetFileNameOffset(); - LOGI(" file '%ls'", buf); + const CString& fileName = pAddOpts->GetFileNames().GetAt(0); + LOGI(" file '%ls'", (LPCWSTR) fileName); /* strip off preservation stuff, and ignore it */ - pathProp.Init(buf); + pathProp.Init(fileName); pathProp.fStripDiskImageSuffix = true; pathProp.LocalToArchive(pAddOpts); @@ -1047,7 +1052,7 @@ bool NufxArchive::AddDisk(ActionProgressDialog* pActionProgress, details.storageType = kBlockSize; details.access = kNuAccessUnlocked; details.extraType = pAddOpts->fpDiskImg->GetNumBlocks(); - origNameA = buf; + origNameA = fileName; // narrowing conversion storageNameA = pathProp.fStoredPathName; details.origName = origNameA; details.storageName = storageNameA; @@ -1056,7 +1061,7 @@ bool NufxArchive::AddDisk(ActionProgressDialog* pActionProgress, time_t now, then; - pathName = buf; + pathName.SetPathName(fileName); now = time(NULL); then = pathName.GetModWhen(); UNIXTimeToDateTime(&now, &details.archiveWhen); @@ -1065,7 +1070,7 @@ bool NufxArchive::AddDisk(ActionProgressDialog* pActionProgress, /* set up the progress updater */ pActionProgress->SetArcName(pathProp.fStoredPathName); - pActionProgress->SetFileName(buf); + pActionProgress->SetFileName(fileName); /* read the disk now that we have progress update titles in place */ int block, numBadBlocks; @@ -1142,7 +1147,7 @@ bail: NuAbort(fpArchive); // abort anything that didn't get flushed NuFreeDataSource(pSource); if (SetCurrentDirectory(curDir) == false) { - errMsg.Format(L"Unable to reset current directory to '%ls'.\n", buf); + errMsg.Format(L"Unable to reset current directory to '%ls'.\n", curDir); ShowFailureMsg(fpMsgWnd, errMsg, IDS_FAILED); // bummer } diff --git a/mdc/Main.cpp b/mdc/Main.cpp index 47c89be..79d86a0 100644 --- a/mdc/Main.cpp +++ b/mdc/Main.cpp @@ -165,7 +165,7 @@ void MainWindow::ScanFiles(void) memset(&scanOpts, 0, sizeof(scanOpts)); // choose input files - SelectFilesDialog2 chooseFiles(L"IDD_CHOOSE_FILES", this); + SelectFilesDialog chooseFiles(L"IDD_CHOOSE_FILES", false, this); chooseFiles.SetWindowTitle(L"Choose Files..."); INT_PTR retval = chooseFiles.DoModal(); if (retval != IDOK) { diff --git a/util/PathName.h b/util/PathName.h index 6ebdd4f..e1e38a2 100644 --- a/util/PathName.h +++ b/util/PathName.h @@ -128,6 +128,8 @@ public: static const WCHAR* FilenameOnly(const WCHAR* pathname, WCHAR fssep); private: + DECLARE_COPY_AND_OPEQ(PathName) + void SplitIFN(void) { if (!fSplit) { _wsplitpath(fPathName, fDrive, fDir, fFileName, fExt); diff --git a/util/SelectFilesDialog.cpp b/util/SelectFilesDialog.cpp index 08f2e6b..5022224 100644 --- a/util/SelectFilesDialog.cpp +++ b/util/SelectFilesDialog.cpp @@ -10,14 +10,18 @@ #include -void SelectFilesDialog2::OnInitDone() +void SelectFilesDialog::OnInitDone() { // Tweak the controls SetControlText(IDOK, L"Accept"); + // we don't take a "files of type" arg, so there's nothing in this combo box HideControl(stc2); // "Files of type" HideControl(cmb1); // (file type combo) + // Let the subclass do control configuration and data exchange. + (void) MyDataExchange(false); + // Configure a window proc to intercept events. We need to do it this // way, rather than using m_ofn.lpfnHook, because the CFileDialog hook // does not receive messages intended for the standard controls of the @@ -31,7 +35,7 @@ void SelectFilesDialog2::OnInitDone() ::SetWindowLongPtr(pParent->m_hWnd, GWL_USERDATA, (LONG_PTR) this); } -void SelectFilesDialog2::OnFolderChange() +void SelectFilesDialog::OnFolderChange() { // We get one of these shortly after OnInitDone. We can't do this in // OnInitDone because the dialog isn't ready yet. @@ -73,7 +77,7 @@ void SelectFilesDialog2::OnFolderChange() LOGD("OnFolderChange: '%ls'", (LPCWSTR) fCurrentDirectory); } -BOOL SelectFilesDialog2::OnFileNameOK() +BOOL SelectFilesDialog::OnFileNameOK() { // This function provides "custom validation of filenames that are // entered into a common file dialog box". We don't need to validate @@ -112,45 +116,49 @@ BOOL SelectFilesDialog2::OnFileNameOK() // not reached } -/*static*/ LRESULT CALLBACK SelectFilesDialog2::MyWindowProc(HWND hwnd, +/*static*/ LRESULT CALLBACK SelectFilesDialog::MyWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { - SelectFilesDialog2* pSFD = - (SelectFilesDialog2*) ::GetWindowLong(hwnd, GWL_USERDATA); + SelectFilesDialog* pSFD = + (SelectFilesDialog*) ::GetWindowLong(hwnd, GWL_USERDATA); if (uMsg == WM_COMMAND) { // React to a click on the OK button (also triggered by hitting return // in the filename text box). - if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDOK) { - // Obtain a CFileDialog pointer from the window handle. The - // SelectFilesDialog pointer only gets us to a child window - // (the one used to implement the templated custom stuff). - CFileDialog* pDialog = (CFileDialog*) CWnd::FromHandle(hwnd); - FPResult fpr = pSFD->OKButtonClicked(pDialog); - switch (fpr) { - case kFPDone: - // Success; close the dialog with IDOK as the return. - LOGD("Calling EndDialog"); - pDialog->EndDialog(IDOK); - return 0; - case kFPPassThru: - LOGD("passing through"); - // User typed a single name that didn't resolve to a - // simple file. Let the CFileDialog have it so it can - // do a directory change. (We don't just want to return - // nonzero -- fall through to call prior window proc.) - break; - case kFPNoFiles: - // No files have been selected. We popped up a message box. - // Discontinue processing. - return 0; - case kFPError: - // Something failed internally. Don't let the parent dialog - // continue on. - return 0; - default: - assert(false); - return 0; + if (HIWORD(wParam) == BN_CLICKED) { + if (LOWORD(wParam) == IDOK) { + // Obtain a CFileDialog pointer from the window handle. The + // SelectFilesDialog pointer only gets us to a child window + // (the one used to implement the templated custom stuff). + CFileDialog* pDialog = (CFileDialog*) CWnd::FromHandle(hwnd); + FPResult fpr = pSFD->OKButtonClicked(pDialog); + switch (fpr) { + case kFPDone: + // Success; close the dialog with IDOK as the return. + LOGD("Calling EndDialog"); + pDialog->EndDialog(IDOK); + return 0; + case kFPPassThru: + LOGD("passing through"); + // User typed a single name that didn't resolve to a + // simple file. Let the CFileDialog have it so it can + // do a directory change. (We don't just want to return + // nonzero -- fall through to call prior window proc.) + break; + case kFPNoFiles: + // No files have been selected. We popped up a message box. + // Discontinue processing. + return 0; + case kFPError: + // Something failed internally. Don't let the parent dialog + // continue on. + return 0; + default: + assert(false); + return 0; + } + } else if (LOWORD(wParam) == pshHelp) { + pSFD->HandleHelp(); } } } @@ -158,7 +166,7 @@ BOOL SelectFilesDialog2::OnFileNameOK() return ::CallWindowProc(pSFD->fPrevWndProc, hwnd, uMsg, wParam, lParam); } -SelectFilesDialog2::FPResult SelectFilesDialog2::OKButtonClicked(CFileDialog* pDialog) +SelectFilesDialog::FPResult SelectFilesDialog::OKButtonClicked(CFileDialog* pDialog) { // There are two not-quite-independent sources of filenames. As // ordinary (non-directory) files are selected, they are added to the @@ -280,10 +288,16 @@ SelectFilesDialog2::FPResult SelectFilesDialog2::OKButtonClicked(CFileDialog* pD } LOGV(" added %d directories", dirCount); + // let sub-classes copy data out, and have an opportunity to reject values + if (!MyDataExchange(true)) { + LOGW("MyDataExchange failed!"); + return kFPError; + } + return kFPDone; } -int SelectFilesDialog2::ParseFileNames(const CString& str) +int SelectFilesDialog::ParseFileNames(const CString& str) { // The filename string can come in two forms. If only one filename was // selected, the entire string will be the filename (spaces and all). If @@ -369,540 +383,3 @@ int SelectFilesDialog2::ParseFileNames(const CString& str) return 0; } - - - - - - - - - - - - - -/* - * Our CFileDialog "hook" function. - * - * "hdlg" is a handle to the child dialog box. Use the GetParent() function - * to get the handle of the dialog box window. - * - * uiMsg identifies the message being received. If it's WM_INITDIALOG, then - * lParam points to the OPENFILENAME structure. - * - * Do not call EndDialog from here. Instead, PostMessage a WM_COMMAND with - * IDABORT. (Looks like you can EndDialog on the parent and have it work, at - * least for IDCANCEL.) - * - * Return zero to enable standard processing, nonzero to claim ownership of - * the message. - */ -/*static*/ UINT CALLBACK SelectFilesDialog::OFNHookProc(HWND hDlg, UINT uiMsg, - WPARAM wParam, LPARAM lParam) -{ - OPENFILENAME* pOfn; - SelectFilesDialog* pSFD = NULL; - pOfn = (OPENFILENAME*) GetWindowLong(hDlg, GWL_USERDATA); - if (pOfn != NULL) { - pSFD = (SelectFilesDialog*) pOfn->lCustData; - /* allow our "this" pointer to play with the window */ - /* [does not seem to cause double-frees on cleanup] */ - if (pSFD->m_hWnd == NULL) - pSFD->m_hWnd = hDlg; - } - - switch (uiMsg) { - case WM_INITDIALOG: - LOGD("WM_INITDIALOG, OFN=0x%08lx", lParam); - SetWindowLong(hDlg, GWL_USERDATA, lParam); - break; - case WM_NOTIFY: // 0x4e - ASSERT(pSFD != NULL); - return pSFD->HandleNotify(hDlg, (LPOFNOTIFY)lParam); - case WM_COMMAND: - ASSERT(pSFD != NULL); - return pSFD->HandleCommand(hDlg, wParam, lParam); - case WM_SIZE: - ASSERT(pSFD != NULL); - return pSFD->HandleSize(hDlg, wParam, LOWORD(lParam), HIWORD(lParam)); - case WM_HELP: - ASSERT(pSFD != NULL); - return pSFD->HandleHelp(hDlg, (LPHELPINFO) lParam); - default: - LOGV("OFNHookProc: hDlg=0x%p uiMsg=0x%08lx " - "wParam=0x%08lx lParam=0x%08lx", - hDlg, uiMsg, wParam, lParam); - break; - } - - return 0; -} - -/* - * Handle WM_NOTIFY messages. - * - * You can indicate displeasure with the CDN_* messages by using SetWindowLong - * to alter the DWL_MSGRESULT value. - */ -UINT SelectFilesDialog::HandleNotify(HWND hDlg, LPOFNOTIFY pofn) -{ -// int count; - - switch (pofn->hdr.code) { - case CDN_INITDONE: - MyOnInitDone(); - return 1; - case CDN_SELCHANGE: - LOGI(" CDN_SELCHANGE"); - MyOnFileNameChange(/*&count*/); - //ClearFileName(); - return 1; - case CDN_FOLDERCHANGE: - LOGI(" CDN_FOLDERCHANGE"); - break; - case CDN_SHAREVIOLATION: - LOGI(" CDN_SHAREVIOLATION"); - break; - case CDN_HELP: - LOGI(" CDN_HELP!"); - break; - case CDN_FILEOK: - LOGI(" CDN_FILEOK"); - /* act like they hit the Accept button */ -// MyOnFileNameChange(&count); - //ClearFileName(); -// if (count != 0) { -// LOGI("Count = %d, accepting CDN_FILEOK", count); -// MyOnAccept(); -// } else { -// OPENFILENAME* pOfn; -// pOfn = (OPENFILENAME*) GetWindowLong(hDlg, GWL_USERDATA); -// LOGI("Count=0, name='%ls'", pOfn->lpstrFile); -// } - PrepEndDialog(); - /* must do this every time, or it fails in funky ways */ - SetWindowLong(hDlg, DWL_MSGRESULT, 1); - return 1; - case CDN_TYPECHANGE: - LOGI(" CDN_TYPECHANGE"); - break; - case CDN_INCLUDEITEM: - LOGI(" CDN_INCLUDEITEM"); - default: - LOGI(" HandleNotify, code=%d, pOfn=0x%p", pofn->hdr.code, pofn); - break; - } - - return 0; -} - -/* - * Handle WM_COMMAND messages. - */ -UINT SelectFilesDialog::HandleCommand(HWND hDlg, WPARAM wParam, LPARAM lParam) -{ - LOGD(" HandleCommand wParam=%d lParam=0x%08lx", wParam, lParam); - - if ((int) wParam == fAcceptButtonID) { - MyOnAccept(); - return 1; - } else if (wParam == IDCANCEL) { - MyOnCancel(); - return 1; - } else { - return MyOnCommand(wParam, lParam); - } -} - -/* - * Handle WM_SIZE. - */ -UINT SelectFilesDialog::HandleSize(HWND hDlg, UINT nType, int cx, int cy) -{ - LOGD("Dialog: old size %d,%d (ready=%d)", - fLastWinSize.Width(), fLastWinSize.Height(), fReady); - LOGD("Dialog: new size %d,%d", cx, cy); - - // we get called once before we have a chance to initialize - if (!fReady) - return 0; - - int deltaX, deltaY; - deltaX = cx - fLastWinSize.Width(); - deltaY = cy - fLastWinSize.Height(); - LOGD("Delta is %d,%d", deltaX, deltaY); - - ShiftControls(deltaX, 0 /*deltaY*/); - - // TODO: this is wrong - GetParent()->GetWindowRect(&fLastWinSize); - - return 1; -} - -/* - * User hit F1 or applied the '?' button to something. Our heritage is - * dubious, so use global functions to access the help file. - */ -UINT SelectFilesDialog::HandleHelp(HWND hDlg, LPHELPINFO lpHelpInfo) -{ - CWnd* pWndMain = ::AfxGetMainWnd(); - CWinApp* pAppMain = ::AfxGetApp(); - DWORD context = lpHelpInfo->iCtrlId; - BOOL result; - - //LOGI("Handling help with context %ld", context); - result = ::WinHelp(pWndMain->m_hWnd, pAppMain->m_pszHelpFilePath, - HELP_CONTEXTPOPUP, context); - //LOGI("SFD WinHelp returned %d", result); - return TRUE; // yes, we handled it -} - -/* - * When the CFileDialog finishes doing its thing, we "fix" stuff a bit. - * We can't really do this earlier, because we'd be destroying windows that - * the parent dialog wants to move. - * - * We need to shift everything up by the difference between the IDOK button - * and our "accept" button. - */ -void SelectFilesDialog::MyOnInitDone(void) -{ - LOGI("OnInitDone!"); - CWnd* pParent = GetParent(); - CWnd* pWnd; - CRect okRect, cancelRect, acceptRect; - int vertDiff; - - ASSERT(pParent != NULL); - pWnd = GetDlgItem(fAcceptButtonID); - ASSERT(pWnd != NULL); - pWnd->GetWindowRect(&acceptRect); - - pWnd = pParent->GetDlgItem(IDOK); - ASSERT(pWnd != NULL); - pWnd->GetWindowRect(&okRect); - pWnd = pParent->GetDlgItem(IDCANCEL); - ASSERT(pWnd != NULL); - pWnd->GetWindowRect(&cancelRect); - - vertDiff = acceptRect.top - okRect.top; - LOGD("vertDiff = %d (horizDiff=%d)", vertDiff, - acceptRect.left - okRect.left); - - ShiftControls(0, -vertDiff); - -// DestroyItem(pParent, stc3); // "File name" -// DestroyItem(pParent, edt1); // (file name edit) - DestroyItem(pParent, stc2); // "Files of type" - DestroyItem(pParent, cmb1); // (file type combo) - DestroyItem(pParent, IDOK); // "Open"/"Save" - DestroyItem(pParent, IDCANCEL); // "Cancel" - DestroyItem(this, stc32); // our placeholder - - pParent->GetWindowRect(&fLastWinSize); - fLastWinSize.bottom -= vertDiff; - pParent->MoveWindow(&fLastWinSize); - - // let sub-classes initialize the data fields - MyDataExchange(false); - - fReady = true; -} - -/* - * Shift the controls when the window size changes. This is a bit tricky - * because the CFileDialog is also moving the controls, though it doesn't - * move them in quite the way we want. - */ -void SelectFilesDialog::ShiftControls(int deltaX, int deltaY) -{ - if (deltaX == 0 && deltaY == 0) { - LOGI("SFD OnSize: no meaningful change"); - return; - } else { - LOGI("ShiftControls x=%d y=%d", deltaX, deltaY); - } - MoveControl(this, fAcceptButtonID, deltaX, deltaY, false); - MoveControl(this, IDCANCEL, deltaX, deltaY, false); - //StretchControl(this, IDC_FVIEW_EDITBOX, deltaX, deltaY); - - // erase & redraw - Invalidate(true); -} - -/* - * Get the list view control out of the common file dialog. - * - * Returns "NULL" if it can't find it. - */ -CWnd* SelectFilesDialog::GetListCtrl(void) -{ - CWnd* pItem; - CWnd* pList; - - /* our dialog is a child; get our parent, then grab the shellview */ - pItem = GetParent()->GetDlgItem(lst2); - ASSERT(pItem != NULL); - if (pItem == NULL) - return NULL; - - /* pull the listview out of the shellview */ - pList = pItem->GetDlgItem( 1); - ASSERT(pList != NULL); - - return pList; -} - -/* - * When the selection changes, update our dialog. - */ -void SelectFilesDialog::MyOnFileNameChange(void) -{ - //LOGI("OnFileNameChange"); - - CListCtrl* pList; - - pList = (CListCtrl*) GetListCtrl(); - if (pList == NULL) { - LOGI("GLITCH: could not get list control"); - return; - } - ASSERT(pList != NULL); - - //LOGI("Selected count=%d", pList->GetSelectedCount()); - //*pCount = pList->GetSelectedCount(); - - //CWnd* pItem; - //pItem = GetDlgItem(IDC_SELECT_ACCEPT); - //ASSERT(pItem != NULL); - //pItem->EnableWindow(*pCount != 0); -} - -/* - * The user hit the "Accept" button. Package up the file selection. - */ -void SelectFilesDialog::MyOnAccept(void) -{ - LOGD("OnAccept!"); - PrepEndDialog(); -} - -/* - * Either the user hit the "Accept" button or the OFN dialog has indicated - * that it wants to close. - * - * Returns "true" if all went well, "false" if it failed (e.g. because the - * user hasn't selected any files). - */ -bool SelectFilesDialog::PrepEndDialog(void) -{ - CListCtrl* pList; - int nextSpot = 0; - - // let sub-classes copy data out - if (!MyDataExchange(true)) { - LOGW("MyDataExchange failed!"); - return false; - } - - /* - * Start with the set of stuff that the window wants to tell us about. - * Run through the list, converting all '\0' to '\\'. Later on we - * convert them back. - * - * nFileOffset will be zero if they click on "accept" instead of hitting - * return in the edit box or double-clicking on files. This can make it - * tricky to have names in the text field and files selected, because - * clicking rather than hitting "enter" will yield different results. - * - * The OFN dialog does add names to the box as they are selected, which - * makes it awkward to do anything reasonable. - * - * Fortunately I believe the world is divided into "typers" and - * "clickers", and so long as their paths don't cross we're fine. - */ - LOGD("PrepEndDialog: got max=%d off=%d str='%ls'", - m_ofn.nMaxFile, m_ofn.nFileOffset, m_ofn.lpstrFile); - if (m_ofn.nFileOffset != 0) { - WCHAR* buf = m_ofn.lpstrFile; - buf += m_ofn.nFileOffset; - while (*buf != '\0') { - if (buf > m_ofn.lpstrFile) - *(buf-1) = '\\'; - LOGD(" File '%ls'", buf); - buf += wcslen(buf) +1; - } - //Sleep(1000); - nextSpot = (buf - m_ofn.lpstrFile) -1; - ASSERT(*(m_ofn.lpstrFile + nextSpot) == '\0'); - ASSERT(*(m_ofn.lpstrFile + nextSpot+1) == '\0'); - - /* stick a '\' on the very end, so we get double-null action later */ - *(m_ofn.lpstrFile + nextSpot) = '\\'; - } - LOGD("Last offset was %d", nextSpot); - -#if 0 - /* make it clear that they're only getting one */ - /* (bad case: click on some files and hit "Accept") */ - if (nextSpot == 0) { - CWnd* pEditWnd; - CString editText; - pEditWnd = GetParent()->GetDlgItem(edt1); - pEditWnd->GetWindowText(editText); - if (!editText.IsEmpty()) { - pEditWnd->SetWindowText(""); - return false; - } - } -#endif - - /* - * Now merge in the selected files. - */ - pList = (CListCtrl*) GetListCtrl(); - if (pList == NULL) { - LOGW("GLITCH: could not get list control"); - return false; - } - ASSERT(pList != NULL); - - CString fileNames; - - int count = pList->GetSelectedCount(); - LOGD("List control has %d items; nextSpot=%d", count, nextSpot); - if (count == 0) { - if (nextSpot == 0) { - MessageBox(L"Please select one or more files and directories.", - m_ofn.lpstrTitle, MB_OK | MB_ICONWARNING); - /* make it clear that we're ignoring the names they typed */ - ClearFileName(); - return false; - } - - /* nothing but typed-in names */ - LOGD("Using typed-in names"); - fileNames = m_ofn.lpstrFile; - fFileNameOffset = m_ofn.nFileOffset; - } else { - bool compare; - if (nextSpot == 0) { - fileNames = GetFolderPath(); - LOGD("set filenames to folder path (%ls)", (LPCWSTR) fileNames); - /* add a trailing '\', which gets stomped to '\0' later on */ - if (fileNames.Right(1) != L"\\") - fileNames += L"\\"; - fFileNameOffset = fileNames.GetLength(); - compare = false; - } else { - fileNames = m_ofn.lpstrFile; - ASSERT(fileNames.Right(1) == L"\\"); - fFileNameOffset = m_ofn.nFileOffset; - compare = true; - } - ASSERT(fFileNameOffset > 0); - - POSITION posn; - posn = pList->GetFirstSelectedItemPosition(); - if (posn == NULL) { - /* shouldn't happen -- Accept button should be dimmed */ - ASSERT(false); - return false; - } - while (posn != NULL) { - /* do this every time, because "fileNames" can be reallocated */ - const WCHAR* tailStr = fileNames; - tailStr += fFileNameOffset-1; - - int num = pList->GetNextSelectedItem(posn); // posn is updated - - /* here we make a big assumption: that GetItemData returns a PIDL */ - LPITEMIDLIST pidl; - WCHAR buf[MAX_PATH]; - pidl = (LPITEMIDLIST) pList->GetItemData(num); - if (SHGetPathFromIDList(pidl, buf)) { - /* it's a relative PIDL but SHGetPathFromIDList wants a full - one, so it returns CWD + filename... strip bogus path off */ - CString compareName(L"\\"); - PathName path(buf); - compareName += path.GetFileName(); - compareName += L"\\"; - //LOGI(" Checking name='%ls'", compareName); - - if (compare && Stristr(tailStr, compareName) != NULL) { - LOGI(" Matched '%ls', not adding", (LPCWSTR) compareName); - } else { - if (compare) { - LOGI(" No match on '%ls', adding", (LPCWSTR) compareName); - } else { - LOGI(" Found '%ls', adding", (LPCWSTR) compareName); - } - fileNames += path.GetFileName(); - fileNames += L"\\"; - } - } else { - /* expected, for things like "Control Panels" or "My Network" */ - LOGI(" No path for '%ls'", - (LPCWSTR) pList->GetItemText(num, 0)); - } - } - - if (fileNames.GetLength() >= (int)m_ofn.nMaxFile) { - LOGW("GLITCH: excessively long file name list"); - return false; - } - } - - LOGI("Final result: names at %d, len=%d, str='%ls'", - fFileNameOffset, wcslen(fileNames), (LPCWSTR) fileNames); - - /* - * Null-terminate with extreme prejudice. Every filename should be - * separated with a null, and the last filename should be followed by - * two. Every component was followed by '\\', and the last one - * naturally has a null, so we should be in great shape after this. - * - * Could probably have done it entirely with CString, but I'm not sure - * how CStrings react to buffers with multiple null-terminated - * sub-strings. - */ - ASSERT(fFileNames != m_ofn.lpstrFile); - delete[] fFileNames; - fFileNames = wcsdup(fileNames); - WCHAR* cp = fFileNames; - cp += fFileNameOffset-1; - while (*cp != '\0') { - if (*cp == '\\') - *cp = '\0'; - cp++; - } - //fileNames.ReleaseBuffer(-1); - - fExitStatus = IDOK; - CDialog* pDialog = (CDialog*) GetParent(); - pDialog->EndDialog(IDCANCEL); - - return true; -} - -/* - * User hit our cancel button. - */ -void SelectFilesDialog::MyOnCancel(void) -{ - fExitStatus = IDCANCEL; - CDialog* pDialog = (CDialog*) GetParent(); - pDialog->EndDialog(IDCANCEL); -} - -/* - * Clear the filename field. - */ -void SelectFilesDialog::ClearFileName(void) -{ - CWnd* pWnd = GetParent()->GetDlgItem(edt1); - if (pWnd != NULL) - pWnd->SetWindowText(L""); -} diff --git a/util/SelectFilesDialog.h b/util/SelectFilesDialog.h index 6540516..e6e7195 100644 --- a/util/SelectFilesDialog.h +++ b/util/SelectFilesDialog.h @@ -40,129 +40,11 @@ #define UTIL_SELECTFILESDIALOG_H /* - * File selection, based on an "open" file dialog. - * - * DoModal will return with IDCANCEL to indicate that the standard fields - * (like lpstrFile) are not actually filled in. Use the provided fields - * to get exit status and filename info. - * - * Defer any dialog initialization to MyDataExchange. + * File selection, based on an "open" common file dialog. */ class SelectFilesDialog : public CFileDialog { public: - enum { kFileNameBufSize = 32768 }; - SelectFilesDialog(const WCHAR* rctmpl, CWnd* pParentWnd = NULL) : - CFileDialog(true, NULL, NULL, OFN_HIDEREADONLY, NULL, pParentWnd, - 0, FALSE /*disable Vista style*/) - { - m_ofn.Flags |= OFN_ENABLETEMPLATE | OFN_ALLOWMULTISELECT | - OFN_HIDEREADONLY | OFN_EXPLORER | OFN_ENABLESIZING; - m_ofn.lpTemplateName = rctmpl; - m_ofn.hInstance = AfxGetInstanceHandle(); - m_ofn.lpstrFile = new WCHAR[kFileNameBufSize]; - m_ofn.lpstrFile[0] = m_ofn.lpstrFile[1] = '\0'; - m_ofn.nMaxFile = kFileNameBufSize; - m_ofn.nFileOffset = -1; - m_ofn.Flags |= OFN_ENABLEHOOK; - m_ofn.lpfnHook = OFNHookProc; - m_ofn.lCustData = (LPARAM) this; - - fExitStatus = IDABORT; - fFileNames = new WCHAR[2]; - fFileNames[0] = fFileNames[1] = '\0'; - fFileNameOffset = 0; - - fAcceptButtonID = IDOK; - - fReady = false; // used by WM_SIZE handler - } - virtual ~SelectFilesDialog(void) { - delete[] m_ofn.lpstrFile; - delete[] fFileNames; - } - - int GetExitStatus(void) const { return fExitStatus; } - const WCHAR* GetFileNames(void) const { return fFileNames; } - int GetFileNameOffset(void) const { return fFileNameOffset; } - - // set the window title; must be called before DoModal - void SetWindowTitle(const WCHAR* title) { - m_ofn.lpstrTitle = title; - } - - // stuff values into our filename holder - void SetFileNames(const WCHAR* fileNames, size_t len, int fileNameOffset) { - ASSERT(len > wcslen(fileNames)); - ASSERT(fileNames[len] == '\0'); - ASSERT(fileNames[len-1] == '\0'); - LOGD("SetFileNames '%ls' %d %d", fileNames, len, fileNameOffset); - delete[] fFileNames; - fFileNames = new WCHAR[len]; - memcpy(fFileNames, fileNames, len * sizeof(WCHAR)); - fFileNameOffset = fileNameOffset; - } - -protected: - // would be overrides - virtual void MyOnInitDone(void); - virtual void MyOnFileNameChange(void); - // like DoDataExchange, but ret val replaces pDX->Fail() - virtual bool MyDataExchange(bool saveAndValidate) { return true; } - - // would be message handlers - virtual UINT HandleNotify(HWND hDlg, LPOFNOTIFY pofn); - virtual UINT HandleCommand(HWND hDlg, WPARAM wParam, LPARAM lParam); - virtual UINT HandleSize(HWND hDlg, UINT nType, int cx, int cy); - virtual UINT HandleHelp(HWND hDlg, LPHELPINFO lpHelp); - virtual UINT MyOnCommand(WPARAM wParam, LPARAM lParam) { return 0; } - - virtual void MyOnAccept(void); - virtual void MyOnCancel(void); - - // utility functions - virtual CWnd* GetListCtrl(void); - virtual void ShiftControls(int deltaX, int deltaY); - virtual void DestroyItem(CWnd* pDlg, int id) { - CWnd* pWnd = pDlg->GetDlgItem(id); - if (pWnd == NULL) { - LOGW("Could not find item %p %d", pDlg, id); - return; - } - pWnd->DestroyWindow(); - } - virtual bool PrepEndDialog(void); - - // make this a little easier - int MessageBox(LPCTSTR lpszText, LPCTSTR lpszCaption = NULL, - UINT nType = MB_OK) - { - return GetParent()->MessageBox(lpszText, lpszCaption, nType); - } - - // we're in a library, so the app resources have to tell us this - int fAcceptButtonID; - -private: - static UINT CALLBACK OFNHookProc(HWND hdlg, UINT uiMsg, WPARAM wParam, - LPARAM lParam); - void ClearFileName(void); - - bool fReady; - int fExitStatus; - int fFileNameOffset; - WCHAR* fFileNames; - CRect fLastWinSize; - - //DECLARE_MESSAGE_MAP() -}; - - -/* - * File selection, based on an "open" common file dialog. - */ -class SelectFilesDialog2 : public CFileDialog { -public: - SelectFilesDialog2(const WCHAR* rctmpl, CWnd* pParentWnd = NULL) : + SelectFilesDialog(const WCHAR* rctmpl, bool showHelp, CWnd* pParentWnd = NULL) : CFileDialog(true, NULL, NULL, OFN_HIDEREADONLY, NULL, pParentWnd, 0, FALSE /*disable Vista style*/) { @@ -170,7 +52,8 @@ public: // we want the multi-select behavior but we don't want to return // the filenames in the filename buf. m_ofn.Flags |= OFN_ENABLETEMPLATE | OFN_ALLOWMULTISELECT | - OFN_HIDEREADONLY | OFN_EXPLORER | OFN_ENABLESIZING; + OFN_HIDEREADONLY | OFN_EXPLORER | OFN_ENABLESIZING | + (showHelp ? OFN_SHOWHELP : 0); // Configure use of a template. The dialog template must have // WS_CHILD and WS_CLIPSIBLINGS set, and should have DS_3DLOOK and @@ -179,7 +62,7 @@ public: m_ofn.hInstance = AfxGetInstanceHandle(); } - virtual ~SelectFilesDialog2(void) {} + virtual ~SelectFilesDialog(void) {} /* * Gets the directory name where the files live. This is a full path. @@ -200,14 +83,40 @@ public: m_ofn.lpstrTitle = (LPCWSTR) fCustomTitle; } -protected: /* - * Finish configuring the file dialog. + * Stuffs a single file/directory into the return value fields. This is + * (mis-)used by code that treats AddFilesDialog as a way to pass data + * around. + * + * TODO: don't do this -- change the callers to pass data around differently + */ + void StuffSingleFilename(const CString& directory, const CString& filename) { + fCurrentDirectory = directory; + fFileNameArray.RemoveAll(); + fFileNameArray.Add(filename); + } + +protected: + // This is much like DoDataExchange, but ret val replaces pDX->Fail(). + // This will be called with saveAndValidate==true during OnInitDialog, + // and with false when we've extracted the filenames and are about to + // close the dialog. + // + // Return true on success, false if something failed and we want to keep + // the dialog open. + virtual bool MyDataExchange(bool saveAndValidate) { return true; } + + // Handles a click on the "help" button. + virtual void HandleHelp() {} + +private: + /* + * Finishes configuring the file dialog. */ virtual void OnInitDone() override; /* - * Track changes to the current directory. + * Tracks changes to the current directory. * * Updates fCurrentDirectory with the path currently being used by the * dialog. For items with no path (e.g. Computer or Libraries), this @@ -215,7 +124,6 @@ protected: */ virtual void OnFolderChange() override; -private: /* * Custom filename validation; in our case, it's a double-click trap. * @@ -266,7 +174,7 @@ private: CString fCustomTitle; // Directory the dialog is currently accessing. Prepend this to the - // entries in fFileNameArray to get the full path. + // entries in fFileNameArray to get full paths. CString fCurrentDirectory; // File names of selected files.