Enhance FileDialog for allowing UNIX stile file path
And use modern CustomFileDialog istead of old FileDialog in Notepad++. In the file dialog, override window procedure for "OK" button and file name edit box to check for input. Transform forward slash file name to a Window path after input. Fix #9374, close #9403
This commit is contained in:
parent
ab5c1d3e2a
commit
e7079d57c6
|
@ -871,7 +871,6 @@ You can define several column markers by using white space to separate the diffe
|
||||||
<Item id="6413" name="Default Open/Save file Directory"/>
|
<Item id="6413" name="Default Open/Save file Directory"/>
|
||||||
<Item id="6414" name="Follow current document"/>
|
<Item id="6414" name="Follow current document"/>
|
||||||
<Item id="6415" name="Remember last used directory"/>
|
<Item id="6415" name="Remember last used directory"/>
|
||||||
<Item id="6430" name="Use new style dialog (without Unix style path capacity)"/>
|
|
||||||
<Item id="6431" name="Open all files of folder instead of launching Folder as Workspace on folder dropping"/>
|
<Item id="6431" name="Open all files of folder instead of launching Folder as Workspace on folder dropping"/>
|
||||||
</DefaultDir>
|
</DefaultDir>
|
||||||
|
|
||||||
|
|
|
@ -868,7 +868,6 @@ You can define several column markers by using white space to separate the diffe
|
||||||
<Item id="6413" name="Default Directory (Open/Save)"/>
|
<Item id="6413" name="Default Directory (Open/Save)"/>
|
||||||
<Item id="6414" name="Follow current document"/>
|
<Item id="6414" name="Follow current document"/>
|
||||||
<Item id="6415" name="Remember last used directory"/>
|
<Item id="6415" name="Remember last used directory"/>
|
||||||
<Item id="6430" name="Use new style dialog (without Unix style path capacity)"/>
|
|
||||||
<Item id="6431" name="Open all files of folder instead of launching Folder as Workspace on folder dropping"/>
|
<Item id="6431" name="Open all files of folder instead of launching Folder as Workspace on folder dropping"/>
|
||||||
</DefaultDir>
|
</DefaultDir>
|
||||||
|
|
||||||
|
|
|
@ -146,13 +146,12 @@ generic_string folderBrowser(HWND parent, const generic_string & title, int outp
|
||||||
else if (directory[0])
|
else if (directory[0])
|
||||||
dlg.setFolder(directory);
|
dlg.setFolder(directory);
|
||||||
|
|
||||||
const TCHAR* szDir = dlg.pickFolder();
|
folderName = dlg.pickFolder();
|
||||||
if (szDir)
|
if (!folderName.empty())
|
||||||
{
|
{
|
||||||
// Send the result back to the edit control
|
// Send the result back to the edit control
|
||||||
if (outputCtrlID != 0)
|
if (outputCtrlID != 0)
|
||||||
::SetDlgItemText(parent, outputCtrlID, szDir);
|
::SetDlgItemText(parent, outputCtrlID, folderName.c_str());
|
||||||
folderName = szDir;
|
|
||||||
}
|
}
|
||||||
return folderName;
|
return folderName;
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
#include "sha-256.h"
|
#include "sha-256.h"
|
||||||
#include "md5Dlgs.h"
|
#include "md5Dlgs.h"
|
||||||
#include "md5Dlgs_rc.h"
|
#include "md5Dlgs_rc.h"
|
||||||
#include "FileDialog.h"
|
#include "CustomFileDialog.h"
|
||||||
#include "Parameters.h"
|
#include "Parameters.h"
|
||||||
#include <shlwapi.h>
|
#include <shlwapi.h>
|
||||||
|
|
||||||
|
@ -63,13 +63,14 @@ INT_PTR CALLBACK HashFromFilesDlg::run_dlgProc(UINT message, WPARAM wParam, LPAR
|
||||||
|
|
||||||
case IDC_HASH_FILEBROWSER_BUTTON:
|
case IDC_HASH_FILEBROWSER_BUTTON:
|
||||||
{
|
{
|
||||||
FileDialog fDlg(_hSelf, ::GetModuleHandle(NULL));
|
CustomFileDialog fDlg(_hSelf);
|
||||||
fDlg.setExtFilter(TEXT("All types"), TEXT(".*"), NULL);
|
fDlg.setExtFilter(TEXT("All types"), TEXT(".*"));
|
||||||
|
|
||||||
if (stringVector *pfns = fDlg.doOpenMultiFilesDlg())
|
const auto& fns = fDlg.doOpenMultiFilesDlg();
|
||||||
|
if (!fns.empty())
|
||||||
{
|
{
|
||||||
std::wstring files2check, hashResultStr;
|
std::wstring files2check, hashResultStr;
|
||||||
for (const auto& it : *pfns)
|
for (const auto& it : fns)
|
||||||
{
|
{
|
||||||
if (_ht == hashType::hash_md5)
|
if (_ht == hashType::hash_md5)
|
||||||
{
|
{
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
#include <wininet.h>
|
#include <wininet.h>
|
||||||
#include "Notepad_plus.h"
|
#include "Notepad_plus.h"
|
||||||
#include "Notepad_plus_Window.h"
|
#include "Notepad_plus_Window.h"
|
||||||
#include "FileDialog.h"
|
#include "CustomFileDialog.h"
|
||||||
#include "Printer.h"
|
#include "Printer.h"
|
||||||
#include "FileNameStringSplitter.h"
|
#include "FileNameStringSplitter.h"
|
||||||
#include "lesDlgs.h"
|
#include "lesDlgs.h"
|
||||||
|
@ -6022,12 +6022,13 @@ void Notepad_plus::setFindReplaceFolderFilter(const TCHAR *dir, const TCHAR *fil
|
||||||
|
|
||||||
vector<generic_string> Notepad_plus::addNppComponents(const TCHAR *destDir, const TCHAR *extFilterName, const TCHAR *extFilter)
|
vector<generic_string> Notepad_plus::addNppComponents(const TCHAR *destDir, const TCHAR *extFilterName, const TCHAR *extFilter)
|
||||||
{
|
{
|
||||||
FileDialog fDlg(_pPublicInterface->getHSelf(), _pPublicInterface->getHinst());
|
CustomFileDialog fDlg(_pPublicInterface->getHSelf());
|
||||||
fDlg.setExtFilter(extFilterName, extFilter, NULL);
|
fDlg.setExtFilter(extFilterName, extFilter);
|
||||||
|
|
||||||
vector<generic_string> copiedFiles;
|
vector<generic_string> copiedFiles;
|
||||||
|
|
||||||
if (stringVector *pfns = fDlg.doOpenMultiFilesDlg())
|
const auto& fns = fDlg.doOpenMultiFilesDlg();
|
||||||
|
if (!fns.empty())
|
||||||
{
|
{
|
||||||
// Get plugins dir
|
// Get plugins dir
|
||||||
generic_string destDirName = (NppParameters::getInstance()).getNppPath();
|
generic_string destDirName = (NppParameters::getInstance()).getNppPath();
|
||||||
|
@ -6040,15 +6041,15 @@ vector<generic_string> Notepad_plus::addNppComponents(const TCHAR *destDir, cons
|
||||||
|
|
||||||
destDirName += TEXT("\\");
|
destDirName += TEXT("\\");
|
||||||
|
|
||||||
size_t sz = pfns->size();
|
size_t sz = fns.size();
|
||||||
for (size_t i = 0 ; i < sz ; ++i)
|
for (size_t i = 0 ; i < sz ; ++i)
|
||||||
{
|
{
|
||||||
if (::PathFileExists(pfns->at(i).c_str()))
|
if (::PathFileExists(fns.at(i).c_str()))
|
||||||
{
|
{
|
||||||
// copy to plugins directory
|
// copy to plugins directory
|
||||||
generic_string destName = destDirName;
|
generic_string destName = destDirName;
|
||||||
destName += ::PathFindFileName(pfns->at(i).c_str());
|
destName += ::PathFindFileName(fns.at(i).c_str());
|
||||||
if (::CopyFile(pfns->at(i).c_str(), destName.c_str(), FALSE))
|
if (::CopyFile(fns.at(i).c_str(), destName.c_str(), FALSE))
|
||||||
copiedFiles.push_back(destName.c_str());
|
copiedFiles.push_back(destName.c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6058,12 +6059,13 @@ vector<generic_string> Notepad_plus::addNppComponents(const TCHAR *destDir, cons
|
||||||
|
|
||||||
vector<generic_string> Notepad_plus::addNppPlugins(const TCHAR *extFilterName, const TCHAR *extFilter)
|
vector<generic_string> Notepad_plus::addNppPlugins(const TCHAR *extFilterName, const TCHAR *extFilter)
|
||||||
{
|
{
|
||||||
FileDialog fDlg(_pPublicInterface->getHSelf(), _pPublicInterface->getHinst());
|
CustomFileDialog fDlg(_pPublicInterface->getHSelf());
|
||||||
fDlg.setExtFilter(extFilterName, extFilter, NULL);
|
fDlg.setExtFilter(extFilterName, extFilter);
|
||||||
|
|
||||||
vector<generic_string> copiedFiles;
|
vector<generic_string> copiedFiles;
|
||||||
|
|
||||||
if (stringVector *pfns = fDlg.doOpenMultiFilesDlg())
|
const auto& fns = fDlg.doOpenMultiFilesDlg();
|
||||||
|
if (!fns.empty())
|
||||||
{
|
{
|
||||||
// Get plugins dir
|
// Get plugins dir
|
||||||
generic_string destDirName = (NppParameters::getInstance()).getPluginRootDir();
|
generic_string destDirName = (NppParameters::getInstance()).getPluginRootDir();
|
||||||
|
@ -6073,15 +6075,15 @@ vector<generic_string> Notepad_plus::addNppPlugins(const TCHAR *extFilterName, c
|
||||||
::CreateDirectory(destDirName.c_str(), NULL);
|
::CreateDirectory(destDirName.c_str(), NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t sz = pfns->size();
|
size_t sz = fns.size();
|
||||||
for (size_t i = 0 ; i < sz ; ++i)
|
for (size_t i = 0 ; i < sz ; ++i)
|
||||||
{
|
{
|
||||||
if (::PathFileExists(pfns->at(i).c_str()))
|
if (::PathFileExists(fns.at(i).c_str()))
|
||||||
{
|
{
|
||||||
// copy to plugins directory
|
// copy to plugins directory
|
||||||
generic_string destName = destDirName;
|
generic_string destName = destDirName;
|
||||||
|
|
||||||
generic_string nameExt = ::PathFindFileName(pfns->at(i).c_str());
|
generic_string nameExt = ::PathFindFileName(fns.at(i).c_str());
|
||||||
auto pos = nameExt.find_last_of(TEXT("."));
|
auto pos = nameExt.find_last_of(TEXT("."));
|
||||||
if (pos == generic_string::npos)
|
if (pos == generic_string::npos)
|
||||||
continue;
|
continue;
|
||||||
|
@ -6094,7 +6096,7 @@ vector<generic_string> Notepad_plus::addNppPlugins(const TCHAR *extFilterName, c
|
||||||
}
|
}
|
||||||
PathAppend(destName, nameExt);
|
PathAppend(destName, nameExt);
|
||||||
|
|
||||||
if (::CopyFile(pfns->at(i).c_str(), destName.c_str(), FALSE))
|
if (::CopyFile(fns.at(i).c_str(), destName.c_str(), FALSE))
|
||||||
copiedFiles.push_back(destName.c_str());
|
copiedFiles.push_back(destName.c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -127,7 +127,7 @@ struct QuoteParams
|
||||||
const wchar_t* _quote = nullptr;
|
const wchar_t* _quote = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
class FileDialog;
|
class CustomFileDialog;
|
||||||
class Notepad_plus_Window;
|
class Notepad_plus_Window;
|
||||||
class AnsiCharPanel;
|
class AnsiCharPanel;
|
||||||
class ClipboardHistoryPanel;
|
class ClipboardHistoryPanel;
|
||||||
|
@ -575,7 +575,7 @@ private:
|
||||||
generic_string getLangFromMenu(const Buffer * buf);
|
generic_string getLangFromMenu(const Buffer * buf);
|
||||||
|
|
||||||
generic_string exts2Filters(const generic_string& exts, int maxExtsLen = -1) const; // maxExtsLen default value -1 makes no limit of whole exts length
|
generic_string exts2Filters(const generic_string& exts, int maxExtsLen = -1) const; // maxExtsLen default value -1 makes no limit of whole exts length
|
||||||
int setFileOpenSaveDlgFilters(FileDialog & fDlg, bool showAllExt, int langType = -1); // showAllExt should be true if it's used for open file dialog - all set exts should be used for filtering files
|
int setFileOpenSaveDlgFilters(CustomFileDialog & fDlg, bool showAllExt, int langType = -1); // showAllExt should be true if it's used for open file dialog - all set exts should be used for filtering files
|
||||||
Style * getStyleFromName(const TCHAR *styleName);
|
Style * getStyleFromName(const TCHAR *styleName);
|
||||||
bool dumpFiles(const TCHAR * outdir, const TCHAR * fileprefix = TEXT("")); //helper func
|
bool dumpFiles(const TCHAR * outdir, const TCHAR * fileprefix = TEXT("")); //helper func
|
||||||
void drawTabbarColoursFromStylerArray();
|
void drawTabbarColoursFromStylerArray();
|
||||||
|
|
|
@ -19,7 +19,6 @@
|
||||||
#include <shlwapi.h>
|
#include <shlwapi.h>
|
||||||
#include <shlobj.h>
|
#include <shlobj.h>
|
||||||
#include "Notepad_plus_Window.h"
|
#include "Notepad_plus_Window.h"
|
||||||
#include "FileDialog.h"
|
|
||||||
#include "CustomFileDialog.h"
|
#include "CustomFileDialog.h"
|
||||||
#include "EncodingMapper.h"
|
#include "EncodingMapper.h"
|
||||||
#include "VerticalFileSwitcher.h"
|
#include "VerticalFileSwitcher.h"
|
||||||
|
@ -825,7 +824,7 @@ generic_string Notepad_plus::exts2Filters(const generic_string& exts, int maxExt
|
||||||
return filters;
|
return filters;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Notepad_plus::setFileOpenSaveDlgFilters(FileDialog & fDlg, bool showAllExt, int langType)
|
int Notepad_plus::setFileOpenSaveDlgFilters(CustomFileDialog & fDlg, bool showAllExt, int langType)
|
||||||
{
|
{
|
||||||
NppParameters& nppParam = NppParameters::getInstance();
|
NppParameters& nppParam = NppParameters::getInstance();
|
||||||
NppGUI & nppGUI = (NppGUI & )nppParam.getNppGUI();
|
NppGUI & nppGUI = (NppGUI & )nppParam.getNppGUI();
|
||||||
|
@ -875,7 +874,7 @@ int Notepad_plus::setFileOpenSaveDlgFilters(FileDialog & fDlg, bool showAllExt,
|
||||||
const TCHAR *filters = stringFilters.c_str();
|
const TCHAR *filters = stringFilters.c_str();
|
||||||
if (filters[0])
|
if (filters[0])
|
||||||
{
|
{
|
||||||
fDlg.setExtsFilter(getLangDesc(lid, false).c_str(), filters);
|
fDlg.setExtFilter(getLangDesc(lid, false).c_str(), filters);
|
||||||
|
|
||||||
//
|
//
|
||||||
// Get index of lang type to find
|
// Get index of lang type to find
|
||||||
|
@ -1629,9 +1628,9 @@ bool Notepad_plus::fileSaveAs(BufferID id, bool isSaveCopy)
|
||||||
bufferID = _pEditView->getCurrentBufferID();
|
bufferID = _pEditView->getCurrentBufferID();
|
||||||
Buffer * buf = MainFileManager.getBufferByID(bufferID);
|
Buffer * buf = MainFileManager.getBufferByID(bufferID);
|
||||||
|
|
||||||
FileDialog fDlg(_pPublicInterface->getHSelf(), _pPublicInterface->getHinst());
|
CustomFileDialog fDlg(_pPublicInterface->getHSelf());
|
||||||
|
|
||||||
fDlg.setExtFilter(TEXT("All types"), TEXT(".*"), NULL);
|
fDlg.setExtFilter(TEXT("All types"), TEXT(".*"));
|
||||||
|
|
||||||
LangType langType = buf->getLangType();
|
LangType langType = buf->getLangType();
|
||||||
|
|
||||||
|
@ -1650,20 +1649,20 @@ bool Notepad_plus::fileSaveAs(BufferID id, bool isSaveCopy)
|
||||||
auto cdBefore = nppParam.getNppGUI()._fileAutoDetection;
|
auto cdBefore = nppParam.getNppGUI()._fileAutoDetection;
|
||||||
(const_cast<NppGUI &>(nppParam.getNppGUI()))._fileAutoDetection = cdDisabled;
|
(const_cast<NppGUI &>(nppParam.getNppGUI()))._fileAutoDetection = cdDisabled;
|
||||||
|
|
||||||
TCHAR *pfn = fDlg.doSaveDlg();
|
generic_string fn = fDlg.doSaveDlg();
|
||||||
|
|
||||||
// Enable file autodetection again.
|
// Enable file autodetection again.
|
||||||
(const_cast<NppGUI &>(nppParam.getNppGUI()))._fileAutoDetection = cdBefore;
|
(const_cast<NppGUI &>(nppParam.getNppGUI()))._fileAutoDetection = cdBefore;
|
||||||
|
|
||||||
if (pfn)
|
if (!fn.empty())
|
||||||
{
|
{
|
||||||
BufferID other = _pDocTab->findBufferByName(pfn);
|
BufferID other = _pDocTab->findBufferByName(fn.c_str());
|
||||||
if (other == BUFFER_INVALID)
|
if (other == BUFFER_INVALID)
|
||||||
other = _pNonDocTab->findBufferByName(pfn);
|
other = _pNonDocTab->findBufferByName(fn.c_str());
|
||||||
|
|
||||||
if (other == BUFFER_INVALID) //can save, as both (same and other) view don't contain buffer
|
if (other == BUFFER_INVALID) //can save, as both (same and other) view don't contain buffer
|
||||||
{
|
{
|
||||||
bool res = doSave(bufferID, pfn, isSaveCopy);
|
bool res = doSave(bufferID, fn.c_str(), isSaveCopy);
|
||||||
//buf->setNeedsLexing(true); //commented to fix wrapping being removed after save as (due to SCI_CLEARSTYLE or something, seems to be Scintilla bug)
|
//buf->setNeedsLexing(true); //commented to fix wrapping being removed after save as (due to SCI_CLEARSTYLE or something, seems to be Scintilla bug)
|
||||||
//Changing lexer after save seems to work properly
|
//Changing lexer after save seems to work properly
|
||||||
return res;
|
return res;
|
||||||
|
@ -1703,16 +1702,16 @@ bool Notepad_plus::fileRename(BufferID id)
|
||||||
bool isFileExisting = PathFileExists(buf->getFullPathName()) != FALSE;
|
bool isFileExisting = PathFileExists(buf->getFullPathName()) != FALSE;
|
||||||
if (isFileExisting)
|
if (isFileExisting)
|
||||||
{
|
{
|
||||||
FileDialog fDlg(_pPublicInterface->getHSelf(), _pPublicInterface->getHinst());
|
CustomFileDialog fDlg(_pPublicInterface->getHSelf());
|
||||||
|
|
||||||
fDlg.setExtFilter(TEXT("All types"), TEXT(".*"), NULL);
|
fDlg.setExtFilter(TEXT("All types"), TEXT(".*"));
|
||||||
setFileOpenSaveDlgFilters(fDlg, false);
|
setFileOpenSaveDlgFilters(fDlg, false);
|
||||||
|
|
||||||
fDlg.setDefFileName(buf->getFileName());
|
fDlg.setDefFileName(buf->getFileName());
|
||||||
TCHAR *pfn = fDlg.doSaveDlg();
|
generic_string fn = fDlg.doSaveDlg();
|
||||||
|
|
||||||
if (pfn)
|
if (!fn.empty())
|
||||||
success = MainFileManager.moveFile(bufferID, pfn);
|
success = MainFileManager.moveFile(bufferID, fn.c_str());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -1830,22 +1829,20 @@ bool Notepad_plus::fileDelete(BufferID id)
|
||||||
|
|
||||||
void Notepad_plus::fileOpen()
|
void Notepad_plus::fileOpen()
|
||||||
{
|
{
|
||||||
FileDialog fDlg(_pPublicInterface->getHSelf(), _pPublicInterface->getHinst());
|
CustomFileDialog fDlg(_pPublicInterface->getHSelf());
|
||||||
fDlg.setExtFilter(TEXT("All types"), TEXT(".*"), NULL);
|
fDlg.setExtFilter(TEXT("All types"), TEXT(".*"));
|
||||||
|
|
||||||
setFileOpenSaveDlgFilters(fDlg, true);
|
setFileOpenSaveDlgFilters(fDlg, true);
|
||||||
|
|
||||||
BufferID lastOpened = BUFFER_INVALID;
|
BufferID lastOpened = BUFFER_INVALID;
|
||||||
if (stringVector *pfns = fDlg.doOpenMultiFilesDlg())
|
const auto& fns = fDlg.doOpenMultiFilesDlg();
|
||||||
{
|
size_t sz = fns.size();
|
||||||
size_t sz = pfns->size();
|
|
||||||
for (size_t i = 0 ; i < sz ; ++i)
|
for (size_t i = 0 ; i < sz ; ++i)
|
||||||
{
|
{
|
||||||
BufferID test = doOpen(pfns->at(i).c_str(), fDlg.isReadOnly());
|
BufferID test = doOpen(fns.at(i).c_str(), fDlg.isReadOnly());
|
||||||
if (test != BUFFER_INVALID)
|
if (test != BUFFER_INVALID)
|
||||||
lastOpened = test;
|
lastOpened = test;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (lastOpened != BUFFER_INVALID)
|
if (lastOpened != BUFFER_INVALID)
|
||||||
{
|
{
|
||||||
|
@ -2190,10 +2187,10 @@ bool Notepad_plus::loadSession(Session & session, bool isSnapshotMode, bool shou
|
||||||
bool Notepad_plus::fileLoadSession(const TCHAR *fn)
|
bool Notepad_plus::fileLoadSession(const TCHAR *fn)
|
||||||
{
|
{
|
||||||
bool result = false;
|
bool result = false;
|
||||||
const TCHAR *sessionFileName = NULL;
|
generic_string sessionFileName;
|
||||||
if (fn == NULL)
|
if (fn == NULL)
|
||||||
{
|
{
|
||||||
FileDialog fDlg(_pPublicInterface->getHSelf(), _pPublicInterface->getHinst());
|
CustomFileDialog fDlg(_pPublicInterface->getHSelf());
|
||||||
const TCHAR *ext = NppParameters::getInstance().getNppGUI()._definedSessionExt.c_str();
|
const TCHAR *ext = NppParameters::getInstance().getNppGUI()._definedSessionExt.c_str();
|
||||||
generic_string sessionExt = TEXT("");
|
generic_string sessionExt = TEXT("");
|
||||||
if (*ext != '\0')
|
if (*ext != '\0')
|
||||||
|
@ -2201,10 +2198,10 @@ bool Notepad_plus::fileLoadSession(const TCHAR *fn)
|
||||||
if (*ext != '.')
|
if (*ext != '.')
|
||||||
sessionExt += TEXT(".");
|
sessionExt += TEXT(".");
|
||||||
sessionExt += ext;
|
sessionExt += ext;
|
||||||
fDlg.setExtFilter(TEXT("Session file"), sessionExt.c_str(), NULL);
|
fDlg.setExtFilter(TEXT("Session file"), sessionExt.c_str());
|
||||||
fDlg.setDefExt(ext);
|
fDlg.setDefExt(ext);
|
||||||
}
|
}
|
||||||
fDlg.setExtFilter(TEXT("All types"), TEXT(".*"), NULL);
|
fDlg.setExtFilter(TEXT("All types"), TEXT(".*"));
|
||||||
sessionFileName = fDlg.doOpenSingleFileDlg();
|
sessionFileName = fDlg.doOpenSingleFileDlg();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -2216,7 +2213,7 @@ bool Notepad_plus::fileLoadSession(const TCHAR *fn)
|
||||||
|
|
||||||
NppParameters& nppParam = NppParameters::getInstance();
|
NppParameters& nppParam = NppParameters::getInstance();
|
||||||
const NppGUI & nppGUI = nppParam.getNppGUI();
|
const NppGUI & nppGUI = nppParam.getNppGUI();
|
||||||
if (sessionFileName)
|
if (!sessionFileName.empty())
|
||||||
{
|
{
|
||||||
bool isEmptyNpp = false;
|
bool isEmptyNpp = false;
|
||||||
if (_mainDocTab.nbItem() == 1 && _subDocTab.nbItem() == 1)
|
if (_mainDocTab.nbItem() == 1 && _subDocTab.nbItem() == 1)
|
||||||
|
@ -2243,7 +2240,7 @@ bool Notepad_plus::fileLoadSession(const TCHAR *fn)
|
||||||
bool isAllSuccessful = true;
|
bool isAllSuccessful = true;
|
||||||
Session session2Load;
|
Session session2Load;
|
||||||
|
|
||||||
if ((NppParameters::getInstance()).loadSession(session2Load, sessionFileName))
|
if ((NppParameters::getInstance()).loadSession(session2Load, sessionFileName.c_str()))
|
||||||
{
|
{
|
||||||
const bool isSnapshotMode = false;
|
const bool isSnapshotMode = false;
|
||||||
const bool shouldLoadFileBrowser = true;
|
const bool shouldLoadFileBrowser = true;
|
||||||
|
@ -2251,7 +2248,7 @@ bool Notepad_plus::fileLoadSession(const TCHAR *fn)
|
||||||
result = true;
|
result = true;
|
||||||
}
|
}
|
||||||
if (!isAllSuccessful)
|
if (!isAllSuccessful)
|
||||||
(NppParameters::getInstance()).writeSession(session2Load, sessionFileName);
|
(NppParameters::getInstance()).writeSession(session2Load, sessionFileName.c_str());
|
||||||
}
|
}
|
||||||
if (result == false)
|
if (result == false)
|
||||||
{
|
{
|
||||||
|
@ -2299,8 +2296,6 @@ const TCHAR * Notepad_plus::fileSaveSession(size_t nbFile, TCHAR ** fileNames, c
|
||||||
|
|
||||||
const TCHAR * Notepad_plus::fileSaveSession(size_t nbFile, TCHAR ** fileNames)
|
const TCHAR * Notepad_plus::fileSaveSession(size_t nbFile, TCHAR ** fileNames)
|
||||||
{
|
{
|
||||||
const TCHAR *sessionFileName = NULL;
|
|
||||||
|
|
||||||
CustomFileDialog fDlg(_pPublicInterface->getHSelf());
|
CustomFileDialog fDlg(_pPublicInterface->getHSelf());
|
||||||
const TCHAR *ext = NppParameters::getInstance().getNppGUI()._definedSessionExt.c_str();
|
const TCHAR *ext = NppParameters::getInstance().getNppGUI()._definedSessionExt.c_str();
|
||||||
|
|
||||||
|
@ -2319,9 +2314,9 @@ const TCHAR * Notepad_plus::fileSaveSession(size_t nbFile, TCHAR ** fileNames)
|
||||||
const generic_string checkboxLabel = _nativeLangSpeaker.getLocalizedStrFromID("session-save-folder-as-workspace",
|
const generic_string checkboxLabel = _nativeLangSpeaker.getLocalizedStrFromID("session-save-folder-as-workspace",
|
||||||
TEXT("Save Folder as Workspace"));
|
TEXT("Save Folder as Workspace"));
|
||||||
fDlg.setCheckbox(checkboxLabel.c_str(), isCheckboxActive);
|
fDlg.setCheckbox(checkboxLabel.c_str(), isCheckboxActive);
|
||||||
sessionFileName = fDlg.doSaveDlg();
|
generic_string sessionFileName = fDlg.doSaveDlg();
|
||||||
|
|
||||||
return fileSaveSession(nbFile, fileNames, sessionFileName, fDlg.getCheckboxState());
|
return fileSaveSession(nbFile, fileNames, sessionFileName.c_str(), fDlg.getCheckboxState());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,6 @@
|
||||||
#include <shlwapi.h>
|
#include <shlwapi.h>
|
||||||
#include <shlobj.h>
|
#include <shlobj.h>
|
||||||
#include "Parameters.h"
|
#include "Parameters.h"
|
||||||
#include "FileDialog.h"
|
|
||||||
#include "ScintillaEditView.h"
|
#include "ScintillaEditView.h"
|
||||||
#include "keys.h"
|
#include "keys.h"
|
||||||
#include "localization.h"
|
#include "localization.h"
|
||||||
|
@ -842,8 +841,6 @@ winVer NppParameters::getWindowsVersion()
|
||||||
return WV_UNKNOWN;
|
return WV_UNKNOWN;
|
||||||
}
|
}
|
||||||
|
|
||||||
int FileDialog::_dialogFileBoxId = (NppParameters::getInstance()).getWinVersion() < WV_W2K?edt1:cmb13;
|
|
||||||
|
|
||||||
|
|
||||||
NppParameters::NppParameters()
|
NppParameters::NppParameters()
|
||||||
{
|
{
|
||||||
|
@ -5266,10 +5263,6 @@ void NppParameters::feedGUIParameters(TiXmlNode *node)
|
||||||
if (optNameWriteTechnologyEngine)
|
if (optNameWriteTechnologyEngine)
|
||||||
_nppGUI._writeTechnologyEngine = (lstrcmp(optNameWriteTechnologyEngine, TEXT("1")) == 0) ? directWriteTechnology : defaultTechnology;
|
_nppGUI._writeTechnologyEngine = (lstrcmp(optNameWriteTechnologyEngine, TEXT("1")) == 0) ? directWriteTechnology : defaultTechnology;
|
||||||
|
|
||||||
const TCHAR * optNameNewStyleSaveDlg = element->Attribute(TEXT("newStyleSaveDlg"));
|
|
||||||
if (optNameNewStyleSaveDlg)
|
|
||||||
_nppGUI._useNewStyleSaveDlg = (lstrcmp(optNameNewStyleSaveDlg, TEXT("yes")) == 0);
|
|
||||||
|
|
||||||
const TCHAR * optNameFolderDroppedOpenFiles = element->Attribute(TEXT("isFolderDroppedOpenFiles"));
|
const TCHAR * optNameFolderDroppedOpenFiles = element->Attribute(TEXT("isFolderDroppedOpenFiles"));
|
||||||
if (optNameFolderDroppedOpenFiles)
|
if (optNameFolderDroppedOpenFiles)
|
||||||
_nppGUI._isFolderDroppedOpenFiles = (lstrcmp(optNameFolderDroppedOpenFiles, TEXT("yes")) == 0);
|
_nppGUI._isFolderDroppedOpenFiles = (lstrcmp(optNameFolderDroppedOpenFiles, TEXT("yes")) == 0);
|
||||||
|
@ -6206,7 +6199,7 @@ void NppParameters::createXmlTreeFromGUIParams()
|
||||||
GUIConfigElement->SetAttribute(TEXT("setting"), _nppGUI._multiInstSetting);
|
GUIConfigElement->SetAttribute(TEXT("setting"), _nppGUI._multiInstSetting);
|
||||||
}
|
}
|
||||||
|
|
||||||
// <GUIConfig name="MISC" fileSwitcherWithoutExtColumn="no" backSlashIsEscapeCharacterForSql="yes" newStyleSaveDlg="no" isFolderDroppedOpenFiles="no" saveDlgExtFilterToAllTypes="no" />
|
// <GUIConfig name="MISC" fileSwitcherWithoutExtColumn="no" backSlashIsEscapeCharacterForSql="yes" isFolderDroppedOpenFiles="no" saveDlgExtFilterToAllTypes="no" />
|
||||||
{
|
{
|
||||||
TiXmlElement *GUIConfigElement = (newGUIRoot->InsertEndChild(TiXmlElement(TEXT("GUIConfig"))))->ToElement();
|
TiXmlElement *GUIConfigElement = (newGUIRoot->InsertEndChild(TiXmlElement(TEXT("GUIConfig"))))->ToElement();
|
||||||
GUIConfigElement->SetAttribute(TEXT("name"), TEXT("MISC"));
|
GUIConfigElement->SetAttribute(TEXT("name"), TEXT("MISC"));
|
||||||
|
@ -6214,7 +6207,6 @@ void NppParameters::createXmlTreeFromGUIParams()
|
||||||
GUIConfigElement->SetAttribute(TEXT("fileSwitcherWithoutExtColumn"), _nppGUI._fileSwitcherWithoutExtColumn ? TEXT("yes") : TEXT("no"));
|
GUIConfigElement->SetAttribute(TEXT("fileSwitcherWithoutExtColumn"), _nppGUI._fileSwitcherWithoutExtColumn ? TEXT("yes") : TEXT("no"));
|
||||||
GUIConfigElement->SetAttribute(TEXT("backSlashIsEscapeCharacterForSql"), _nppGUI._backSlashIsEscapeCharacterForSql ? TEXT("yes") : TEXT("no"));
|
GUIConfigElement->SetAttribute(TEXT("backSlashIsEscapeCharacterForSql"), _nppGUI._backSlashIsEscapeCharacterForSql ? TEXT("yes") : TEXT("no"));
|
||||||
GUIConfigElement->SetAttribute(TEXT("writeTechnologyEngine"), _nppGUI._writeTechnologyEngine);
|
GUIConfigElement->SetAttribute(TEXT("writeTechnologyEngine"), _nppGUI._writeTechnologyEngine);
|
||||||
GUIConfigElement->SetAttribute(TEXT("newStyleSaveDlg"), _nppGUI._useNewStyleSaveDlg ? TEXT("yes") : TEXT("no"));
|
|
||||||
GUIConfigElement->SetAttribute(TEXT("isFolderDroppedOpenFiles"), _nppGUI._isFolderDroppedOpenFiles ? TEXT("yes") : TEXT("no"));
|
GUIConfigElement->SetAttribute(TEXT("isFolderDroppedOpenFiles"), _nppGUI._isFolderDroppedOpenFiles ? TEXT("yes") : TEXT("no"));
|
||||||
GUIConfigElement->SetAttribute(TEXT("docPeekOnTab"), _nppGUI._isDocPeekOnTab ? TEXT("yes") : TEXT("no"));
|
GUIConfigElement->SetAttribute(TEXT("docPeekOnTab"), _nppGUI._isDocPeekOnTab ? TEXT("yes") : TEXT("no"));
|
||||||
GUIConfigElement->SetAttribute(TEXT("docPeekOnMap"), _nppGUI._isDocPeekOnMap ? TEXT("yes") : TEXT("no"));
|
GUIConfigElement->SetAttribute(TEXT("docPeekOnMap"), _nppGUI._isDocPeekOnMap ? TEXT("yes") : TEXT("no"));
|
||||||
|
|
|
@ -897,7 +897,6 @@ struct NppGUI final
|
||||||
size_t _snapshotBackupTiming = 7000;
|
size_t _snapshotBackupTiming = 7000;
|
||||||
generic_string _cloudPath; // this option will never be read/written from/to config.xml
|
generic_string _cloudPath; // this option will never be read/written from/to config.xml
|
||||||
unsigned char _availableClouds = '\0'; // this option will never be read/written from/to config.xml
|
unsigned char _availableClouds = '\0'; // this option will never be read/written from/to config.xml
|
||||||
bool _useNewStyleSaveDlg = true;
|
|
||||||
|
|
||||||
enum SearchEngineChoice{ se_custom = 0, se_duckDuckGo = 1, se_google = 2, se_bing = 3, se_yahoo = 4, se_stackoverflow = 5 };
|
enum SearchEngineChoice{ se_custom = 0, se_duckDuckGo = 1, se_google = 2, se_bing = 3, se_yahoo = 4, se_stackoverflow = 5 };
|
||||||
SearchEngineChoice _searchEngineChoice = se_google;
|
SearchEngineChoice _searchEngineChoice = se_google;
|
||||||
|
@ -1664,14 +1663,6 @@ public:
|
||||||
_currentDefaultFgColor = c;
|
_currentDefaultFgColor = c;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool useNewStyleSaveDlg() const {
|
|
||||||
return _nppGUI._useNewStyleSaveDlg;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setUseNewStyleSaveDlg(bool v) {
|
|
||||||
_nppGUI._useNewStyleSaveDlg = v;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setCmdSettingsDir(const generic_string& settingsDir) {
|
void setCmdSettingsDir(const generic_string& settingsDir) {
|
||||||
_cmdSettingsDir = settingsDir;
|
_cmdSettingsDir = settingsDir;
|
||||||
};
|
};
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
#include "Parameters.h"
|
#include "Parameters.h"
|
||||||
#include "resource.h"
|
#include "resource.h"
|
||||||
#include "Notepad_plus_msgs.h"
|
#include "Notepad_plus_msgs.h"
|
||||||
#include "FileDialog.h"
|
#include "CustomFileDialog.h"
|
||||||
#include "Common.h"
|
#include "Common.h"
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
@ -1260,11 +1260,10 @@ INT_PTR CALLBACK UserDefineDialog::run_dlgProc(UINT message, WPARAM wParam, LPAR
|
||||||
}
|
}
|
||||||
case IDC_IMPORT_BUTTON :
|
case IDC_IMPORT_BUTTON :
|
||||||
{
|
{
|
||||||
FileDialog fDlg(_hSelf, ::GetModuleHandle(NULL));
|
CustomFileDialog fDlg(_hSelf);
|
||||||
fDlg.setExtFilter(TEXT("UDL"), TEXT(".xml"), NULL);
|
fDlg.setExtFilter(TEXT("UDL"), TEXT(".xml"));
|
||||||
TCHAR *fn = fDlg.doOpenSingleFileDlg();
|
generic_string sourceFile = fDlg.doOpenSingleFileDlg();
|
||||||
if (!fn) break;
|
if (sourceFile.empty()) break;
|
||||||
generic_string sourceFile = fn;
|
|
||||||
|
|
||||||
bool isSuccessful = nppParam.importUDLFromFile(sourceFile);
|
bool isSuccessful = nppParam.importUDLFromFile(sourceFile);
|
||||||
if (isSuccessful)
|
if (isSuccessful)
|
||||||
|
@ -1291,12 +1290,11 @@ INT_PTR CALLBACK UserDefineDialog::run_dlgProc(UINT message, WPARAM wParam, LPAR
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
FileDialog fDlg(_hSelf, ::GetModuleHandle(NULL));
|
CustomFileDialog fDlg(_hSelf);
|
||||||
fDlg.setExtFilter(TEXT("UDL"), TEXT(".xml"), NULL);
|
fDlg.setExtFilter(TEXT("UDL"), TEXT(".xml"));
|
||||||
fDlg.setExtIndex(0); // 0 Default index else file will be saved without extension
|
fDlg.setExtIndex(0); // 0 Default index else file will be saved without extension
|
||||||
TCHAR *fn = fDlg.doSaveDlg();
|
generic_string fileName2save = fDlg.doSaveDlg();
|
||||||
if (!fn) break;
|
if (fileName2save.empty()) break;
|
||||||
generic_string fileName2save = fn;
|
|
||||||
|
|
||||||
if (i2Export > 0)
|
if (i2Export > 0)
|
||||||
{
|
{
|
||||||
|
|
|
@ -18,7 +18,6 @@
|
||||||
#include "fileBrowser.h"
|
#include "fileBrowser.h"
|
||||||
#include "resource.h"
|
#include "resource.h"
|
||||||
#include "tinyxml.h"
|
#include "tinyxml.h"
|
||||||
#include "FileDialog.h"
|
|
||||||
#include "localization.h"
|
#include "localization.h"
|
||||||
#include "Parameters.h"
|
#include "Parameters.h"
|
||||||
#include "RunDlg.h"
|
#include "RunDlg.h"
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// This file is part of Notepad++ project
|
// This file is part of Notepad++ project
|
||||||
// Copyright (C)2021 Don HO <don.h@free.fr>
|
// Copyright (C) 2021 The Notepad++ Contributors.
|
||||||
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
// This program is free software: you can redistribute it and/or modify
|
||||||
// it under the terms of the GNU General Public License as published by
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
@ -15,39 +15,429 @@
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
// Windows Vista is a minimum required version for Common File Dialogs.
|
|
||||||
// Define it only for current source file.
|
|
||||||
#if defined(_WIN32_WINNT) && (!defined(_WIN32_WINNT_VISTA) || (_WIN32_WINNT < _WIN32_WINNT_VISTA))
|
|
||||||
#undef _WIN32_WINNT
|
|
||||||
#define _WIN32_WINNT _WIN32_WINNT_VISTA
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <shobjidl.h>
|
#include <shobjidl.h>
|
||||||
|
#include <shlwapi.h> // PathIsDirectory
|
||||||
|
#ifdef __MINGW32__
|
||||||
|
#include <cwchar>
|
||||||
|
#endif
|
||||||
|
#include <comdef.h> // _com_error
|
||||||
|
#include <comip.h> // _com_ptr_t
|
||||||
|
|
||||||
#include "CustomFileDialog.h"
|
#include "CustomFileDialog.h"
|
||||||
#include "Parameters.h"
|
#include "Parameters.h"
|
||||||
|
|
||||||
// Private impelemnation to avoid pollution with includes and defines in header.
|
// Workaround for MinGW because its implementation of __uuidof is different.
|
||||||
|
template<class T>
|
||||||
|
struct ComTraits
|
||||||
|
{
|
||||||
|
static const GUID uid;
|
||||||
|
};
|
||||||
|
template<class T>
|
||||||
|
const GUID ComTraits<T>::uid = __uuidof(T);
|
||||||
|
|
||||||
|
// Smart pointer alias for COM objects that makes reference counting easier.
|
||||||
|
template<class T>
|
||||||
|
using com_ptr = _com_ptr_t<_com_IIID<T, &ComTraits<T>::uid>>;
|
||||||
|
|
||||||
|
|
||||||
|
namespace // anonymous
|
||||||
|
{
|
||||||
|
// Note: these common functions could be moved to some header.
|
||||||
|
|
||||||
|
struct Filter
|
||||||
|
{
|
||||||
|
generic_string name;
|
||||||
|
generic_string ext;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Returns a first extension from the extension specification string.
|
||||||
|
// Multiple extensions are separated with ';'.
|
||||||
|
// Example: input - ".c;.cpp;.h", output - ".c"
|
||||||
|
generic_string get1stExt(const generic_string& extSpec)
|
||||||
|
{
|
||||||
|
size_t pos = extSpec.find('.');
|
||||||
|
if (pos != generic_string::npos)
|
||||||
|
{
|
||||||
|
size_t posEnd = extSpec.find(';', pos + 1);
|
||||||
|
if (posEnd != generic_string::npos)
|
||||||
|
{
|
||||||
|
size_t extLen = posEnd - pos;
|
||||||
|
return extSpec.substr(pos, extLen);
|
||||||
|
}
|
||||||
|
return extSpec.substr(pos);
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool replaceExt(generic_string& name, const generic_string& ext)
|
||||||
|
{
|
||||||
|
if (!name.empty() && !ext.empty())
|
||||||
|
{
|
||||||
|
// Remove an existing extension from the name.
|
||||||
|
size_t posNameExt = name.find_last_of('.');
|
||||||
|
if (posNameExt != generic_string::npos)
|
||||||
|
name.erase(posNameExt);
|
||||||
|
// Append a new extension.
|
||||||
|
name += ext;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hasExt(const generic_string& name)
|
||||||
|
{
|
||||||
|
return name.find_last_of('.') != generic_string::npos;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool endsWith(const generic_string& s, const generic_string& suffix)
|
||||||
|
{
|
||||||
|
#if defined(_MSVC_LANG) && (_MSVC_LANG > 201402L)
|
||||||
|
#error Replace this function with basic_string::ends_with
|
||||||
|
#endif
|
||||||
|
size_t pos = s.find(suffix);
|
||||||
|
return pos != s.npos && ((s.length() - pos) == suffix.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool setDialogFolder(IFileDialog* dialog, const TCHAR* folder)
|
||||||
|
{
|
||||||
|
IShellItem* psi = nullptr;
|
||||||
|
HRESULT hr = SHCreateItemFromParsingName(folder,
|
||||||
|
0,
|
||||||
|
IID_IShellItem,
|
||||||
|
reinterpret_cast<void**>(&psi));
|
||||||
|
if (SUCCEEDED(hr))
|
||||||
|
hr = dialog->SetFolder(psi);
|
||||||
|
return SUCCEEDED(hr);
|
||||||
|
}
|
||||||
|
|
||||||
|
generic_string getDialogFileName(IFileDialog* dialog)
|
||||||
|
{
|
||||||
|
PWSTR pszFilePath = nullptr;
|
||||||
|
dialog->GetFileName(&pszFilePath);
|
||||||
|
generic_string fileName = pszFilePath;
|
||||||
|
CoTaskMemFree(pszFilePath);
|
||||||
|
return fileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Backups the current directory in constructor and restores it in destructor.
|
||||||
|
// This is needed in case dialog changes the current directory.
|
||||||
|
class CurrentDirBackup
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CurrentDirBackup()
|
||||||
|
{
|
||||||
|
::GetCurrentDirectory(MAX_PATH, _dir);
|
||||||
|
}
|
||||||
|
~CurrentDirBackup()
|
||||||
|
{
|
||||||
|
NppParameters& params = NppParameters::getInstance();
|
||||||
|
if (params.getNppGUI()._openSaveDir == dir_last)
|
||||||
|
{
|
||||||
|
::GetCurrentDirectory(MAX_PATH, _dir);
|
||||||
|
params.setWorkingDir(_dir);
|
||||||
|
}
|
||||||
|
::SetCurrentDirectory(_dir);
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
TCHAR _dir[MAX_PATH];
|
||||||
|
};
|
||||||
|
|
||||||
|
} // anonymous namespace
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
class FileDialogEventHandler : public IFileDialogEvents
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static HRESULT createInstance(const std::vector<Filter>& filterSpec, REFIID riid, void **ppv)
|
||||||
|
{
|
||||||
|
*ppv = nullptr;
|
||||||
|
FileDialogEventHandler *pDialogEventHandler = new (std::nothrow) FileDialogEventHandler(filterSpec);
|
||||||
|
HRESULT hr = pDialogEventHandler ? S_OK : E_OUTOFMEMORY;
|
||||||
|
if (SUCCEEDED(hr))
|
||||||
|
{
|
||||||
|
hr = pDialogEventHandler->QueryInterface(riid, ppv);
|
||||||
|
pDialogEventHandler->Release();
|
||||||
|
}
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// IUnknown methods
|
||||||
|
|
||||||
|
IFACEMETHODIMP QueryInterface(REFIID riid, void** ppv) override
|
||||||
|
{
|
||||||
|
// Always set out parameter to NULL, validating it first.
|
||||||
|
if (!ppv)
|
||||||
|
return E_INVALIDARG;
|
||||||
|
*ppv = nullptr;
|
||||||
|
if (riid == __uuidof(IUnknown) || riid == __uuidof(IFileDialogEvents))
|
||||||
|
{
|
||||||
|
// Increment the reference count and return the pointer.
|
||||||
|
*ppv = static_cast<IFileDialogEvents*>(this);
|
||||||
|
AddRef();
|
||||||
|
return NOERROR;
|
||||||
|
}
|
||||||
|
return E_NOINTERFACE;
|
||||||
|
}
|
||||||
|
|
||||||
|
IFACEMETHODIMP_(ULONG) AddRef() override
|
||||||
|
{
|
||||||
|
return InterlockedIncrement(&_cRef);
|
||||||
|
}
|
||||||
|
|
||||||
|
IFACEMETHODIMP_(ULONG) Release() override
|
||||||
|
{
|
||||||
|
long cRef = InterlockedDecrement(&_cRef);
|
||||||
|
if (!cRef)
|
||||||
|
delete this;
|
||||||
|
return cRef;
|
||||||
|
}
|
||||||
|
|
||||||
|
// IFileDialogEvents methods
|
||||||
|
|
||||||
|
IFACEMETHODIMP OnFileOk(IFileDialog*) override
|
||||||
|
{
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
IFACEMETHODIMP OnFolderChange(IFileDialog* dlg) override
|
||||||
|
{
|
||||||
|
// First launch order: 3. Custom controls are added but inactive.
|
||||||
|
if (!_dialog)
|
||||||
|
initDialog(dlg);
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
IFACEMETHODIMP OnFolderChanging(IFileDialog*, IShellItem*) override
|
||||||
|
{
|
||||||
|
// First launch order: 2. Buttons are added, correct window title.
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
IFACEMETHODIMP OnSelectionChange(IFileDialog*) override
|
||||||
|
{
|
||||||
|
// First launch order: 4. Main window is shown.
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
IFACEMETHODIMP OnShareViolation(IFileDialog*, IShellItem*, FDE_SHAREVIOLATION_RESPONSE*) override
|
||||||
|
{
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
IFACEMETHODIMP OnTypeChange(IFileDialog* dlg) override
|
||||||
|
{
|
||||||
|
// First launch order: 1. Inactive, window title might be wrong.
|
||||||
|
generic_string name = getDialogFileName(dlg);
|
||||||
|
if (changeExt(name, dlg))
|
||||||
|
dlg->SetFileName(name.c_str());
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
IFACEMETHODIMP OnOverwrite(IFileDialog*, IShellItem*, FDE_OVERWRITE_RESPONSE*) override
|
||||||
|
{
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
// Use createInstance() instead
|
||||||
|
FileDialogEventHandler(const std::vector<Filter>& filterSpec) : _cRef(1), _filterSpec(filterSpec)
|
||||||
|
{
|
||||||
|
_staticThis = this;
|
||||||
|
}
|
||||||
|
~FileDialogEventHandler()
|
||||||
|
{
|
||||||
|
_staticThis = nullptr;
|
||||||
|
}
|
||||||
|
FileDialogEventHandler(const FileDialogEventHandler&) = delete;
|
||||||
|
FileDialogEventHandler& operator=(const FileDialogEventHandler&) = delete;
|
||||||
|
FileDialogEventHandler(FileDialogEventHandler&&) = delete;
|
||||||
|
FileDialogEventHandler& operator=(FileDialogEventHandler&&) = delete;
|
||||||
|
|
||||||
|
void initDialog(IFileDialog * d)
|
||||||
|
{
|
||||||
|
assert(!_dialog);
|
||||||
|
_dialog = d;
|
||||||
|
_okButtonProc = nullptr;
|
||||||
|
_fileNameProc = nullptr;
|
||||||
|
com_ptr<IOleWindow> pOleWnd = _dialog;
|
||||||
|
if (pOleWnd)
|
||||||
|
{
|
||||||
|
HWND hwndDlg = nullptr;
|
||||||
|
HRESULT hr = pOleWnd->GetWindow(&hwndDlg);
|
||||||
|
if (SUCCEEDED(hr) && hwndDlg)
|
||||||
|
{
|
||||||
|
EnumChildWindows(hwndDlg, &EnumChildProc, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Changes the name extension according to currently selected file type index.
|
||||||
|
bool changeExt(generic_string& name, IFileDialog* dlg)
|
||||||
|
{
|
||||||
|
UINT typeIndex = 0;
|
||||||
|
if (FAILED(dlg->GetFileTypeIndex(&typeIndex)))
|
||||||
|
return false;
|
||||||
|
// Index starts from 1
|
||||||
|
if (typeIndex > 0 && typeIndex - 1 < _filterSpec.size())
|
||||||
|
{
|
||||||
|
const generic_string ext = get1stExt(_filterSpec[typeIndex - 1].ext);
|
||||||
|
if (!endsWith(ext, _T(".*")))
|
||||||
|
return replaceExt(name, ext);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called after the user input but before OnFileOk() and before any name validation.
|
||||||
|
// Prefer SendMessage communication with the edit box here rather than IFileDialog methods.
|
||||||
|
// The setter methods post the message to the queue, and it may not be processed in time.
|
||||||
|
void onPreFileOk()
|
||||||
|
{
|
||||||
|
if (!_dialog)
|
||||||
|
return;
|
||||||
|
// Get the entered name.
|
||||||
|
generic_string fileName = getDialogFileName(_dialog);
|
||||||
|
bool nameChanged = transformPath(fileName);
|
||||||
|
// Update the controls.
|
||||||
|
if (not ::PathIsDirectory(fileName.c_str()))
|
||||||
|
{
|
||||||
|
// Name is a file path.
|
||||||
|
// Add file extension if missing.
|
||||||
|
if (!hasExt(fileName))
|
||||||
|
nameChanged |= changeExt(fileName, _dialog);
|
||||||
|
}
|
||||||
|
// Update the edit box text.
|
||||||
|
// It will update the address if the path is a directory.
|
||||||
|
if (nameChanged)
|
||||||
|
sendDialogFileName(fileName.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transforms a forward-slash path to a canonical Windows path.
|
||||||
|
static bool transformPath(generic_string& fileName)
|
||||||
|
{
|
||||||
|
if (fileName.empty())
|
||||||
|
return false;
|
||||||
|
bool transformed = false;
|
||||||
|
// Transform to a Windows path.
|
||||||
|
size_t pos = 0;
|
||||||
|
while ((pos = fileName.find('/', pos)) != generic_string::npos)
|
||||||
|
{
|
||||||
|
fileName[pos] = '\\';
|
||||||
|
++pos;
|
||||||
|
transformed = true;
|
||||||
|
}
|
||||||
|
// If there are two or more double backslash, then change it to single.
|
||||||
|
while (fileName.find(_T("\\\\")) != generic_string::npos)
|
||||||
|
{
|
||||||
|
fileName.replace(fileName.find(_T("\\\\")), 2, _T("\\"));
|
||||||
|
transformed = true;
|
||||||
|
}
|
||||||
|
return transformed;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sets the file name and waits until it is processed by the edit control.
|
||||||
|
void sendDialogFileName(const TCHAR* name)
|
||||||
|
{
|
||||||
|
::SendMessage(_hwndNameEdit, WM_SETTEXT, 0, reinterpret_cast<LPARAM>(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enumerates the child windows of a dialog.
|
||||||
|
// Sets up window procedure overrides for "OK" button and file name edit box.
|
||||||
|
static BOOL CALLBACK EnumChildProc(HWND hwnd, LPARAM)
|
||||||
|
{
|
||||||
|
const int bufferLen = MAX_PATH;
|
||||||
|
static TCHAR buffer[bufferLen];
|
||||||
|
if (GetClassName(hwnd, buffer, bufferLen) != 0)
|
||||||
|
{
|
||||||
|
if (lstrcmpi(buffer, _T("ComboBox")) == 0)
|
||||||
|
{
|
||||||
|
// The edit box of interest is a child of the combo box and has empty window text.
|
||||||
|
// Note that file type dropdown is a combo box also (but without an edit box).
|
||||||
|
HWND hwndChild = FindWindowEx(hwnd, nullptr, _T("Edit"), _T(""));
|
||||||
|
if (hwndChild)
|
||||||
|
{
|
||||||
|
_fileNameProc = (WNDPROC)SetWindowLongPtr(hwndChild, GWLP_WNDPROC, (LPARAM)&FileNameWndProc);
|
||||||
|
_staticThis->_hwndNameEdit = hwndChild;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (lstrcmpi(buffer, _T("Button")) == 0)
|
||||||
|
{
|
||||||
|
// The button of interest has a focus by default.
|
||||||
|
LONG style = GetWindowLong(hwnd, GWL_STYLE);
|
||||||
|
if (style & BS_DEFPUSHBUTTON)
|
||||||
|
_okButtonProc = (WNDPROC)SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LPARAM)&OkButtonWndProc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (_okButtonProc && _fileNameProc)
|
||||||
|
return FALSE; // Found all children, stop enumeration.
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static LRESULT CALLBACK OkButtonWndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
|
||||||
|
{
|
||||||
|
if (msg == WM_LBUTTONDOWN)
|
||||||
|
_staticThis->onPreFileOk();
|
||||||
|
return CallWindowProc(_okButtonProc, hwnd, msg, wparam, lparam);
|
||||||
|
}
|
||||||
|
|
||||||
|
static LRESULT CALLBACK FileNameWndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
|
||||||
|
{
|
||||||
|
// WM_KEYDOWN with wparam == VK_RETURN isn't delivered here.
|
||||||
|
// So watch for the keyboard input while the control has focus.
|
||||||
|
// Initially, the control has focus.
|
||||||
|
// WM_SETFOCUS is sent if control regains focus after losing it.
|
||||||
|
static bool processingReturn = false;
|
||||||
|
switch (msg)
|
||||||
|
{
|
||||||
|
case WM_SETFOCUS:
|
||||||
|
_staticThis->_monitorKeyboard = true;
|
||||||
|
break;
|
||||||
|
case WM_KILLFOCUS:
|
||||||
|
_staticThis->_monitorKeyboard = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (_staticThis->_monitorKeyboard && !processingReturn)
|
||||||
|
{
|
||||||
|
SHORT state = GetAsyncKeyState(VK_RETURN);
|
||||||
|
if (state & 0x8000)
|
||||||
|
{
|
||||||
|
// Avoid re-entrance because the call might generate some messages.
|
||||||
|
processingReturn = true;
|
||||||
|
_staticThis->onPreFileOk();
|
||||||
|
processingReturn = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return CallWindowProc(_fileNameProc, hwnd, msg, wparam, lparam);
|
||||||
|
}
|
||||||
|
|
||||||
|
static WNDPROC _okButtonProc;
|
||||||
|
static WNDPROC _fileNameProc;
|
||||||
|
static FileDialogEventHandler* _staticThis;
|
||||||
|
|
||||||
|
long _cRef;
|
||||||
|
IFileDialog* _dialog = nullptr;
|
||||||
|
const std::vector<Filter> _filterSpec;
|
||||||
|
HWND _hwndNameEdit = nullptr;
|
||||||
|
bool _monitorKeyboard = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
WNDPROC FileDialogEventHandler::_okButtonProc;
|
||||||
|
WNDPROC FileDialogEventHandler::_fileNameProc;
|
||||||
|
FileDialogEventHandler* FileDialogEventHandler::_staticThis;
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// Private implementation to avoid pollution with includes and defines in header.
|
||||||
class CustomFileDialog::Impl
|
class CustomFileDialog::Impl
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Impl()
|
Impl() = default;
|
||||||
{
|
|
||||||
memset(_fileName, 0, std::size(_fileName));
|
|
||||||
}
|
|
||||||
|
|
||||||
~Impl()
|
~Impl() = default;
|
||||||
{
|
|
||||||
if (_customize)
|
|
||||||
_customize->Release();
|
|
||||||
if (_dialog)
|
|
||||||
_dialog->Release();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool init(CLSID id)
|
bool init(CLSID id)
|
||||||
{
|
{
|
||||||
if (_dialog)
|
if (_dialog)
|
||||||
return false; // Avoid double initizliation
|
return false; // Avoid double initialization
|
||||||
|
|
||||||
|
// Sanitize data.
|
||||||
|
if (_fileTypeIndex >= static_cast<int>(_filterSpec.size()))
|
||||||
|
_fileTypeIndex = 0;
|
||||||
|
|
||||||
HRESULT hr = CoCreateInstance(id,
|
HRESULT hr = CoCreateInstance(id,
|
||||||
NULL,
|
NULL,
|
||||||
|
@ -58,19 +448,36 @@ public:
|
||||||
hr = _dialog->SetTitle(_title);
|
hr = _dialog->SetTitle(_title);
|
||||||
|
|
||||||
if (SUCCEEDED(hr) && _folder)
|
if (SUCCEEDED(hr) && _folder)
|
||||||
hr = setInitDir(_folder) ? S_OK : E_FAIL;
|
hr = setFolder(_folder) ? S_OK : E_FAIL;
|
||||||
|
|
||||||
if (SUCCEEDED(hr) && _defExt && _defExt[0] != '\0')
|
if (SUCCEEDED(hr) && _defExt && _defExt[0] != '\0')
|
||||||
hr = _dialog->SetDefaultExtension(_defExt);
|
hr = _dialog->SetDefaultExtension(_defExt);
|
||||||
|
|
||||||
|
if (SUCCEEDED(hr) && _initialFileName)
|
||||||
|
{
|
||||||
|
generic_string newFileName = _initialFileName;
|
||||||
|
if (_fileTypeIndex >= 0 && _fileTypeIndex < static_cast<int>(_filterSpec.size()))
|
||||||
|
{
|
||||||
|
if (!hasExt(newFileName))
|
||||||
|
{
|
||||||
|
const generic_string ext = get1stExt(_filterSpec[_fileTypeIndex].ext);
|
||||||
|
if (!endsWith(ext, _T(".*")))
|
||||||
|
newFileName += ext;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
hr = _dialog->SetFileName(newFileName.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
if (SUCCEEDED(hr) && !_filterSpec.empty())
|
if (SUCCEEDED(hr) && !_filterSpec.empty())
|
||||||
{
|
{
|
||||||
std::vector<COMDLG_FILTERSPEC> fileTypes;
|
std::vector<COMDLG_FILTERSPEC> fileTypes;
|
||||||
|
fileTypes.reserve(_filterSpec.size());
|
||||||
for (auto&& filter : _filterSpec)
|
for (auto&& filter : _filterSpec)
|
||||||
fileTypes.push_back({ filter.first.data(), filter.second.data() });
|
fileTypes.push_back({ filter.name.data(), filter.ext.data() });
|
||||||
hr = _dialog->SetFileTypes(static_cast<UINT>(fileTypes.size()), fileTypes.data());
|
hr = _dialog->SetFileTypes(static_cast<UINT>(fileTypes.size()), fileTypes.data());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The selected index should be set after the file types are set.
|
||||||
if (SUCCEEDED(hr) && _fileTypeIndex >= 0)
|
if (SUCCEEDED(hr) && _fileTypeIndex >= 0)
|
||||||
hr = _dialog->SetFileTypeIndex(_fileTypeIndex + 1); // This index is 1-based
|
hr = _dialog->SetFileTypeIndex(_fileTypeIndex + 1); // This index is 1-based
|
||||||
|
|
||||||
|
@ -103,38 +510,44 @@ public:
|
||||||
|
|
||||||
bool addControls()
|
bool addControls()
|
||||||
{
|
{
|
||||||
HRESULT hr = _dialog->QueryInterface(IID_PPV_ARGS(&_customize));
|
_customize = _dialog;
|
||||||
if (SUCCEEDED(hr))
|
if (!_customize)
|
||||||
{
|
return false;
|
||||||
if (_checkboxLabel && _checkboxLabel[0] != '\0')
|
if (_checkboxLabel && _checkboxLabel[0] != '\0')
|
||||||
{
|
{
|
||||||
const BOOL isChecked = FALSE;
|
const BOOL isChecked = FALSE;
|
||||||
hr = _customize->AddCheckButton(IDC_FILE_CHECKBOX, _checkboxLabel, isChecked);
|
HRESULT hr = _customize->AddCheckButton(IDC_FILE_CHECKBOX, _checkboxLabel, isChecked);
|
||||||
if (SUCCEEDED(hr) && !_isCheckboxActive)
|
if (SUCCEEDED(hr) && !_isCheckboxActive)
|
||||||
{
|
{
|
||||||
hr = _customize->SetControlState(IDC_FILE_CHECKBOX, CDCS_INACTIVE | CDCS_VISIBLE);
|
hr = _customize->SetControlState(IDC_FILE_CHECKBOX, CDCS_INACTIVE | CDCS_VISIBLE);
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return SUCCEEDED(hr);
|
return SUCCEEDED(hr);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool setInitDir(const TCHAR* dir)
|
bool setFolder(const TCHAR* dir)
|
||||||
{
|
{
|
||||||
IShellItem* psi = nullptr;
|
return setDialogFolder(_dialog, dir);
|
||||||
HRESULT hr = SHCreateItemFromParsingName(dir,
|
|
||||||
0,
|
|
||||||
IID_IShellItem,
|
|
||||||
reinterpret_cast<void**>(&psi));
|
|
||||||
if (SUCCEEDED(hr))
|
|
||||||
hr = _dialog->SetFolder(psi);
|
|
||||||
return SUCCEEDED(hr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool show()
|
bool show()
|
||||||
{
|
{
|
||||||
HRESULT hr = _dialog->Show(_hwndOwner);
|
bool okPressed = false;
|
||||||
return SUCCEEDED(hr);
|
HRESULT hr = FileDialogEventHandler::createInstance(_filterSpec, IID_PPV_ARGS(&_events));
|
||||||
|
if (SUCCEEDED(hr))
|
||||||
|
{
|
||||||
|
DWORD dwCookie;
|
||||||
|
hr = _dialog->Advise(_events, &dwCookie);
|
||||||
|
if (SUCCEEDED(hr))
|
||||||
|
{
|
||||||
|
hr = _dialog->Show(_hwndOwner);
|
||||||
|
okPressed = SUCCEEDED(hr);
|
||||||
|
|
||||||
|
_dialog->Unadvise(dwCookie);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return okPressed;
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOL getCheckboxState() const
|
BOOL getCheckboxState() const
|
||||||
|
@ -149,28 +562,70 @@ public:
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
const TCHAR* getResultFilename()
|
generic_string getResultFilename()
|
||||||
{
|
{
|
||||||
bool bOk = false;
|
generic_string fileName;
|
||||||
IShellItem* psiResult = nullptr;
|
com_ptr<IShellItem> psiResult;
|
||||||
HRESULT hr = _dialog->GetResult(&psiResult);
|
HRESULT hr = _dialog->GetResult(&psiResult);
|
||||||
if (SUCCEEDED(hr))
|
if (SUCCEEDED(hr))
|
||||||
{
|
{
|
||||||
PWSTR pszFilePath = NULL;
|
fileName = getFilename(psiResult);
|
||||||
hr = psiResult->GetDisplayName(SIGDN_FILESYSPATH, &pszFilePath);
|
_hasReadonly = hasReadonlyAttr(psiResult);
|
||||||
if (SUCCEEDED(hr))
|
|
||||||
{
|
|
||||||
size_t len = pszFilePath ? wcslen(pszFilePath) : 0;
|
|
||||||
if (len > 0 && len <= std::size(_fileName))
|
|
||||||
{
|
|
||||||
wcsncpy_s(_fileName, pszFilePath, len);
|
|
||||||
bOk = true;
|
|
||||||
}
|
}
|
||||||
|
return fileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
static generic_string getFilename(IShellItem* psi)
|
||||||
|
{
|
||||||
|
generic_string result;
|
||||||
|
PWSTR pszFilePath = NULL;
|
||||||
|
HRESULT hr = psi->GetDisplayName(SIGDN_FILESYSPATH, &pszFilePath);
|
||||||
|
if (SUCCEEDED(hr) && pszFilePath)
|
||||||
|
{
|
||||||
|
result = pszFilePath;
|
||||||
CoTaskMemFree(pszFilePath);
|
CoTaskMemFree(pszFilePath);
|
||||||
}
|
}
|
||||||
psiResult->Release();
|
return result;
|
||||||
}
|
}
|
||||||
return bOk ? _fileName : nullptr;
|
|
||||||
|
static bool hasReadonlyAttr(IShellItem* psi)
|
||||||
|
{
|
||||||
|
SFGAOF attrs = 0;
|
||||||
|
HRESULT hr = psi->GetAttributes(SFGAO_READONLY, &attrs);
|
||||||
|
if (SUCCEEDED(hr))
|
||||||
|
return attrs & SFGAO_READONLY;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<generic_string> getFilenames()
|
||||||
|
{
|
||||||
|
std::vector<generic_string> result;
|
||||||
|
// Only the open dialog can have multiple results.
|
||||||
|
com_ptr<IFileOpenDialog> pfd = _dialog;
|
||||||
|
if (pfd)
|
||||||
|
{
|
||||||
|
com_ptr<IShellItemArray> psiaResults;
|
||||||
|
HRESULT hr = pfd->GetResults(&psiaResults);
|
||||||
|
if (SUCCEEDED(hr))
|
||||||
|
{
|
||||||
|
DWORD count = 0;
|
||||||
|
hr = psiaResults->GetCount(&count);
|
||||||
|
if (SUCCEEDED(hr))
|
||||||
|
{
|
||||||
|
for (DWORD i = 0; i != count; ++i)
|
||||||
|
{
|
||||||
|
com_ptr<IShellItem> psi;
|
||||||
|
hr = psiaResults->GetItemAt(i, &psi);
|
||||||
|
if (SUCCEEDED(hr))
|
||||||
|
{
|
||||||
|
_hasReadonly |= hasReadonlyAttr(psi);
|
||||||
|
result.push_back(getFilename(psi));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const int IDC_FILE_CHECKBOX = 4;
|
static const int IDC_FILE_CHECKBOX = 4;
|
||||||
|
@ -180,16 +635,20 @@ public:
|
||||||
const TCHAR* _defExt = nullptr;
|
const TCHAR* _defExt = nullptr;
|
||||||
const TCHAR* _folder = nullptr;
|
const TCHAR* _folder = nullptr;
|
||||||
const TCHAR* _checkboxLabel = nullptr;
|
const TCHAR* _checkboxLabel = nullptr;
|
||||||
|
const TCHAR* _initialFileName = nullptr;
|
||||||
bool _isCheckboxActive = true;
|
bool _isCheckboxActive = true;
|
||||||
std::vector<std::pair<generic_string, generic_string>> _filterSpec; // text + extension
|
std::vector<Filter> _filterSpec;
|
||||||
int _fileTypeIndex = -1;
|
int _fileTypeIndex = -1;
|
||||||
|
bool _hasReadonly = false; // set during the result handling
|
||||||
|
|
||||||
private:
|
private:
|
||||||
IFileDialog* _dialog = nullptr;
|
com_ptr<IFileDialog> _dialog;
|
||||||
IFileDialogCustomize* _customize = nullptr;
|
com_ptr<IFileDialogCustomize> _customize;
|
||||||
TCHAR _fileName[MAX_PATH * 8];
|
com_ptr<IFileDialogEvents> _events;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
CustomFileDialog::CustomFileDialog(HWND hwnd) : _impl{std::make_unique<Impl>()}
|
CustomFileDialog::CustomFileDialog(HWND hwnd) : _impl{std::make_unique<Impl>()}
|
||||||
{
|
{
|
||||||
_impl->_hwndOwner = hwnd;
|
_impl->_hwndOwner = hwnd;
|
||||||
|
@ -205,7 +664,7 @@ void CustomFileDialog::setTitle(const TCHAR* title)
|
||||||
void CustomFileDialog::setExtFilter(const TCHAR *extText, const TCHAR *exts)
|
void CustomFileDialog::setExtFilter(const TCHAR *extText, const TCHAR *exts)
|
||||||
{
|
{
|
||||||
// Add an asterisk before each dot in file patterns
|
// Add an asterisk before each dot in file patterns
|
||||||
generic_string newExts{ exts };
|
generic_string newExts{ exts ? exts : _T("") };
|
||||||
for (size_t pos = 0; pos < newExts.size(); ++pos)
|
for (size_t pos = 0; pos < newExts.size(); ++pos)
|
||||||
{
|
{
|
||||||
pos = newExts.find(_T('.'), pos);
|
pos = newExts.find(_T('.'), pos);
|
||||||
|
@ -221,11 +680,29 @@ void CustomFileDialog::setExtFilter(const TCHAR *extText, const TCHAR *exts)
|
||||||
_impl->_filterSpec.push_back({ extText, newExts });
|
_impl->_filterSpec.push_back({ extText, newExts });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void CustomFileDialog::setExtFilter(const TCHAR *extText, std::initializer_list<const TCHAR*> extList)
|
||||||
|
{
|
||||||
|
generic_string exts;
|
||||||
|
for (auto&& x : extList)
|
||||||
|
{
|
||||||
|
exts += x;
|
||||||
|
exts += _T(';');
|
||||||
|
}
|
||||||
|
exts.pop_back(); // remove the last ';'
|
||||||
|
setExtFilter(extText, exts.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
void CustomFileDialog::setDefExt(const TCHAR* ext)
|
void CustomFileDialog::setDefExt(const TCHAR* ext)
|
||||||
{
|
{
|
||||||
_impl->_defExt = ext;
|
_impl->_defExt = ext;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CustomFileDialog::setDefFileName(const TCHAR* fn)
|
||||||
|
{
|
||||||
|
_impl->_initialFileName = fn;
|
||||||
|
}
|
||||||
|
|
||||||
void CustomFileDialog::setFolder(const TCHAR* folder)
|
void CustomFileDialog::setFolder(const TCHAR* folder)
|
||||||
{
|
{
|
||||||
_impl->_folder = folder;
|
_impl->_folder = folder;
|
||||||
|
@ -247,36 +724,64 @@ bool CustomFileDialog::getCheckboxState() const
|
||||||
return _impl->getCheckboxState();
|
return _impl->getCheckboxState();
|
||||||
}
|
}
|
||||||
|
|
||||||
const TCHAR* CustomFileDialog::doSaveDlg()
|
bool CustomFileDialog::isReadOnly() const
|
||||||
|
{
|
||||||
|
return _impl->_hasReadonly;
|
||||||
|
}
|
||||||
|
|
||||||
|
generic_string CustomFileDialog::doSaveDlg()
|
||||||
{
|
{
|
||||||
if (!_impl->initSave())
|
if (!_impl->initSave())
|
||||||
return nullptr;
|
return {};
|
||||||
|
|
||||||
TCHAR dir[MAX_PATH];
|
CurrentDirBackup backup;
|
||||||
::GetCurrentDirectory(MAX_PATH, dir);
|
|
||||||
|
|
||||||
NppParameters& params = NppParameters::getInstance();
|
NppParameters& params = NppParameters::getInstance();
|
||||||
_impl->setInitDir(params.getWorkingDir());
|
_impl->setFolder(params.getWorkingDir());
|
||||||
|
|
||||||
_impl->addFlags(FOS_PATHMUSTEXIST | FOS_FILEMUSTEXIST | FOS_FORCEFILESYSTEM);
|
_impl->addFlags(FOS_PATHMUSTEXIST | FOS_FILEMUSTEXIST | FOS_FORCEFILESYSTEM);
|
||||||
bool bOk = _impl->show();
|
bool bOk = _impl->show();
|
||||||
|
return bOk ? _impl->getResultFilename() : _T("");
|
||||||
if (params.getNppGUI()._openSaveDir == dir_last)
|
|
||||||
{
|
|
||||||
::GetCurrentDirectory(MAX_PATH, dir);
|
|
||||||
params.setWorkingDir(dir);
|
|
||||||
}
|
|
||||||
::SetCurrentDirectory(dir);
|
|
||||||
|
|
||||||
return bOk ? _impl->getResultFilename() : nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const TCHAR* CustomFileDialog::pickFolder()
|
generic_string CustomFileDialog::doOpenSingleFileDlg()
|
||||||
{
|
{
|
||||||
if (!_impl->initOpen())
|
if (!_impl->initOpen())
|
||||||
return nullptr;
|
return {};
|
||||||
|
|
||||||
|
CurrentDirBackup backup;
|
||||||
|
|
||||||
|
NppParameters& params = NppParameters::getInstance();
|
||||||
|
_impl->setFolder(params.getWorkingDir());
|
||||||
|
|
||||||
|
_impl->addFlags(FOS_PATHMUSTEXIST | FOS_FILEMUSTEXIST | FOS_FORCEFILESYSTEM);
|
||||||
|
bool bOk = _impl->show();
|
||||||
|
return bOk ? _impl->getResultFilename() : _T("");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<generic_string> CustomFileDialog::doOpenMultiFilesDlg()
|
||||||
|
{
|
||||||
|
if (!_impl->initOpen())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
CurrentDirBackup backup;
|
||||||
|
|
||||||
|
NppParameters& params = NppParameters::getInstance();
|
||||||
|
_impl->setFolder(params.getWorkingDir());
|
||||||
|
|
||||||
|
_impl->addFlags(FOS_PATHMUSTEXIST | FOS_FILEMUSTEXIST | FOS_FORCEFILESYSTEM | FOS_ALLOWMULTISELECT);
|
||||||
|
bool bOk = _impl->show();
|
||||||
|
if (bOk)
|
||||||
|
return _impl->getFilenames();
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
generic_string CustomFileDialog::pickFolder()
|
||||||
|
{
|
||||||
|
if (!_impl->initOpen())
|
||||||
|
return {};
|
||||||
|
|
||||||
_impl->addFlags(FOS_PATHMUSTEXIST | FOS_FILEMUSTEXIST | FOS_FORCEFILESYSTEM | FOS_PICKFOLDERS);
|
_impl->addFlags(FOS_PATHMUSTEXIST | FOS_FILEMUSTEXIST | FOS_FORCEFILESYSTEM | FOS_PICKFOLDERS);
|
||||||
bool bOk = _impl->show();
|
bool bOk = _impl->show();
|
||||||
return bOk ? _impl->getResultFilename() : nullptr;
|
return bOk ? _impl->getResultFilename() : _T("");
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// This file is part of Notepad++ project
|
// This file is part of Notepad++ project
|
||||||
// Copyright (C)2021 Don HO <don.h@free.fr>
|
// Copyright (C) 2021 The Notepad++ Contributors.
|
||||||
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
// This program is free software: you can redistribute it and/or modify
|
||||||
// it under the terms of the GNU General Public License as published by
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
@ -31,15 +31,21 @@ public:
|
||||||
~CustomFileDialog();
|
~CustomFileDialog();
|
||||||
void setTitle(const TCHAR* title);
|
void setTitle(const TCHAR* title);
|
||||||
void setExtFilter(const TCHAR* text, const TCHAR* ext);
|
void setExtFilter(const TCHAR* text, const TCHAR* ext);
|
||||||
|
void setExtFilter(const TCHAR* text, std::initializer_list<const TCHAR*> exts);
|
||||||
void setDefExt(const TCHAR* ext);
|
void setDefExt(const TCHAR* ext);
|
||||||
|
void setDefFileName(const TCHAR *fn);
|
||||||
void setFolder(const TCHAR* folder);
|
void setFolder(const TCHAR* folder);
|
||||||
void setCheckbox(const TCHAR* text, bool isActive = true);
|
void setCheckbox(const TCHAR* text, bool isActive = true);
|
||||||
void setExtIndex(int extTypeIndex);
|
void setExtIndex(int extTypeIndex);
|
||||||
|
|
||||||
const TCHAR* doSaveDlg();
|
// Empty string is not a valid file name and may signal that the dialog was canceled.
|
||||||
const TCHAR* pickFolder();
|
generic_string doSaveDlg();
|
||||||
|
generic_string pickFolder();
|
||||||
|
generic_string doOpenSingleFileDlg();
|
||||||
|
std::vector<generic_string> doOpenMultiFilesDlg();
|
||||||
|
|
||||||
bool getCheckboxState() const;
|
bool getCheckboxState() const;
|
||||||
|
bool isReadOnly() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
class Impl;
|
class Impl;
|
||||||
|
|
|
@ -1,545 +0,0 @@
|
||||||
// This file is part of Notepad++ project
|
|
||||||
// Copyright (C)2021 Don HO <don.h@free.fr>
|
|
||||||
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// at your option any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
|
|
||||||
#include <shlwapi.h>
|
|
||||||
|
|
||||||
#include "FileDialog.h"
|
|
||||||
#include "Parameters.h"
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
FileDialog *FileDialog::staticThis = NULL;
|
|
||||||
|
|
||||||
FileDialog::FileDialog(HWND hwnd, HINSTANCE hInst)
|
|
||||||
{
|
|
||||||
staticThis = this;
|
|
||||||
|
|
||||||
memset(_fileName, 0, sizeof(_fileName));
|
|
||||||
_winVersion = (NppParameters::getInstance()).getWinVersion();
|
|
||||||
|
|
||||||
_ofn.lStructSize = sizeof(_ofn);
|
|
||||||
if (_winVersion < WV_W2K)
|
|
||||||
_ofn.lStructSize = sizeof(OPENFILENAME);
|
|
||||||
_ofn.hwndOwner = hwnd;
|
|
||||||
_ofn.hInstance = hInst;
|
|
||||||
_ofn.lpstrCustomFilter = (LPTSTR) NULL;
|
|
||||||
_ofn.nMaxCustFilter = 0L;
|
|
||||||
_ofn.nFilterIndex = 1L;
|
|
||||||
_ofn.lpstrFile = _fileName;
|
|
||||||
_ofn.nMaxFile = sizeof(_fileName)/sizeof(TCHAR);
|
|
||||||
_ofn.lpstrFileTitle = NULL;
|
|
||||||
_ofn.nMaxFileTitle = 0;
|
|
||||||
_ofn.lpstrInitialDir = NULL;
|
|
||||||
_ofn.lpstrTitle = NULL;
|
|
||||||
_ofn.nFileOffset = 0;
|
|
||||||
_ofn.nFileExtension = 0;
|
|
||||||
_ofn.lpfnHook = NULL;
|
|
||||||
_ofn.lpstrDefExt = NULL; // No default extension
|
|
||||||
_ofn.lCustData = 0;
|
|
||||||
_ofn.Flags = OFN_PATHMUSTEXIST | OFN_EXPLORER | OFN_LONGNAMES | OFN_HIDEREADONLY;
|
|
||||||
_ofn.pvReserved = NULL;
|
|
||||||
_ofn.dwReserved = 0;
|
|
||||||
_ofn.FlagsEx = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
FileDialog::~FileDialog()
|
|
||||||
{
|
|
||||||
delete[] _fileExt;
|
|
||||||
_fileExt = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// This function set and concatenate the filter into the list box of FileDialog.
|
|
||||||
// The 1st parameter is the description of the file type, the 2nd .. Nth parameter(s) is (are)
|
|
||||||
// the file extension which should be ".WHATEVER", otherwise it (they) will be considered as
|
|
||||||
// a file name to filter. Since the nb of arguments is variable, you have to add NULL at the end.
|
|
||||||
// example :
|
|
||||||
// FileDialog.setExtFilter(TEXT("c/c++ src file"), TEXT(".c"), TEXT(".cpp"), TEXT(".cxx"), TEXT(".h"), NULL);
|
|
||||||
// FileDialog.setExtFilter(TEXT("Makefile"), TEXT("makefile"), TEXT("GNUmakefile"), NULL);
|
|
||||||
void FileDialog::setExtFilter(const TCHAR *extText, const TCHAR *ext, ...)
|
|
||||||
{
|
|
||||||
// fill out the ext array for save as file dialog
|
|
||||||
generic_string exts;
|
|
||||||
|
|
||||||
va_list pArg;
|
|
||||||
va_start(pArg, ext);
|
|
||||||
|
|
||||||
const TCHAR *ext2Concat;
|
|
||||||
ext2Concat = ext;
|
|
||||||
do
|
|
||||||
{
|
|
||||||
if (ext2Concat[0] == TEXT('.'))
|
|
||||||
exts += TEXT("*");
|
|
||||||
exts += ext2Concat;
|
|
||||||
exts += TEXT(";");
|
|
||||||
}
|
|
||||||
while ( (ext2Concat = va_arg(pArg, const TCHAR *)) != NULL );
|
|
||||||
|
|
||||||
va_end(pArg);
|
|
||||||
|
|
||||||
// remove the last ';'
|
|
||||||
exts = exts.substr(0, exts.length()-1);
|
|
||||||
|
|
||||||
setExtsFilter(extText, exts.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
int FileDialog::setExtsFilter(const TCHAR *extText, const TCHAR *exts)
|
|
||||||
{
|
|
||||||
// fill out the ext array for save as file dialog
|
|
||||||
generic_string extFilter = extText;
|
|
||||||
TCHAR *oldFilter = NULL;
|
|
||||||
|
|
||||||
extFilter += TEXT(" (");
|
|
||||||
extFilter += exts;
|
|
||||||
extFilter += TEXT(")");
|
|
||||||
|
|
||||||
// Resize filter buffer
|
|
||||||
int nbCharAdditional = static_cast<int32_t>(extFilter.length() + _tcsclen(exts) + 3); // 3 additional for nulls
|
|
||||||
if (_fileExt)
|
|
||||||
{
|
|
||||||
oldFilter = new TCHAR[_nbCharFileExt];
|
|
||||||
memcpy(oldFilter, _fileExt, _nbCharFileExt * sizeof(TCHAR));
|
|
||||||
|
|
||||||
delete[] _fileExt;
|
|
||||||
_fileExt = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
int nbCharNewFileExt = _nbCharFileExt + nbCharAdditional;
|
|
||||||
_fileExt = new TCHAR[nbCharNewFileExt];
|
|
||||||
memset(_fileExt, 0, nbCharNewFileExt * sizeof(TCHAR));
|
|
||||||
|
|
||||||
// Restore previous filters
|
|
||||||
if (oldFilter)
|
|
||||||
{
|
|
||||||
memcpy(_fileExt, oldFilter, _nbCharFileExt * sizeof(TCHAR));
|
|
||||||
delete[] oldFilter;
|
|
||||||
oldFilter = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Append new filter
|
|
||||||
TCHAR *pFileExt = _fileExt + _nbCharFileExt;
|
|
||||||
auto curLen = extFilter.length() + 1;
|
|
||||||
wcscpy_s(pFileExt, curLen, extFilter.c_str());
|
|
||||||
_nbCharFileExt += static_cast<int32_t>(curLen);
|
|
||||||
|
|
||||||
pFileExt = _fileExt + _nbCharFileExt;
|
|
||||||
curLen = _tcsclen(exts) + 1;
|
|
||||||
wcscpy_s(pFileExt, curLen, exts);
|
|
||||||
_nbCharFileExt += static_cast<int32_t>(curLen);
|
|
||||||
|
|
||||||
// Set file dialog pointer
|
|
||||||
_ofn.lpstrFilter = _fileExt;
|
|
||||||
|
|
||||||
return _nbExt;
|
|
||||||
}
|
|
||||||
|
|
||||||
TCHAR* FileDialog::doOpenSingleFileDlg()
|
|
||||||
{
|
|
||||||
TCHAR dir[MAX_PATH];
|
|
||||||
::GetCurrentDirectory(MAX_PATH, dir);
|
|
||||||
NppParameters& params = NppParameters::getInstance();
|
|
||||||
_ofn.lpstrInitialDir = params.getWorkingDir();
|
|
||||||
_ofn.lpstrDefExt = _defExt.c_str();
|
|
||||||
|
|
||||||
_ofn.Flags |= OFN_FILEMUSTEXIST;
|
|
||||||
|
|
||||||
if (!params.useNewStyleSaveDlg())
|
|
||||||
{
|
|
||||||
_ofn.Flags |= OFN_ENABLEHOOK | OFN_NOVALIDATE;
|
|
||||||
_ofn.lpfnHook = OFNHookProc;
|
|
||||||
}
|
|
||||||
|
|
||||||
TCHAR *fn = NULL;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
fn = ::GetOpenFileName(&_ofn) ? _fileName : NULL;
|
|
||||||
|
|
||||||
if (params.getNppGUI()._openSaveDir == dir_last)
|
|
||||||
{
|
|
||||||
::GetCurrentDirectory(MAX_PATH, dir);
|
|
||||||
params.setWorkingDir(dir);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (std::exception& e)
|
|
||||||
{
|
|
||||||
generic_string msg = TEXT("An exception occurred while opening file: ");
|
|
||||||
msg += _fileName;
|
|
||||||
msg += TEXT("\r\n\r\nException reason: ");
|
|
||||||
msg += s2ws(e.what());
|
|
||||||
|
|
||||||
::MessageBox(NULL, msg.c_str(), TEXT("File Open Exception"), MB_OK);
|
|
||||||
}
|
|
||||||
catch (...)
|
|
||||||
{
|
|
||||||
::MessageBox(NULL, TEXT("doOpenSingleFileDlg crashes!!!"), TEXT(""), MB_OK);
|
|
||||||
}
|
|
||||||
|
|
||||||
::SetCurrentDirectory(dir);
|
|
||||||
|
|
||||||
return (fn);
|
|
||||||
}
|
|
||||||
|
|
||||||
stringVector * FileDialog::doOpenMultiFilesDlg()
|
|
||||||
{
|
|
||||||
TCHAR dir[MAX_PATH];
|
|
||||||
::GetCurrentDirectory(MAX_PATH, dir);
|
|
||||||
|
|
||||||
NppParameters& params = NppParameters::getInstance();
|
|
||||||
_ofn.lpstrInitialDir = params.getWorkingDir();
|
|
||||||
|
|
||||||
_ofn.Flags |= OFN_FILEMUSTEXIST | OFN_ALLOWMULTISELECT | OFN_ENABLESIZING;
|
|
||||||
|
|
||||||
if (!params.useNewStyleSaveDlg())
|
|
||||||
{
|
|
||||||
_ofn.Flags |= OFN_ENABLEHOOK | OFN_NOVALIDATE;
|
|
||||||
_ofn.lpfnHook = OFNHookProc;
|
|
||||||
}
|
|
||||||
|
|
||||||
BOOL res = ::GetOpenFileName(&_ofn);
|
|
||||||
if (params.getNppGUI()._openSaveDir == dir_last)
|
|
||||||
{
|
|
||||||
::GetCurrentDirectory(MAX_PATH, dir);
|
|
||||||
params.setWorkingDir(dir);
|
|
||||||
}
|
|
||||||
::SetCurrentDirectory(dir);
|
|
||||||
|
|
||||||
if (res)
|
|
||||||
{
|
|
||||||
TCHAR* pFn = _fileName + lstrlen(_fileName) + 1;
|
|
||||||
TCHAR fn[MAX_PATH*8];
|
|
||||||
memset(fn, 0x0, sizeof(fn));
|
|
||||||
|
|
||||||
if (!(*pFn))
|
|
||||||
{
|
|
||||||
_fileNames.push_back(generic_string(_fileName));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
wcscpy_s(fn, _fileName);
|
|
||||||
if (fn[lstrlen(fn) - 1] != '\\')
|
|
||||||
wcscat_s(fn, TEXT("\\"));
|
|
||||||
}
|
|
||||||
|
|
||||||
int term = lstrlen(fn);
|
|
||||||
|
|
||||||
while (*pFn)
|
|
||||||
{
|
|
||||||
fn[term] = '\0';
|
|
||||||
wcscat_s(fn, pFn);
|
|
||||||
_fileNames.push_back(generic_string(fn));
|
|
||||||
pFn += lstrlen(pFn) + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return &_fileNames;
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
TCHAR * FileDialog::doSaveDlg()
|
|
||||||
{
|
|
||||||
TCHAR dir[MAX_PATH];
|
|
||||||
::GetCurrentDirectory(MAX_PATH, dir);
|
|
||||||
|
|
||||||
NppParameters& params = NppParameters::getInstance();
|
|
||||||
_ofn.lpstrInitialDir = params.getWorkingDir();
|
|
||||||
_ofn.lpstrDefExt = _defExt.c_str();
|
|
||||||
if (_extTypeIndex != -1)
|
|
||||||
_ofn.nFilterIndex = _extTypeIndex + 1; // +1 for the file extension combobox index starts from 1
|
|
||||||
|
|
||||||
_ofn.Flags |= OFN_OVERWRITEPROMPT | OFN_HIDEREADONLY | OFN_ENABLESIZING;
|
|
||||||
|
|
||||||
if (!params.useNewStyleSaveDlg())
|
|
||||||
{
|
|
||||||
_ofn.Flags |= OFN_ENABLEHOOK | OFN_NOVALIDATE;
|
|
||||||
_ofn.lpfnHook = OFNHookProc;
|
|
||||||
}
|
|
||||||
|
|
||||||
TCHAR *fn = NULL;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
fn = ::GetSaveFileName(&_ofn) ? _fileName : NULL;
|
|
||||||
if (params.getNppGUI()._openSaveDir == dir_last)
|
|
||||||
{
|
|
||||||
::GetCurrentDirectory(MAX_PATH, dir);
|
|
||||||
params.setWorkingDir(dir);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (std::exception& e)
|
|
||||||
{
|
|
||||||
generic_string msg = TEXT("An exception occurred while saving file: ");
|
|
||||||
msg += _fileName;
|
|
||||||
msg += TEXT("\r\n\r\nException reason: ");
|
|
||||||
msg += s2ws(e.what());
|
|
||||||
|
|
||||||
::MessageBox(NULL, msg.c_str(), TEXT("File Save Exception"), MB_OK);
|
|
||||||
}
|
|
||||||
catch (...)
|
|
||||||
{
|
|
||||||
::MessageBox(NULL, TEXT("GetSaveFileName crashes!!!"), TEXT(""), MB_OK);
|
|
||||||
}
|
|
||||||
|
|
||||||
::SetCurrentDirectory(dir);
|
|
||||||
|
|
||||||
return (fn);
|
|
||||||
}
|
|
||||||
|
|
||||||
static HWND hFileDlg = NULL;
|
|
||||||
static WNDPROC oldProc = NULL;
|
|
||||||
static generic_string currentExt = TEXT("");
|
|
||||||
|
|
||||||
|
|
||||||
static LRESULT CALLBACK fileDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
|
|
||||||
{
|
|
||||||
switch (message)
|
|
||||||
{
|
|
||||||
case WM_COMMAND :
|
|
||||||
{
|
|
||||||
switch (wParam)
|
|
||||||
{
|
|
||||||
case IDOK :
|
|
||||||
{
|
|
||||||
HWND fnControl = ::GetDlgItem(hwnd, FileDialog::_dialogFileBoxId);
|
|
||||||
TCHAR fn[MAX_PATH];
|
|
||||||
::GetWindowText(fnControl, fn, MAX_PATH);
|
|
||||||
|
|
||||||
// Check condition to have the compability of default behaviour
|
|
||||||
if (*fn == '\0')
|
|
||||||
return oldProc(hwnd, message, wParam, lParam);
|
|
||||||
else if (::PathIsDirectory(fn))
|
|
||||||
return oldProc(hwnd, message, wParam, lParam);
|
|
||||||
|
|
||||||
// Process
|
|
||||||
if (currentExt != TEXT(""))
|
|
||||||
{
|
|
||||||
generic_string fnExt = changeExt(fn, currentExt, false);
|
|
||||||
::SetWindowText(fnControl, fnExt.c_str());
|
|
||||||
}
|
|
||||||
return oldProc(hwnd, message, wParam, lParam);
|
|
||||||
}
|
|
||||||
|
|
||||||
default :
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return oldProc(hwnd, message, wParam, lParam);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
static TCHAR * get1stExt(TCHAR *ext)
|
|
||||||
{
|
|
||||||
// precondition : ext should be under the format : Batch (*.bat;*.cmd;*.nt)
|
|
||||||
TCHAR *begin = ext;
|
|
||||||
for ( ; *begin != '.' ; begin++);
|
|
||||||
TCHAR *end = ++begin;
|
|
||||||
for ( ; *end != ';' && *end != ')' ; end++);
|
|
||||||
*end = '\0';
|
|
||||||
if (*begin == '*')
|
|
||||||
*begin = '\0';
|
|
||||||
return begin;
|
|
||||||
};
|
|
||||||
|
|
||||||
static generic_string addExt(HWND textCtrl, HWND typeCtrl)
|
|
||||||
{
|
|
||||||
TCHAR fn[MAX_PATH];
|
|
||||||
::GetWindowText(textCtrl, fn, MAX_PATH);
|
|
||||||
|
|
||||||
auto i = ::SendMessage(typeCtrl, CB_GETCURSEL, 0, 0);
|
|
||||||
|
|
||||||
auto cbTextLen = ::SendMessage(typeCtrl, CB_GETLBTEXTLEN, i, 0);
|
|
||||||
TCHAR * ext = new TCHAR[cbTextLen + 1];
|
|
||||||
::SendMessage(typeCtrl, CB_GETLBTEXT, i, reinterpret_cast<LPARAM>(ext));
|
|
||||||
|
|
||||||
TCHAR *pExt = get1stExt(ext);
|
|
||||||
if (*fn != '\0')
|
|
||||||
{
|
|
||||||
generic_string fnExt = changeExt(fn, pExt);
|
|
||||||
::SetWindowText(textCtrl, fnExt.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
generic_string returnExt = pExt;
|
|
||||||
delete[] ext;
|
|
||||||
return returnExt;
|
|
||||||
};
|
|
||||||
|
|
||||||
UINT_PTR CALLBACK FileDialog::OFNHookProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
||||||
{
|
|
||||||
switch(uMsg)
|
|
||||||
{
|
|
||||||
case WM_INITDIALOG :
|
|
||||||
{
|
|
||||||
NppParameters& nppParam = NppParameters::getInstance();
|
|
||||||
int index = nppParam.getFileSaveDlgFilterIndex();
|
|
||||||
|
|
||||||
::SetWindowLongPtr(hWnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(staticThis));
|
|
||||||
hFileDlg = ::GetParent(hWnd);
|
|
||||||
goToCenter(hFileDlg);
|
|
||||||
|
|
||||||
if (index != -1)
|
|
||||||
{
|
|
||||||
HWND typeControl = ::GetDlgItem(hFileDlg, cmb1);
|
|
||||||
::SendMessage(typeControl, CB_SETCURSEL, index, 0);
|
|
||||||
}
|
|
||||||
// Don't touch the following 3 lines, they are cursed !!!
|
|
||||||
oldProc = reinterpret_cast<WNDPROC>(::GetWindowLongPtr(hFileDlg, GWLP_WNDPROC));
|
|
||||||
if (oldProc)
|
|
||||||
::SetWindowLongPtr(hFileDlg, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(fileDlgProc));
|
|
||||||
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
default :
|
|
||||||
{
|
|
||||||
FileDialog *pFileDialog = reinterpret_cast<FileDialog *>(::GetWindowLongPtr(hWnd, GWLP_USERDATA));
|
|
||||||
if (!pFileDialog)
|
|
||||||
{
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
return pFileDialog->run(hWnd, uMsg, wParam, lParam);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
BOOL APIENTRY FileDialog::run(HWND hWnd, UINT uMsg, WPARAM, LPARAM lParam)
|
|
||||||
{
|
|
||||||
switch (uMsg)
|
|
||||||
{
|
|
||||||
case WM_NOTIFY :
|
|
||||||
{
|
|
||||||
LPNMHDR pNmhdr = (LPNMHDR)lParam;
|
|
||||||
switch(pNmhdr->code)
|
|
||||||
{
|
|
||||||
case CDN_INITDONE :
|
|
||||||
{
|
|
||||||
if (_extTypeIndex == -1)
|
|
||||||
return TRUE;
|
|
||||||
|
|
||||||
HWND fnControl = ::GetDlgItem(::GetParent(hWnd), _dialogFileBoxId);
|
|
||||||
HWND typeControl = ::GetDlgItem(::GetParent(hWnd), cmb1);
|
|
||||||
::SendMessage(typeControl, CB_SETCURSEL, _extTypeIndex, 0);
|
|
||||||
|
|
||||||
currentExt = addExt(fnControl, typeControl);
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
case CDN_TYPECHANGE :
|
|
||||||
{
|
|
||||||
HWND fnControl = ::GetDlgItem(::GetParent(hWnd), _dialogFileBoxId);
|
|
||||||
HWND typeControl = ::GetDlgItem(::GetParent(hWnd), cmb1);
|
|
||||||
currentExt = addExt(fnControl, typeControl);
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
case CDN_FILEOK :
|
|
||||||
{
|
|
||||||
HWND typeControl = ::GetDlgItem(::GetParent(hWnd), cmb1);
|
|
||||||
int index = static_cast<int32_t>(::SendMessage(typeControl, CB_GETCURSEL, 0, 0));
|
|
||||||
NppParameters& nppParam = NppParameters::getInstance();
|
|
||||||
nppParam.setFileSaveDlgFilterIndex(index);
|
|
||||||
|
|
||||||
// change forward-slash to back-slash directory paths so dialog can interpret
|
|
||||||
OPENFILENAME* ofn = reinterpret_cast<LPOFNOTIFY>(lParam)->lpOFN;
|
|
||||||
TCHAR* fileName = ofn->lpstrFile;
|
|
||||||
|
|
||||||
// note: this check is essential, because otherwise we could return True
|
|
||||||
// with a OFN_NOVALIDATE dialog, which leads to opening every file
|
|
||||||
// in the specified directory. Multi-select terminator is \0\0.
|
|
||||||
if ((ofn->Flags & OFN_ALLOWMULTISELECT) &&
|
|
||||||
(*(fileName + lstrlen(fileName) + 1) != '\0'))
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
if (::PathIsDirectory(fileName))
|
|
||||||
{
|
|
||||||
// change to backslash, and insert trailing '\' to indicate directory
|
|
||||||
hFileDlg = ::GetParent(hWnd);
|
|
||||||
std::wstring filePath(fileName);
|
|
||||||
std::replace(filePath.begin(), filePath.end(), '/', '\\');
|
|
||||||
|
|
||||||
if (filePath.back() != '\\')
|
|
||||||
filePath.insert(filePath.end(), '\\');
|
|
||||||
|
|
||||||
// There are two or more double backslash, then change it to single
|
|
||||||
while (filePath.find(L"\\\\") != std::wstring::npos)
|
|
||||||
filePath.replace(filePath.find(TEXT("\\\\")), 2, TEXT("\\"));
|
|
||||||
|
|
||||||
// change the dialog directory selection
|
|
||||||
::SendMessage(hFileDlg, CDM_SETCONTROLTEXT, edt1,
|
|
||||||
reinterpret_cast<LPARAM>(filePath.c_str()));
|
|
||||||
::PostMessage(hFileDlg, WM_COMMAND, IDOK, 0);
|
|
||||||
::SetWindowLongPtr(hWnd, 0 /*DWL_MSGRESULT*/, 1);
|
|
||||||
}
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
default :
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
default :
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void goToCenter(HWND hwnd)
|
|
||||||
{
|
|
||||||
RECT rc;
|
|
||||||
HWND hParent = ::GetParent(hwnd);
|
|
||||||
::GetClientRect(hParent, &rc);
|
|
||||||
|
|
||||||
//If window coordinates are all zero(ie,window is minimised),then assign desktop as the parent window.
|
|
||||||
if (rc.left == 0 && rc.right == 0 && rc.top == 0 && rc.bottom == 0)
|
|
||||||
{
|
|
||||||
//hParent = ::GetDesktopWindow();
|
|
||||||
::ShowWindow(hParent, SW_SHOWNORMAL);
|
|
||||||
::GetClientRect(hParent,&rc);
|
|
||||||
}
|
|
||||||
|
|
||||||
POINT center;
|
|
||||||
center.x = rc.left + (rc.right - rc.left)/2;
|
|
||||||
center.y = rc.top + (rc.bottom - rc.top)/2;
|
|
||||||
::ClientToScreen(hParent, ¢er);
|
|
||||||
|
|
||||||
RECT _rc;
|
|
||||||
::GetWindowRect(hwnd, &_rc);
|
|
||||||
int x = center.x - (_rc.right - _rc.left)/2;
|
|
||||||
int y = center.y - (_rc.bottom - _rc.top)/2;
|
|
||||||
|
|
||||||
::SetWindowPos(hwnd, HWND_TOP, x, y, _rc.right - _rc.left, _rc.bottom - _rc.top, SWP_SHOWWINDOW);
|
|
||||||
}
|
|
||||||
|
|
||||||
generic_string changeExt(generic_string fn, generic_string ext, bool forceReplaced)
|
|
||||||
{
|
|
||||||
if (ext == TEXT(""))
|
|
||||||
return fn;
|
|
||||||
|
|
||||||
generic_string fnExt = fn;
|
|
||||||
|
|
||||||
auto index = fnExt.find_last_of(TEXT("."));
|
|
||||||
generic_string extension = TEXT(".");
|
|
||||||
extension += ext;
|
|
||||||
if (index == generic_string::npos)
|
|
||||||
{
|
|
||||||
fnExt += extension;
|
|
||||||
}
|
|
||||||
else if (forceReplaced)
|
|
||||||
{
|
|
||||||
auto len = (extension.length() > fnExt.length() - index + 1)?extension.length():fnExt.length() - index + 1;
|
|
||||||
fnExt.replace(index, len, extension);
|
|
||||||
}
|
|
||||||
return fnExt;
|
|
||||||
}
|
|
|
@ -1,70 +0,0 @@
|
||||||
// This file is part of Notepad++ project
|
|
||||||
// Copyright (C)2021 Don HO <don.h@free.fr>
|
|
||||||
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// at your option any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "Common.h"
|
|
||||||
#include "Notepad_plus_msgs.h"
|
|
||||||
|
|
||||||
const int nbExtMax = 256;
|
|
||||||
const int extLenMax = 64;
|
|
||||||
|
|
||||||
typedef std::vector<generic_string> stringVector;
|
|
||||||
|
|
||||||
generic_string changeExt(generic_string fn, generic_string ext, bool forceReplaced = true);
|
|
||||||
void goToCenter(HWND hwnd);
|
|
||||||
|
|
||||||
|
|
||||||
class FileDialog
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
FileDialog(HWND hwnd, HINSTANCE hInst);
|
|
||||||
~FileDialog();
|
|
||||||
void setExtFilter(const TCHAR *, const TCHAR *, ...);
|
|
||||||
|
|
||||||
int setExtsFilter(const TCHAR *extText, const TCHAR *exts);
|
|
||||||
void setDefFileName(const TCHAR *fn){ wcscpy_s(_fileName, fn);}
|
|
||||||
void setDefExt(const TCHAR *ext){ _defExt = ext;}
|
|
||||||
|
|
||||||
TCHAR * doSaveDlg();
|
|
||||||
stringVector * doOpenMultiFilesDlg();
|
|
||||||
TCHAR * doOpenSingleFileDlg();
|
|
||||||
bool isReadOnly() {return _ofn.Flags & OFN_READONLY;};
|
|
||||||
void setExtIndex(int extTypeIndex) {_extTypeIndex = extTypeIndex;};
|
|
||||||
|
|
||||||
static int _dialogFileBoxId;
|
|
||||||
protected :
|
|
||||||
static UINT_PTR CALLBACK OFNHookProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
|
|
||||||
BOOL APIENTRY run(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
|
|
||||||
|
|
||||||
private:
|
|
||||||
TCHAR _fileName[MAX_PATH*8];
|
|
||||||
generic_string _defExt;
|
|
||||||
|
|
||||||
TCHAR * _fileExt = nullptr;
|
|
||||||
int _nbCharFileExt = 0;
|
|
||||||
|
|
||||||
stringVector _fileNames;
|
|
||||||
|
|
||||||
OPENFILENAME _ofn;
|
|
||||||
winVer _winVersion;
|
|
||||||
|
|
||||||
int _nbExt = 0;
|
|
||||||
int _extTypeIndex = -1;
|
|
||||||
static FileDialog *staticThis;
|
|
||||||
};
|
|
||||||
|
|
|
@ -150,8 +150,7 @@ BEGIN
|
||||||
CONTROL "",IDC_OPENSAVEDIR_ALWAYSON_RADIO,"Button",BS_AUTORADIOBUTTON,118,89,11,10
|
CONTROL "",IDC_OPENSAVEDIR_ALWAYSON_RADIO,"Button",BS_AUTORADIOBUTTON,118,89,11,10
|
||||||
EDITTEXT IDC_OPENSAVEDIR_ALWAYSON_EDIT,134,88,179,14,ES_AUTOHSCROLL
|
EDITTEXT IDC_OPENSAVEDIR_ALWAYSON_EDIT,134,88,179,14,ES_AUTOHSCROLL
|
||||||
PUSHBUTTON "...",IDD_OPENSAVEDIR_ALWAYSON_BROWSE_BUTTON,320,87,16,14
|
PUSHBUTTON "...",IDD_OPENSAVEDIR_ALWAYSON_BROWSE_BUTTON,320,87,16,14
|
||||||
CONTROL "Use new style dialog (without Unix style path capacity)",IDC_OPENSAVEDIR_CHECK_USENEWSTYLESAVEDIALOG, "Button",BS_AUTOCHECKBOX | WS_TABSTOP,110,145,342,10
|
CONTROL "Open all files of folder instead of launching Folder as Workspace on folder dropping",IDC_OPENSAVEDIR_CHECK_DRROPFOLDEROPENFILES, "Button",BS_AUTOCHECKBOX | WS_TABSTOP,110, 145,342,10
|
||||||
CONTROL "Open all files of folder instead of launching Folder as Workspace on folder dropping",IDC_OPENSAVEDIR_CHECK_DRROPFOLDEROPENFILES, "Button",BS_AUTOCHECKBOX | WS_TABSTOP,110,158,342,10
|
|
||||||
END
|
END
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1407,7 +1407,6 @@ INT_PTR CALLBACK DefaultDirectorySubDlg::run_dlgProc(UINT message, WPARAM wParam
|
||||||
if (enableDlgTheme)
|
if (enableDlgTheme)
|
||||||
enableDlgTheme(_hSelf, ETDT_ENABLETAB);
|
enableDlgTheme(_hSelf, ETDT_ENABLETAB);
|
||||||
|
|
||||||
::SendDlgItemMessage(_hSelf, IDC_OPENSAVEDIR_CHECK_USENEWSTYLESAVEDIALOG, BM_SETCHECK, nppGUI._useNewStyleSaveDlg ? BST_CHECKED : BST_UNCHECKED, 0);
|
|
||||||
::SendDlgItemMessage(_hSelf, IDC_OPENSAVEDIR_CHECK_DRROPFOLDEROPENFILES, BM_SETCHECK, nppGUI._isFolderDroppedOpenFiles ? BST_CHECKED : BST_UNCHECKED, 0);
|
::SendDlgItemMessage(_hSelf, IDC_OPENSAVEDIR_CHECK_DRROPFOLDEROPENFILES, BM_SETCHECK, nppGUI._isFolderDroppedOpenFiles ? BST_CHECKED : BST_UNCHECKED, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1451,10 +1450,6 @@ INT_PTR CALLBACK DefaultDirectorySubDlg::run_dlgProc(UINT message, WPARAM wParam
|
||||||
folderBrowser(_hSelf, TEXT("Select a folder as default directory"), IDC_OPENSAVEDIR_ALWAYSON_EDIT);
|
folderBrowser(_hSelf, TEXT("Select a folder as default directory"), IDC_OPENSAVEDIR_ALWAYSON_EDIT);
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
|
||||||
case IDC_OPENSAVEDIR_CHECK_USENEWSTYLESAVEDIALOG:
|
|
||||||
nppGUI._useNewStyleSaveDlg = isCheckedOrNot(IDC_OPENSAVEDIR_CHECK_USENEWSTYLESAVEDIALOG);
|
|
||||||
return TRUE;
|
|
||||||
|
|
||||||
case IDC_OPENSAVEDIR_CHECK_DRROPFOLDEROPENFILES:
|
case IDC_OPENSAVEDIR_CHECK_DRROPFOLDEROPENFILES:
|
||||||
nppGUI._isFolderDroppedOpenFiles = isCheckedOrNot(IDC_OPENSAVEDIR_CHECK_DRROPFOLDEROPENFILES);
|
nppGUI._isFolderDroppedOpenFiles = isCheckedOrNot(IDC_OPENSAVEDIR_CHECK_DRROPFOLDEROPENFILES);
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
|
|
@ -237,7 +237,6 @@
|
||||||
#define IDC_RADIO_CUSTOMIZELENTH (IDD_PREFERENCE_SUB_NEWDOCUMENT + 27)
|
#define IDC_RADIO_CUSTOMIZELENTH (IDD_PREFERENCE_SUB_NEWDOCUMENT + 27)
|
||||||
#define IDC_CUSTOMIZELENGTHVAL_STATIC (IDD_PREFERENCE_SUB_NEWDOCUMENT + 28)
|
#define IDC_CUSTOMIZELENGTHVAL_STATIC (IDD_PREFERENCE_SUB_NEWDOCUMENT + 28)
|
||||||
#define IDC_DISPLAY_STATIC (IDD_PREFERENCE_SUB_NEWDOCUMENT + 29)
|
#define IDC_DISPLAY_STATIC (IDD_PREFERENCE_SUB_NEWDOCUMENT + 29)
|
||||||
#define IDC_OPENSAVEDIR_CHECK_USENEWSTYLESAVEDIALOG (IDD_PREFERENCE_SUB_NEWDOCUMENT + 30)
|
|
||||||
#define IDC_OPENSAVEDIR_CHECK_DRROPFOLDEROPENFILES (IDD_PREFERENCE_SUB_NEWDOCUMENT + 31)
|
#define IDC_OPENSAVEDIR_CHECK_DRROPFOLDEROPENFILES (IDD_PREFERENCE_SUB_NEWDOCUMENT + 31)
|
||||||
|
|
||||||
#define IDD_PREFERENCE_SUB_DEFAULTDIRECTORY 6450 //(IDD_PREFERENCE_BOX + 400)
|
#define IDD_PREFERENCE_SUB_DEFAULTDIRECTORY 6450 //(IDD_PREFERENCE_BOX + 400)
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
#include "ProjectPanel.h"
|
#include "ProjectPanel.h"
|
||||||
#include "resource.h"
|
#include "resource.h"
|
||||||
#include "tinyxml.h"
|
#include "tinyxml.h"
|
||||||
#include "FileDialog.h"
|
#include "CustomFileDialog.h"
|
||||||
#include "localization.h"
|
#include "localization.h"
|
||||||
#include "Parameters.h"
|
#include "Parameters.h"
|
||||||
|
|
||||||
|
@ -439,7 +439,7 @@ bool ProjectPanel::saveWorkSpace()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ProjectPanel::writeWorkSpace(TCHAR *projectFileName)
|
bool ProjectPanel::writeWorkSpace(const TCHAR *projectFileName)
|
||||||
{
|
{
|
||||||
//write <NotepadPlus>: use the default file name if new file name is not given
|
//write <NotepadPlus>: use the default file name if new file name is not given
|
||||||
const TCHAR * fn2write = projectFileName?projectFileName:_workSpaceFilePath.c_str();
|
const TCHAR * fn2write = projectFileName?projectFileName:_workSpaceFilePath.c_str();
|
||||||
|
@ -1032,11 +1032,12 @@ void ProjectPanel::popupMenuCmd(int cmdID)
|
||||||
if (!saveWorkspaceRequest())
|
if (!saveWorkspaceRequest())
|
||||||
break;
|
break;
|
||||||
|
|
||||||
FileDialog fDlg(_hSelf, ::GetModuleHandle(NULL));
|
CustomFileDialog fDlg(_hSelf);
|
||||||
setFileExtFilter(fDlg);
|
setFileExtFilter(fDlg);
|
||||||
if (TCHAR *fn = fDlg.doOpenSingleFileDlg())
|
const generic_string fn = fDlg.doOpenSingleFileDlg();
|
||||||
|
if (!fn.empty())
|
||||||
{
|
{
|
||||||
if (!openWorkSpace(fn, true))
|
if (!openWorkSpace(fn.c_str(), true))
|
||||||
{
|
{
|
||||||
NativeLangSpeaker *pNativeSpeaker = (NppParameters::getInstance()).getNativeLangSpeaker();
|
NativeLangSpeaker *pNativeSpeaker = (NppParameters::getInstance()).getNativeLangSpeaker();
|
||||||
pNativeSpeaker->messageBox("ProjectPanelOpenFailed",
|
pNativeSpeaker->messageBox("ProjectPanelOpenFailed",
|
||||||
|
@ -1183,13 +1184,14 @@ void ProjectPanel::popupMenuCmd(int cmdID)
|
||||||
|
|
||||||
bool ProjectPanel::saveWorkSpaceAs(bool saveCopyAs)
|
bool ProjectPanel::saveWorkSpaceAs(bool saveCopyAs)
|
||||||
{
|
{
|
||||||
FileDialog fDlg(_hSelf, ::GetModuleHandle(NULL));
|
CustomFileDialog fDlg(_hSelf);
|
||||||
setFileExtFilter(fDlg);
|
setFileExtFilter(fDlg);
|
||||||
fDlg.setExtIndex(0); // 0 index for "custom extention" type if any else for "All types *.*"
|
fDlg.setExtIndex(0); // 0 index for "custom extention" type if any else for "All types *.*"
|
||||||
|
|
||||||
if (TCHAR *fn = fDlg.doSaveDlg())
|
const generic_string fn = fDlg.doSaveDlg();
|
||||||
|
if (!fn.empty())
|
||||||
{
|
{
|
||||||
writeWorkSpace(fn);
|
writeWorkSpace(fn.c_str());
|
||||||
if (!saveCopyAs)
|
if (!saveCopyAs)
|
||||||
{
|
{
|
||||||
_workSpaceFilePath = fn;
|
_workSpaceFilePath = fn;
|
||||||
|
@ -1200,7 +1202,7 @@ bool ProjectPanel::saveWorkSpaceAs(bool saveCopyAs)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProjectPanel::setFileExtFilter(FileDialog & fDlg)
|
void ProjectPanel::setFileExtFilter(CustomFileDialog & fDlg)
|
||||||
{
|
{
|
||||||
const TCHAR *ext = NppParameters::getInstance().getNppGUI()._definedWorkspaceExt.c_str();
|
const TCHAR *ext = NppParameters::getInstance().getNppGUI()._definedWorkspaceExt.c_str();
|
||||||
generic_string workspaceExt = TEXT("");
|
generic_string workspaceExt = TEXT("");
|
||||||
|
@ -1209,25 +1211,26 @@ void ProjectPanel::setFileExtFilter(FileDialog & fDlg)
|
||||||
if (*ext != '.')
|
if (*ext != '.')
|
||||||
workspaceExt += TEXT(".");
|
workspaceExt += TEXT(".");
|
||||||
workspaceExt += ext;
|
workspaceExt += ext;
|
||||||
fDlg.setExtFilter(TEXT("Workspace file"), workspaceExt.c_str(), NULL);
|
fDlg.setExtFilter(TEXT("Workspace file"), workspaceExt.c_str());
|
||||||
fDlg.setDefExt(ext);
|
fDlg.setDefExt(ext);
|
||||||
}
|
}
|
||||||
fDlg.setExtFilter(TEXT("All types"), TEXT(".*"), NULL);
|
fDlg.setExtFilter(TEXT("All types"), TEXT(".*"));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProjectPanel::addFiles(HTREEITEM hTreeItem)
|
void ProjectPanel::addFiles(HTREEITEM hTreeItem)
|
||||||
{
|
{
|
||||||
FileDialog fDlg(_hSelf, ::GetModuleHandle(NULL));
|
CustomFileDialog fDlg(_hSelf);
|
||||||
fDlg.setExtFilter(TEXT("All types"), TEXT(".*"), NULL);
|
fDlg.setExtFilter(TEXT("All types"), TEXT(".*"));
|
||||||
|
|
||||||
if (stringVector *pfns = fDlg.doOpenMultiFilesDlg())
|
const auto& fns = fDlg.doOpenMultiFilesDlg();
|
||||||
|
if (!fns.empty())
|
||||||
{
|
{
|
||||||
size_t sz = pfns->size();
|
size_t sz = fns.size();
|
||||||
for (size_t i = 0 ; i < sz ; ++i)
|
for (size_t i = 0 ; i < sz ; ++i)
|
||||||
{
|
{
|
||||||
TCHAR *strValueLabel = ::PathFindFileName(pfns->at(i).c_str());
|
TCHAR *strValueLabel = ::PathFindFileName(fns.at(i).c_str());
|
||||||
|
|
||||||
generic_string* pathFileStr = new generic_string(pfns->at(i));
|
generic_string* pathFileStr = new generic_string(fns.at(i));
|
||||||
fullPathStrs.push_back(pathFileStr);
|
fullPathStrs.push_back(pathFileStr);
|
||||||
LPARAM lParamPathFileStr = reinterpret_cast<LPARAM>(pathFileStr);
|
LPARAM lParamPathFileStr = reinterpret_cast<LPARAM>(pathFileStr);
|
||||||
|
|
||||||
|
|
|
@ -52,7 +52,7 @@ enum NodeType {
|
||||||
};
|
};
|
||||||
|
|
||||||
class TiXmlNode;
|
class TiXmlNode;
|
||||||
class FileDialog;
|
class CustomFileDialog;
|
||||||
|
|
||||||
class ProjectPanel : public DockingDlgInterface {
|
class ProjectPanel : public DockingDlgInterface {
|
||||||
public:
|
public:
|
||||||
|
@ -124,7 +124,7 @@ protected:
|
||||||
void recursiveAddFilesFrom(const TCHAR *folderPath, HTREEITEM hTreeItem);
|
void recursiveAddFilesFrom(const TCHAR *folderPath, HTREEITEM hTreeItem);
|
||||||
HTREEITEM addFolder(HTREEITEM hTreeItem, const TCHAR *folderName);
|
HTREEITEM addFolder(HTREEITEM hTreeItem, const TCHAR *folderName);
|
||||||
|
|
||||||
bool writeWorkSpace(TCHAR *projectFileName = NULL);
|
bool writeWorkSpace(const TCHAR *projectFileName = NULL);
|
||||||
generic_string getRelativePath(const generic_string & fn, const TCHAR *workSpaceFileName);
|
generic_string getRelativePath(const generic_string & fn, const TCHAR *workSpaceFileName);
|
||||||
void buildProjectXml(TiXmlNode *root, HTREEITEM hItem, const TCHAR* fn2write);
|
void buildProjectXml(TiXmlNode *root, HTREEITEM hItem, const TCHAR* fn2write);
|
||||||
NodeType getNodeType(HTREEITEM hItem);
|
NodeType getNodeType(HTREEITEM hItem);
|
||||||
|
@ -139,7 +139,7 @@ protected:
|
||||||
HMENU getMenuHandler(HTREEITEM selectedItem);
|
HMENU getMenuHandler(HTREEITEM selectedItem);
|
||||||
generic_string getAbsoluteFilePath(const TCHAR * relativePath);
|
generic_string getAbsoluteFilePath(const TCHAR * relativePath);
|
||||||
void openSelectFile();
|
void openSelectFile();
|
||||||
void setFileExtFilter(FileDialog & fDlg);
|
void setFileExtFilter(CustomFileDialog & fDlg);
|
||||||
std::vector<generic_string*> fullPathStrs;
|
std::vector<generic_string*> fullPathStrs;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
|
|
||||||
#include "StaticDialog.h"
|
#include "StaticDialog.h"
|
||||||
#include "RunDlg.h"
|
#include "RunDlg.h"
|
||||||
#include "FileDialog.h"
|
#include "CustomFileDialog.h"
|
||||||
#include "Notepad_plus_msgs.h"
|
#include "Notepad_plus_msgs.h"
|
||||||
#include "shortcut.h"
|
#include "shortcut.h"
|
||||||
#include "Parameters.h"
|
#include "Parameters.h"
|
||||||
|
@ -318,13 +318,14 @@ INT_PTR CALLBACK RunDlg::run_dlgProc(UINT message, WPARAM wParam, LPARAM lParam)
|
||||||
|
|
||||||
case IDC_BUTTON_FILE_BROWSER :
|
case IDC_BUTTON_FILE_BROWSER :
|
||||||
{
|
{
|
||||||
FileDialog fd(_hSelf, _hInst);
|
CustomFileDialog fd(_hSelf);
|
||||||
fd.setExtFilter(TEXT("Executable file : "), TEXT(".exe"), TEXT(".com"), TEXT(".cmd"), TEXT(".bat"), NULL);
|
fd.setExtFilter(TEXT("Executable file : "), { TEXT(".exe"), TEXT(".com"), TEXT(".cmd"), TEXT(".bat") });
|
||||||
fd.setExtFilter(TEXT("All files : "), TEXT(".*"), NULL);
|
fd.setExtFilter(TEXT("All files : "), TEXT(".*"));
|
||||||
|
|
||||||
if (const TCHAR *fn = fd.doOpenSingleFileDlg())
|
generic_string fn = fd.doOpenSingleFileDlg();
|
||||||
|
if (!fn.empty())
|
||||||
{
|
{
|
||||||
if (wcschr(fn, ' ') != NULL)
|
if (fn.find(' ') != generic_string::npos)
|
||||||
{
|
{
|
||||||
generic_string fn_quotes(fn);
|
generic_string fn_quotes(fn);
|
||||||
fn_quotes = TEXT("\"") + fn_quotes + TEXT("\"");
|
fn_quotes = TEXT("\"") + fn_quotes + TEXT("\"");
|
||||||
|
@ -332,7 +333,7 @@ INT_PTR CALLBACK RunDlg::run_dlgProc(UINT message, WPARAM wParam, LPARAM lParam)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
addTextToCombo(fn);
|
addTextToCombo(fn.c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
|
|
@ -301,7 +301,6 @@ copy ..\src\contextMenu.xml ..\bin64\contextMenu.xml
|
||||||
<ClCompile Include="..\src\ScitillaComponent\DocTabView.cpp" />
|
<ClCompile Include="..\src\ScitillaComponent\DocTabView.cpp" />
|
||||||
<ClCompile Include="..\src\WinControls\DocumentMap\documentMap.cpp" />
|
<ClCompile Include="..\src\WinControls\DocumentMap\documentMap.cpp" />
|
||||||
<ClCompile Include="..\src\EncodingMapper.cpp" />
|
<ClCompile Include="..\src\EncodingMapper.cpp" />
|
||||||
<ClCompile Include="..\src\WinControls\OpenSaveFileDialog\FileDialog.cpp" />
|
|
||||||
<ClCompile Include="..\src\WinControls\FindCharsInRange\FindCharsInRange.cpp" />
|
<ClCompile Include="..\src\WinControls\FindCharsInRange\FindCharsInRange.cpp" />
|
||||||
<ClCompile Include="..\src\ScitillaComponent\FindReplaceDlg.cpp" />
|
<ClCompile Include="..\src\ScitillaComponent\FindReplaceDlg.cpp" />
|
||||||
<ClCompile Include="..\src\ScitillaComponent\FunctionCallTip.cpp" />
|
<ClCompile Include="..\src\ScitillaComponent\FunctionCallTip.cpp" />
|
||||||
|
@ -594,7 +593,6 @@ copy ..\src\contextMenu.xml ..\bin64\contextMenu.xml
|
||||||
<ClInclude Include="..\src\ScitillaComponent\DocTabView.h" />
|
<ClInclude Include="..\src\ScitillaComponent\DocTabView.h" />
|
||||||
<ClInclude Include="..\src\WinControls\DocumentMap\documentMap.h" />
|
<ClInclude Include="..\src\WinControls\DocumentMap\documentMap.h" />
|
||||||
<ClInclude Include="..\src\EncodingMapper.h" />
|
<ClInclude Include="..\src\EncodingMapper.h" />
|
||||||
<ClInclude Include="..\src\WinControls\OpenSaveFileDialog\FileDialog.h" />
|
|
||||||
<ClInclude Include="..\src\MISC\FileNameStringSplitter.h" />
|
<ClInclude Include="..\src\MISC\FileNameStringSplitter.h" />
|
||||||
<ClInclude Include="..\src\WinControls\FindCharsInRange\FindCharsInRange.h" />
|
<ClInclude Include="..\src\WinControls\FindCharsInRange\FindCharsInRange.h" />
|
||||||
<ClInclude Include="..\src\ScitillaComponent\FindReplaceDlg.h" />
|
<ClInclude Include="..\src\ScitillaComponent\FindReplaceDlg.h" />
|
||||||
|
|
Loading…
Reference in New Issue