[ENHANCEMENT] (Author: Pavel Nedev) Show progress window during FindInFiles and ReplaceInFiles.

git-svn-id: svn://svn.tuxfamily.org/svnroot/notepadplus/repository/trunk@1345 f5eea248-9336-0410-98b8-ebc06183d4e3
remotes/trunk
Don Ho 2015-02-19 01:01:12 +00:00
parent 78bbeb4624
commit d9299e644f
3 changed files with 376 additions and 43 deletions

View File

@ -1420,12 +1420,6 @@ void Notepad_plus::getMatchedFileNames(const TCHAR *dir, const vector<generic_st
::FindClose(hFile);
}
DWORD WINAPI AsyncCancelFindInFiles(LPVOID NppHWND)
{
MessageBox((HWND) NULL, TEXT("Searching...\nPress Enter to Cancel"), TEXT("Find In Files"), MB_OK);
PostMessage((HWND) NppHWND, NPPM_INTERNAL_CANCEL_FIND_IN_FILES, 0, 0);
return 0;
}
bool Notepad_plus::replaceInFiles()
{
@ -1443,7 +1437,6 @@ bool Notepad_plus::replaceInFiles()
_pEditView = &_invisibleEditView;
Document oldDoc = _invisibleEditView.execute(SCI_GETDOCPOINTER);
Buffer * oldBuf = _invisibleEditView.getCurrentBuffer(); //for manually setting the buffer, so notifications can be handled properly
HANDLE CancelThreadHandle = NULL;
vector<generic_string> patterns2Match;
_findReplaceDlg.getPatterns(patterns2Match);
@ -1456,24 +1449,28 @@ bool Notepad_plus::replaceInFiles()
getMatchedFileNames(dir2Search, patterns2Match, fileNames, isRecursive, isInHiddenDir);
if (fileNames.size() > 1)
CancelThreadHandle = ::CreateThread(NULL, 0, AsyncCancelFindInFiles, _pPublicInterface->getHSelf(), 0, NULL);
bool dontClose = false;
for (size_t i = 0, len = fileNames.size(); i < len ; ++i)
{
MSG msg;
if (PeekMessage(&msg, _pPublicInterface->getHSelf(), NPPM_INTERNAL_CANCEL_FIND_IN_FILES, NPPM_INTERNAL_CANCEL_FIND_IN_FILES, PM_REMOVE)) break;
CProgress progress;
size_t filesCount = fileNames.size();
size_t filesPerPercent = 1;
if (filesCount > 1)
{
if (filesCount >= 200)
filesPerPercent = filesCount / 100;
progress.Open(NULL, TEXT("Replace In Files progress..."));
}
for (size_t i = 0, updateOnCount = filesPerPercent; i < filesCount; ++i)
{
if (progress.IsCancelled()) break;
bool closeBuf = false;
BufferID id = MainFileManager->getBufferFromName(fileNames.at(i).c_str());
if (id != BUFFER_INVALID)
{
dontClose = true;
}
else
if (id == BUFFER_INVALID)
{
id = MainFileManager->loadFile(fileNames.at(i).c_str());
dontClose = false;
closeBuf = true;
}
if (id != BUFFER_INVALID)
@ -1491,13 +1488,17 @@ bool Notepad_plus::replaceInFiles()
MainFileManager->saveBuffer(id, pBuf->getFullPathName());
}
if (!dontClose)
if (closeBuf)
MainFileManager->closeBuffer(id, _pEditView);
}
if (i == updateOnCount)
{
updateOnCount += filesPerPercent;
progress.SetPercent((i * 100) / filesCount, fileNames.at(i).c_str());
}
}
if (CancelThreadHandle)
TerminateThread(CancelThreadHandle, 0);
progress.Close();
_invisibleEditView.execute(SCI_SETDOCPOINTER, 0, oldDoc);
_invisibleEditView.setCurrentBuffer(oldBuf);
@ -1525,7 +1526,6 @@ bool Notepad_plus::findInFiles()
ScintillaEditView *pOldView = _pEditView;
_pEditView = &_invisibleEditView;
Document oldDoc = _invisibleEditView.execute(SCI_GETDOCPOINTER);
HANDLE CancelThreadHandle = NULL;
vector<generic_string> patterns2Match;
_findReplaceDlg.getPatterns(patterns2Match);
@ -1537,26 +1537,29 @@ bool Notepad_plus::findInFiles()
vector<generic_string> fileNames;
getMatchedFileNames(dir2Search, patterns2Match, fileNames, isRecursive, isInHiddenDir);
if (fileNames.size() > 1)
CancelThreadHandle = ::CreateThread(NULL, 0, AsyncCancelFindInFiles, _pPublicInterface->getHSelf(), 0, NULL);
_findReplaceDlg.beginNewFilesSearch();
bool dontClose = false;
for (size_t i = 0, len = fileNames.size(); i < len; ++i)
CProgress progress;
size_t filesCount = fileNames.size();
size_t filesPerPercent = 1;
if (filesCount > 1)
{
if (filesCount >= 200)
filesPerPercent = filesCount / 100;
progress.Open(NULL, TEXT("Find In Files progress..."));
}
for (size_t i = 0, updateOnCount = filesPerPercent; i < filesCount; ++i)
{
MSG msg;
if (PeekMessage(&msg, _pPublicInterface->getHSelf(), NPPM_INTERNAL_CANCEL_FIND_IN_FILES, NPPM_INTERNAL_CANCEL_FIND_IN_FILES, PM_REMOVE)) break;
if (progress.IsCancelled()) break;
bool closeBuf = false;
BufferID id = MainFileManager->getBufferFromName(fileNames.at(i).c_str());
if (id != BUFFER_INVALID)
{
dontClose = true;
}
else
if (id == BUFFER_INVALID)
{
id = MainFileManager->loadFile(fileNames.at(i).c_str());
dontClose = false;
closeBuf = true;
}
if (id != BUFFER_INVALID)
@ -1567,13 +1570,17 @@ bool Notepad_plus::findInFiles()
_invisibleEditView.execute(SCI_SETCODEPAGE, pBuf->getUnicodeMode() == uni8Bit ? cp : SC_CP_UTF8);
nbTotal += _findReplaceDlg.processAll(ProcessFindAll, FindReplaceDlg::_env, true, fileNames.at(i).c_str());
if (!dontClose)
if (closeBuf)
MainFileManager->closeBuffer(id, _pEditView);
}
if (i == updateOnCount)
{
updateOnCount += filesPerPercent;
progress.SetPercent((i * 100) / filesCount, fileNames.at(i).c_str());
}
}
if (CancelThreadHandle)
TerminateThread(CancelThreadHandle, 0);
progress.Close();
_findReplaceDlg.finishFilesSearch(nbTotal);

