From d9299e644f4deaaebd2a00dd9e97986565c83e02 Mon Sep 17 00:00:00 2001 From: Don Ho Date: Thu, 19 Feb 2015 01:01:12 +0000 Subject: [PATCH] [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 --- PowerEditor/src/Notepad_plus.cpp | 91 +++--- .../src/ScitillaComponent/FindReplaceDlg.cpp | 284 ++++++++++++++++++ .../src/ScitillaComponent/FindReplaceDlg.h | 44 ++- 3 files changed, 376 insertions(+), 43 deletions(-) diff --git a/PowerEditor/src/Notepad_plus.cpp b/PowerEditor/src/Notepad_plus.cpp index 8cc50febf..c6f0a4d98 100644 --- a/PowerEditor/src/Notepad_plus.cpp +++ b/PowerEditor/src/Notepad_plus.cpp @@ -1420,12 +1420,6 @@ void Notepad_plus::getMatchedFileNames(const TCHAR *dir, const vector 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 patterns2Match; _findReplaceDlg.getPatterns(patterns2Match); @@ -1537,26 +1537,29 @@ bool Notepad_plus::findInFiles() vector 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); diff --git a/PowerEditor/src/ScitillaComponent/FindReplaceDlg.cpp b/PowerEditor/src/ScitillaComponent/FindReplaceDlg.cpp index 9bb8a6c19..d8be7a15f 100644 --- a/PowerEditor/src/ScitillaComponent/FindReplaceDlg.cpp +++ b/PowerEditor/src/ScitillaComponent/FindReplaceDlg.cpp @@ -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(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(static_cast + (::GetWindowLongPtr(hwnd, GWLP_USERDATA))); + ::SetFocus(pw->_hBtn); + return 0; + } + + case WM_COMMAND: + if (HIWORD(wparam) == BN_CLICKED) + { + CProgress* pw = + reinterpret_cast(static_cast + (::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); +} diff --git a/PowerEditor/src/ScitillaComponent/FindReplaceDlg.h b/PowerEditor/src/ScitillaComponent/FindReplaceDlg.h index 482ef745d..0392e879c 100644 --- a/PowerEditor/src/ScitillaComponent/FindReplaceDlg.h +++ b/PowerEditor/src/ScitillaComponent/FindReplaceDlg.h @@ -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