// grepWin - regex search and replace for Windows // Copyright (C) 2007-2021 - Stefan Kueng // 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 2 // 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, write to the Free Software Foundation, // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // #include "stdafx.h" #include "strsafe.h" #include "resource.h" #include "SearchDlg.h" #include "Registry.h" #include "DirFileEnum.h" #include "TextFile.h" #include "SearchInfo.h" #include "UnicodeUtils.h" #include "StringUtils.h" #include "BrowseFolder.h" #include "SysImageList.h" #include "ShellContextMenu.h" #include "RegexTestDlg.h" #include "NameDlg.h" #include "BookmarksDlg.h" #include "MultiLineEditDlg.h" #include "AboutDlg.h" #include "DropFiles.h" #include "RegexReplaceFormatter.h" #include "LineData.h" #include "Settings.h" #include "ResString.h" #include "Language.h" #include "Monitor.h" #include "SmartHandle.h" #include "PathUtils.h" #include "DebugOutput.h" #include "Theme.h" #include "DarkModeHelper.h" #include "SearchMT.h" // multi-threading helper #include "OnOutOfScope.h" #include "COMPtrs.h" #include "PreserveChdir.h" #ifdef NP3_ALLOW_UPDATE #include "TempFile.h" #include "Monitor.h" #include "version.h" #endif #include #include #include #include #include #include #include #pragma warning(push) #pragma warning(disable: 4996) // warning STL4010: Various members of std::allocator are deprecated in C++17 #include #include #include #pragma warning(pop) #define GREPWIN_DATEBUFFER 100 #define LABELUPDATETIMER 10 DWORD WINAPI SearchThreadEntry(LPVOID lpParam); DWORD WINAPI EvaluationThreadEntry(LPVOID lpParam); // ReSharper disable once CppInconsistentNaming UINT CSearchDlg::m_grepwinStartupmsg = RegisterWindowMessage(L"grepWinNP3_StartupMessage"); std::map linePositions; extern ULONGLONG g_startTime; extern HANDLE hInitProtection; static volatile LONG s_SearchThreadRunning = FALSE; static volatile LONG s_EvaluationThreadRunning = FALSE; static volatile LONG s_Cancelled = FALSE; static volatile LONG s_NOTSearch = FALSE; inline bool IsSearchThreadRunning() { return InterlockedAnd(&s_SearchThreadRunning, TRUE); } inline bool IsEvaluationThreadRunning() { return InterlockedAnd(&s_EvaluationThreadRunning, TRUE); } inline bool IsCancelled() { return InterlockedAnd(&s_Cancelled, TRUE); } inline bool IsNOTSearch() { return InterlockedAnd(&s_NOTSearch, TRUE); } static BackupAndTempFilesLog s_BackupAndTmpFiles; static SearchThreadMap s_SearchThreadMap; // ================================================================================== CSearchDlg::CSearchDlg(HWND hParent) : m_hParent(hParent) //, m_dwThreadRunning(FALSE) //, m_cancelled(FALSE) , m_bookmarksDlg(nullptr) , m_patternRegexC(false) , m_excludeDirsPatternRegexC(false) , m_bUseRegex(false) , m_bUseRegexForPaths(false) , m_bAllSize(false) , m_lSize(2000 << 10) , m_sizeCmp(0) , m_bIncludeSystem(false) , m_bIncludeSystemC(false) , m_bIncludeHidden(false) , m_bIncludeHiddenC(false) , m_bIncludeSubfolders(false) , m_bIncludeSubfoldersC(false) , m_bIncludeBinary(false) , m_bIncludeBinaryC(false) , m_bCreateBackup(false) , m_bCreateBackupC(false) , m_bCreateBackupInFolders(false) , m_bCreateBackupInFoldersC(false) , m_bWholeWords(false) , m_bWholeWordsC(false) , m_bUTF8(false) , m_bUTF8C(false) , m_bForceBinary(false) , m_bCaseSensitive(false) , m_bCaseSensitiveC(false) , m_bDotMatchesNewline(false) , m_bDotMatchesNewlineC(false) , m_bCaptureSearch(false) , m_bSizeC(false) , m_endDialog(false) , m_executeImmediately(ExecuteAction::None) , m_dateLimit(0) , m_bDateLimitC(false) , m_date1({0}) , m_date2({0}) , m_bNoSaveSettings(false) , m_bReplace(false) , m_bConfirmationOnReplace(true) , m_showContent(false) , m_showContentSet(false) , m_totalItems(0) , m_searchedItems(0) , m_totalMatches(0) , m_selectedItems(0) , m_bAscending(true) , m_themeCallbackId(0) , m_pDropTarget(nullptr) , m_autoCompleteFilePatterns(bPortable ? &g_iniFile : nullptr) , m_autoCompleteExcludeDirsPatterns(bPortable ? &g_iniFile : nullptr) , m_autoCompleteSearchPatterns(bPortable ? &g_iniFile : nullptr) , m_autoCompleteReplacePatterns(bPortable ? &g_iniFile : nullptr) , m_autoCompleteSearchPaths(bPortable ? &g_iniFile : nullptr) , m_regUseRegex(L"Software\\grepWinNP3\\UseRegex", 1) , m_regAllSize(L"Software\\grepWinNP3\\AllSize") , m_regSize(L"Software\\grepWinNP3\\Size", L"2000") , m_regSizeCombo(L"Software\\grepWinNP3\\SizeCombo", 0) , m_regIncludeSystem(L"Software\\grepWinNP3\\IncludeSystem") , m_regIncludeHidden(L"Software\\grepWinNP3\\IncludeHidden") , m_regIncludeSubfolders(L"Software\\grepWinNP3\\IncludeSubfolders", 1) , m_regIncludeBinary(L"Software\\grepWinNP3\\IncludeBinary", 1) , m_regCreateBackup(L"Software\\grepWinNP3\\CreateBackup") , m_regWholeWords(L"Software\\grepWinNP3\\WholeWords") , m_regUTF8(L"Software\\grepWinNP3\\UTF8") , m_regCaseSensitive(L"Software\\grepWinNP3\\CaseSensitive") , m_regDotMatchesNewline(L"Software\\grepWinNP3\\DotMatchesNewline") , m_regUseRegexForPaths(L"Software\\grepWinNP3\\UseFileMatchRegex") , m_regPattern(L"Software\\grepWinNP3\\pattern") , m_regExcludeDirsPattern(L"Software\\grepWinNP3\\ExcludeDirsPattern") , m_regSearchPath(L"Software\\grepWinNP3\\searchpath") , m_regEditorCmd(L"Software\\grepWinNP3\\editorcmd") , m_regBackupInFolder(L"Software\\grepWinNP3\\backupinfolder", FALSE) , m_regDateLimit(L"Software\\grepWinNP3\\DateLimit", 0) , m_regDate1Low(L"Software\\grepWinNP3\\Date1Low", 0) , m_regDate1High(L"Software\\grepWinNP3\\Date1High", 0) , m_regDate2Low(L"Software\\grepWinNP3\\Date2Low", 0) , m_regDate2High(L"Software\\grepWinNP3\\Date2High", 0) , m_regShowContent(L"Software\\grepWinNP3\\ShowContent", 0) , m_regOpacityNoFocus(L"Software\\grepWinNP3\\OpacityNoFocus", 100) , m_regStayOnTop(L"Software\\grepWinNP3\\StayOnTop", 0) , m_OpacityNoFocus(100) , m_bStayOnTop(false) { if (FAILED(CoCreateInstance(CLSID_TaskbarList, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(m_pTaskbarList.GetAddressOf())))) m_pTaskbarList = nullptr; else if (m_pTaskbarList) m_pTaskbarList->HrInit(); } CSearchDlg::~CSearchDlg() { if (m_pDropTarget) delete m_pDropTarget; } LRESULT CSearchDlg::DlgFunc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { UNREFERENCED_PARAMETER(lParam); if (uMsg == m_grepwinStartupmsg) { if ((GetTickCount64() - 4000) < g_startTime) { if (wParam == 0) g_startTime = GetTickCount64(); return TRUE; } if (wParam == 0) g_startTime = GetTickCount64(); return FALSE; } switch (uMsg) { case WM_INITDIALOG: { SetTransparency(ALPHA_OPAQUE); SHAutoComplete(GetDlgItem(*this, IDC_SEARCHPATH), SHACF_FILESYSTEM|SHACF_AUTOSUGGEST_FORCE_ON); m_autoCompleteFilePatterns.Load(L"Software\\grepWinNP3\\History", L"FilePattern"); m_autoCompleteFilePatterns.Init(GetDlgItem(hwndDlg, IDC_PATTERN)); m_autoCompleteExcludeDirsPatterns.Load(L"Software\\grepWinNP3\\History", L"ExcludeDirsPattern"); m_autoCompleteExcludeDirsPatterns.Init(GetDlgItem(hwndDlg, IDC_EXCLUDEDIRSPATTERN)); m_autoCompleteSearchPatterns.Load(L"Software\\grepWinNP3\\History", L"SearchPattern"); m_autoCompleteSearchPatterns.Init(GetDlgItem(hwndDlg, IDC_SEARCHTEXT)); m_autoCompleteReplacePatterns.Load(L"Software\\grepWinNP3\\History", L"ReplacePattern"); m_autoCompleteReplacePatterns.Init(GetDlgItem(hwndDlg, IDC_REPLACETEXT)); m_autoCompleteSearchPaths.Load(L"Software\\grepWinNP3\\History", L"SearchPaths"); m_autoCompleteSearchPaths.Init(GetDlgItem(hwndDlg, IDC_SEARCHPATH)); m_themeCallbackId = CTheme::Instance().RegisterThemeChangeCallback( [this]() { auto bDark = CTheme::Instance().IsDarkTheme(); DarkModeHelper::Instance().AllowDarkModeForApp(bDark); CTheme::Instance().SetThemeForDialog(*this, bDark); CTheme::Instance().SetFontForDialog(*this, CTheme::Instance().GetDlgFontFaceName(), CTheme::Instance().GetDlgFontSize()); DarkModeHelper::Instance().AllowDarkModeForWindow(GetToolTipHWND(), bDark); DarkModeHelper::Instance().RefreshTitleBarThemeColor(*this, bDark); }); auto bDark = CTheme::Instance().IsDarkTheme(); if (bDark) DarkModeHelper::Instance().AllowDarkModeForApp(bDark); CTheme::Instance().SetThemeForDialog(*this, CTheme::Instance().IsDarkTheme()); CTheme::Instance().SetFontForDialog(*this, CTheme::Instance().GetDlgFontFaceName(), CTheme::Instance().GetDlgFontSize()); DarkModeHelper::Instance().AllowDarkModeForWindow(GetToolTipHWND(), bDark); if (!bDark) DarkModeHelper::Instance().AllowDarkModeForApp(bDark); SetWindowTheme(GetToolTipHWND(), L"Explorer", nullptr); CLanguage::Instance().TranslateWindow(*this); AddToolTip(IDC_NEWINSTANCE, TranslatedString(hResource, IDS_NEWINSTANCE_TT).c_str()); AddToolTip(IDC_PATTERN, TranslatedString(hResource, IDS_PATTERN_TT).c_str()); AddToolTip(IDC_EXCLUDEDIRSPATTERN, TranslatedString(hResource, IDS_EXCLUDEDIR_TT).c_str()); AddToolTip(IDC_SEARCHPATH, TranslatedString(hResource, IDS_SEARCHPATH_TT).c_str()); AddToolTip(IDC_DOTMATCHNEWLINE, TranslatedString(hResource, IDS_DOTMATCHNEWLINE_TT).c_str()); AddToolTip(IDC_SEARCHTEXT, TranslatedString(hResource, IDS_SEARCHTEXT_TT).c_str()); AddToolTip(IDC_EDITMULTILINE1, TranslatedString(hResource, IDS_EDITMULTILINE_TT).c_str()); AddToolTip(IDC_EDITMULTILINE2, TranslatedString(hResource, IDS_EDITMULTILINE_TT).c_str()); AddToolTip(IDC_EXPORT, TranslatedString(hResource, IDS_EXPORT_TT).c_str()); AddToolTip(IDOK, TranslatedString(hResource, IDS_SHIFT_NOTSEARCH).c_str()); AddToolTip(IDC_REPLACETEXT, LPSTR_TEXTCALLBACK); if (m_searchPath.empty()) { if (bPortable) m_searchPath = g_iniFile.GetValue(L"global", L"searchpath", L""); else m_searchPath = std::wstring(m_regSearchPath); } else { // expand a possible 'short' path DWORD ret = 0; ret = ::GetLongPathName(m_searchPath.c_str(), nullptr, 0); if (ret) { auto pathBuf = std::make_unique(ret + 2LL); ret = ::GetLongPathName(m_searchPath.c_str(), pathBuf.get(), ret + 1); m_searchPath = std::wstring(pathBuf.get(), ret); } } if (m_patternRegex.empty() && !m_patternRegexC) { if (bPortable) { m_patternRegex = g_iniFile.GetValue(L"global", L"pattern", L""); m_bUseRegexForPaths = g_iniFile.GetBoolValue(L"global", L"UseFileMatchRegex", false); } else { m_patternRegex = std::wstring(m_regPattern); m_bUseRegexForPaths = !!static_cast(m_regUseRegexForPaths); } } if (m_excludeDirsPatternRegex.empty() && !m_excludeDirsPatternRegexC) { if (bPortable) m_excludeDirsPatternRegex = g_iniFile.GetValue(L"global", L"ExcludeDirsPattern", L""); else m_excludeDirsPatternRegex = std::wstring(m_regExcludeDirsPattern); } // initialize the controls SetDlgItemText(hwndDlg, IDC_SEARCHPATH, m_searchPath.c_str()); SetDlgItemText(hwndDlg, IDC_SEARCHTEXT, m_searchString.c_str()); SetDlgItemText(hwndDlg, IDC_EXCLUDEDIRSPATTERN, m_excludeDirsPatternRegex.c_str()); SetDlgItemText(hwndDlg, IDC_PATTERN, m_patternRegex.c_str()); SetDlgItemText(hwndDlg, IDC_REPLACETEXT, m_replaceString.c_str()); // the path edit control should work as a drop target for files and folders HWND hSearchPath = GetDlgItem(hwndDlg, IDC_SEARCHPATH); m_pDropTarget = new CFileDropTarget(hSearchPath); RegisterDragDrop(hSearchPath, m_pDropTarget); // create the supported formats: FORMATETC ftEtc = {0}; ftEtc.cfFormat = CF_TEXT; ftEtc.dwAspect = DVASPECT_CONTENT; ftEtc.lindex = -1; ftEtc.tymed = TYMED_HGLOBAL; m_pDropTarget->AddSuportedFormat(ftEtc); ftEtc.cfFormat = CF_HDROP; m_pDropTarget->AddSuportedFormat(ftEtc); m_pDropTarget->SetMultipathConcatenate('|'); m_editFilePatterns.Subclass(hwndDlg, IDC_PATTERN); m_editExcludeDirsPatterns.Subclass(hwndDlg, IDC_EXCLUDEDIRSPATTERN); m_editSearchPatterns.Subclass(hwndDlg, IDC_SEARCHTEXT); m_editReplacePatterns.Subclass(hwndDlg, IDC_REPLACETEXT); m_editSearchPaths.Subclass(hwndDlg, IDC_SEARCHPATH); // add an "About" entry to the system menu HMENU hSysMenu = GetSystemMenu(hwndDlg, FALSE); if (hSysMenu) { int menuItemsCount = GetMenuItemCount(hSysMenu); if (menuItemsCount > 2) { InsertMenu(hSysMenu, menuItemsCount - 2, MF_STRING | MF_BYPOSITION, ID_STAY_ON_TOP, TranslatedString(hResource, IDS_STAY_ON_TOP).c_str()); InsertMenu(hSysMenu, menuItemsCount - 2, MF_SEPARATOR | MF_BYPOSITION, NULL, NULL); InsertMenu(hSysMenu, menuItemsCount - 2, MF_STRING | MF_BYPOSITION, ID_ABOUTBOX, TranslatedString(hResource, IDS_ABOUT).c_str()); InsertMenu(hSysMenu, menuItemsCount - 2, MF_STRING | MF_BYPOSITION, ID_CLONE, TranslatedString(hResource, IDS_CLONE).c_str()); InsertMenu(hSysMenu, menuItemsCount - 2, MF_SEPARATOR | MF_BYPOSITION, NULL, nullptr); } else { AppendMenu(hSysMenu, MF_SEPARATOR, NULL, nullptr); AppendMenu(hSysMenu, MF_STRING, ID_CLONE, TranslatedString(hResource, IDS_CLONE).c_str()); AppendMenu(hSysMenu, MF_STRING, ID_ABOUTBOX, TranslatedString(hResource, IDS_ABOUT).c_str()); AppendMenu(hSysMenu, MF_SEPARATOR, NULL, NULL); AppendMenu(hSysMenu, MF_STRING, ID_STAY_ON_TOP, TranslatedString(hResource, IDS_STAY_ON_TOP).c_str()); } } wchar_t buf[MAX_PATH] = {0}; if (m_bSizeC && (m_lSize != MaxFileSize())) { swprintf_s(buf, _countof(buf), L"%I64u", m_lSize >> 10); SetDlgItemText(hwndDlg, IDC_SIZEEDIT, buf); } else { uint64_t const s = bPortable ? g_iniFile.GetLongValue(L"global", L"Size", 2000) : _wtoll(std::wstring(m_regSize).c_str()); swprintf_s(buf, _countof(buf), L"%I64u", s); SetDlgItemText(hwndDlg, IDC_SIZEEDIT, buf); } SendDlgItemMessage(hwndDlg, IDC_SIZECOMBO, CB_INSERTSTRING, static_cast(-1), reinterpret_cast(static_cast(TranslatedString(hResource, IDS_LESSTHAN).c_str()))); SendDlgItemMessage(hwndDlg, IDC_SIZECOMBO, CB_INSERTSTRING, static_cast(-1), reinterpret_cast(static_cast(TranslatedString(hResource, IDS_EQUALTO).c_str()))); SendDlgItemMessage(hwndDlg, IDC_SIZECOMBO, CB_INSERTSTRING, static_cast(-1), reinterpret_cast(static_cast(TranslatedString(hResource, IDS_GREATERTHAN).c_str()))); if (!m_bIncludeSubfoldersC) m_bIncludeSubfolders = bPortable ? g_iniFile.GetBoolValue(L"global", L"IncludeSubfolders", true) : !!static_cast(m_regIncludeSubfolders); if (!m_bIncludeSystemC) m_bIncludeSystem = bPortable ? g_iniFile.GetBoolValue(L"global", L"IncludeSystem", true) : !!static_cast(m_regIncludeSystem); if (!m_bIncludeHiddenC) m_bIncludeHidden = bPortable ? g_iniFile.GetBoolValue(L"global", L"IncludeHidden", false) : !!static_cast(m_regIncludeHidden); if (!m_bIncludeBinaryC) m_bIncludeBinaryC = bPortable ? g_iniFile.GetBoolValue(L"global", L"IncludeBinary", false) : !!static_cast(m_regIncludeBinary); if (!m_bCaseSensitiveC) m_bCaseSensitive = bPortable ? g_iniFile.GetBoolValue(L"global", L"CaseSensitive", false) : !!static_cast(m_regCaseSensitive); if (!m_bDotMatchesNewlineC) m_bDotMatchesNewline = bPortable ? g_iniFile.GetBoolValue(L"global", L"DotMatchesNewline", false) : !!static_cast(m_regDotMatchesNewline); if (!m_bCreateBackupC) m_bCreateBackup = bPortable ? g_iniFile.GetBoolValue(L"global", L"CreateBackup", false) : !!static_cast(m_regCreateBackup); if (!m_bCreateBackupInFoldersC) m_bCreateBackupInFolders = bPortable ? g_iniFile.GetBoolValue(L"settings", L"backupinfolder", false) : !!static_cast(m_regBackupInFolder); if (!m_bWholeWordsC) m_bWholeWords = bPortable ? g_iniFile.GetBoolValue(L"global", L"WholeWords", false) : !!static_cast(m_regWholeWords); if (!m_bUTF8C) { m_bUTF8 = bPortable ? g_iniFile.GetBoolValue(L"global", L"UTF8", false) : !!static_cast(m_regUTF8); m_bForceBinary = bPortable ? !!g_iniFile.GetBoolValue(L"global", L"Binary", false) : !!static_cast(m_regBinary); } if (!m_bDotMatchesNewlineC) m_bDotMatchesNewline = bPortable ? g_iniFile.GetBoolValue(L"global", L"DotMatchesNewline", false) : !!static_cast(m_regDotMatchesNewline); if (!m_bSizeC) { m_bAllSize = bPortable ? g_iniFile.GetBoolValue(L"global", L"AllSize", false) : !!static_cast(m_regAllSize); m_sizeCmp = bPortable ? g_iniFile.GetLongValue(L"global", L"SizeCombo", 0) : (int)static_cast(m_regSizeCombo); } if (!m_bDateLimitC) { m_dateLimit = bPortable ? g_iniFile.GetLongValue(L"global", L"DateLimit", 0) : (int)static_cast(m_regDateLimit); m_date1.dwLowDateTime = bPortable ? g_iniFile.GetLongValue(L"global", L"Date1Low", 0) : static_cast(m_regDate1Low); m_date1.dwHighDateTime = bPortable ? g_iniFile.GetLongValue(L"global", L"Date1High", 0) : static_cast(m_regDate1High); m_date2.dwLowDateTime = bPortable ? g_iniFile.GetLongValue(L"global", L"Date2Low", 0) : static_cast(m_regDate2Low); m_date2.dwHighDateTime = bPortable ? g_iniFile.GetLongValue(L"global", L"Date2High", 0) : static_cast(m_regDate2High); } m_bUseRegex = (bPortable ? g_iniFile.GetBoolValue(L"global", L"UseRegex", false) : static_cast(m_regUseRegex)); m_bStayOnTop = (BYTE)(bPortable ? g_iniFile.GetBoolValue(L"global", L"StayOnTop", false) : static_cast(m_regStayOnTop)); m_OpacityNoFocus = (BYTE)(bPortable ? g_iniFile.GetLongValue(L"global", L"OpacityNoFocus", 100) : static_cast(m_regOpacityNoFocus)); m_OpacityNoFocus = (m_OpacityNoFocus > 100) ? 100 : m_OpacityNoFocus; SendDlgItemMessage(hwndDlg, IDC_SIZECOMBO, CB_SETCURSEL, m_sizeCmp, 0); SendDlgItemMessage(hwndDlg, IDC_INCLUDESUBFOLDERS, BM_SETCHECK, m_bIncludeSubfolders ? BST_CHECKED : BST_UNCHECKED, 0); SendDlgItemMessage(hwndDlg, IDC_CREATEBACKUP, BM_SETCHECK, m_bCreateBackup ? BST_CHECKED : BST_UNCHECKED, 0); SendDlgItemMessage(hwndDlg, IDC_UTF8, BM_SETCHECK, m_bUTF8 ? BST_CHECKED : BST_UNCHECKED, 0); SendDlgItemMessage(hwndDlg, IDC_BINARY, BM_SETCHECK, m_bForceBinary ? BST_CHECKED : BST_UNCHECKED, 0); SendDlgItemMessage(hwndDlg, IDC_INCLUDESYSTEM, BM_SETCHECK, m_bIncludeSystem ? BST_CHECKED : BST_UNCHECKED, 0); SendDlgItemMessage(hwndDlg, IDC_INCLUDEHIDDEN, BM_SETCHECK, m_bIncludeHidden ? BST_CHECKED : BST_UNCHECKED, 0); SendDlgItemMessage(hwndDlg, IDC_INCLUDEBINARY, BM_SETCHECK, m_bIncludeBinary ? BST_CHECKED : BST_UNCHECKED, 0); SendDlgItemMessage(hwndDlg, IDC_CASE_SENSITIVE, BM_SETCHECK, m_bCaseSensitive ? BST_CHECKED : BST_UNCHECKED, 0); SendDlgItemMessage(hwndDlg, IDC_DOTMATCHNEWLINE, BM_SETCHECK, m_bDotMatchesNewline ? BST_CHECKED : BST_UNCHECKED, 0); CheckRadioButton(hwndDlg, IDC_REGEXRADIO, IDC_TEXTRADIO, (bPortable ? g_iniFile.GetLongValue(L"global", L"UseRegex", 0) : static_cast(m_regUseRegex)) ? IDC_REGEXRADIO : IDC_TEXTRADIO); CheckRadioButton(hwndDlg, IDC_ALLSIZERADIO, IDC_SIZERADIO, m_bAllSize ? IDC_ALLSIZERADIO : IDC_SIZERADIO); DialogEnableWindow(IDC_SIZEEDIT, !m_bAllSize); DialogEnableWindow(IDC_SIZECOMBO, !m_bAllSize); CheckRadioButton(hwndDlg, IDC_FILEPATTERNREGEX, IDC_FILEPATTERNTEXT, m_bUseRegexForPaths ? IDC_FILEPATTERNREGEX : IDC_FILEPATTERNTEXT); SendDlgItemMessage(hwndDlg, IDC_WHOLEWORDS, BM_SETCHECK, m_bWholeWords ? BST_CHECKED : BST_UNCHECKED, 0); DialogEnableWindow(IDC_WHOLEWORDS, IsDlgButtonChecked(hwndDlg, IDC_TEXTRADIO)); if (!m_searchString.empty()) CheckRadioButton(*this, IDC_REGEXRADIO, IDC_TEXTRADIO, m_bUseRegex ? IDC_REGEXRADIO : IDC_TEXTRADIO); DialogEnableWindow(IDC_TESTREGEX, !IsDlgButtonChecked(*this, IDC_TEXTRADIO)); DialogEnableWindow(IDC_ADDTOBOOKMARKS, FALSE); DialogEnableWindow(IDC_EXCLUDEDIRSPATTERN, !!m_bIncludeSubfolders); ::SetDlgItemText(*this, IDOK, TranslatedString(hResource, IDS_SEARCH).c_str()); if (!m_showContentSet) { if (bPortable) { m_showContent = g_iniFile.GetBoolValue(L"global", L"showcontent", false); } else { m_showContent = static_cast(m_regShowContent) != 0; } } CheckRadioButton(*this, IDC_RESULTFILES, IDC_RESULTCONTENT, m_showContent ? IDC_RESULTCONTENT : IDC_RESULTFILES); CheckRadioButton(hwndDlg, IDC_RADIO_DATE_ALL, IDC_RADIO_DATE_BETWEEN, m_dateLimit + IDC_RADIO_DATE_ALL); SYSTEMTIME sysTime{}; auto hTime1 = GetDlgItem(hwndDlg, IDC_DATEPICK1); FileTimeToSystemTime(&m_date1, &sysTime); DateTime_SetSystemtime(hTime1, GDT_VALID, &sysTime); auto hTime2 = GetDlgItem(hwndDlg, IDC_DATEPICK2); FileTimeToSystemTime(&m_date2, &sysTime); DateTime_SetSystemtime(hTime2, GDT_VALID, &sysTime); ShowWindow(GetDlgItem(*this, IDC_DATEPICK2), (m_dateLimit == IDC_RADIO_DATE_BETWEEN - IDC_RADIO_DATE_ALL) ? SW_SHOW : SW_HIDE); ShowWindow(GetDlgItem(*this, IDC_DATEPICK1), (m_dateLimit != 0) ? SW_SHOW : SW_HIDE); SetFocus(GetDlgItem(hwndDlg, IDC_SEARCHTEXT)); m_resizer.Init(hwndDlg); m_resizer.UseSizeGrip(!CTheme::Instance().IsDarkTheme()); m_resizer.AddControl(hwndDlg, IDC_HELPLABEL, RESIZER_TOPLEFT); m_resizer.AddControl(hwndDlg, IDC_ABOUTLINK, RESIZER_TOPRIGHT); m_resizer.AddControl(hwndDlg, IDC_GROUPSEARCHIN, RESIZER_TOPLEFTRIGHT); m_resizer.AddControl(hwndDlg, IDC_PATHMRU, RESIZER_TOPLEFT); m_resizer.AddControl(hwndDlg, IDC_SEARCHPATH, RESIZER_TOPLEFTRIGHT); m_resizer.AddControl(hwndDlg, IDC_NEWINSTANCE, RESIZER_TOPRIGHT); m_resizer.AddControl(hwndDlg, IDC_SEARCHPATHBROWSE, RESIZER_TOPRIGHT); m_resizer.AddControl(hwndDlg, IDC_GROUPSEARCHFOR, RESIZER_TOPLEFTRIGHT); m_resizer.AddControl(hwndDlg, IDC_REGEXRADIO, RESIZER_TOPLEFT); m_resizer.AddControl(hwndDlg, IDC_TEXTRADIO, RESIZER_TOPLEFT); m_resizer.AddControl(hwndDlg, IDC_WHOLEWORDS, RESIZER_TOPLEFT); m_resizer.AddControl(hwndDlg, IDC_SEARCHFORLABEL, RESIZER_TOPLEFT); m_resizer.AddControl(hwndDlg, IDC_SEARCHTEXT, RESIZER_TOPLEFTRIGHT); m_resizer.AddControl(hwndDlg, IDC_EDITMULTILINE1, RESIZER_TOPRIGHT); m_resizer.AddControl(hwndDlg, IDC_REPLACEWITHLABEL, RESIZER_TOPLEFT); m_resizer.AddControl(hwndDlg, IDC_REPLACETEXT, RESIZER_TOPLEFTRIGHT); m_resizer.AddControl(hwndDlg, IDC_EDITMULTILINE2, RESIZER_TOPRIGHT); m_resizer.AddControl(hwndDlg, IDC_CASE_SENSITIVE, RESIZER_TOPLEFT); m_resizer.AddControl(hwndDlg, IDC_DOTMATCHNEWLINE, RESIZER_TOPLEFT); m_resizer.AddControl(hwndDlg, IDC_REGEXOKLABEL, RESIZER_TOPRIGHT); m_resizer.AddControl(hwndDlg, IDC_CREATEBACKUP, RESIZER_TOPLEFT); m_resizer.AddControl(hwndDlg, IDC_UTF8, RESIZER_TOPLEFT); m_resizer.AddControl(hwndDlg, IDC_BINARY, RESIZER_TOPLEFTRIGHT); m_resizer.AddControl(hwndDlg, IDC_TESTREGEX, RESIZER_TOPLEFT); m_resizer.AddControl(hwndDlg, IDC_ADDTOBOOKMARKS, RESIZER_TOPLEFT); m_resizer.AddControl(hwndDlg, IDC_BOOKMARKS, RESIZER_TOPLEFT); m_resizer.AddControl(hwndDlg, IDC_UPDATELINK, RESIZER_TOPRIGHT); m_resizer.AddControl(hwndDlg, IDC_GROUPLIMITSEARCH, RESIZER_TOPLEFTRIGHT); m_resizer.AddControl(hwndDlg, IDC_ALLSIZERADIO, RESIZER_TOPLEFT); m_resizer.AddControl(hwndDlg, IDC_SIZERADIO, RESIZER_TOPLEFT); m_resizer.AddControl(hwndDlg, IDC_SIZECOMBO, RESIZER_TOPLEFT); m_resizer.AddControl(hwndDlg, IDC_SIZEEDIT, RESIZER_TOPLEFT); m_resizer.AddControl(hwndDlg, IDC_KBTEXT, RESIZER_TOPLEFT); m_resizer.AddControl(hwndDlg, IDC_RADIO_DATE_ALL, RESIZER_TOPLEFT); m_resizer.AddControl(hwndDlg, IDC_RADIO_DATE_NEWER, RESIZER_TOPLEFT); m_resizer.AddControl(hwndDlg, IDC_RADIO_DATE_OLDER, RESIZER_TOPLEFT); m_resizer.AddControl(hwndDlg, IDC_RADIO_DATE_BETWEEN, RESIZER_TOPLEFT); m_resizer.AddControl(hwndDlg, IDC_DATEPICK1, RESIZER_TOPLEFT); m_resizer.AddControl(hwndDlg, IDC_DATEPICK2, RESIZER_TOPLEFT); m_resizer.AddControl(hwndDlg, IDC_INCLUDESYSTEM, RESIZER_TOPLEFT); m_resizer.AddControl(hwndDlg, IDC_INCLUDEHIDDEN, RESIZER_TOPLEFT); m_resizer.AddControl(hwndDlg, IDC_INCLUDESUBFOLDERS, RESIZER_TOPLEFT); m_resizer.AddControl(hwndDlg, IDC_INCLUDEBINARY, RESIZER_TOPLEFT); m_resizer.AddControl(hwndDlg, IDC_PATTERNLABEL, RESIZER_TOPLEFT); m_resizer.AddControl(hwndDlg, IDC_PATTERN, RESIZER_TOPLEFTRIGHT); m_resizer.AddControl(hwndDlg, IDC_PATTERNMRU, RESIZER_TOPRIGHT); m_resizer.AddControl(hwndDlg, IDC_EXCLUDE_DIRS_PATTERNLABEL, RESIZER_TOPLEFT); m_resizer.AddControl(hwndDlg, IDC_EXCLUDEDIRSPATTERN, RESIZER_TOPLEFTRIGHT); m_resizer.AddControl(hwndDlg, IDC_EXCLUDEDIRMRU, RESIZER_TOPRIGHT); m_resizer.AddControl(hwndDlg, IDC_FILEPATTERNREGEX, RESIZER_TOPLEFT); m_resizer.AddControl(hwndDlg, IDC_FILEPATTERNTEXT, RESIZER_TOPLEFT); m_resizer.AddControl(hwndDlg, IDC_SETTINGSBUTTON, RESIZER_TOPLEFT); m_resizer.AddControl(hwndDlg, IDC_PROGRESS, RESIZER_TOPLEFTRIGHT); m_resizer.AddControl(hwndDlg, IDC_REPLACE, RESIZER_TOPRIGHT); m_resizer.AddControl(hwndDlg, IDOK, RESIZER_TOPRIGHT); m_resizer.AddControl(hwndDlg, IDC_GROUPSEARCHRESULTS, RESIZER_TOPLEFTBOTTOMRIGHT); m_resizer.AddControl(hwndDlg, IDC_RESULTLIST, RESIZER_TOPLEFTBOTTOMRIGHT); m_resizer.AddControl(hwndDlg, IDC_SEARCHINFOLABEL, RESIZER_BOTTOMLEFTRIGHT); m_resizer.AddControl(hwndDlg, IDC_RESULTFILES, RESIZER_TOPLEFT); m_resizer.AddControl(hwndDlg, IDC_RESULTCONTENT, RESIZER_TOPLEFT); m_resizer.AddControl(hwndDlg, IDC_EXPORT, RESIZER_TOPLEFT); InitDialog(hwndDlg, IDI_GREPWIN); WINDOWPLACEMENT wpl = {0}; DWORD size = sizeof(wpl); std::wstring winPosKey = L"windowpos_" + GetMonitorSetupHash(); if (bPortable) { std::wstring sPos = g_iniFile.GetValue(L"global", winPosKey.c_str(), L""); if (!sPos.empty()) { auto read = swscanf_s(sPos.c_str(), L"%d;%d;%d;%d;%d;%d;%d;%d;%d;%d", &wpl.flags, &wpl.showCmd, &wpl.ptMinPosition.x, &wpl.ptMinPosition.y, &wpl.ptMaxPosition.x, &wpl.ptMaxPosition.y, &wpl.rcNormalPosition.left, &wpl.rcNormalPosition.top, &wpl.rcNormalPosition.right, &wpl.rcNormalPosition.bottom); wpl.length = sizeof(wpl); if (read == 10) SetWindowPlacement(*this, &wpl); else ShowWindow(*this, SW_SHOW); } else ShowWindow(*this, SW_SHOW); } else { if (SHGetValue(HKEY_CURRENT_USER, L"Software\\grepWinNP3", winPosKey.c_str(), REG_NONE, &wpl, &size) == ERROR_SUCCESS) SetWindowPlacement(*this, &wpl); else ShowWindow(*this, SW_SHOW); } SetWindowPos(*this, m_bStayOnTop ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); CheckMenuItem(GetSystemMenu(*this, FALSE), ID_STAY_ON_TOP, m_bStayOnTop ? MF_BYCOMMAND | MF_CHECKED : MF_BYCOMMAND | MF_UNCHECKED); InitResultList(); #ifdef NP3_ALLOW_UPDATE bool doCheck = true; if (bPortable) doCheck = g_iniFile.GetBoolValue(L"global", L"CheckForUpdates", true)); else doCheck = !!static_cast(CRegStdDWORD(L"Software\\grepWin\\CheckForUpdates", 1)); if (doCheck) { m_updateCheckThread = std::move(std::thread([&]() { CheckForUpdates(); })); ShowUpdateAvailable(); } #endif if (hInitProtection) CloseHandle(hInitProtection); hInitProtection = nullptr; switch (m_executeImmediately) { case ExecuteAction::Search: DoCommand(IDOK, 0); break; case ExecuteAction::Replace: DoCommand(IDC_REPLACE, 0); break; case ExecuteAction::Capture: DoCommand(IDC_CAPTURESEARCH, 0); break; case ExecuteAction::None: default: break; } } return FALSE; case WM_CLOSE: { #ifdef NP3_ALLOW_UPDATE if (m_updateCheckThread.joinable()) m_updateCheckThread.join(); #endif if (IsEvaluationThreadRunning()) InterlockedExchange(&s_Cancelled, TRUE); else { SaveSettings(); if (!m_bNoSaveSettings) { m_autoCompleteFilePatterns.Save(); m_autoCompleteSearchPatterns.Save(); m_autoCompleteReplacePatterns.Save(); m_autoCompleteSearchPaths.Save(); } EndDialog(*this, IDCANCEL); } } break; case WM_DESTROY: CTheme::Instance().RemoveRegisteredCallback(m_themeCallbackId); break; case WM_COMMAND: return DoCommand(LOWORD(wParam), HIWORD(wParam)); case WM_CONTEXTMENU: { if (reinterpret_cast(wParam) == GetDlgItem(*this, IDC_RESULTLIST)) { ShowContextMenu(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); } } break; case WM_NOTIFY: { if (reinterpret_cast(lParam)->code == TTN_GETDISPINFO) { auto lpnmtdi = reinterpret_cast(lParam); auto buf = GetDlgItemText(IDC_REPLACETEXT); m_toolTipReplaceString = ExpandString(buf.get()); lpnmtdi->lpszText = m_toolTipReplaceString.data(); } switch (wParam) { case IDC_RESULTLIST: DoListNotify(reinterpret_cast(lParam)); break; case IDOK: switch (reinterpret_cast(lParam)->code) { case BCN_DROPDOWN: { const NMBCDROPDOWN* pDropDown = reinterpret_cast(lParam); // Get screen coordinates of the button. POINT pt; pt.x = pDropDown->rcButton.left; pt.y = pDropDown->rcButton.bottom; ClientToScreen(pDropDown->hdr.hwndFrom, &pt); // Create a menu and add items. HMENU hSplitMenu = CreatePopupMenu(); if (!hSplitMenu) break; OnOutOfScope(DestroyMenu(hSplitMenu)); if (pDropDown->hdr.hwndFrom == GetDlgItem(*this, IDOK)) { auto buf = GetDlgItemText(IDC_SEARCHPATH); bool bIsDir = !!PathIsDirectory(buf.get()); if ((!bIsDir) && wcschr(buf.get(), '|')) bIsDir = true; // assume directories in case of multiple paths auto sInverseSearch = TranslatedString(hResource, IDS_INVERSESEARCH); auto sSearchInFoundFiles = TranslatedString(hResource, IDS_SEARCHINFOUNDFILES); auto sCaptureSearch = TranslatedString(hResource, IDS_CAPTURESEARCH); AppendMenu(hSplitMenu, bIsDir ? MF_STRING : MF_STRING | MF_DISABLED, IDC_INVERSESEARCH, sInverseSearch.c_str()); AppendMenu(hSplitMenu, m_items.empty() ? MF_STRING | MF_DISABLED : MF_STRING, IDC_SEARCHINFOUNDFILES, sSearchInFoundFiles.c_str()); AppendMenu(hSplitMenu, GetDlgItemTextLength(IDC_REPLACETEXT) ? MF_STRING : MF_STRING | MF_DISABLED, IDC_CAPTURESEARCH, sCaptureSearch.c_str()); } // Display the menu. TrackPopupMenu(hSplitMenu, TPM_LEFTALIGN | TPM_TOPALIGN, pt.x, pt.y, 0, *this, nullptr); return TRUE; } default: break; } break; #ifdef NP3_ALLOW_UPDATE case IDC_UPDATELINK: switch (reinterpret_cast(lParam)->code) { case NM_CLICK: case NM_RETURN: { PNMLINK pNMLink = reinterpret_cast(lParam); LITEM item = pNMLink->item; if (item.iLink == 0) { ShellExecute(*this, L"open", item.szUrl, nullptr, nullptr, SW_SHOW); } break; } default: break; } break; #endif case IDC_ABOUTLINK: switch (reinterpret_cast(lParam)->code) { case NM_CLICK: case NM_RETURN: { PNMLINK pNMLink = reinterpret_cast(lParam); LITEM item = pNMLink->item; if (item.iLink == 0) { CAboutDlg dlgAbout(*this); dlgAbout.DoModal(hResource, IDD_ABOUT, *this); } break; } default: break; } break; } } break; case WM_SIZE: { m_resizer.DoResize(LOWORD(lParam), HIWORD(lParam)); } break; case WM_GETMINMAXINFO: { MINMAXINFO* mmi = reinterpret_cast(lParam); mmi->ptMinTrackSize.x = m_resizer.GetDlgRect()->right; mmi->ptMinTrackSize.y = m_resizer.GetDlgRect()->bottom; return 0; } case WM_DPICHANGED: { const RECT* rect = reinterpret_cast(lParam); SetWindowPos(*this, nullptr, rect->left, rect->top, rect->right - rect->left, rect->bottom - rect->top, SWP_NOZORDER | SWP_NOACTIVATE); ::RedrawWindow(*this, nullptr, nullptr, RDW_FRAME | RDW_INVALIDATE | RDW_ERASE | RDW_INTERNALPAINT | RDW_ALLCHILDREN | RDW_UPDATENOW); } break; case WM_SETCURSOR: { if (IsEvaluationThreadRunning() && LOWORD(lParam) == 1) { SetCursor(LoadCursor(nullptr, IDC_APPSTARTING)); return TRUE; } return FALSE; } case SEARCH_START: { m_totalItems = 0; m_searchedItems = 0; m_totalMatches = 0; m_selectedItems = 0; UpdateInfoLabel(); SetTimer(*this, LABELUPDATETIMER, 200, nullptr); SendDlgItemMessage(*this, IDC_PROGRESS, PBM_SETSTATE, PBST_NORMAL, 0); SendDlgItemMessage(*this, IDC_PROGRESS, PBM_SETMARQUEE, 1, 0); } break; //case SEARCH_FOUND: case SEARCH_PROGRESS: { const CSearchInfo* const sInfo = (const CSearchInfo* const)(wParam); int const nFound = static_cast(lParam); m_totalMatches += static_cast(sInfo->matchCount); if ((nFound > 0) || (m_searchString.empty()) || (sInfo->readError) || IsNOTSearch()) { AddFoundEntry(sInfo); UpdateInfoLabel(); } if (nFound >= 0) m_searchedItems++; m_totalItems++; } break; case SEARCH_END: { AddFoundEntry(nullptr, true); AutoSizeAllColumns(); UpdateInfoLabel(); ::SetDlgItemText(*this, IDOK, TranslatedString(hResource, IDS_SEARCH).c_str()); DialogEnableWindow(IDC_RESULTFILES, true); DialogEnableWindow(IDC_RESULTCONTENT, true); ShowWindow(GetDlgItem(*this, IDC_PROGRESS), SW_HIDE); SendDlgItemMessage(*this, IDC_PROGRESS, PBM_SETMARQUEE, 0, 0); if (m_pTaskbarList) m_pTaskbarList->SetProgressState(*this, TBPF_NOPROGRESS); ShowWindow(GetDlgItem(*this, IDC_EXPORT), m_items.empty() ? SW_HIDE : SW_SHOW); KillTimer(*this, LABELUPDATETIMER); } break; case WM_TIMER: { if (wParam == LABELUPDATETIMER) { AddFoundEntry(nullptr, true); UpdateInfoLabel(); } } break; case WM_HELP: { if (m_rtfDialog == nullptr) { m_rtfDialog = std::make_unique(); } m_rtfDialog->ShowModeless(g_hInst, *this, "grepWinNP3 help", IDR_INFODLG, L"RTF", IDI_GREPWIN, 400, 600); } break; case WM_SYSCOMMAND: { switch (wParam & 0xFFFF) { case ID_ABOUTBOX: { CAboutDlg dlgAbout(*this); dlgAbout.DoModal(hResource, IDD_ABOUT, *this); } break; case ID_CLONE: { CloneWindow(); } break; case ID_STAY_ON_TOP: { m_bStayOnTop = !m_bStayOnTop; // toggle SetWindowPos(*this, m_bStayOnTop ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); CheckMenuItem(GetSystemMenu(*this, FALSE), ID_STAY_ON_TOP, m_bStayOnTop ? MF_BYCOMMAND | MF_CHECKED : MF_BYCOMMAND | MF_UNCHECKED); } break; default: break; } } break; case WM_COPYDATA: { if (lParam) { auto pCopyData = (PCOPYDATASTRUCT)lParam; switch(pCopyData->dwData) { case GREPWINNP3_CPYDAT: { const wchar_t* const searchPath = ((CopyData_t*)(pCopyData->lpData))->searchPath; if (searchPath && (searchPath[0] != L'\0')) { m_searchPath.clear(); m_searchPath = searchPath; SetDlgItemText(hwndDlg, IDC_SEARCHPATH, m_searchPath.c_str()); } const wchar_t* const searchFor = ((CopyData_t*)(pCopyData->lpData))->searchFor; if (searchFor && (searchFor[0] != L'\0')) { m_searchString.clear(); m_searchString = searchFor; SetDlgItemText(hwndDlg, IDC_SEARCHTEXT, m_searchString.c_str()); } g_startTime = GetTickCount64(); } break; default: break; } } return TRUE; } case WM_EDITDBLCLICK: { switch (wParam) { case IDC_PATTERN: { m_autoCompleteFilePatterns.SetOptions(ACO_UPDOWNKEYDROPSLIST | ACO_AUTOSUGGEST | ACO_NOPREFIXFILTERING); ::SetFocus(GetDlgItem(*this, IDC_PATTERN)); SendDlgItemMessage(*this, IDC_PATTERN, WM_KEYDOWN, VK_DOWN, 0); } break; case IDC_EXCLUDEDIRSPATTERN: { m_autoCompleteExcludeDirsPatterns.SetOptions(ACO_UPDOWNKEYDROPSLIST | ACO_AUTOSUGGEST | ACO_NOPREFIXFILTERING); ::SetFocus(GetDlgItem(*this, IDC_EXCLUDEDIRSPATTERN)); SendDlgItemMessage(*this, IDC_EXCLUDEDIRSPATTERN, WM_KEYDOWN, VK_DOWN, 0); } break; case IDC_SEARCHTEXT: { m_autoCompleteSearchPatterns.SetOptions(ACO_UPDOWNKEYDROPSLIST | ACO_AUTOSUGGEST | ACO_NOPREFIXFILTERING); ::SetFocus(GetDlgItem(*this, IDC_SEARCHTEXT)); SendDlgItemMessage(*this, IDC_SEARCHTEXT, WM_KEYDOWN, VK_DOWN, 0); } break; case IDC_REPLACETEXT: { m_autoCompleteReplacePatterns.SetOptions(ACO_UPDOWNKEYDROPSLIST | ACO_AUTOSUGGEST | ACO_NOPREFIXFILTERING); ::SetFocus(GetDlgItem(*this, IDC_REPLACETEXT)); SendDlgItemMessage(*this, IDC_REPLACETEXT, WM_KEYDOWN, VK_DOWN, 0); } break; case IDC_SEARCHPATH: { m_autoCompleteSearchPaths.SetOptions(ACO_UPDOWNKEYDROPSLIST | ACO_AUTOSUGGEST | ACO_NOPREFIXFILTERING); ::SetFocus(GetDlgItem(*this, IDC_SEARCHPATH)); SendDlgItemMessage(*this, IDC_SEARCHPATH, WM_KEYDOWN, VK_DOWN, 0); } break; default: break; } return TRUE; } case WM_GREPWIN_THREADEND: { if (m_endDialog) EndDialog(m_hwnd, IDOK); } break; case WM_BOOKMARK: { if (m_bookmarksDlg) { m_searchString = m_bookmarksDlg->GetSelectedSearchString(); m_replaceString = m_bookmarksDlg->GetSelectedReplaceString(); m_bUseRegex = m_bookmarksDlg->GetSelectedUseRegex(); m_bCaseSensitive = m_bookmarksDlg->GetSelectedSearchCase(); m_bDotMatchesNewline = m_bookmarksDlg->GetSelectedDotMatchNewline(); m_bCreateBackup = m_bookmarksDlg->GetSelectedBackup(); m_bWholeWords = m_bookmarksDlg->GetSelectedWholeWords(); m_bUTF8 = m_bookmarksDlg->GetSelectedTreatAsUtf8(); m_bForceBinary = m_bookmarksDlg->GetSelectedTreatAsBinary(); m_bIncludeSystem = m_bookmarksDlg->GetSelectedIncludeSystem(); m_bIncludeSubfolders = m_bookmarksDlg->GetSelectedIncludeFolder(); m_bIncludeHidden = m_bookmarksDlg->GetSelectedIncludeHidden(); m_bIncludeBinary = m_bookmarksDlg->GetSelectedIncludeBinary(); m_excludeDirsPatternRegex = m_bookmarksDlg->GetSelectedExcludeDirs(); m_patternRegex = m_bookmarksDlg->GetSelectedFileMatch(); m_bUseRegexForPaths = m_bookmarksDlg->GetSelectedFileMatchRegex(); if (!m_bookmarksDlg->GetPath().empty()) { m_searchPath = m_bookmarksDlg->GetPath(); SetDlgItemText(*this, IDC_SEARCHPATH, m_searchPath.c_str()); } SetDlgItemText(*this, IDC_SEARCHTEXT, m_searchString.c_str()); SetDlgItemText(*this, IDC_REPLACETEXT, m_replaceString.c_str()); CheckRadioButton(*this, IDC_REGEXRADIO, IDC_TEXTRADIO, m_bUseRegex ? IDC_REGEXRADIO : IDC_TEXTRADIO); DialogEnableWindow(IDC_TESTREGEX, !IsDlgButtonChecked(*this, IDC_TEXTRADIO)); SendDlgItemMessage(*this, IDC_INCLUDESUBFOLDERS, BM_SETCHECK, m_bIncludeSubfolders ? BST_CHECKED : BST_UNCHECKED, 0); SendDlgItemMessage(*this, IDC_CREATEBACKUP, BM_SETCHECK, m_bCreateBackup ? BST_CHECKED : BST_UNCHECKED, 0); SendDlgItemMessage(*this, IDC_UTF8, BM_SETCHECK, m_bUTF8 ? BST_CHECKED : BST_UNCHECKED, 0); SendDlgItemMessage(*this, IDC_BINARY, BM_SETCHECK, m_bForceBinary ? BST_CHECKED : BST_UNCHECKED, 0); SendDlgItemMessage(*this, IDC_INCLUDESYSTEM, BM_SETCHECK, m_bIncludeSystem ? BST_CHECKED : BST_UNCHECKED, 0); SendDlgItemMessage(*this, IDC_INCLUDEHIDDEN, BM_SETCHECK, m_bIncludeHidden ? BST_CHECKED : BST_UNCHECKED, 0); SendDlgItemMessage(*this, IDC_INCLUDEBINARY, BM_SETCHECK, m_bIncludeBinary ? BST_CHECKED : BST_UNCHECKED, 0); SendDlgItemMessage(*this, IDC_CASE_SENSITIVE, BM_SETCHECK, m_bCaseSensitive ? BST_CHECKED : BST_UNCHECKED, 0); SendDlgItemMessage(*this, IDC_DOTMATCHNEWLINE, BM_SETCHECK, m_bDotMatchesNewline ? BST_CHECKED : BST_UNCHECKED, 0); SendDlgItemMessage(*this, IDC_WHOLEWORDS, BM_SETCHECK, m_bWholeWords ? BST_CHECKED : BST_UNCHECKED, 0); CheckRadioButton(*this, IDC_FILEPATTERNREGEX, IDC_FILEPATTERNTEXT, m_bUseRegexForPaths ? IDC_FILEPATTERNREGEX : IDC_FILEPATTERNTEXT); SetDlgItemText(*this, IDC_EXCLUDEDIRSPATTERN, m_excludeDirsPatternRegex.c_str()); SetDlgItemText(*this, IDC_PATTERN, m_patternRegex.c_str()); DialogEnableWindow(IDC_WHOLEWORDS, IsDlgButtonChecked(hwndDlg, IDC_TEXTRADIO)); } } break; case WM_ACTIVATE: switch (LOWORD(wParam)) { case WA_INACTIVE: SetTransparency((BYTE)MulDiv(m_OpacityNoFocus, 255, 100)); break; case WA_CLICKACTIVE: // mouse click activation case WA_ACTIVE: SetTransparency(ALPHA_OPAQUE); break; } break; default: return FALSE; } return FALSE; } LRESULT CSearchDlg::DoCommand(int id, int msg) { switch (id) { case IDC_REPLACE: case IDOK: case IDC_INVERSESEARCH: case IDC_SEARCHINFOUNDFILES: case IDC_CAPTURESEARCH: { if (IsSearchThreadRunning()) { InterlockedExchange(&s_Cancelled, TRUE); SendDlgItemMessage(*this, IDC_PROGRESS, PBM_SETSTATE, PBST_PAUSED, 0); } else { ::SetFocus(GetDlgItem(*this, IDOK)); if (!SaveSettings()) break; CStringUtils::rtrim(m_searchPath, L"\\/"); if (PathIsRelative(m_searchPath.c_str())) { ShowEditBalloon(IDC_SEARCHPATH, TranslatedString(hResource, IDS_ERR_INVALID_PATH).c_str(), TranslatedString(hResource, IDS_ERR_RELATIVEPATH).c_str()); break; } std::vector searchPaths; stringtok(searchPaths, m_searchPath, true); for (const auto& sp : searchPaths) { if (!PathFileExists(sp.c_str())) { ShowEditBalloon(IDC_SEARCHPATH, TranslatedString(hResource, IDS_ERR_INVALID_PATH).c_str(), TranslatedString(hResource, IDS_ERR_PATHNOTEXIST).c_str()); break; } } if ((id == IDC_SEARCHINFOUNDFILES) && (!m_items.empty())) { m_searchPath.clear(); for (const auto& item : m_items) { if (!m_searchPath.empty()) m_searchPath += L"|"; m_searchPath += item.filePath; } } std::vector searchpaths; stringtok(searchpaths, m_searchPath, true); bool ok = true; for (const auto& sp : searchpaths) { if (!PathFileExists(sp.c_str())) { ShowEditBalloon(IDC_SEARCHPATH, TranslatedString(hResource, IDS_ERR_INVALID_PATH).c_str(), TranslatedString(hResource, IDS_ERR_PATHNOTEXIST).c_str()); ok = false; break; } } if (!ok) break; m_searchedItems = 0; m_totalItems = 0; ShowWindow(GetDlgItem(*this, IDC_EXPORT), SW_HIDE); m_items.clear(); m_listItems.clear(); m_listItems.reserve(500000); s_BackupAndTmpFiles.clear(); HWND hListControl = GetDlgItem(*this, IDC_RESULTLIST); ListView_SetItemCount(hListControl, 0); DialogEnableWindow(IDC_RESULTFILES, false); DialogEnableWindow(IDC_RESULTCONTENT, false); m_autoCompleteFilePatterns.AddEntry(m_patternRegex.c_str()); m_autoCompleteExcludeDirsPatterns.AddEntry(m_excludeDirsPatternRegex.c_str()); m_autoCompleteSearchPatterns.AddEntry(m_searchString.c_str()); m_autoCompleteReplacePatterns.AddEntry(m_replaceString.c_str()); m_autoCompleteSearchPaths.AddEntry(m_searchPath.c_str()); if (!m_bNoSaveSettings) { m_autoCompleteFilePatterns.Save(); m_autoCompleteSearchPatterns.Save(); m_autoCompleteReplacePatterns.Save(); m_autoCompleteSearchPaths.Save(); } m_bReplace = id == IDC_REPLACE; if (m_bReplace && !m_bCreateBackup && m_bConfirmationOnReplace) { auto nowarnifnobackup = bPortable ? g_iniFile.GetBoolValue(L"settings", L"nowarnifnobackup", false) : static_cast(CRegStdDWORD(L"Software\\grepWinNP3\\nowarnifnobackup", FALSE)); if (!nowarnifnobackup) { auto msgText = CStringUtils::Format(static_cast(TranslatedString(hResource, IDS_REPLACECONFIRM).c_str()), m_searchString.c_str(), m_replaceString.empty() ? static_cast(TranslatedString(hResource, IDS_ANEMPTYSTRING).c_str()) : m_replaceString.c_str()); if (::MessageBox(*this, msgText.c_str(), L"grepWinNP3", MB_ICONQUESTION | MB_YESNO) != IDYES) { break; } } } if (m_bReplace && m_bUTF8) { auto utf8OptionText = GetDlgItemText(IDC_UTF8); auto msgText = CStringUtils::Format(TranslatedString(hResource, IDS_REPLACEUTF8).c_str(), utf8OptionText.get()); if (::MessageBox(*this, msgText.c_str(), L"grepWinNP3", MB_ICONWARNING | MB_YESNO | MB_DEFBUTTON2) != IDYES) { break; } } m_bConfirmationOnReplace = true; if (id == IDC_INVERSESEARCH) InterlockedExchange(&s_NOTSearch, TRUE); else InterlockedExchange(&s_NOTSearch, ((GetKeyState(VK_SHIFT) & 0x8000) != 0) ? TRUE : FALSE); if (id == IDC_CAPTURESEARCH) { m_bCaptureSearch = true; InterlockedExchange(&s_NOTSearch, FALSE); m_bReplace = false; } if (m_bReplace) { m_replaceString = ExpandString(m_replaceString); } if (m_searchString.empty() || IsNOTSearch()) { // switch to file view CheckRadioButton(*this, IDC_RESULTFILES, IDC_RESULTCONTENT, IDC_RESULTFILES); m_showContent = false; InitResultList(); } InterlockedExchange(&s_SearchThreadRunning, TRUE); InterlockedExchange(&s_Cancelled, FALSE); SetDlgItemText(*this, IDOK, TranslatedString(hResource, IDS_STOP).c_str()); SetDlgItemText(*this, IDOK, TranslatedString(hResource, IDS_STOP).c_str()); ShowWindow(GetDlgItem(*this, IDC_PROGRESS), SW_SHOW); SendDlgItemMessage(*this, IDC_PROGRESS, PBM_SETMARQUEE, 1, 0); if (m_pTaskbarList) m_pTaskbarList->SetProgressState(*this, TBPF_INDETERMINATE); // now start the thread which does the searching DWORD dwThreadId = 0; HANDLE hThread = CreateThread(nullptr, // no security attribute 0, // default stack size SearchThreadEntry, static_cast(this), // thread parameter 0, // not suspended &dwThreadId); // returns thread ID if (hThread != nullptr) { // Closing the handle of a running thread just decreases // the ref count for the thread object. CloseHandle(hThread); } else { InterlockedExchange(&s_Cancelled, TRUE); InterlockedExchange(&s_SearchThreadRunning, FALSE); ShowWindow(GetDlgItem(*this, IDC_PROGRESS), SW_HIDE); EnableWindow(GetDlgItem(*this, IDC_SETTINGSBUTTON), TRUE); } DWORD const nMax = std::thread::hardware_concurrency() << 2; DWORD const nOfWorker = bPortable ? g_iniFile.GetLongValue(L"global", L"MaxNumOfWorker", nMax) : static_cast(CRegStdDWORD(L"Software\\grepWinNP3\\MaxNumOfWorker", nMax)); if (nOfWorker > 1) { InterlockedExchange(&s_EvaluationThreadRunning, TRUE); // now start the thread which does result evaluation dwThreadId = 0; hThread = CreateThread( nullptr, // no security attribute 0, // default stack size EvaluationThreadEntry, (LPVOID)this, // thread parameter 0, // not suspended &dwThreadId); // returns thread ID if (hThread != nullptr) { // Closing the handle of a running thread just decreases // the ref count for the thread object. CloseHandle(hThread); } else { InterlockedExchange(&s_Cancelled, TRUE); SendMessage(*this, SEARCH_END, 0, 0); InterlockedExchange(&s_EvaluationThreadRunning, FALSE); ShowWindow(GetDlgItem(*this, IDC_PROGRESS), SW_HIDE); EnableWindow(GetDlgItem(*this, IDC_SETTINGSBUTTON), TRUE); } } } } break; case IDCANCEL: { #ifdef NP3_ALLOW_UPDATE if (m_updateCheckThread.joinable()) m_updateCheckThread.join(); #endif bool const escClose = bPortable ? g_iniFile.GetBoolValue(L"settings", L"escclose", false) : !!static_cast(CRegStdDWORD(L"Software\\grepWinNP3\\escclose", FALSE)); if (escClose) { if (IsEvaluationThreadRunning()) InterlockedExchange(&s_Cancelled, TRUE); else { SaveSettings(); if (!m_bNoSaveSettings) { m_autoCompleteFilePatterns.Save(); m_autoCompleteSearchPatterns.Save(); m_autoCompleteReplacePatterns.Save(); m_autoCompleteSearchPaths.Save(); } EndDialog(*this, IDCANCEL); } } } break; case IDC_RADIO_DATE_ALL: case IDC_RADIO_DATE_NEWER: case IDC_RADIO_DATE_OLDER: case IDC_RADIO_DATE_BETWEEN: { auto isBetween = IsDlgButtonChecked(*this, IDC_RADIO_DATE_BETWEEN) == BST_CHECKED; ShowWindow(GetDlgItem(*this, IDC_DATEPICK2), isBetween ? SW_SHOW : SW_HIDE); ShowWindow(GetDlgItem(*this, IDC_DATEPICK1), IsDlgButtonChecked(*this, IDC_RADIO_DATE_ALL) ? SW_HIDE : SW_SHOW); } break; case IDC_TESTREGEX: { // get all the information we need from the dialog auto buf = GetDlgItemText(IDC_SEARCHTEXT); m_searchString = buf.get(); buf = GetDlgItemText(IDC_REPLACETEXT); m_replaceString = buf.get(); SaveSettings(); CRegexTestDlg dlg(*this); dlg.bCaseSensitive = m_bCaseSensitive; dlg.bDotMatchesNewline = m_bDotMatchesNewline; dlg.SetStrings(m_searchString, m_replaceString); if (dlg.DoModal(hResource, IDD_REGEXTEST, *this) == IDOK) { m_searchString = dlg.GetSearchString(); m_replaceString = dlg.GetReplaceString(); SetDlgItemText(*this, IDC_SEARCHTEXT, m_searchString.c_str()); SetDlgItemText(*this, IDC_REPLACETEXT, m_replaceString.c_str()); } } break; case IDC_NEWINSTANCE: CloneWindow(); break; case IDC_SEARCHPATHBROWSE: { CBrowseFolder browse; auto path = GetDlgItemText(IDC_SEARCHPATH); if (!PathFileExists(path.get())) { auto ptr = wcsstr(path.get(), L"|"); if (ptr) *ptr = 0; else path.get()[0] = 0; } if (wcsstr(path.get(), L"..") != nullptr) { ShowEditBalloon(IDC_SEARCHPATH, TranslatedString(hResource, IDS_ERR_INVALID_PATH).c_str(), TranslatedString(hResource, IDS_ERR_RELATIVEPATH).c_str()); break; } std::vector paths; if (browse.Show(*this, paths, m_searchPath) == CBrowseFolder::RetVal::Ok) { std::wstring pathString; for (const auto& selPath : paths) { if (pathString.empty()) pathString = selPath; else { pathString += L"|"; pathString += selPath; } } SetDlgItemText(*this, IDC_SEARCHPATH, pathString.c_str()); m_searchPath = pathString; } } break; case IDC_SEARCHPATH: { if (msg == EN_CHANGE) { if (m_autoCompleteSearchPaths.GetOptions() & ACO_NOPREFIXFILTERING) m_autoCompleteSearchPaths.SetOptions(ACO_UPDOWNKEYDROPSLIST | ACO_AUTOSUGGEST); int len = GetDlgItemTextLength(IDC_SEARCHTEXT); auto buf = GetDlgItemText(IDC_SEARCHPATH); bool bIsDir = !!PathIsDirectory(buf.get()); if ((!bIsDir) && wcschr(buf.get(), '|')) bIsDir = true; // assume directories in case of multiple paths bool bIncludeSubfolders = (IsDlgButtonChecked(*this, IDC_INCLUDESUBFOLDERS) == BST_CHECKED); DialogEnableWindow(IDC_ALLSIZERADIO, bIsDir); DialogEnableWindow(IDC_SIZERADIO, bIsDir); DialogEnableWindow(IDC_SIZECOMBO, bIsDir && !m_bAllSize); DialogEnableWindow(IDC_SIZEEDIT, bIsDir && !m_bAllSize); DialogEnableWindow(IDC_INCLUDESYSTEM, bIsDir); DialogEnableWindow(IDC_INCLUDEHIDDEN, bIsDir); DialogEnableWindow(IDC_INCLUDESUBFOLDERS, bIsDir); DialogEnableWindow(IDC_INCLUDEBINARY, bIsDir && len > 0); DialogEnableWindow(IDC_PATTERN, bIsDir); DialogEnableWindow(IDC_EXCLUDEDIRSPATTERN, bIsDir || bIncludeSubfolders); DialogEnableWindow(IDC_FILEPATTERNREGEX, bIsDir); DialogEnableWindow(IDC_FILEPATTERNTEXT, bIsDir); // change the dialog title to "grepWinNP3 : search/path" wchar_t compactPath[100] = {0}; PathCompactPathEx(compactPath, buf.get(), 40, 0); wchar_t titleBuf[MAX_PATH] = {0}; swprintf_s(titleBuf, _countof(titleBuf), L"grepWinNP3 : %s", compactPath); SetWindowText(*this, titleBuf); } } break; case IDC_INCLUDESUBFOLDERS: { if (msg == BN_CLICKED) { auto buf = GetDlgItemText(IDC_SEARCHPATH); bool bIncludeSubfolders = (IsDlgButtonChecked(*this, IDC_INCLUDESUBFOLDERS) == BST_CHECKED); bool bIsDir = !!PathIsDirectory(buf.get()); if ((!bIsDir) && wcschr(buf.get(), '|')) bIsDir = true; // assume directories in case of multiple paths DialogEnableWindow(IDC_EXCLUDEDIRSPATTERN, bIsDir || bIncludeSubfolders); } } break; case IDC_SEARCHTEXT: { if (msg == EN_CHANGE) { if (m_autoCompleteSearchPatterns.GetOptions() & ACO_NOPREFIXFILTERING) m_autoCompleteSearchPatterns.SetOptions(ACO_UPDOWNKEYDROPSLIST | ACO_AUTOSUGGEST); int len = CheckRegex(); DialogEnableWindow(IDC_ADDTOBOOKMARKS, len > 0); DialogEnableWindow(IDC_INCLUDEBINARY, len > 0); } } break; case IDC_ALLSIZERADIO: case IDC_SIZERADIO: m_bAllSize = (IsDlgButtonChecked(*this, IDC_ALLSIZERADIO) == BST_CHECKED); DialogEnableWindow(IDC_SIZECOMBO, !m_bAllSize); DialogEnableWindow(IDC_SIZEEDIT, !m_bAllSize); break; case IDC_SIZEEDIT: break; case IDC_REGEXRADIO: case IDC_TEXTRADIO: { CheckRegex(); DialogEnableWindow(IDC_TESTREGEX, !IsDlgButtonChecked(*this, IDC_TEXTRADIO)); DialogEnableWindow(IDC_WHOLEWORDS, IsDlgButtonChecked(*this, IDC_TEXTRADIO)); } break; case IDC_FILEPATTERNTEXT: case IDC_FILEPATTERNREGEX: { m_bUseRegexForPaths = (IsDlgButtonChecked(*this, IDC_FILEPATTERNREGEX) == BST_CHECKED); } break; case IDC_ADDTOBOOKMARKS: { auto buf = GetDlgItemText(IDC_SEARCHTEXT); m_searchString = buf.get(); buf = GetDlgItemText(IDC_REPLACETEXT); m_replaceString = buf.get(); buf = GetDlgItemText(IDC_EXCLUDEDIRSPATTERN); m_excludeDirsPatternRegex = buf.get(); buf = GetDlgItemText(IDC_PATTERN); m_patternRegex = buf.get(); m_bUseRegex = (IsDlgButtonChecked(*this, IDC_REGEXRADIO) == BST_CHECKED); CNameDlg nameDlg(*this); if (nameDlg.DoModal(hResource, IDD_NAME, *this) == IDOK) { // add the bookmark CBookmarks bks; Bookmark bk; bk.Name = nameDlg.GetName(); bk.Path = nameDlg.IncludePath() ? m_searchPath : L""; bk.Search = m_searchString; bk.Replace = m_replaceString; bk.UseRegex = m_bUseRegex; bk.CaseSensitive = (IsDlgButtonChecked(*this, IDC_CASE_SENSITIVE) == BST_CHECKED); bk.DotMatchesNewline = (IsDlgButtonChecked(*this, IDC_DOTMATCHNEWLINE) == BST_CHECKED); bk.Backup = (IsDlgButtonChecked(*this, IDC_CREATEBACKUP) == BST_CHECKED); bk.Utf8 = (IsDlgButtonChecked(*this, IDC_UTF8) == BST_CHECKED); bk.IncludeSystem = (IsDlgButtonChecked(*this, IDC_INCLUDESYSTEM) == BST_CHECKED); bk.IncludeFolder = (IsDlgButtonChecked(*this, IDC_INCLUDESUBFOLDERS) == BST_CHECKED); bk.IncludeHidden = (IsDlgButtonChecked(*this, IDC_INCLUDEHIDDEN) == BST_CHECKED); bk.IncludeBinary = (IsDlgButtonChecked(*this, IDC_INCLUDEBINARY) == BST_CHECKED); bk.ExcludeDirs = m_excludeDirsPatternRegex; bk.FileMatch = m_patternRegex; bk.FileMatchRegex = (IsDlgButtonChecked(*this, IDC_FILEPATTERNREGEX) == BST_CHECKED); bks.Load(); bks.AddBookmark(bk); bks.Save(); } } break; case IDC_BOOKMARKS: { if (!m_bookmarksDlg) m_bookmarksDlg = std::make_unique(*this); else m_bookmarksDlg->InitBookmarks(); m_bookmarksDlg->ShowModeless(hResource, IDD_BOOKMARKS, *this); } break; case IDC_RESULTFILES: case IDC_RESULTCONTENT: { InitResultList(); FillResultList(); } break; case IDC_SETTINGSBUTTON: { CSettingsDlg dlgSettings(*this); dlgSettings.DoModal(hResource, IDD_SETTINGS, *this); if (bPortable) m_bCreateBackupInFolders = g_iniFile.GetBoolValue(L"settings", L"backupinfolder", false); else { m_regBackupInFolder.read(); m_bCreateBackupInFolders = !!static_cast(m_regBackupInFolder); } CTheme::Instance().SetFontForDialog(*this, CTheme::Instance().GetDlgFontFaceName(), CTheme::Instance().GetDlgFontSize()); CLanguage::Instance().TranslateWindow(*this); // re-apply, cause update problems? } break; case IDC_EDITMULTILINE1: case IDC_EDITMULTILINE2: { int uID = (id == IDC_EDITMULTILINE1 ? IDC_SEARCHTEXT : IDC_REPLACETEXT); auto buf = GetDlgItemText(static_cast(uID)); std::wstring ctrlText = buf.get(); // replace all \r\n strings with real CRLFs try { int ft = boost::regex::normal; boost::wregex expression = boost::wregex(L"\\\\r\\\\n", ft); boost::match_results whatC; boost::match_flag_type rFlags = boost::match_default | boost::format_all; ctrlText = regex_replace(ctrlText, expression, L"\\r\\n", rFlags); } catch (const std::exception&) { } CMultiLineEditDlg editDlg(*this); editDlg.SetString(ctrlText); if (editDlg.DoModal(hResource, IDD_MULTILINEEDIT, *this) == IDOK) { std::wstring text = editDlg.GetSearchString(); // replace all CRLFs with \r\n strings (literal) try { int ft = boost::regex::normal; boost::wregex expression = boost::wregex(L"\\r\\n", ft); boost::match_results whatC; boost::match_flag_type rFlags = boost::match_default | boost::format_all; text = regex_replace(text, expression, L"\\\\r\\\\n", rFlags); } catch (const std::exception&) { } SetDlgItemText(*this, static_cast(uID), text.c_str()); } ::SetFocus(GetDlgItem(*this, uID)); } break; case IDC_PATHMRU: { m_autoCompleteSearchPaths.SetOptions(ACO_UPDOWNKEYDROPSLIST | ACO_AUTOSUGGEST | ACO_NOPREFIXFILTERING); ::SetFocus(GetDlgItem(*this, IDC_SEARCHPATH)); SendDlgItemMessage(*this, IDC_SEARCHPATH, WM_KEYDOWN, VK_DOWN, 0); } break; case IDC_EXCLUDEDIRMRU: { m_autoCompleteExcludeDirsPatterns.SetOptions(ACO_UPDOWNKEYDROPSLIST | ACO_AUTOSUGGEST | ACO_NOPREFIXFILTERING); ::SetFocus(GetDlgItem(*this, IDC_EXCLUDEDIRSPATTERN)); SendDlgItemMessage(*this, IDC_EXCLUDEDIRSPATTERN, WM_KEYDOWN, VK_DOWN, 0); } break; case IDC_PATTERNMRU: { m_autoCompleteFilePatterns.SetOptions(ACO_UPDOWNKEYDROPSLIST | ACO_AUTOSUGGEST | ACO_NOPREFIXFILTERING); ::SetFocus(GetDlgItem(*this, IDC_PATTERN)); SendDlgItemMessage(*this, IDC_PATTERN, WM_KEYDOWN, VK_DOWN, 0); } break; case IDC_PATTERN: { if (msg == EN_CHANGE) { if (m_autoCompleteFilePatterns.GetOptions() & ACO_NOPREFIXFILTERING) m_autoCompleteFilePatterns.SetOptions(ACO_UPDOWNKEYDROPSLIST | ACO_AUTOSUGGEST); } } break; case IDC_EXCLUDEDIRSPATTERN: { if (msg == EN_CHANGE) { if (m_autoCompleteExcludeDirsPatterns.GetOptions() & ACO_NOPREFIXFILTERING) m_autoCompleteExcludeDirsPatterns.SetOptions(ACO_UPDOWNKEYDROPSLIST | ACO_AUTOSUGGEST); } } break; case IDC_REPLACETEXT: { if (msg == EN_CHANGE) { if (m_autoCompleteReplacePatterns.GetOptions() & ACO_NOPREFIXFILTERING) m_autoCompleteReplacePatterns.SetOptions(ACO_UPDOWNKEYDROPSLIST | ACO_AUTOSUGGEST); } } break; case IDC_EXPORT: { PreserveChdir keepCwd; IFileSaveDialogPtr pfd; HRESULT hr = pfd.CreateInstance(CLSID_FileSaveDialog, nullptr, CLSCTX_INPROC_SERVER); if (FailedShowMessage(hr)) break; // Set the dialog options DWORD dwOptions; hr = pfd->GetOptions(&dwOptions); if (FailedShowMessage(hr)) break; hr = pfd->SetOptions(dwOptions | FOS_FORCEFILESYSTEM | FOS_OVERWRITEPROMPT); if (FailedShowMessage(hr)) break; hr = pfd->SetTitle(TranslatedString(hResource, IDS_EXPORTTITLE).c_str()); if (FailedShowMessage(hr)) break; COMDLG_FILTERSPEC const aFileTypes[] = { {L"Text files", L"*.txt; *.lst"}, {L"All types", L"*.*"} }; hr = pfd->SetFileTypes(_countof(aFileTypes), aFileTypes); if (FailedShowMessage(hr)) break; hr = pfd->SetDefaultExtension(L"txt"); if (FailedShowMessage(hr)) break; hr = pfd->SetFileName(L"gw_search_results.txt"); if (FailedShowMessage(hr)) break; IFileDialogCustomizePtr pfdCustomize; hr = pfd.QueryInterface(IID_PPV_ARGS(&pfdCustomize)); if (SUCCEEDED(hr)) { bool exportpaths = bPortable ? g_iniFile.GetBoolValue(L"export", L"paths", false) : static_cast(CRegStdDWORD(L"Software\\grepWinNP3\\export_paths")) != 0; bool const exportlinenumbers = bPortable ? g_iniFile.GetBoolValue(L"export", L"linenumbers", false) : static_cast(CRegStdDWORD(L"Software\\grepWinNP3\\export_linenumbers")) != 0; bool const exportlinecontent = bPortable ? g_iniFile.GetBoolValue(L"export", L"linecontent", false) : static_cast(CRegStdDWORD(L"Software\\grepWinNP3\\export_linecontent")) != 0; if (!exportpaths && !exportlinenumbers && !exportlinecontent) exportpaths = true; pfdCustomize->AddCheckButton(101, TranslatedString(hResource, IDS_EXPORTPATHS).c_str(), exportpaths); pfdCustomize->AddCheckButton(102, TranslatedString(hResource, IDS_EXPORTMATCHLINENUMBER).c_str(), exportlinenumbers); pfdCustomize->AddCheckButton(103, TranslatedString(hResource, IDS_EXPORTMATCHLINECONTENT).c_str(), exportlinecontent); } // Show the save file dialog hr = pfd->Show(*this); if (hr == HRESULT_FROM_WIN32(ERROR_CANCELLED)) break; if (FailedShowMessage(hr)) break; IShellItemPtr psiResult = nullptr; hr = pfd->GetResult(&psiResult); if (FailedShowMessage(hr)) break; PWSTR pszPath = nullptr; hr = psiResult->GetDisplayName(SIGDN_FILESYSPATH, &pszPath); if (FailedShowMessage(hr)) break; std::wstring path = pszPath; CoTaskMemFree(pszPath); bool includePaths = true; bool includeMatchLineNumbers = false; bool includeMatchLineTexts = false; IFileDialogCustomizePtr pfdCustomizeRet; hr = pfd.QueryInterface(IID_PPV_ARGS(&pfdCustomizeRet)); if (SUCCEEDED(hr)) { BOOL bChecked = FALSE; pfdCustomizeRet->GetCheckButtonState(101, &bChecked); includePaths = (bChecked != 0); pfdCustomizeRet->GetCheckButtonState(102, &bChecked); includeMatchLineNumbers = (bChecked != 0); pfdCustomizeRet->GetCheckButtonState(103, &bChecked); includeMatchLineTexts = (bChecked != 0); } if (!includePaths && !includeMatchLineNumbers && !includeMatchLineTexts) includePaths = true; bool onlyPaths = !includeMatchLineNumbers && !includeMatchLineTexts; if (!path.empty()) { std::ofstream file; file.open(path); if (file.is_open()) { if (onlyPaths) { for (const auto& item : m_items) { file << CUnicodeUtils::StdGetUTF8(item.filePath) << std::endl; } } else { constexpr char separator = '*'; for (const auto& item : m_items) { for (size_t i = 0; i < item.matchLinesNumbers.size(); ++i) { bool needSeparator = false; if (includePaths) { std::string fpath = CUnicodeUtils::StdGetUTF8(item.filePath); std::transform(fpath.begin(), fpath.end(), fpath.begin(), [](char c) { return (c == '\\' ? '/' : c); }); file << std::string("file://"); file << fpath; needSeparator = true; } if (includeMatchLineNumbers) { if (needSeparator) file << separator; file << CStringUtils::Format("%lu", item.matchLinesNumbers[i]); needSeparator = true; } if (includeMatchLineTexts) { if (needSeparator) file << separator; auto line = item.matchLines[i]; CStringUtils::rtrim(line, L"\r\n"); file << CUnicodeUtils::StdGetUTF8(line); } file << std::endl; } } } file.close(); if (bPortable) { g_iniFile.SetBoolValue(L"export", L"paths", includePaths); g_iniFile.SetBoolValue(L"export", L"linenumbers", includeMatchLineNumbers); g_iniFile.SetBoolValue(L"export", L"linecontent", includeMatchLineTexts); } else { // ReSharper disable CppEntityAssignedButNoRead auto exportpaths = CRegStdDWORD(L"Software\\grepWinNP3\\export_paths"); auto exportlinenumbers = CRegStdDWORD(L"Software\\grepWinNP3\\export_linenumbers"); auto exportlinecontent = CRegStdDWORD(L"Software\\grepWinNP3\\export_linecontent"); // ReSharper restore CppEntityAssignedButNoRead exportpaths = includePaths ? 1 : 0; exportlinenumbers = includeMatchLineNumbers ? 1 : 0; exportlinecontent = includeMatchLineTexts ? 1 : 0; } // open file std::wstring cmd = bPortable ? g_iniFile.GetValue(L"global", L"editorcmd", L"") : (std::wstring)CRegStdString(L"Software\\grepWinNP3\\editorcmd", L""); if (!cmd.empty()) { SearchReplace(cmd, L"%mode%", L"mb"); SearchReplace(cmd, L"%pattern%", L""); SearchReplace(cmd, L"%line%", L"0"); SearchReplace(cmd, L"%path%", path.c_str()); STARTUPINFO startupInfo; PROCESS_INFORMATION processInfo; SecureZeroMemory(&startupInfo, sizeof(startupInfo)); startupInfo.cb = sizeof(STARTUPINFO); SecureZeroMemory(&processInfo, sizeof(processInfo)); CreateProcess(NULL, const_cast(cmd.c_str()), NULL, NULL, FALSE, 0, 0, NULL, &startupInfo, &processInfo); CloseHandle(processInfo.hThread); CloseHandle(processInfo.hProcess); } else { SHELLEXECUTEINFO sei = {0}; sei.cbSize = sizeof(SHELLEXECUTEINFO); sei.lpVerb = TEXT("open"); sei.lpFile = path.c_str(); sei.nShow = SW_SHOWNORMAL; ShellExecuteEx(&sei); } } } } break; case IDC_UTF8: { if (IsDlgButtonChecked(*this, IDC_UTF8)) CheckDlgButton(*this, IDC_BINARY, BST_UNCHECKED); } break; case IDC_BINARY: { if (IsDlgButtonChecked(*this, IDC_BINARY)) CheckDlgButton(*this, IDC_UTF8, BST_UNCHECKED); } break; default: break; } return 1; } void CSearchDlg::SaveWndPosition() { WINDOWPLACEMENT wpl = {0}; wpl.length = sizeof(WINDOWPLACEMENT); GetWindowPlacement(*this, &wpl); std::wstring winPosKey = L"windowpos_" + GetMonitorSetupHash(); if (bPortable) { auto sPos = CStringUtils::Format(L"%d;%d;%d;%d;%d;%d;%d;%d;%d;%d", wpl.flags, wpl.showCmd, wpl.ptMinPosition.x, wpl.ptMinPosition.y, wpl.ptMaxPosition.x, wpl.ptMaxPosition.y, wpl.rcNormalPosition.left, wpl.rcNormalPosition.top, wpl.rcNormalPosition.right, wpl.rcNormalPosition.bottom); g_iniFile.SetValue(L"global", winPosKey.c_str(), sPos.c_str()); } else { SHSetValue(HKEY_CURRENT_USER, L"Software\\grepWinNP3", winPosKey.c_str(), REG_NONE, &wpl, sizeof(wpl)); } } void CSearchDlg::UpdateInfoLabel() { std::wstring sText; wchar_t buf[1024] = {0}; if (m_selectedItems) swprintf_s(buf, _countof(buf), TranslatedString(hResource, IDS_INFOLABELSEL).c_str(), m_searchedItems, m_totalItems - m_searchedItems, m_totalMatches, m_items.size(), m_selectedItems); else swprintf_s(buf, _countof(buf), TranslatedString(hResource, IDS_INFOLABEL).c_str(), m_searchedItems, m_totalItems - m_searchedItems, m_totalMatches, m_items.size()); sText = buf; SetDlgItemText(*this, IDC_SEARCHINFOLABEL, sText.c_str()); } bool CSearchDlg::InitResultList() { HWND hListControl = GetDlgItem(*this, IDC_RESULTLIST); bool filelist = (IsDlgButtonChecked(*this, IDC_RESULTFILES) == BST_CHECKED); DWORD exStyle = LVS_EX_DOUBLEBUFFER | LVS_EX_INFOTIP | LVS_EX_FULLROWSELECT; SetWindowTheme(hListControl, L"Explorer", nullptr); ListView_SetItemCount(hListControl, 0); int c = Header_GetItemCount(ListView_GetHeader(hListControl)) - 1; while (c >= 0) ListView_DeleteColumn(hListControl, c--); ListView_SetExtendedListViewStyle(hListControl, exStyle); ListView_SetImageList(hListControl, reinterpret_cast(static_cast(CSysImageList::GetInstance())), LVSIL_SMALL); std::wstring sName = TranslatedString(hResource, IDS_NAME); std::wstring sSize = TranslatedString(hResource, IDS_SIZE); std::wstring sLine = TranslatedString(hResource, IDS_LINE); std::wstring sMatches = TranslatedString(hResource, IDS_MATCHES); std::wstring sText = TranslatedString(hResource, IDS_TEXT); std::wstring sPath = TranslatedString(hResource, IDS_PATH); std::wstring sEncoding = TranslatedString(hResource, IDS_ENCODING); std::wstring sDateModified = TranslatedString(hResource, IDS_DATEMODIFIED); std::wstring sExtension = TranslatedString(hResource, IDS_FILEEXT); LVCOLUMN lvc = {0}; lvc.mask = LVCF_TEXT | LVCF_FMT; lvc.fmt = LVCFMT_LEFT; lvc.cx = -1; lvc.pszText = const_cast(static_cast(sName.c_str())); ListView_InsertColumn(hListControl, 0, &lvc); lvc.pszText = filelist ? const_cast(static_cast(sSize.c_str())) : const_cast(static_cast(sLine.c_str())); lvc.fmt = filelist ? LVCFMT_RIGHT : LVCFMT_LEFT; ListView_InsertColumn(hListControl, 1, &lvc); lvc.fmt = LVCFMT_LEFT; lvc.pszText = filelist ? const_cast(static_cast(sMatches.c_str())) : const_cast(static_cast(sText.c_str())); ListView_InsertColumn(hListControl, 2, &lvc); lvc.pszText = const_cast(static_cast(sPath.c_str())); ListView_InsertColumn(hListControl, 3, &lvc); if (filelist) { lvc.pszText = const_cast(static_cast(sExtension.c_str())); ListView_InsertColumn(hListControl, 4, &lvc); lvc.pszText = const_cast(static_cast(sEncoding.c_str())); ListView_InsertColumn(hListControl, 5, &lvc); lvc.pszText = const_cast(static_cast(sDateModified.c_str())); ListView_InsertColumn(hListControl, 6, &lvc); } ListView_SetColumnWidth(hListControl, 0, 300); ListView_SetColumnWidth(hListControl, 1, 50); ListView_SetColumnWidth(hListControl, 2, LVSCW_AUTOSIZE_USEHEADER); ListView_SetColumnWidth(hListControl, 3, LVSCW_AUTOSIZE_USEHEADER); ListView_SetColumnWidth(hListControl, 4, LVSCW_AUTOSIZE_USEHEADER); ListView_SetColumnWidth(hListControl, 5, LVSCW_AUTOSIZE_USEHEADER); ListView_SetColumnWidth(hListControl, 6, LVSCW_AUTOSIZE_USEHEADER); SendMessage(ListView_GetToolTips(hListControl), TTM_SETDELAYTIME, TTDT_AUTOPOP, SHRT_MAX); m_selectedItems = 0; return true; } bool CSearchDlg::AddFoundEntry(const CSearchInfo* pInfo, bool bOnlyListControl) { if (!bOnlyListControl) { m_items.push_back(*pInfo); int index = static_cast(m_items.size() - 1); int subIndex = 0; for (const auto& lineNumber : pInfo->matchLinesNumbers) { UNREFERENCED_PARAMETER(lineNumber); m_listItems.emplace_back(index, subIndex); ++subIndex; } } else { HWND hListControl = GetDlgItem(*this, IDC_RESULTLIST); bool fileList = (IsDlgButtonChecked(*this, IDC_RESULTFILES) == BST_CHECKED); auto count = static_cast(ListView_GetItemCount(hListControl)); if (count != (fileList ? m_items.size() : m_listItems.size())) ListView_SetItemCountEx(hListControl, fileList ? m_items.size() : m_listItems.size(), LVSICF_NOINVALIDATEALL | LVSICF_NOSCROLL); } return true; } void CSearchDlg::FillResultList() { ProfileTimer profile(L"FillResultList"); SetCursor(LoadCursor(nullptr, IDC_APPSTARTING)); // refresh cursor POINT pt; GetCursorPos(&pt); SetCursorPos(pt.x, pt.y); bool filelist = (IsDlgButtonChecked(*this, IDC_RESULTFILES) == BST_CHECKED); HWND hListControl = GetDlgItem(*this, IDC_RESULTLIST); SendMessage(hListControl, WM_SETREDRAW, FALSE, 0); ListView_SetItemCountEx(hListControl, filelist ? m_items.size() : m_listItems.size(), LVSICF_NOINVALIDATEALL | LVSICF_NOSCROLL); AutoSizeAllColumns(); SendMessage(hListControl, WM_SETREDRAW, TRUE, 0); SetCursor(LoadCursor(nullptr, IDC_ARROW)); // refresh cursor GetCursorPos(&pt); SetCursorPos(pt.x, pt.y); RedrawWindow(hListControl, nullptr, nullptr, RDW_ERASE | RDW_FRAME | RDW_INVALIDATE | RDW_ALLCHILDREN); } void CSearchDlg::ShowContextMenu(int x, int y) { HWND hListControl = GetDlgItem(*this, IDC_RESULTLIST); int nCount = ListView_GetItemCount(hListControl); if (nCount == 0) return; CShellContextMenu shellMenu; int iItem = -1; std::vector paths; while ((iItem = ListView_GetNextItem(hListControl, iItem, LVNI_SELECTED)) != (-1)) { int selIndex = GetSelectedListIndex(iItem); if ((selIndex < 0) || (selIndex >= static_cast(m_items.size()))) continue; paths.push_back(m_items[selIndex]); } if (paths.empty()) return; std::vector lines; bool fileList = (IsDlgButtonChecked(*this, IDC_RESULTFILES) == BST_CHECKED); if (!fileList) { WCHAR numBuf[40] = {0}; while ((iItem = ListView_GetNextItem(hListControl, iItem, LVNI_SELECTED)) != (-1)) { ListView_GetItemText(hListControl, iItem, 1, numBuf, 40); DWORD line = _wtoi(numBuf); if (line) { LineData data; const CSearchInfo info = m_items[GetSelectedListIndex(iItem)]; data.path = info.filePath; const auto matchLinesNumbers = info.matchLinesNumbers; size_t lineIndex = 0; for (unsigned long matchlinesnumber : matchLinesNumbers) { if (matchlinesnumber == line) { LineDataLine dataLine; dataLine.number = info.matchLinesNumbers[lineIndex]; if (info.matchLines.size() > lineIndex) dataLine.text = info.matchLines[lineIndex]; data.lines.push_back(dataLine); } ++lineIndex; } lines.push_back(data); } } } shellMenu.SetObjects(paths, lines); POINT pt = {x, y}; if ((x == -1) && (y == -1)) { RECT rc; ListView_GetItemRect(hListControl, ListView_GetSelectionMark(hListControl), &rc, LVIR_LABEL); pt.x = (rc.right - rc.left) / 2; pt.y = (rc.bottom - rc.top) / 2; ClientToScreen(hListControl, &pt); } shellMenu.ShowContextMenu(hListControl, pt); } bool CSearchDlg::PreTranslateMessage(MSG* pMsg) { if (pMsg->message == WM_KEYDOWN) { HWND hListControl = GetDlgItem(*this, IDC_RESULTLIST); auto bCtrl = (GetKeyState(VK_CONTROL) & 0x8000) != 0; auto bShift = (GetKeyState(VK_SHIFT) & 0x8000) != 0; auto bAlt = (GetKeyState(VK_MENU) & 0x8000) != 0; switch (pMsg->wParam) { case VK_RETURN: { if (GetFocus() == hListControl) { int iItem = -1; while ((iItem = ListView_GetNextItem(hListControl, iItem, LVNI_SELECTED)) != (-1)) { NMITEMACTIVATE itemActivate = {nullptr}; itemActivate.hdr.code = NM_DBLCLK; itemActivate.iItem = iItem; DoListNotify(&itemActivate); } return true; } } break; case 'A': { if ((GetFocus() == hListControl) && bCtrl && !bShift && !bAlt) { // select all entries int nCount = ListView_GetItemCount(hListControl); for (int i = 0; i < nCount; ++i) { ListView_SetItemState(hListControl, i, LVIS_SELECTED, LVIS_SELECTED); } return true; } } break; case 'C': { if ((GetFocus() == hListControl) && bCtrl && !bShift && !bAlt) { // copy all selected entries to the clipboard std::wstring clipBoardText; HWND hHeader = ListView_GetHeader(hListControl); int columns = Header_GetItemCount(hHeader); WCHAR buf[MAX_PATH] = {0}; for (int i = 0; i < columns; ++i) { HD_ITEM hdi = {0}; hdi.mask = HDI_TEXT; hdi.pszText = buf; hdi.cchTextMax = _countof(buf); Header_GetItem(hHeader, i, &hdi); if (i > 0) clipBoardText += L"\t"; clipBoardText += hdi.pszText; } clipBoardText += L"\r\n"; int iItem = -1; while ((iItem = ListView_GetNextItem(hListControl, iItem, LVNI_SELECTED)) != (-1)) { for (int i = 0; i < columns; ++i) { ListView_GetItemText(hListControl, iItem, i, buf, _countof(buf)); if (i > 0) clipBoardText += L"\t"; clipBoardText += buf; } clipBoardText += L"\r\n"; } WriteAsciiStringToClipboard(clipBoardText.c_str(), *this); } } break; case VK_DELETE: { m_autoCompleteFilePatterns.RemoveSelected(); m_autoCompleteSearchPatterns.RemoveSelected(); m_autoCompleteReplacePatterns.RemoveSelected(); m_autoCompleteSearchPaths.RemoveSelected(); } break; case 'K': case 'S': case 'F': case 'E': { if (bCtrl && !bShift && !bAlt) { SetFocus(GetDlgItem(*this, IDC_SEARCHTEXT)); } } break; case 'L': { if (bCtrl && !bShift && !bAlt) { SetFocus(GetDlgItem(*this, IDC_PATTERN)); } } break; case 'O': { if (bCtrl && !bShift && !bAlt) { int iItem = -1; while ((iItem = ListView_GetNextItem(hListControl, iItem, LVNI_SELECTED)) != (-1)) { int selIndex = GetSelectedListIndex(iItem); if ((selIndex < 0) || (selIndex >= static_cast(m_items.size()))) continue; OpenFileAtListIndex(selIndex); } } } break; default: break; } } return false; } void CSearchDlg::DoListNotify(LPNMITEMACTIVATE lpNMItemActivate) { if (lpNMItemActivate->hdr.code == NM_DBLCLK) { if (lpNMItemActivate->iItem >= 0) { OpenFileAtListIndex(lpNMItemActivate->iItem); } } if (lpNMItemActivate->hdr.code == LVN_ITEMCHANGED) { if ((lpNMItemActivate->uOldState & LVIS_SELECTED) || (lpNMItemActivate->uNewState & LVIS_SELECTED)) { HWND hListControl = GetDlgItem(*this, IDC_RESULTLIST); m_selectedItems = ListView_GetSelectedCount(hListControl); UpdateInfoLabel(); } } if (lpNMItemActivate->hdr.code == LVN_BEGINDRAG) { CDropFiles dropFiles; // class for creating DROPFILES struct HWND hListControl = GetDlgItem(*this, IDC_RESULTLIST); int nCount = ListView_GetItemCount(hListControl); if (nCount == 0) return; int iItem = -1; while ((iItem = ListView_GetNextItem(hListControl, iItem, LVNI_SELECTED)) != (-1)) { dropFiles.AddFile(m_items[GetSelectedListIndex(iItem)].filePath); } if (dropFiles.GetCount() > 0) { dropFiles.CreateStructure(hListControl); } } if (lpNMItemActivate->hdr.code == LVN_COLUMNCLICK) { bool fileList = (IsDlgButtonChecked(*this, IDC_RESULTFILES) == BST_CHECKED); m_bAscending = !m_bAscending; bool bDidSort = false; switch (lpNMItemActivate->iSubItem) { case 0: if (m_bAscending) std::ranges::sort(m_items, NameCompareAsc); else std::ranges::sort(m_items, NameCompareDesc); bDidSort = true; break; case 1: if (fileList) { if (m_bAscending) std::ranges::sort(m_items, SizeCompareAsc); else std::ranges::sort(m_items, SizeCompareDesc); bDidSort = true; } break; case 2: if (fileList) { if (m_bAscending) std::ranges::sort(m_items, MatchesCompareAsc); else std::ranges::sort(m_items, MatchesCompareDesc); bDidSort = true; } break; case 3: if (m_bAscending) std::ranges::sort(m_items, PathCompareAsc); else std::ranges::sort(m_items, PathCompareDesc); bDidSort = true; break; case 4: if (m_bAscending) std::ranges::sort(m_items, ExtCompareAsc); else std::ranges::sort(m_items, ExtCompareDesc); bDidSort = true; break; case 5: if (m_bAscending) std::ranges::sort(m_items, EncodingCompareAsc); else std::ranges::sort(m_items, EncodingCompareDesc); bDidSort = true; break; case 6: if (m_bAscending) std::ranges::sort(m_items, ModifiedTimeCompareAsc); else std::ranges::sort(m_items, ModifiedTimeCompareDesc); bDidSort = true; break; default: break; } if (bDidSort) { auto size = m_listItems.size(); m_listItems.clear(); m_listItems.reserve(size); int index = 0; for (const auto& item : m_items) { int subIndex = 0; for (const auto& lineNumber : item.matchLinesNumbers) { UNREFERENCED_PARAMETER(lineNumber); m_listItems.emplace_back(index, subIndex); ++subIndex; } ++index; } } HWND hListControl = GetDlgItem(*this, IDC_RESULTLIST); SendMessage(hListControl, WM_SETREDRAW, FALSE, 0); ListView_SetItemCountEx(hListControl, fileList ? m_items.size() : m_listItems.size(), LVSICF_NOINVALIDATEALL | LVSICF_NOSCROLL); AutoSizeAllColumns(); HDITEM hd = {0}; hd.mask = HDI_FORMAT; HWND hHeader = ListView_GetHeader(hListControl); int iCount = Header_GetItemCount(hHeader); for (int i = 0; i < iCount; ++i) { Header_GetItem(hHeader, i, &hd); hd.fmt &= ~(HDF_SORTDOWN | HDF_SORTUP); Header_SetItem(hHeader, i, &hd); } if (bDidSort) { Header_GetItem(hHeader, lpNMItemActivate->iSubItem, &hd); hd.fmt |= (m_bAscending ? HDF_SORTUP : HDF_SORTDOWN); Header_SetItem(hHeader, lpNMItemActivate->iSubItem, &hd); } SendMessage(hListControl, WM_SETREDRAW, TRUE, 0); RedrawWindow(hListControl, nullptr, nullptr, RDW_ERASE | RDW_FRAME | RDW_INVALIDATE | RDW_ALLCHILDREN); } if (lpNMItemActivate->hdr.code == LVN_GETINFOTIP) { NMLVGETINFOTIP* pInfoTip = reinterpret_cast(lpNMItemActivate); // Which item number? size_t itemId = pInfoTip->iItem; int iItem = GetSelectedListIndex(static_cast(itemId)); pInfoTip->pszText[0] = 0; if (static_cast(m_items.size()) > iItem) { CSearchInfo inf = m_items[iItem]; std::wstring matchString = inf.filePath + L"\n"; std::wstring sFormat = TranslatedString(hResource, IDS_CONTEXTLINE); for (size_t i = 0; i < std::min(inf.matchLines.size(), 5); ++i) { std::wstring matchText = inf.matchLines[i]; CStringUtils::trim(matchText); matchString += CStringUtils::Format(sFormat.c_str(), inf.matchLinesNumbers[i], matchText.c_str()); } if (inf.matchLines.size() > 5) { std::wstring sx = TranslatedString(hResource, IDS_XMOREMATCHES); std::wstring ssx = CStringUtils::Format(sx.c_str(), static_cast(inf.matchLines.size() - 5)); matchString += ssx; } StringCchCopy(pInfoTip->pszText, pInfoTip->cchTextMax, matchString.c_str()); } } if (lpNMItemActivate->hdr.code == LVN_GETDISPINFO) { static const std::wstring sBinary = TranslatedString(hResource, IDS_BINARY); NMLVDISPINFO* pDispInfo = reinterpret_cast(lpNMItemActivate); LV_ITEM* pItem = &(pDispInfo)->item; int iItem = pItem->iItem; bool fileList = (IsDlgButtonChecked(*this, IDC_RESULTFILES) == BST_CHECKED); if (fileList) { const auto& pInfo = &m_items[iItem]; if (pItem->mask & LVIF_TEXT) { switch (pItem->iSubItem) { case 0: // name of the file wcsncpy_s(pItem->pszText, pItem->cchTextMax, pInfo->filePath.substr(pInfo->filePath.find_last_of('\\') + 1).c_str(), pItem->cchTextMax - 1LL); break; case 1: // file size if (!pInfo->folder) StrFormatByteSizeW(pInfo->fileSize, pItem->pszText, pItem->cchTextMax); break; case 2: // match count or read error if (pInfo->readError) wcsncpy_s(pItem->pszText, pItem->cchTextMax, TranslatedString(hResource, IDS_READERROR).c_str(), pItem->cchTextMax - 1LL); else swprintf_s(pItem->pszText, pItem->cchTextMax, L"%lld", pInfo->matchCount); break; case 3: // path if (m_searchPath.find('|') != std::wstring::npos) wcsncpy_s(pItem->pszText, pItem->cchTextMax, pInfo->filePath.substr(0, pInfo->filePath.size() - pInfo->filePath.substr(pInfo->filePath.find_last_of('\\')).size()).c_str(), pItem->cchTextMax - 1LL); else { auto filePart = pInfo->filePath.substr(pInfo->filePath.find_last_of('\\')); auto len = pInfo->filePath.size() - m_searchPath.size() - filePart.size(); if (len > 0) --len; if (m_searchPath.size() < pInfo->filePath.size()) { wcsncpy_s(pItem->pszText, pItem->cchTextMax, pInfo->filePath.substr(m_searchPath.size() + 1, len).c_str(), pItem->cchTextMax - 1LL); if (pItem->pszText[0] == 0) wcscpy_s(pItem->pszText, pItem->cchTextMax, L"\\."); } else wcsncpy_s(pItem->pszText, pItem->cchTextMax, pInfo->filePath.c_str(), pItem->cchTextMax - 1LL); } break; case 4: // extension of the file { pItem->pszText[0] = 0; if (!pInfo->folder) { auto dotPos = pInfo->filePath.find_last_of('.'); if (dotPos != std::wstring::npos) { if (pInfo->filePath.find('\\', dotPos) == std::wstring::npos) wcsncpy_s(pItem->pszText, pItem->cchTextMax, pInfo->filePath.substr(dotPos + 1).c_str(), pItem->cchTextMax - 1LL); } } } break; case 5: // encoding switch (pInfo->encoding) { case CTextFile::Ansi: wcsncpy_s(pItem->pszText, pItem->cchTextMax, L"ANSI", pItem->cchTextMax - 1LL); break; case CTextFile::Unicode_Le: wcsncpy_s(pItem->pszText, pItem->cchTextMax, L"UTF-16-LE", pItem->cchTextMax - 1LL); break; case CTextFile::Unicode_Be: wcsncpy_s(pItem->pszText, pItem->cchTextMax, L"UTF-16-BE", pItem->cchTextMax - 1LL); break; case CTextFile::UTF8: wcsncpy_s(pItem->pszText, pItem->cchTextMax, L"UTF8", pItem->cchTextMax - 1LL); break; case CTextFile::Binary: wcsncpy_s(pItem->pszText, pItem->cchTextMax, L"BINARY", pItem->cchTextMax - 1LL); break; default: wcsncpy_s(pItem->pszText, pItem->cchTextMax, L"", pItem->cchTextMax - 1LL); break; } break; case 6: // modification date FormatDate(pItem->pszText, pInfo->modifiedTime, true); break; default: pItem->pszText[0] = 0; break; } } if (pItem->mask & LVIF_IMAGE) { pItem->iImage = pInfo->folder ? CSysImageList::GetInstance().GetDirIconIndex() : CSysImageList::GetInstance().GetFileIconIndex(pInfo->filePath); } } else { auto tup = m_listItems[iItem]; auto index = std::get<0>(tup); auto subIndex = std::get<1>(tup); const auto& item = m_items[index]; const auto& pInfo = &item; if (item.encoding == CTextFile::Binary) { if (pItem->mask & LVIF_TEXT) { switch (pItem->iSubItem) { case 0: // name of the file wcsncpy_s(pItem->pszText, pItem->cchTextMax, pInfo->filePath.substr(pInfo->filePath.find_last_of('\\') + 1).c_str(), pItem->cchTextMax - 1LL); break; case 1: // binary wcsncpy_s(pItem->pszText, pItem->cchTextMax, sBinary.c_str(), pItem->cchTextMax); break; case 3: // path wcsncpy_s(pItem->pszText, pItem->cchTextMax, pInfo->filePath.substr(0, pInfo->filePath.size() - pInfo->filePath.substr(pInfo->filePath.find_last_of('\\') + 1).size() - 1).c_str(), pItem->cchTextMax - 1LL); break; default: pItem->pszText[0] = 0; break; } } if (pItem->mask & LVIF_IMAGE) { pItem->iImage = pInfo->folder ? CSysImageList::GetInstance().GetDirIconIndex() : CSysImageList::GetInstance().GetFileIconIndex(pInfo->filePath); } } else { if (pItem->mask & LVIF_TEXT) { switch (pItem->iSubItem) { case 0: // name of the file wcsncpy_s(pItem->pszText, pItem->cchTextMax, pInfo->filePath.substr(pInfo->filePath.find_last_of('\\') + 1).c_str(), pItem->cchTextMax - 1LL); break; case 1: // line number swprintf_s(pItem->pszText, pItem->cchTextMax, L"%ld", pInfo->matchLinesNumbers[subIndex]); break; case 2: // line { std::wstring line; if (pInfo->matchLines.size() > static_cast(subIndex)) line = pInfo->matchLines[subIndex]; std::ranges::replace(line, '\t', ' '); std::ranges::replace(line, '\n', ' '); std::ranges::replace(line, '\r', ' '); wcsncpy_s(pItem->pszText, pItem->cchTextMax, line.c_str(), pItem->cchTextMax - 1LL); } break; case 3: // path wcsncpy_s(pItem->pszText, pItem->cchTextMax, pInfo->filePath.substr(0, pInfo->filePath.size() - pInfo->filePath.substr(pInfo->filePath.find_last_of('\\') + 1).size() - 1).c_str(), pItem->cchTextMax - 1LL); break; default: pItem->pszText[0] = 0; break; } } if (pItem->mask & LVIF_IMAGE) { pItem->iImage = pInfo->folder ? CSysImageList::GetInstance().GetDirIconIndex() : CSysImageList::GetInstance().GetFileIconIndex(pInfo->filePath); } } } } } static void EscCtrlCharacters(std::wstring& pattern) { std::wstring result; for (const wchar_t& ch : pattern) { switch (ch) { case L'\n': result.append(L"\\n"); break; case L'\r': result.append(L"\\r"); break; case L'\t': result.append(L"\\t"); break; case L'\f': result.append(L"\\f"); break; case L'\v': result.append(L"\\v"); break; case L'\a': result.append(L"\\a"); break; case L'\b': result.append(L"\\b"); break; case L'\x1B': result.append(L"\\e"); break; // protect quotes too case L'"': result.append(L"\\\""); break; default: result.push_back(ch); break; } if (ch == L'\0') break; // loop } pattern = std::move(result); } void CSearchDlg::OpenFileAtListIndex(int listIndex) { int iItem = GetSelectedListIndex(listIndex); CSearchInfo inf = m_items[iItem]; size_t dotPos = inf.filePath.rfind('.'); std::wstring ext; if (dotPos != std::wstring::npos) ext = inf.filePath.substr(dotPos); std::wstring cmd = bPortable ? g_iniFile.GetValue(L"global", L"editorcmd", L"") : (std::wstring)CRegStdString(L"Software\\grepWinNP3\\editorcmd", L""); if (!cmd.empty()) { bool fileList = (IsDlgButtonChecked(*this, IDC_RESULTFILES) == BST_CHECKED); if (!fileList) { HWND hListControl = GetDlgItem(*this, IDC_RESULTLIST); wchar_t textLineBuf[MAX_PATH] = {0}; LVITEM lv = {0}; lv.iItem = listIndex; lv.iSubItem = 1; // line number lv.mask = LVIF_TEXT; lv.pszText = textLineBuf; lv.cchTextMax = _countof(textLineBuf); if (ListView_GetItem(hListControl, &lv)) { SearchReplace(cmd, L"%line%", textLineBuf); } } else { // use the first matching line in this file if (!inf.matchLinesNumbers.empty()) SearchReplace(cmd, L"%line%", CStringUtils::Format(L"%lu", inf.matchLinesNumbers[0])); else SearchReplace(cmd, L"%line%", L"0"); } SearchReplace(cmd, L"%path%", inf.filePath.c_str()); // Notepad3 special std::wstring mode = L"mb"; if (m_bUseRegex) mode.append(L"r"); if (m_bCaseSensitive) mode.append(L"c"); if (m_bDotMatchesNewline) mode.append(L"a"); SearchReplace(cmd, L"%mode%", mode.c_str()); std::wstring searchfor = m_searchString; EscCtrlCharacters(searchfor); SearchReplace(cmd, L"%pattern%", searchfor.c_str()); STARTUPINFO startupInfo{}; PROCESS_INFORMATION processInfo{}; startupInfo.cb = sizeof(STARTUPINFO); CreateProcess(nullptr, const_cast(cmd.c_str()), nullptr, nullptr, FALSE, 0, nullptr, nullptr, &startupInfo, &processInfo); CloseHandle(processInfo.hThread); CloseHandle(processInfo.hProcess); return; } DWORD bufLen = 0; if (AssocQueryString(ASSOCF_INIT_DEFAULTTOSTAR, ASSOCSTR_DDECOMMAND, ext.c_str(), nullptr, nullptr, &bufLen) == S_OK) { if (bufLen) { // application requires DDE to open the file: // since we can't do this the easy way with CreateProcess, we use ShellExecute instead ShellExecute(*this, nullptr, inf.filePath.c_str(), nullptr, nullptr, SW_SHOW); return; } } bufLen = 0; AssocQueryString(ASSOCF_INIT_DEFAULTTOSTAR, ASSOCSTR_COMMAND, ext.c_str(), nullptr, nullptr, &bufLen); auto cmdBuf = std::make_unique(bufLen + 1LL); AssocQueryString(ASSOCF_INIT_DEFAULTTOSTAR, ASSOCSTR_COMMAND, ext.c_str(), nullptr, cmdBuf.get(), &bufLen); std::wstring application = cmdBuf.get(); // normalize application path DWORD len = ExpandEnvironmentStrings(application.c_str(), nullptr, 0); cmdBuf = std::make_unique(len + 1LL); ExpandEnvironmentStrings(application.c_str(), cmdBuf.get(), len); application = cmdBuf.get(); // resolve parameters if (application.find(L"%1") == std::wstring::npos) application += L" %1"; bool fileList = (IsDlgButtonChecked(*this, IDC_RESULTFILES) == BST_CHECKED); std::wstring lineNumberParamBefore; std::wstring lineNumberParam; wchar_t textLineBuf[MAX_PATH] = {0}; if (!fileList) { HWND hListControl = GetDlgItem(*this, IDC_RESULTLIST); LVITEM lv = {0}; lv.iItem = listIndex; lv.iSubItem = 1; // line number lv.mask = LVIF_TEXT; lv.pszText = textLineBuf; lv.cchTextMax = _countof(textLineBuf); if (!ListView_GetItem(hListControl, &lv)) { textLineBuf[0] = '\0'; } } else if (!inf.matchLinesNumbers.empty()) { // use the first matching line in this file swprintf_s(textLineBuf, L"%ld", inf.matchLinesNumbers[0]); } if (textLineBuf[0] == 0) wcscpy_s(textLineBuf, L"0"); std::wstring appname = application; std::ranges::transform(appname, appname.begin(), ::towlower); // now find out if the application which opens the file is known to us // and if it has a 'linenumber' switch to jump directly to a specific // line number. if (appname.find(L"notepad++.exe") != std::wstring::npos) { // notepad++ lineNumberParam = CStringUtils::Format(L"-n%s", textLineBuf); } else if (appname.find(L"xemacs.exe") != std::wstring::npos) { // XEmacs lineNumberParam = CStringUtils::Format(L"+%s", textLineBuf); } else if (appname.find(L"uedit32.exe") != std::wstring::npos) { // UltraEdit lineNumberParam = CStringUtils::Format(L"-l%s", textLineBuf); } else if (appname.find(L"codewright.exe") != std::wstring::npos) { // CodeWright lineNumberParam = CStringUtils::Format(L"-G%s", textLineBuf); } else if (appname.find(L"notepad2.exe") != std::wstring::npos) { // Notepad2 auto escapedSearch = m_searchString; SearchReplace(escapedSearch, L"\"", L"\\\""); lineNumberParamBefore = CStringUtils::Format(L"/g %s /mr \"%s\"", textLineBuf, escapedSearch.c_str()); } else if (appname.find(L"notepad3.exe") != std::wstring::npos) { // Notepad3 std::wstring mode = L"mb"; if (m_bUseRegex) mode.append(L"r"); if (m_bCaseSensitive) mode.append(L"c"); if (m_bDotMatchesNewline) mode.append(L"a"); std::wstring escapedsearch = m_searchString; EscCtrlCharacters(escapedsearch); lineNumberParamBefore = CStringUtils::Format(L"/%s \"%s\" /g %s -", mode.c_str(), escapedsearch.c_str(), textLineBuf); } else if ((appname.find(L"bowpad.exe") != std::wstring::npos) || (appname.find(L"bowpad64.exe") != std::wstring::npos)) { // BowPad lineNumberParam = CStringUtils::Format(L"/line:%s", textLineBuf); } else if (appname.find(L"code.exe") != std::wstring::npos) { // Visual Studio Code lineNumberParamBefore = L"--goto"; lineNumberParam = CStringUtils::Format(L":%s", textLineBuf); } // replace "%1" with %1 std::wstring tag = L"\"%1\""; std::wstring repl = L"%1"; std::wstring::iterator itBegin = search(application.begin(), application.end(), tag.begin(), tag.end()); if (itBegin != application.end()) { std::wstring::iterator itEnd = itBegin + tag.size(); application.replace(itBegin, itEnd, repl); } // replace %1 with "path/of/selected/file" tag = L"%1"; if (application.find(L"rundll32.exe") == std::wstring::npos && application.find(L"--single-argument") == std::wstring::npos) repl = L"\"" + inf.filePath + L"\""; else repl = inf.filePath; if (!lineNumberParamBefore.empty()) { repl = lineNumberParamBefore + L" " + repl; } itBegin = std::ranges::search(application, tag).begin(); if (itBegin != application.end()) { std::wstring::iterator itEnd = itBegin + tag.size(); application.replace(itBegin, itEnd, repl); } if (!lineNumberParam.empty()) { if (!lineNumberParam.starts_with(L":")) { application += L" "; } application += lineNumberParam; } STARTUPINFO startupInfo{}; PROCESS_INFORMATION processInfo{}; startupInfo.cb = sizeof(STARTUPINFO); CreateProcess(nullptr, const_cast(application.c_str()), nullptr, nullptr, FALSE, 0, nullptr, nullptr, &startupInfo, &processInfo); CloseHandle(processInfo.hThread); CloseHandle(processInfo.hProcess); } bool grepWinIsRegexValid(const std::wstring& searchString) { // check if the regex is valid bool bValid = true; try { boost::wregex expression = boost::wregex(searchString); } catch (const std::exception&) { bValid = false; } return bValid; } bool CSearchDlg::SaveSettings() { // get all the information we need from the dialog auto buf = GetDlgItemText(IDC_SEARCHPATH); m_searchPath = buf.get(); buf = GetDlgItemText(IDC_SEARCHTEXT); m_searchString = buf.get(); buf = GetDlgItemText(IDC_REPLACETEXT); m_replaceString = buf.get(); buf = GetDlgItemText(IDC_EXCLUDEDIRSPATTERN); m_excludeDirsPatternRegex = buf.get(); buf = GetDlgItemText(IDC_PATTERN); m_patternRegex = buf.get(); // split the pattern string into single patterns and // add them to an array auto pBuf = buf.get(); size_t pos = 0; m_patterns.clear(); do { pos = wcscspn(pBuf, L"|"); std::wstring s = std::wstring(pBuf, pos); if (!s.empty()) { std::ranges::transform(s, s.begin(), ::towlower); m_patterns.push_back(s); auto endPart = s.rbegin(); if (*endPart == '*' && s.size() > 2) { ++endPart; if (*endPart == '.') { m_patterns.push_back(s.substr(0, s.size() - 2)); } } } pBuf += pos; pBuf++; } while (*pBuf && (*(pBuf - 1))); m_bUseRegex = (IsDlgButtonChecked(*this, IDC_REGEXRADIO) == BST_CHECKED); if (m_bUseRegex) { // check if the regex is valid before doing the search if (!grepWinIsRegexValid(m_searchString) && !m_searchString.empty()) { return false; } } m_bUseRegexForPaths = (IsDlgButtonChecked(*this, IDC_FILEPATTERNREGEX) == BST_CHECKED); if (m_bUseRegexForPaths) { // check if the regex is valid before doing the search if (!grepWinIsRegexValid(m_patternRegex) && !m_patternRegex.empty()) { return false; } } // check if the Exclude Dirs regex is valid before doing the search if (!grepWinIsRegexValid(m_excludeDirsPatternRegex) && !m_excludeDirsPatternRegex.empty()) { return false; } m_bAllSize = (IsDlgButtonChecked(*this, IDC_ALLSIZERADIO) == BST_CHECKED); DialogEnableWindow(IDC_SIZEEDIT, !m_bAllSize); DialogEnableWindow(IDC_SIZECOMBO, !m_bAllSize); m_sizeCmp = 0; if (!m_bAllSize) { buf = GetDlgItemText(IDC_SIZEEDIT); m_lSize = _wtol(buf.get()); m_lSize *= 1024; m_sizeCmp = static_cast(SendDlgItemMessage(*this, IDC_SIZECOMBO, CB_GETCURSEL, 0, 0)); } m_bIncludeSystem = (IsDlgButtonChecked(*this, IDC_INCLUDESYSTEM) == BST_CHECKED); m_bIncludeHidden = (IsDlgButtonChecked(*this, IDC_INCLUDEHIDDEN) == BST_CHECKED); m_bIncludeSubfolders = (IsDlgButtonChecked(*this, IDC_INCLUDESUBFOLDERS) == BST_CHECKED); m_bIncludeBinary = (IsDlgButtonChecked(*this, IDC_INCLUDEBINARY) == BST_CHECKED); m_bCreateBackup = (IsDlgButtonChecked(*this, IDC_CREATEBACKUP) == BST_CHECKED); m_bWholeWords = (IsDlgButtonChecked(*this, IDC_WHOLEWORDS) == BST_CHECKED); m_bUTF8 = (IsDlgButtonChecked(*this, IDC_UTF8) == BST_CHECKED); m_bForceBinary = (IsDlgButtonChecked(*this, IDC_BINARY) == BST_CHECKED); m_bCaseSensitive = (IsDlgButtonChecked(*this, IDC_CASE_SENSITIVE) == BST_CHECKED); m_bDotMatchesNewline = (IsDlgButtonChecked(*this, IDC_DOTMATCHNEWLINE) == BST_CHECKED); m_dateLimit = 0; if (IsDlgButtonChecked(*this, IDC_RADIO_DATE_ALL) == BST_CHECKED) m_dateLimit = 0; if (IsDlgButtonChecked(*this, IDC_RADIO_DATE_NEWER) == BST_CHECKED) m_dateLimit = IDC_RADIO_DATE_NEWER - IDC_RADIO_DATE_ALL; if (IsDlgButtonChecked(*this, IDC_RADIO_DATE_OLDER) == BST_CHECKED) m_dateLimit = IDC_RADIO_DATE_OLDER - IDC_RADIO_DATE_ALL; if (IsDlgButtonChecked(*this, IDC_RADIO_DATE_BETWEEN) == BST_CHECKED) m_dateLimit = IDC_RADIO_DATE_BETWEEN - IDC_RADIO_DATE_ALL; SYSTEMTIME sysTime = {0}; DateTime_GetSystemtime(GetDlgItem(*this, IDC_DATEPICK1), &sysTime); SystemTimeToFileTime(&sysTime, &m_date1); DateTime_GetSystemtime(GetDlgItem(*this, IDC_DATEPICK2), &sysTime); SystemTimeToFileTime(&sysTime, &m_date2); m_showContent = IsDlgButtonChecked(*this, IDC_RESULTCONTENT) == BST_CHECKED; if (m_searchPath.empty()) return false; if (m_bNoSaveSettings) return true; if (bPortable) g_iniFile.SetValue(L"global", L"searchfor", m_searchString.c_str()); //else // m_regSearchString = m_searchString if (bPortable) g_iniFile.SetValue(L"global", L"searchpath", m_searchPath.c_str()); else m_regSearchPath = m_searchPath; if (bPortable) g_iniFile.SetBoolValue(L"global", L"UseRegex", m_bUseRegex); else m_regUseRegex = static_cast(m_bUseRegex); if (bPortable) g_iniFile.SetBoolValue(L"global", L"UseFileMatchRegex", m_bUseRegexForPaths); else m_regUseRegexForPaths = static_cast(m_bUseRegexForPaths); if (bPortable) g_iniFile.SetBoolValue(L"global", L"AllSize", m_bAllSize); else m_regAllSize = static_cast(m_bAllSize); if (bPortable) g_iniFile.SetValue(L"global", L"Size", CStringUtils::Format(L"%I64u", m_lSize >> 10).c_str()); else m_regSize = CStringUtils::Format(L"%I64u", m_lSize >> 10).c_str(); if (bPortable) g_iniFile.SetValue(L"global", L"SizeCombo", CStringUtils::Format(L"%d", m_sizeCmp).c_str()); else m_regSizeCombo = m_sizeCmp; if (bPortable) { g_iniFile.SetBoolValue(L"global", L"IncludeSystem", m_bIncludeSystem); g_iniFile.SetBoolValue(L"global", L"IncludeHidden", m_bIncludeHidden); g_iniFile.SetBoolValue(L"global", L"IncludeSubfolders", m_bIncludeSubfolders); g_iniFile.SetBoolValue(L"global", L"IncludeBinary", m_bIncludeBinary); g_iniFile.SetBoolValue(L"global", L"CreateBackup", m_bCreateBackup); g_iniFile.SetBoolValue(L"global", L"WholeWords", m_bWholeWords); g_iniFile.SetBoolValue(L"global", L"UTF8", m_bUTF8); g_iniFile.SetBoolValue(L"global", L"StayOnTop", m_bStayOnTop); g_iniFile.SetBoolValue(L"global", L"Binary", m_bForceBinary); g_iniFile.SetBoolValue(L"global", L"CaseSensitive", m_bCaseSensitive); g_iniFile.SetBoolValue(L"global", L"DotMatchesNewline", m_bDotMatchesNewline); g_iniFile.SetValue(L"global", L"pattern", m_patternRegex.c_str()); g_iniFile.SetValue(L"global", L"ExcludeDirsPattern", m_excludeDirsPatternRegex.c_str()); g_iniFile.SetValue(L"global", L"DateLimit", std::to_wstring(m_dateLimit).c_str()); g_iniFile.SetValue(L"global", L"Date1Low", std::to_wstring(m_date1.dwLowDateTime).c_str()); g_iniFile.SetValue(L"global", L"Date1High", std::to_wstring(m_date1.dwHighDateTime).c_str()); g_iniFile.SetValue(L"global", L"Date2Low", std::to_wstring(m_date2.dwLowDateTime).c_str()); g_iniFile.SetValue(L"global", L"Date2High", std::to_wstring(m_date2.dwHighDateTime).c_str()); if (!m_showContentSet) g_iniFile.SetBoolValue(L"global", L"showcontent", m_showContent); } else { m_regIncludeSystem = static_cast(m_bIncludeSystem); m_regIncludeHidden = static_cast(m_bIncludeHidden); m_regIncludeSubfolders = static_cast(m_bIncludeSubfolders); m_regIncludeBinary = static_cast(m_bIncludeBinary); m_regCreateBackup = static_cast(m_bCreateBackup); m_regWholeWords = static_cast(m_bWholeWords); m_regUTF8 = static_cast(m_bUTF8); m_regStayOnTop = static_cast(m_bStayOnTop); m_regBinary = static_cast(m_bForceBinary); m_regCaseSensitive = static_cast(m_bCaseSensitive); m_regDotMatchesNewline = static_cast(m_bDotMatchesNewline); m_regPattern = m_patternRegex; m_regExcludeDirsPattern = m_excludeDirsPatternRegex; m_regDateLimit = m_dateLimit; m_regDate1Low = m_date1.dwLowDateTime; m_regDate1High = m_date1.dwHighDateTime; m_regDate2Low = m_date2.dwLowDateTime; m_regDate2High = m_date2.dwHighDateTime; if (!m_showContentSet) m_regShowContent = m_showContent; } SaveWndPosition(); return true; } bool CSearchDlg::NameCompareAsc(const CSearchInfo& entry1, const CSearchInfo& entry2) { std::wstring name1 = entry1.filePath.substr(entry1.filePath.find_last_of('\\') + 1); std::wstring name2 = entry2.filePath.substr(entry2.filePath.find_last_of('\\') + 1); return StrCmpLogicalW(name1.c_str(), name2.c_str()) < 0; } bool CSearchDlg::SizeCompareAsc(const CSearchInfo& entry1, const CSearchInfo& entry2) { return entry1.fileSize < entry2.fileSize; } bool CSearchDlg::MatchesCompareAsc(const CSearchInfo& entry1, const CSearchInfo& entry2) { return entry1.matchCount < entry2.matchCount; } bool CSearchDlg::PathCompareAsc(const CSearchInfo& entry1, const CSearchInfo& entry2) { std::wstring name1 = entry1.filePath.substr(entry1.filePath.find_last_of('\\') + 1); std::wstring name2 = entry2.filePath.substr(entry2.filePath.find_last_of('\\') + 1); std::wstring path1 = entry1.filePath.substr(0, entry1.filePath.size() - name1.size() - 1); std::wstring path2 = entry2.filePath.substr(0, entry2.filePath.size() - name2.size() - 1); int cmp = path1.compare(path2); if (cmp != 0) return cmp < 0; return StrCmpLogicalW(name1.c_str(), name2.c_str()) < 0; } bool CSearchDlg::EncodingCompareAsc(const CSearchInfo& entry1, const CSearchInfo& entry2) { return entry1.encoding < entry2.encoding; } bool CSearchDlg::ModifiedTimeCompareAsc(const CSearchInfo& entry1, const CSearchInfo& entry2) { return CompareFileTime(&entry1.modifiedTime, &entry2.modifiedTime) < 0; } bool CSearchDlg::ExtCompareAsc(const CSearchInfo& entry1, const CSearchInfo& entry2) { auto dotPos1 = entry1.filePath.find_last_of('.'); auto dotPos2 = entry2.filePath.find_last_of('.'); std::wstring ext1 = dotPos1 != std::wstring::npos ? entry1.filePath.substr(dotPos1 + 1) : L""; std::wstring ext2 = dotPos2 != std::wstring::npos ? entry2.filePath.substr(dotPos2 + 1) : L""; return StrCmpLogicalW(ext1.c_str(), ext2.c_str()) < 0; } bool CSearchDlg::NameCompareDesc(const CSearchInfo& entry1, const CSearchInfo& entry2) { std::wstring name1 = entry1.filePath.substr(entry1.filePath.find_last_of('\\') + 1); std::wstring name2 = entry2.filePath.substr(entry2.filePath.find_last_of('\\') + 1); return StrCmpLogicalW(name1.c_str(), name2.c_str()) > 0; } bool CSearchDlg::SizeCompareDesc(const CSearchInfo& entry1, const CSearchInfo& entry2) { return entry1.fileSize > entry2.fileSize; } bool CSearchDlg::MatchesCompareDesc(const CSearchInfo& entry1, const CSearchInfo& entry2) { return entry1.matchCount > entry2.matchCount; } bool CSearchDlg::PathCompareDesc(const CSearchInfo& entry1, const CSearchInfo& entry2) { std::wstring name1 = entry1.filePath.substr(entry1.filePath.find_last_of('\\') + 1); std::wstring name2 = entry2.filePath.substr(entry2.filePath.find_last_of('\\') + 1); std::wstring path1 = entry1.filePath.substr(0, entry1.filePath.size() - name1.size() - 1); std::wstring path2 = entry2.filePath.substr(0, entry2.filePath.size() - name2.size() - 1); int cmp = path1.compare(path2); if (cmp != 0) return cmp > 0; return StrCmpLogicalW(name1.c_str(), name2.c_str()) > 0; } bool CSearchDlg::EncodingCompareDesc(const CSearchInfo& entry1, const CSearchInfo& entry2) { return entry1.encoding > entry2.encoding; } bool CSearchDlg::ModifiedTimeCompareDesc(const CSearchInfo& entry1, const CSearchInfo& entry2) { return CompareFileTime(&entry1.modifiedTime, &entry2.modifiedTime) > 0; } bool CSearchDlg::ExtCompareDesc(const CSearchInfo& entry1, const CSearchInfo& entry2) { auto dotPos1 = entry1.filePath.find_last_of('.'); auto dotPos2 = entry2.filePath.find_last_of('.'); std::wstring ext1 = dotPos1 != std::wstring::npos ? entry1.filePath.substr(dotPos1 + 1) : L""; std::wstring ext2 = dotPos2 != std::wstring::npos ? entry2.filePath.substr(dotPos2 + 1) : L""; return StrCmpLogicalW(ext1.c_str(), ext2.c_str()) > 0; } bool grepWinMatchI(const std::wstring& theRegex, const wchar_t* pText) { try { boost::wregex expression = boost::wregex(theRegex, boost::regex::normal | boost::regbase::icase); boost::wcmatch whatc; if (boost::regex_match(pText, whatc, expression)) { return true; } } catch (const std::exception&) { } return false; } DWORD CSearchDlg::SearchThread() { ProfileTimer profile(L"SearchThread"); auto pathBuf = std::make_unique(MAX_PATH_NEW); DWORD const nMaxNumOfWorker = std::thread::hardware_concurrency() << 2; DWORD const nOfWorker = std::max(std::min(bPortable ? g_iniFile.GetLongValue(L"global", L"MaxNumOfWorker", nMaxNumOfWorker >> 1) : static_cast(CRegStdDWORD(L"Software\\grepWinNP3\\MaxNumOfWorker", nMaxNumOfWorker >> 1)), nMaxNumOfWorker), 1); s_SearchThreadMap.clear(); s_SearchThreadMap.set_max_worker(nOfWorker); // split the path string into single paths and // add them to an array const auto* pBufSearchPath = m_searchPath.c_str(); size_t pos = 0; std::vector pathVector; do { pos = wcscspn(pBufSearchPath, L"|"); std::wstring s = std::wstring(pBufSearchPath, pos); if (!s.empty()) { // Remove extra backslashes except for the UNC identifier std::string::size_type found = s.find_first_of('\\', 1); while (found != std::string::npos) { while (s[found + 1] == '\\') { s.erase(found + 1, 1); } found = s.find_first_of('\\', found + 1); } CStringUtils::rtrim(s, L"\\/"); pathVector.push_back(s); } pBufSearchPath += pos; pBufSearchPath++; } while (*pBufSearchPath && (*(pBufSearchPath - 1))); if (!m_bUseRegex && !m_replaceString.empty()) { // escape all characters in the replace string std::wstring sRepl; for (const auto& c : m_replaceString) { switch (c) { case '$': case '\\': case '(': case ')': case '?': case ':': sRepl += L"\\"; break; default: break; } sRepl += c; } m_replaceString = sRepl; } SendMessage(*this, SEARCH_START, 0, 0); std::wstring searchStringutf16; for (auto c : m_searchString) { searchStringutf16 += c; searchStringutf16 += L"\\x00"; } for (std::wstring searchPath : pathVector) { size_t endPos = searchPath.find_last_not_of(L" \\"); if (std::wstring::npos != endPos) { searchPath = searchPath.substr(0, endPos + 1); if (searchPath[searchPath.length() - 1] == ':') searchPath += L"\\"; } std::wstring searchRoot = searchPath; if (!searchPath.empty()) { bool bAlwaysSearch = false; if (!PathIsDirectory(searchPath.c_str())) { bAlwaysSearch = true; searchRoot = searchRoot.substr(0, searchRoot.find_last_of('\\')); } bool bIsDirectory = false; CDirFileEnum fileEnumerator(searchPath.c_str()); bool bRecurse = m_bIncludeSubfolders; std::wstring sPath; while ((fileEnumerator.NextFile(sPath, &bIsDirectory, bRecurse) || bAlwaysSearch) && !IsCancelled()) { if (bAlwaysSearch && _wcsicmp(searchPath.c_str(), sPath.c_str())) bAlwaysSearch = false; if (s_BackupAndTmpFiles.contains(sPath)) continue; // don't search own backup files wcscpy_s(pathBuf.get(), MAX_PATH_NEW, sPath.c_str()); if (!bIsDirectory) { bool bSearch = false; DWORD nFileSizeLow = 0; uint64_t fullFileSize = 0; FILETIME ft = {0}; if (bAlwaysSearch) { wcscpy_s(pathBuf.get(), MAX_PATH_NEW, searchPath.c_str()); CAutoFile hFile = CreateFile(searchPath.c_str(), FILE_READ_EA, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile) { BY_HANDLE_FILE_INFORMATION bhfi = {0}; GetFileInformationByHandle(hFile, &bhfi); nFileSizeLow = bhfi.nFileSizeLow; fullFileSize = (((uint64_t) bhfi.nFileSizeHigh) << 32) | bhfi.nFileSizeLow; ft = bhfi.ftLastWriteTime; } } else { const WIN32_FIND_DATA * pFindData = fileEnumerator.GetFileInfo(); if (pFindData) { bSearch = ((m_bIncludeHidden) || ((pFindData->dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) == 0)); bSearch = bSearch && ((m_bIncludeSystem) || ((pFindData->dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) == 0)); fullFileSize = (static_cast(pFindData->nFileSizeHigh) << 32) | pFindData->nFileSizeLow; ft = pFindData->ftLastWriteTime; nFileSizeLow = pFindData->nFileSizeLow; if (!m_bAllSize && bSearch) { switch (m_sizeCmp) { case 0: // less than bSearch = bSearch && (fullFileSize < m_lSize); break; case 1: // equal bSearch = bSearch && (fullFileSize == m_lSize); break; case 2: // greater than bSearch = bSearch && (fullFileSize > m_lSize); break; default: break; } } } if (bSearch) { switch (m_dateLimit + IDC_RADIO_DATE_ALL) { default: case IDC_RADIO_DATE_ALL: break; case IDC_RADIO_DATE_NEWER: bSearch = CompareFileTime(&ft, &m_date1) >= 0; break; case IDC_RADIO_DATE_OLDER: bSearch = CompareFileTime(&ft, &m_date1) <= 0; break; case IDC_RADIO_DATE_BETWEEN: bSearch = CompareFileTime(&ft, &m_date1) >= 0; bSearch = bSearch && (CompareFileTime(&ft, &m_date2) <= 0); break; } } } bRecurse = ((m_bIncludeSubfolders) && (bSearch)); bool bPattern = MatchPath(pathBuf.get()); auto sInfoPtr = std::make_shared(pathBuf.get()); sInfoPtr->fileSize = fullFileSize; sInfoPtr->modifiedTime = ft; SearchFlags_t searchFlags = { bAlwaysSearch, m_bUTF8, m_bForceBinary, m_bIncludeBinary, m_bUseRegex, m_bCaseSensitive, m_bDotMatchesNewline, m_bWholeWords, m_bCreateBackup, m_bCreateBackupInFolders, m_bReplace, m_bCaptureSearch }; if (nOfWorker > 1) { if ((bSearch && bPattern) || bAlwaysSearch) { if (m_searchString.empty()) { s_SearchThreadMap.insert_ready(sInfoPtr, 1); } else { std::shared_future foundFuture = std::async(std::launch::async, SearchFile, sInfoPtr, searchRoot, searchFlags, m_searchString, searchStringutf16, m_replaceString); s_SearchThreadMap.insert_future(sInfoPtr, foundFuture); } } else { s_SearchThreadMap.insert_ready(sInfoPtr, -1); } } else { int const nFound = SearchFile(sInfoPtr, searchRoot, searchFlags, m_searchString, searchStringutf16, m_replaceString); SendMessage(*this, SEARCH_PROGRESS, (WPARAM)sInfoPtr.get(), nFound); } } else // directory: search for filename { const WIN32_FIND_DATA * pFindData = fileEnumerator.GetFileInfo(); if (pFindData) { bool bSearch = ((m_bIncludeHidden) || ((pFindData->dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) == 0)); bSearch = bSearch && ((m_bIncludeSystem) || ((pFindData->dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) == 0)); std::wstring relpath = pathBuf.get(); relpath = relpath.substr(searchPath.size()); if (!relpath.empty()) { if (relpath[0] == '\\') relpath = relpath.substr(1); } bool bExcludeDir = bSearch && !m_excludeDirsPatternRegex.empty() && (grepWinMatchI(m_excludeDirsPatternRegex, pFindData->cFileName) || grepWinMatchI(m_excludeDirsPatternRegex, pathBuf.get()) || grepWinMatchI(m_excludeDirsPatternRegex, relpath.c_str())); bSearch = bSearch && !bExcludeDir; bRecurse = ((bIsDirectory) && (m_bIncludeSubfolders) && (bSearch)); if (m_searchString.empty() && m_replaceString.empty()) { // if there's no search and replace string, include folders in the 'matched' list if they // match the specified file pattern if (MatchPath(pathBuf.get())) { if (bSearch) { switch (m_dateLimit + IDC_RADIO_DATE_ALL) { default: case IDC_RADIO_DATE_ALL: break; case IDC_RADIO_DATE_NEWER: bSearch = CompareFileTime(&pFindData->ftLastWriteTime, &m_date1) >= 0; break; case IDC_RADIO_DATE_OLDER: bSearch = CompareFileTime(&pFindData->ftLastWriteTime, &m_date1) <= 0; break; case IDC_RADIO_DATE_BETWEEN: bSearch = CompareFileTime(&pFindData->ftLastWriteTime, &m_date1) >= 0; bSearch = bSearch && (CompareFileTime(&pFindData->ftLastWriteTime, &m_date2) <= 0); break; } } if (!m_bAllSize && bSearch) { // assume a 'file'-size of zero for dirs switch (m_sizeCmp) { case 0: // less than bSearch = bSearch && (0 < m_lSize); break; case 1: // equal bSearch = bSearch && (0 == m_lSize); break; case 2: // greater than bSearch = bSearch && (0 > m_lSize); break; default: break; } } if (bSearch) { auto sInfoPtr = std::make_shared(pathBuf.get()); sInfoPtr->modifiedTime = pFindData->ftLastWriteTime; sInfoPtr->folder = true; s_SearchThreadMap.insert_ready(sInfoPtr, -1); } } } } } bAlwaysSearch = false; } // while next file } // empty searchpath } // pathvector if (nOfWorker <= 1) { SendMessage(*this, SEARCH_END, 0, 0); // refresh cursor POINT pt; GetCursorPos(&pt); SetCursorPos(pt.x, pt.y); PostMessage(m_hwnd, WM_GREPWIN_THREADEND, 0, 0); } InterlockedExchange(&s_SearchThreadRunning, FALSE); return 0L; } DWORD WINAPI SearchThreadEntry(LPVOID lpParam) { auto* const pThis = (CSearchDlg* const)lpParam; if (pThis) return pThis->SearchThread(); return 0L; } DWORD CSearchDlg::EvaluationThread() { while (IsSearchThreadRunning() || !s_SearchThreadMap.empty()) { int nFound = -1; std::shared_ptr const sInfoPtr = s_SearchThreadMap.retrieve(nFound); if (sInfoPtr != nullptr) { SendMessage(*this, SEARCH_PROGRESS, (WPARAM)sInfoPtr.get(), nFound); } } SendMessage(*this, SEARCH_END, 0, 0); // refresh cursor POINT pt; GetCursorPos(&pt); SetCursorPos(pt.x, pt.y); s_SearchThreadMap.clear(); InterlockedExchange(&s_EvaluationThreadRunning, FALSE); PostMessage(m_hwnd, WM_GREPWIN_THREADEND, 0, 0); return 0L; } DWORD WINAPI EvaluationThreadEntry(LPVOID lpParam) { auto* const pThis = (CSearchDlg* const)lpParam; if (pThis) return pThis->EvaluationThread(); return 0L; } void CSearchDlg::SetSearchPath(const std::wstring& path) { m_searchPath = path; SearchReplace(m_searchPath, L"/", L"\\"); } void CSearchDlg::SetPreset(const std::wstring& preset) { CBookmarks bookmarks; bookmarks.Load(); auto bk = bookmarks.GetBookmark(preset); if (bk.Name == preset) { auto removeQuotes = [](std::wstring& str) { if (!str.empty()) { if (str[0] == '"') str = str.substr(1); if (!str.empty()) { if (str[str.size() - 1] == '"') str = str.substr(0, str.size() - 1); } } }; m_searchString = bk.Search; m_replaceString = bk.Replace; m_bUseRegex = bk.UseRegex; m_bCaseSensitive = bk.CaseSensitive; m_bDotMatchesNewline = bk.DotMatchesNewline; m_bCreateBackup = bk.Backup; m_bWholeWords = bk.WholeWords; m_bUTF8 = bk.Utf8; m_bForceBinary = bk.Binary; m_bIncludeSystem = bk.IncludeSystem; m_bIncludeSubfolders = bk.IncludeFolder; m_bIncludeHidden = bk.IncludeHidden; m_bIncludeBinary = bk.IncludeBinary; m_excludeDirsPatternRegex = bk.ExcludeDirs; m_patternRegex = bk.FileMatch; m_bUseRegexForPaths = bk.FileMatchRegex; if (!bk.Path.empty()) m_searchPath = bk.Path; m_bIncludeSystemC = true; m_bIncludeHiddenC = true; m_bIncludeSubfoldersC = true; m_bIncludeBinaryC = true; m_bCreateBackupC = true; m_bCreateBackupInFoldersC = true; m_bWholeWordsC = true; m_bUTF8C = true; m_bCaseSensitiveC = true; m_bDotMatchesNewlineC = true; m_patternRegexC = true; m_excludeDirsPatternRegexC = true; removeQuotes(m_searchString); removeQuotes(m_replaceString); removeQuotes(m_excludeDirsPatternRegex); removeQuotes(m_patternRegex); } } #if 0 void CSearchDlg::SetCaseSensitive(bool bSet) { m_bCaseSensitiveC = true; m_bCaseSensitive = bSet; } void CSearchDlg::SetMatchesNewline(bool bSet) { m_bDotMatchesNewlineC = true; m_bDotMatchesNewline = bSet; } void CSearchDlg::SetCreateBackups(bool bSet) { m_bCreateBackupC = true; m_bCreateBackup = bSet; m_bConfirmationOnReplace = false; } void CSearchDlg::SetCreateBackupsInFolders(bool bSet) { m_bCreateBackupInFoldersC = true; m_bCreateBackupInFolders = bSet; SetCreateBackups(bSet); } void CSearchDlg::SetWholeWords(bool bSet) { m_bWholeWordsC = true; m_bWholeWords = bSet; } void CSearchDlg::SetUTF8(bool bSet) { m_bUTF8C = true; m_bUTF8 = bSet; m_bForceBinary = false; } void CSearchDlg::SetBinary(bool bSet) { m_bUTF8C = true; m_bForceBinary = bSet; m_bUTF8 = false; } void CSearchDlg::SetSize(uint64_t size, int cmp) { m_bSizeC = true; m_lSize = size; m_sizeCmp = cmp; m_bAllSize = (size == static_cast(-1)); } void CSearchDlg::SetIncludeSystem(bool bSet) { m_bIncludeSystemC = true; m_bIncludeSystem = bSet; } void CSearchDlg::SetIncludeHidden(bool bSet) { m_bIncludeHiddenC = true; m_bIncludeHidden = bSet; } void CSearchDlg::SetIncludeSubfolders(bool bSet) { m_bIncludeSubfoldersC = true; m_bIncludeSubfolders = bSet; } void CSearchDlg::SetIncludeBinary(bool bSet) { m_bIncludeBinaryC = true; m_bIncludeBinary = bSet; } void CSearchDlg::SetDateLimit(int dateLimit, FILETIME t1, FILETIME t2) { m_bDateLimitC = true; m_dateLimit = dateLimit; m_date1 = t1; m_date2 = t2; } #endif bool CSearchDlg::MatchPath(LPCTSTR pathBuf) { bool bPattern = false; // find start of pathname const auto* pName = wcsrchr(pathBuf, '\\'); if (pName == nullptr) pName = pathBuf; else pName++; // skip the last '\\' char if (m_bUseRegexForPaths) { if (m_patterns.empty()) bPattern = true; else { if (grepWinMatchI(m_patternRegex, pName)) bPattern = true; // for a regex check, also test with the full path else if (grepWinMatchI(m_patternRegex, pathBuf)) bPattern = true; } } else { if (!m_patterns.empty()) { if (!(m_patterns[0]).empty() && (m_patterns[0][0]=='-')) bPattern = true; std::wstring fName = pName; std::ranges::transform(fName, fName.begin(), ::towlower); for (const auto& pattern : m_patterns) { if (!pattern.empty() && pattern.at(0)=='-') bPattern = bPattern && !wcswildcmp(&pattern[1], fName.c_str()); else bPattern = bPattern || wcswildcmp(pattern.c_str(), fName.c_str()); } } else bPattern = true; } return bPattern; } int CSearchDlg::SearchFile(std::shared_ptr sinfoPtr, const std::wstring& searchRoot, const SearchFlags_t searchFlags, const std::wstring& searchString, const std::wstring& searchStringUtf16le, const std::wstring& replaceString) { if (IsCancelled()) return -1; // don't start this search thread int nFound = 0; // we keep it simple: // files bigger than 30MB are considered binary. Binary files are searched // as if they're ANSI text files. std::wstring localSearchString = searchString; if (!searchFlags.bUseRegex) { SearchReplace(localSearchString, L"\\E", L"\\\\E"); localSearchString = L"\\Q" + localSearchString + L"\\E"; if (searchFlags.bWholeWords) localSearchString = L"\\b" + localSearchString + L"\\b"; } SearchReplace(localSearchString, L"${filepath}", sinfoPtr->filePath); std::wstring const fileNameFull = sinfoPtr->filePath.substr(sinfoPtr->filePath.find_last_of('\\') + 1); auto dotPos = fileNameFull.find_last_of('.'); if (dotPos != std::string::npos) { std::wstring fileName = fileNameFull.substr(0, dotPos); SearchReplace(localSearchString, L"${filepath}", fileName); if (fileNameFull.size() > dotPos) { std::wstring fileExt = fileNameFull.substr(dotPos + 1); SearchReplace(localSearchString, L"${fileext}", fileExt); } } CTextFile textFile; CTextFile::UnicodeType type = CTextFile::AutoType; bool bLoadResult = false; if (searchFlags.bForceBinary) { type = CTextFile::Binary; } else { ProfileTimer profile((L"file load and parse: " + sinfoPtr->filePath).c_str()); auto nNullCount = bPortable ? int(g_iniFile.GetLongValue(L"settings", L"nullbytes", 0)) : int(static_cast(CRegStdDWORD(L"Software\\grepWinNP3\\nullbytes", 0))); if (nNullCount > 0) { constexpr __int64 oneMB = 1024 * 1024; auto megs = sinfoPtr->fileSize / oneMB; textFile.SetNullbyteCountForBinary(nNullCount * ((int)megs + 1)); } bLoadResult = textFile.Load(sinfoPtr->filePath.c_str(), type, searchFlags.bUTF8, nullptr); } sinfoPtr->encoding = type; if ((bLoadResult) && ((type != CTextFile::Binary) || (searchFlags.bIncludeBinary) || searchFlags.bSearchAlways)) { sinfoPtr->readError = false; std::wstring::const_iterator start, end; start = textFile.GetFileString().begin(); end = textFile.GetFileString().end(); boost::match_results what; try { int ft = boost::regex::normal; if (!searchFlags.bCaseSensitive) ft |= boost::regbase::icase; boost::wregex expression = boost::wregex(localSearchString, ft); boost::match_results whatC; boost::match_flag_type flags = boost::match_default | boost::format_all; if (!searchFlags.bDotMatchesNewline) flags |= boost::match_not_dot_newline; long prevLineStart = 0; long prevLineEnd = 0; while (regex_search(start, end, whatC, expression, flags) && !IsCancelled()) { if (whatC[0].matched) { nFound++; if (IsNOTSearch()) break; long lineStart = textFile.LineFromPosition(long(whatC[0].first - textFile.GetFileString().begin())); long lineEnd = textFile.LineFromPosition(long(whatC[0].second - textFile.GetFileString().begin())); if ((lineStart != prevLineStart)||(lineEnd != prevLineEnd)) { for (long l = lineStart; l <= lineEnd; ++l) { auto sLine = textFile.GetLineString(l); if (searchFlags.bCaptureSearch) { auto out = whatC.format(replaceString, flags); sinfoPtr->matchLines.push_back(std::move(out)); } else sinfoPtr->matchLines.push_back(std::move(sLine)); sinfoPtr->matchLinesNumbers.push_back(l); } } ++sinfoPtr->matchCount; prevLineStart = lineStart; prevLineEnd = lineEnd; } // update search position: if (start == whatC[0].second) { if (start == end) break; ++start; } else start = whatC[0].second; // update flags: flags |= boost::match_prev_avail; flags |= boost::match_not_bob; } if (type == CTextFile::Binary) { boost::wregex expressionUtf16 = boost::wregex(searchStringUtf16le, ft); while (regex_search(start, end, whatC, expressionUtf16, flags) && !IsCancelled()) { if (whatC[0].matched) { nFound++; if (IsNOTSearch()) break; long lineStart = textFile.LineFromPosition(long(whatC[0].first - textFile.GetFileString().begin())); long lineEnd = textFile.LineFromPosition(long(whatC[0].second - textFile.GetFileString().begin())); if (searchFlags.bCaptureSearch) { auto out = whatC.format(replaceString, flags); sinfoPtr->matchLines.push_back(out); sinfoPtr->matchLinesNumbers.push_back(lineStart); } else { if ((lineStart != prevLineStart) || (lineEnd != prevLineEnd)) { for (long l = lineStart; l <= lineEnd; ++l) { sinfoPtr->matchLines.push_back(textFile.GetLineString(l)); sinfoPtr->matchLinesNumbers.push_back(l); } } } ++sinfoPtr->matchCount; prevLineStart = lineStart; prevLineEnd = lineEnd; } // update search position: if (start == whatC[0].second) { if (start == end) break; ++start; } else start = whatC[0].second; // update flags: flags |= boost::match_prev_avail; flags |= boost::match_not_bob; } } if ((searchFlags.bReplace) && (nFound > 0)) { flags &= ~boost::match_prev_avail; flags &= ~boost::match_not_bob; RegexReplaceFormatter replaceFmt(replaceString); replaceFmt.SetReplacePair(L"${filepath}", sinfoPtr->filePath); std::wstring fileNameFullW = sinfoPtr->filePath.substr(sinfoPtr->filePath.find_last_of('\\') + 1); auto dotPosW = fileNameFullW.find_last_of('.'); if (dotPosW != std::string::npos) { std::wstring filename = fileNameFullW.substr(0, dotPosW); replaceFmt.SetReplacePair(L"${filename}", filename); if (fileNameFullW.size() > dotPosW) { std::wstring fileExt = fileNameFullW.substr(dotPosW + 1); replaceFmt.SetReplacePair(L"${fileext}", fileExt); } } std::wstring replaced = regex_replace(textFile.GetFileString(), expression, replaceFmt, flags); if (replaced.compare(textFile.GetFileString())) { textFile.SetFileContent(replaced); if (searchFlags.bCreateBackup) { std::wstring backupfile = sinfoPtr->filePath + L".bak"; if (searchFlags.bBackupInFolder) { std::wstring backupFolder = searchRoot + L"\\grepWinNP3_backup\\"; backupFolder += sinfoPtr->filePath.substr(searchRoot.size() + 1); backupFolder = CPathUtils::GetParentDirectory(backupFolder); CPathUtils::CreateRecursiveDirectory(backupFolder); backupfile = backupFolder + L"\\" + CPathUtils::GetFileName(sinfoPtr->filePath); } s_BackupAndTmpFiles.insert(backupfile); CopyFile(sinfoPtr->filePath.c_str(), backupfile.c_str(), FALSE); } if (!textFile.Save(sinfoPtr->filePath.c_str())) { // saving the file failed. Find out why... DWORD err = GetLastError(); if (err == ERROR_ACCESS_DENIED) { // access denied can happen if the file has the // read-only flag and/or the hidden flag set // those are not situations where we should fail, so // we reset those flags and restore them // again after saving the file DWORD origAttributes = GetFileAttributes(sinfoPtr->filePath.c_str()); DWORD newAttributes = origAttributes & (~(FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_SYSTEM)); SetFileAttributes(sinfoPtr->filePath.c_str(), newAttributes); bool bRet = textFile.Save(sinfoPtr->filePath.c_str()); // restore the attributes SetFileAttributes(sinfoPtr->filePath.c_str(), origAttributes); if (!bRet) { //SendMessage(*this, SEARCH_PROGRESS, 0, 0); return -1; } } } } } } catch (const std::exception&) { //SendMessage(*this, SEARCH_PROGRESS, 0, 0); return -1; } } else { if (type == CTextFile::AutoType) { sinfoPtr->readError = true; //SendMessage(*this, SEARCH_FOUND, 0, reinterpret_cast(&sInfo)); //SendMessage(*this, SEARCH_PROGRESS, 1, 0); return -1; } // file is either too big or binary. // in any case, use the search function that uses a file iterator // instead of a string iterator to reduce the memory consumption if ((type != CTextFile::Binary) || searchFlags.bIncludeBinary || searchFlags.bSearchAlways || searchFlags.bForceBinary) { sinfoPtr->encoding = type; std::string filePath = CUnicodeUtils::StdGetANSI(sinfoPtr->filePath); std::string searchFor = (type == CTextFile::Ansi) ? CUnicodeUtils::StdGetANSI(searchString) : CUnicodeUtils::StdGetUTF8(searchString); if (!searchFlags.bUseRegex) { searchFor = "\\Q"; searchFor += CUnicodeUtils::StdGetUTF8(searchString); searchFor += "\\E"; } boost::match_results what; boost::match_flag_type flags = boost::match_default | boost::format_all; if (!searchFlags.bDotMatchesNewline) flags |= boost::match_not_dot_newline; int ft = boost::regex::normal; if (!searchFlags.bCaseSensitive) ft |= boost::regbase::icase; try { boost::regex expression = boost::regex(searchFor, ft); std::vector matchLinesNumbers; bool bFound = false; { boost::spirit::classic::file_iterator<> start(filePath.c_str()); boost::spirit::classic::file_iterator<> fbeg = start; boost::spirit::classic::file_iterator<> end = start.make_end(); boost::match_results> whatC; while (boost::regex_search(start, end, whatC, expression, flags) && !IsCancelled()) { nFound++; if (IsNOTSearch()) break; matchLinesNumbers.push_back(static_cast(whatC[0].first - fbeg)); ++sinfoPtr->matchCount; // update search position: start = whatC[0].second; // update flags: flags |= boost::match_prev_avail; flags |= boost::match_not_bob; bFound = true; } } if ((type == CTextFile::Binary) && !searchFlags.bReplace) { boost::regex expressionUtf16le = boost::regex(CUnicodeUtils::StdGetUTF8(searchStringUtf16le), ft); boost::spirit::classic::file_iterator<> start(filePath.c_str()); boost::spirit::classic::file_iterator<> fbeg = start; boost::spirit::classic::file_iterator<> end = start.make_end(); boost::match_results> whatC; while (boost::regex_search(start, end, whatC, expression, flags) && !IsCancelled()) { nFound++; if (IsNOTSearch()) break; matchLinesNumbers.push_back(static_cast(whatC[0].first - fbeg)); ++sinfoPtr->matchCount; // update search position: start = whatC[0].second; // update flags: flags |= boost::match_prev_avail; flags |= boost::match_not_bob; bFound = true; } } if (bFound && !IsCancelled()) { if (!bLoadResult && (type != CTextFile::Binary) && !IsNOTSearch()) { linePositions.clear(); // open the file and set up a vector of all lines CAutoFile hFile; int retrycounter = 0; do { if (retrycounter) Sleep(20); hFile = CreateFile(sinfoPtr->filePath.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); retrycounter++; } while (!hFile && retrycounter < 5); if (hFile) { auto fBuf = std::make_unique(4096); DWORD bytesRead = 0; size_t pos = 0; while (ReadFile(hFile, fBuf.get(), 4096, &bytesRead, nullptr)) { if (bytesRead == 0) break; for (DWORD br = 0; br < bytesRead; ++br) { if (fBuf[br] == '\r') { ++br; ++pos; if (br < bytesRead) { if (fBuf[br] == '\n') { // crlf lineending auto lp = linePositions.size(); linePositions[pos] = static_cast(lp); } else { // cr lineending auto lp = linePositions.size(); linePositions[pos - 1] = static_cast(lp); } } else break; } else if (fBuf[br] == '\n') { // lf lineending auto lp = linePositions.size(); linePositions[pos] = static_cast(lp); } ++pos; } } for (unsigned long & matchlinesnumber : matchLinesNumbers) { auto fp = linePositions.lower_bound(matchlinesnumber); if (fp != linePositions.end()) matchlinesnumber = fp->second; } } } sinfoPtr->matchLinesNumbers = matchLinesNumbers; if (searchFlags.bReplace) { std::wstring backupfile = sinfoPtr->filePath + L".bak"; if (searchFlags.bCreateBackup) { if (searchFlags.bBackupInFolder) { std::wstring backupFolder = searchRoot + L"\\grepWinNP3_backup\\"; backupFolder += sinfoPtr->filePath.substr(searchRoot.size() + 1); backupFolder = CPathUtils::GetParentDirectory(backupFolder); CPathUtils::CreateRecursiveDirectory(backupFolder); backupfile = backupFolder + L"\\" + CPathUtils::GetFileName(sinfoPtr->filePath); } CopyFile(sinfoPtr->filePath.c_str(), backupfile.c_str(), FALSE); s_BackupAndTmpFiles.insert(backupfile); } flags &= ~boost::match_prev_avail; flags &= ~boost::match_not_bob; RegexReplaceFormatterA replaceFmt(CUnicodeUtils::StdGetUTF8(replaceString)); replaceFmt.SetReplacePair("${filepath}", CUnicodeUtils::StdGetUTF8(sinfoPtr->filePath)); std::string fileNameFullA = CUnicodeUtils::StdGetUTF8(sinfoPtr->filePath.substr(sinfoPtr->filePath.find_last_of('\\') + 1)); auto dotPosA = fileNameFullA.find_last_of('.'); if (dotPosA != std::string::npos) { std::string filename = fileNameFullA.substr(0, dotPosA); replaceFmt.SetReplacePair("${filename}", filename); if (fileNameFull.size() > dotPosA) { std::string fileExt = fileNameFullA.substr(dotPosA + 1); replaceFmt.SetReplacePair("${fileext}", fileExt); } } std::string filepathout = searchFlags.bCreateBackup ? filePath : filePath + ".grepwinreplaced"; if (!searchFlags.bCreateBackup) s_BackupAndTmpFiles.insert(sinfoPtr->filePath + L".grepwinreplaced"); boost::iostreams::mapped_file_source replaceinfile(searchFlags.bCreateBackup ? CUnicodeUtils::StdGetANSI(backupfile).c_str() : filePath.c_str()); std::ofstream os(filepathout.c_str(), std::ios::out | std::ios::trunc | std::ios::binary); std::ostream_iterator out(os); regex_replace(out, replaceinfile.begin(), replaceinfile.end(), expression, replaceFmt, flags); os.close(); replaceinfile.close(); if (!searchFlags.bCreateBackup) MoveFileExA(filepathout.c_str(), filePath.c_str(), MOVEFILE_REPLACE_EXISTING); } } } catch (const std::exception&) { return -1; } catch (...) { return -1; } } else nFound = -1; // skipped } if (IsNOTSearch()) return ((nFound > 0) ? 0 : 1); return nFound; } void CSearchDlg::FormatDate(wchar_t dateNative[], const FILETIME& fileTime, bool forceShortFmt) const { dateNative[0] = '\0'; // Convert UTC to local time SYSTEMTIME systemTime; FileTimeToSystemTime(&fileTime, &systemTime); static TIME_ZONE_INFORMATION timeZone = {-1}; if (timeZone.Bias == -1) GetTimeZoneInformation(&timeZone); SYSTEMTIME localSystime; SystemTimeToTzSpecificLocalTime(&timeZone, &systemTime, &localSystime); wchar_t timeBuf[GREPWIN_DATEBUFFER] = {0}; wchar_t dateBuf[GREPWIN_DATEBUFFER] = {0}; LCID locale = MAKELCID(MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), SORT_DEFAULT); /// reusing this instance is vital for \ref formatDate performance DWORD flags = forceShortFmt ? DATE_SHORTDATE : DATE_LONGDATE; GetDateFormat(locale, flags, &localSystime, nullptr, dateBuf, GREPWIN_DATEBUFFER); GetTimeFormat(locale, 0, &localSystime, nullptr, timeBuf, GREPWIN_DATEBUFFER); wcsncat_s(dateNative, GREPWIN_DATEBUFFER, dateBuf, GREPWIN_DATEBUFFER); wcsncat_s(dateNative, GREPWIN_DATEBUFFER, L" ", GREPWIN_DATEBUFFER); wcsncat_s(dateNative, GREPWIN_DATEBUFFER, timeBuf, GREPWIN_DATEBUFFER); } int CSearchDlg::CheckRegex() { auto buf = GetDlgItemText(IDC_SEARCHTEXT); int len = static_cast(wcslen(buf.get())); if (IsDlgButtonChecked(*this, IDC_REGEXRADIO) == BST_CHECKED) { m_bUseRegex = true; // check if the regex is valid bool bValid = true; if (len) { try { std::wstring localSearchString = buf.get(); SearchReplace(localSearchString, L"${filepath}", L""); SearchReplace(localSearchString, L"${filepath}", L""); SearchReplace(localSearchString, L"${fileext}", L""); boost::wregex expression = boost::wregex(localSearchString); } catch (const std::exception&) { bValid = false; } } if (len) { if (bValid) { SetDlgItemText(*this, IDC_REGEXOKLABEL, TranslatedString(hResource, IDS_REGEXOK).c_str()); DialogEnableWindow(IDOK, true); DialogEnableWindow(IDC_REPLACE, true); DialogEnableWindow(IDC_CREATEBACKUP, true); } else { SetDlgItemText(*this, IDC_REGEXOKLABEL, TranslatedString(hResource, IDS_REGEXINVALID).c_str()); DialogEnableWindow(IDOK, false); DialogEnableWindow(IDC_REPLACE, false); DialogEnableWindow(IDC_CREATEBACKUP, false); } } else { SetDlgItemText(*this, IDC_REGEXOKLABEL, L""); DialogEnableWindow(IDOK, true); DialogEnableWindow(IDC_REPLACE, false); DialogEnableWindow(IDC_CREATEBACKUP, false); } } else { m_bUseRegex = false; SetDlgItemText(*this, IDC_REGEXOKLABEL, L""); DialogEnableWindow(IDOK, true); DialogEnableWindow(IDC_REPLACE, len > 0); DialogEnableWindow(IDC_CREATEBACKUP, len > 0); } return len; } void CSearchDlg::AutoSizeAllColumns() { HWND hListControl = GetDlgItem(*this, IDC_RESULTLIST); auto headerCtrl = ListView_GetHeader(hListControl); int nItemCount = ListView_GetItemCount(hListControl); wchar_t textBuf[MAX_PATH * 4] = {0}; std::vector colWidths; if (headerCtrl) { int maxCol = Header_GetItemCount(headerCtrl) - 1; int imgWidth = 0; auto hImgList = ListView_GetImageList(hListControl, LVSIL_SMALL); if ((hImgList) && (ImageList_GetImageCount(hImgList))) { IMAGEINFO imgInfo; ImageList_GetImageInfo(hImgList, 0, &imgInfo); imgWidth = (imgInfo.rcImage.right - imgInfo.rcImage.left) + 3; // 3 pixels between icon and text } for (int col = 0; col <= maxCol; col++) { HDITEM hdi = {0}; hdi.mask = HDI_TEXT; hdi.pszText = textBuf; hdi.cchTextMax = _countof(textBuf); Header_GetItem(headerCtrl, col, &hdi); int cx = ListView_GetStringWidth(hListControl, hdi.pszText) + 20; // 20 pixels for col separator and margin int inc = std::max(1, nItemCount / 1000); for (int index = 0; index < nItemCount; index = index + inc) { // get the width of the string and add 14 pixels for the column separator and margins ListView_GetItemText(hListControl, index, col, textBuf, _countof(textBuf)); int lineWidth = ListView_GetStringWidth(hListControl, textBuf) + 14; // add the image size if (col == 0) lineWidth += imgWidth; if (cx < lineWidth) cx = lineWidth; } colWidths.push_back(cx); } } bool fileList = (IsDlgButtonChecked(*this, IDC_RESULTFILES) == BST_CHECKED); if (!fileList) { RECT rc{}; ListView_GetItemRect(hListControl, 0, &rc, LVIR_BOUNDS); auto itemWidth = rc.right - rc.left; ListView_GetItemRect(hListControl, 0, &rc, LVIR_ICON); auto iconWidth = rc.right - rc.left; itemWidth -= iconWidth; itemWidth -= 2 * GetSystemMetrics(SM_CXBORDER); auto totalWidth = std::accumulate(colWidths.begin(), colWidths.end(), 0); totalWidth -= colWidths[colWidths.size() - 2]; auto textWidth = itemWidth - totalWidth; if (textWidth > 0) colWidths[colWidths.size() - 2] = textWidth; else { colWidths[colWidths.size() - 1] = 100; totalWidth = std::accumulate(colWidths.begin(), colWidths.end(), 0); totalWidth -= colWidths[colWidths.size() - 2]; textWidth = itemWidth - totalWidth; if (textWidth > 0) colWidths[colWidths.size() - 2] = textWidth; } } int col = 0; for (const auto& colWidth : colWidths) { ListView_SetColumnWidth(hListControl, col, colWidth); ++col; } } int CSearchDlg::GetSelectedListIndex(int index) { bool fileList = (IsDlgButtonChecked(*this, IDC_RESULTFILES) == BST_CHECKED); if (fileList) return index; auto tup = m_listItems[index]; return std::get<0>(tup); } bool CSearchDlg::FailedShowMessage(HRESULT hr) { if (FAILED(hr)) { _com_error err(hr); MessageBox(nullptr, L"grepWinNP3", err.ErrorMessage(), MB_ICONERROR); return true; } return false; } #ifdef NP3_ALLOW_UPDATE void CSearchDlg::CheckForUpdates(bool force) { bool bNewerAvailable = false; // check for newer versions bool doCheck = true; if (bPortable) doCheck = g_iniFile.GetBoolValue(L"global", L"CheckForUpdates", true)); else doCheck = !!static_cast(CRegStdDWORD(L"Software\\grepWinNP3\\CheckForUpdates", 1)); if (doCheck) { time_t now; time(&now); time_t last = 0; if (bPortable) { last = _wtoll(g_iniFile.GetValue(L"global", L"CheckForUpdatesLast", L"0")); } else { last = _wtoll(static_cast(CRegStdString(L"Software\\grepWinNP3\\CheckForUpdatesLast", L"0")).c_str()); } double days = std::difftime(now, last) / (60LL * 60LL * 24LL); if ((days >= 7.0) || force) { std::wstring tempFile = CTempFiles::Instance().GetTempFilePath(true); std::wstring sCheckURL = L"https://raw.githubusercontent.com/stefankueng/grepWin/main/version.txt"; HRESULT res = URLDownloadToFile(nullptr, sCheckURL.c_str(), tempFile.c_str(), 0, nullptr); if (res == S_OK) { if (bPortable) { g_iniFile.SetValue(L"global", L"CheckForUpdatesLast", std::to_wstring(now).c_str()); } else { // ReSharper disable once CppEntityAssignedButNoRead auto regLast = CRegStdString(L"Software\\grepWin\\CheckForUpdatesLast", L"0"); regLast = std::to_wstring(now); } std::ifstream file; file.open(tempFile.c_str()); if (file.good()) { char line[1024]; file.getline(line, sizeof(line)); auto verLine = CUnicodeUtils::StdGetUnicode(line); bNewerAvailable = IsVersionNewer(verLine); file.getline(line, sizeof(line)); auto updateUrl = CUnicodeUtils::StdGetUnicode(line); if (bNewerAvailable) { if (bPortable) { g_iniFile.SetValue(L"global", L"CheckForUpdatesVersion", verLine.c_str()); g_iniFile.SetValue(L"global", L"CheckForUpdatesUrl", updateUrl.c_str()); } else { // ReSharper disable once CppEntityAssignedButNoRead auto regVersion = CRegStdString(L"Software\\grepWinNP3\\CheckForUpdatesVersion", L""); regVersion = verLine; // ReSharper disable once CppEntityAssignedButNoRead auto regUpdateUrl = CRegStdString(L"Software\\grepWinNP3\\CheckForUpdatesUrl", L""); regUpdateUrl = updateUrl; } ShowUpdateAvailable(); } } file.close(); DeleteFile(tempFile.c_str()); } } } } void CSearchDlg::ShowUpdateAvailable() { std::wstring sVersion; std::wstring updateUrl; if (bPortable) { sVersion = g_iniFile.GetValue(L"global", L"CheckForUpdatesVersion", L""); updateUrl = g_iniFile.GetValue(L"global", L"CheckForUpdatesUrl", L""); } else { sVersion = CRegStdString(L"Software\\grepWinNP3\\CheckForUpdatesVersion", L""); updateUrl = CRegStdString(L"Software\\grepWinNP3\\CheckForUpdatesUrl", L""); } if (IsVersionNewer(sVersion)) { auto sUpdateAvailable = TranslatedString(hResource, IDS_UPDATEAVAILABLE); sUpdateAvailable = CStringUtils::Format(sUpdateAvailable.c_str(), sVersion.c_str()); auto sLinkText = CStringUtils::Format(L"%s", updateUrl.c_str(), sUpdateAvailable.c_str()); SetDlgItemText(*this, IDC_UPDATELINK, sLinkText.c_str()); ShowWindow(GetDlgItem(*this, IDC_UPDATELINK), SW_SHOW); } } bool CSearchDlg::IsVersionNewer(const std::wstring& sVer) const { int major = 0; int minor = 0; int micro = 0; int build = 0; const wchar_t* pLine = sVer.c_str(); major = _wtoi(pLine); pLine = wcschr(pLine, '.'); if (pLine) { pLine++; minor = _wtoi(pLine); pLine = wcschr(pLine, '.'); if (pLine) { pLine++; micro = _wtoi(pLine); pLine = wcschr(pLine, '.'); if (pLine) { pLine++; build = _wtoi(pLine); } } } bool isNewer = false; if (major > GREPWIN_VERMAJOR) isNewer = true; else if ((minor > GREPWIN_VERMINOR) && (major == GREPWIN_VERMAJOR)) isNewer = true; else if ((micro > GREPWIN_VERMICRO) && (minor == GREPWIN_VERMINOR) && (major == GREPWIN_VERMAJOR)) isNewer = true; else if ((build > GREPWIN_VERBUILD) && (micro == GREPWIN_VERMICRO) && (minor == GREPWIN_VERMINOR) && (major == GREPWIN_VERMAJOR)) isNewer = true; return isNewer; } #endif bool CSearchDlg::CloneWindow() { if (!SaveSettings()) { return false; } if (bPortable) { g_iniFile.SaveFile(g_iniPath.c_str()); } auto const dir = CPathUtils::GetModuleDir(); auto const file = CPathUtils::GetFileName(CPathUtils::GetModulePath()); std::wstring arguments; arguments += CStringUtils::Format(L" /inipath:\"%s\"", g_iniPath.c_str()); arguments += CStringUtils::Format(L" /searchpath:\"%s\"", m_searchPath.c_str()); arguments += CStringUtils::Format(L" /searchfor:\"%s\"", m_searchString.c_str()); arguments += CStringUtils::Format(L" /replacewith:\"%s\"", m_replaceString.c_str()); arguments += L" /new"; // prevents searching of existing instance SHELLEXECUTEINFO sei = {0}; sei.cbSize = sizeof(SHELLEXECUTEINFO); sei.lpVerb = TEXT("open"); sei.lpFile = file.c_str(); sei.lpParameters = arguments.c_str(); sei.lpDirectory = dir.c_str(); sei.nShow = SW_SHOWNORMAL; ShellExecuteEx(&sei); return true; } std::wstring CSearchDlg::ExpandString(const std::wstring& replaceString) const { // ${now,formatString} wchar_t buf[4096] = {}; GetDateFormat(LOCALE_USER_DEFAULT, DATE_SHORTDATE, nullptr, nullptr, buf, _countof(buf)); std::wstring dateStr = buf; GetTimeFormat(LOCALE_USER_DEFAULT, 0, nullptr, nullptr, buf, _countof(buf)); dateStr += L" - "; dateStr += buf; std::time_t now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); int ft = boost::regex::normal; boost::wregex expression = boost::wregex(L"\\$\\{now\\s*,?([^}]*)\\}", ft); boost::match_results whatC; boost::match_flag_type flags = boost::match_default | boost::format_all; auto resultString = replaceString; try { while (regex_search(resultString.cbegin(), resultString.cend(), whatC, expression, flags)) { if (whatC[0].matched) { auto fullMatch = whatC.str(); std::wstring formatStr; if (whatC.size() > 1) { formatStr = whatC[1].str(); if (!formatStr.empty()) { std::wstring formattedDateStr(4096, '\0'); struct tm locTime; _localtime64_s(&locTime, &now); std::wcsftime(&formattedDateStr[0], formattedDateStr.size(), formatStr.c_str(), &locTime); SearchReplace(resultString, fullMatch, formattedDateStr); } } else { SearchReplace(resultString, fullMatch, dateStr); } } } } catch (const std::exception&) { } return resultString; }