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:
mere-human 2021-01-17 18:24:37 +02:00 committed by Don HO
parent ab5c1d3e2a
commit e7079d57c6
No known key found for this signature in database
GPG Key ID: 6C429F1D8D84F46E
22 changed files with 693 additions and 827 deletions

View File

@ -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>

View File

@ -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>

View File

@ -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;
} }

View File

@ -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)
{ {

View File

@ -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());
} }
} }

View File

@ -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();

View File

@ -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());
} }

View File

@ -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"));

View File

@ -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;
}; };

View File

@ -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)
{ {

View File

@ -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"

View File

@ -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("");
} }

View File

@ -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;

View File

@ -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, &center);
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;
}

View File

@ -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;
};

View File

@ -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

View File

@ -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;

View File

@ -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)

View File

@ -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);

View File

@ -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;
}; };

View File

@ -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;

View File

@ -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" />