View File

@ -2902,3 +2902,287 @@ void FindIncrementDlg::addToRebar(ReBar * rebar)
_pRebar->addBand(&_rbBand, true);
_pRebar->setGrayBackground(_rbBand.wID);
}
const TCHAR CProgress::cClassName[] = TEXT("NppProgressClass");
const TCHAR CProgress::cDefaultHeader[] = TEXT("Operation progress...");
const int CProgress::cBackgroundColor = COLOR_3DFACE;
const int CProgress::cPBwidth = 600;
const int CProgress::cPBheight = 10;
const int CProgress::cBTNwidth = 80;
const int CProgress::cBTNheight = 25;
volatile LONG CProgress::RefCount = 0;
HINSTANCE CProgress::HInst = NULL;
CProgress::CProgress() : _hwnd(NULL)
{
if (::InterlockedIncrement(&RefCount) == 1)
{
::GetModuleHandleEx(
GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
GET_MODULE_HANDLE_EX_FLAG_PIN, cClassName, &HInst);
WNDCLASSEX wcex;
::SecureZeroMemory(&wcex, sizeof(wcex));
wcex.cbSize = sizeof(wcex);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = wndProc;
wcex.hInstance = HInst;
wcex.hCursor = ::LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = ::GetSysColorBrush(cBackgroundColor);
wcex.lpszClassName = cClassName;
::RegisterClassEx(&wcex);
INITCOMMONCONTROLSEX icex;
::SecureZeroMemory(&icex, sizeof(icex));
icex.dwSize = sizeof(icex);
icex.dwICC = ICC_STANDARD_CLASSES | ICC_PROGRESS_CLASS;
::InitCommonControlsEx(&icex);
}
}
CProgress::~CProgress()
{
Close();
if (::InterlockedDecrement(&RefCount) == 0)
::UnregisterClass(cClassName, HInst);
}
HWND CProgress::Open(HWND hOwner, const TCHAR* header)
{
if (_hwnd)
return _hwnd;
_hOwner = hOwner;
if (header)
_tcscpy_s(_header, _countof(_header), header);
else
_tcscpy_s(_header, _countof(_header), cDefaultHeader);
// Create manually reset non-signaled event
_hActiveState = ::CreateEvent(NULL, TRUE, FALSE, NULL);
if (!_hActiveState)
return NULL;
_hThread = ::CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)threadFunc,
(LPVOID)this, 0, NULL);
if (!_hThread)
{
::CloseHandle(_hActiveState);
return NULL;
}
// Wait for the progress window to be created
::WaitForSingleObject(_hActiveState, INFINITE);
// On progress window create fail
if (!_hwnd)
{
::WaitForSingleObject(_hThread, INFINITE);
::CloseHandle(_hThread);
::CloseHandle(_hActiveState);
}
return _hwnd;
}
bool CProgress::IsCancelled() const
{
if (_hwnd)
return (::WaitForSingleObject(_hActiveState, 0) != WAIT_OBJECT_0);
return false;
}
void CProgress::SetPercent(unsigned percent, const TCHAR *fileName) const
{
if (_hwnd)
{
::SendNotifyMessage(_hPBar, PBM_SETPOS, (WPARAM)percent, 0);
::SendMessage(_hPText, WM_SETTEXT, 0, (LPARAM)fileName);
}
}
void CProgress::Close()
{
if (_hwnd)
{
::SendMessage(_hwnd, WM_CLOSE, 0, 0);
_hwnd = NULL;
::WaitForSingleObject(_hThread, INFINITE);
::CloseHandle(_hThread);
::CloseHandle(_hActiveState);
}
}
DWORD CProgress::threadFunc(LPVOID data)
{
CProgress* pw = static_cast<CProgress*>(data);
return (DWORD)pw->thread();
}
int CProgress::thread()
{
BOOL r = createProgressWindow();
::SetEvent(_hActiveState);
if (r)
return r;
// Window message loop
MSG msg;
while ((r = ::GetMessage(&msg, NULL, 0, 0)) != 0 && r != -1)
{
::TranslateMessage(&msg);
::DispatchMessage(&msg);
}
return r;
}
int CProgress::createProgressWindow()
{
DWORD styleEx = WS_EX_OVERLAPPEDWINDOW;
if (_hOwner)
styleEx |= WS_EX_TOOLWINDOW;
_hwnd = ::CreateWindowEx(styleEx,
cClassName, _header, WS_POPUP | WS_CAPTION,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
_hOwner, NULL, HInst, (LPVOID)this);
if (!_hwnd)
return -1;
int width = cPBwidth + 10;
int height = cPBheight + cBTNheight + 35;
RECT win = adjustSizeAndPos(width, height);
::MoveWindow(_hwnd, win.left, win.top,
win.right - win.left, win.bottom - win.top, TRUE);
::GetClientRect(_hwnd, &win);
width = win.right - win.left;
height = win.bottom - win.top;
_hPText = ::CreateWindowEx(0, TEXT("STATIC"), TEXT(""),
WS_CHILD | WS_VISIBLE | BS_TEXT,
5, 5,
width - 10, 20, _hwnd, NULL, HInst, NULL);
HFONT hf = (HFONT)::GetStockObject(DEFAULT_GUI_FONT);
if (hf)
::SendMessage(_hPText, WM_SETFONT, (WPARAM)hf, MAKELPARAM(TRUE, 0));
_hPBar = ::CreateWindowEx(0, PROGRESS_CLASS, TEXT("Progress Bar"),
WS_CHILD | WS_VISIBLE | PBS_SMOOTH,
5, 25, width - 10, cPBheight,
_hwnd, NULL, HInst, NULL);
SendMessage(_hPBar, PBM_SETRANGE, 0, MAKELPARAM(0, 100));
_hBtn = ::CreateWindowEx(0, TEXT("BUTTON"), TEXT("Cancel"),
WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON | BS_TEXT,
(width - cBTNwidth) / 2, height - cBTNheight - 5,
cBTNwidth, cBTNheight, _hwnd, NULL, HInst, NULL);
if (hf)
::SendMessage(_hBtn, WM_SETFONT, (WPARAM)hf, MAKELPARAM(TRUE, 0));
::ShowWindow(_hwnd, SW_SHOWNORMAL);
::UpdateWindow(_hwnd);
return 0;
}
RECT CProgress::adjustSizeAndPos(int width, int height)
{
RECT win, maxWin;
::GetWindowRect(::GetDesktopWindow(), &maxWin);
win = maxWin;
win.right = win.left + width;
win.bottom = win.top + height;
::AdjustWindowRectEx(&win, ::GetWindowLongPtr(_hwnd, GWL_STYLE),
FALSE, ::GetWindowLongPtr(_hwnd, GWL_EXSTYLE));
width = win.right - win.left;
height = win.bottom - win.top;
if (width < maxWin.right - maxWin.left)
{
win.left = (maxWin.left + maxWin.right - width) / 2;
win.right = win.left + width;
}
else
{
win.left = maxWin.left;
win.right = maxWin.right;
}
if (height < maxWin.right - maxWin.left)
{
win.top = (maxWin.top + maxWin.bottom - height) / 2;
win.bottom = win.top + height;
}
else
{
win.top = maxWin.top;
win.bottom = maxWin.bottom;
}
return win;
}
LRESULT APIENTRY CProgress::wndProc(HWND hwnd, UINT umsg, WPARAM wparam, LPARAM lparam)
{
switch (umsg)
{
case WM_CREATE:
{
CProgress* pw =(CProgress*)((LPCREATESTRUCT)lparam)->lpCreateParams;
::SetWindowLongPtr(hwnd, GWLP_USERDATA, PtrToUlong(pw));
return 0;
}
case WM_SETFOCUS:
{
CProgress* pw = reinterpret_cast<CProgress*>(static_cast<LONG_PTR>
(::GetWindowLongPtr(hwnd, GWLP_USERDATA)));
::SetFocus(pw->_hBtn);
return 0;
}
case WM_COMMAND:
if (HIWORD(wparam) == BN_CLICKED)
{
CProgress* pw =
reinterpret_cast<CProgress*>(static_cast<LONG_PTR>
(::GetWindowLongPtr(hwnd, GWLP_USERDATA)));
::ResetEvent(pw->_hActiveState);
::EnableWindow(pw->_hBtn, FALSE);
return 0;
}
break;
case WM_DESTROY:
::PostQuitMessage(0);
return 0;
}
return ::DefWindowProc(hwnd, umsg, wparam, lparam);
}

View File

@ -404,6 +404,48 @@ private :
};
class CProgress
{
public:
CProgress();
~CProgress();
HWND Open(HWND hOwner = NULL, const TCHAR* header = NULL);
bool IsCancelled() const;
void SetPercent(unsigned percent, const TCHAR *fileName) const;
void Close();
private:
static const TCHAR cClassName[];
static const TCHAR cDefaultHeader[];
static const int cBackgroundColor;
static const int cPBwidth;
static const int cPBheight;
static const int cBTNwidth;
static const int cBTNheight;
static volatile LONG RefCount;
static HINSTANCE HInst;
static DWORD threadFunc(LPVOID data);
static LRESULT APIENTRY wndProc(HWND hwnd, UINT umsg, WPARAM wparam, LPARAM lparam);
// Disable copy construction and operator=
CProgress(const CProgress&);
const CProgress& operator=(const CProgress&);
int thread();
int createProgressWindow();
RECT adjustSizeAndPos(int width, int height);
volatile HWND _hwnd;
HWND _hOwner;
TCHAR _header[128];
HANDLE _hThread;
HANDLE _hActiveState;
HWND _hPText;
HWND _hPBar;
HWND _hBtn;
};
#endif //FIND_REPLACE_DLG_H