// // AUTHOR // Stef Walter // // LICENSE // This software is in the public domain. // // The software is provided "as is", without warranty of any kind, // express or implied, including but not limited to the warranties // of merchantability, fitness for a particular purpose, and // noninfringement. In no event shall the author(s) be liable for any // claim, damages, or other liability, whether in an action of // contract, tort, or otherwise, arising from, out of, or in connection // with the software or the use or other dealings in the software. // // SUPPORT // Send bug reports to: // #include "stdafx.h" #include "MonitorDlg.h" #include "../hook/hook.h" ///////////////////////////////////////////////////////////////////////////// // CMonitorDlg CMonitorDlg::CMonitorDlg() : m_Icon(ID_TRAY_WPM, _Module.GetResourceInstance()), m_settings(HKEY_CURRENT_USER, _T("SOFTWARE\\WPM\\")), CPersistPosWindow("Window") { m_hIcon = NULL; m_szIcon.cx = ::GetSystemMetrics(SM_CXSMICON); m_szIcon.cy = ::GetSystemMetrics(SM_CYSMICON); memset(&m_aSpeeds, 0, sizeof(m_aSpeeds)); m_nIndex = 0; } CMonitorDlg::~CMonitorDlg() { if(m_hIcon) ::DestroyIcon(m_hIcon); // Remove Icon just in case m_Icon.SetIcon(0); } // Points in the icon to draw different counters const POINT kHundreds = { 1, 1 }; const POINT kTens = { 6, 1 }; const POINT kOnes = { 11, 1 }; const POINT kHundredsAv = { 1, 10 }; const POINT kTensAv = { 6, 10 }; const POINT kOnesAv = { 11, 10 }; bool CMonitorDlg::CreateIcon(long lSpeed, long lAverage) { bool bRet = false; if(m_hIcon) ::DestroyIcon(m_hIcon); HDC hScreenDC = ::GetDC(NULL); HDC hDC = ::CreateCompatibleDC(hScreenDC); if(hScreenDC && hDC) { if(SetStretchBltMode(hDC, HALFTONE)) { if(SetBrushOrgEx(hDC, 0, 0, NULL)) { ICONINFO info; info.fIcon = TRUE; info.hbmColor = ::CreateCompatibleBitmap(hScreenDC, m_szIcon.cx, m_szIcon.cy); info.hbmMask = ::CreateCompatibleBitmap(hDC, m_szIcon.cx, m_szIcon.cy); if(info.hbmColor && info.hbmMask) { HBITMAP hOldBm = (HBITMAP)SelectObject(hDC, info.hbmMask); ::PatBlt(hDC, 0, 0, m_szIcon.cx, m_szIcon.cy, WHITENESS); int nHundreds = lSpeed / 100; if(nHundreds >= 10) nHundreds = 9; int nTens = (lSpeed - (100 * nHundreds)) / 10; ATLASSERT(nTens < 10); int nOnes = (lSpeed - (100 * nHundreds) - (10 * nTens)); ATLASSERT(nOnes < 10); m_imgList.Draw(hDC, nHundreds, kHundreds, ILD_TRANSPARENT); m_imgList.Draw(hDC, nTens, kTens, ILD_TRANSPARENT); m_imgList.Draw(hDC, nOnes, kOnes, ILD_TRANSPARENT); nHundreds = lAverage / 100; if(nHundreds >= 10) nHundreds = 9; nTens = (lAverage - (100 * nHundreds)) / 10; ATLASSERT(nTens < 10); nOnes = (lAverage - (100 * nHundreds) - (10 * nTens)); ATLASSERT(nOnes < 10); m_imgList.Draw(hDC, nHundreds, kHundredsAv, ILD_TRANSPARENT); m_imgList.Draw(hDC, nTens, kTensAv, ILD_TRANSPARENT); m_imgList.Draw(hDC, nOnes, kOnesAv, ILD_TRANSPARENT); ::SelectObject(hDC, info.hbmColor); RECT rc = { 0, 0, m_szIcon.cx, m_szIcon.cy }; ::FillRect(hDC, &rc, (HBRUSH)(COLOR_WINDOWTEXT + 1)); m_hIcon = CreateIconIndirect(&info); DeleteObject(info.hbmColor); DeleteObject(info.hbmMask); ::SelectObject(hDC, hOldBm); bRet = m_hIcon != NULL; } } } ::DeleteDC(hDC); ::ReleaseDC(NULL, hScreenDC); } return bRet; } LRESULT CMonitorDlg::OnTop(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled) { SetWindowPos(IsDlgButtonChecked(IDC_ONTOP) ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); return 0; } LRESULT CMonitorDlg::OnInitDialog(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { BOOL ret = m_imgList.Create(MAKEINTRESOURCE(IDB_NUMBERS), 4, 0, RGB(255, 255, 255)); ATLASSERT(ret); InstallSpeedHook(); SetTimer(100, TIME_LAPSE); // Setup Tray Icon (Actual setting of current icon is done by // UpdateControls m_Icon.SetNotificationWnd(*this, WM_TRAYICON); m_Icon.SetIcon(ID_TRAY_WPM); ShowWindow(SW_HIDE); LoadPosition(m_settings); ShowWindow(SW_HIDE); return 1; // Let the system set the focus } LRESULT CMonitorDlg::OnOK(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled) { ShowWindow(SW_HIDE); return 0; } LRESULT CMonitorDlg::OnCancel(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled) { ShowWindow(SW_HIDE); return 0; } long CMonitorDlg::GetAverage(long lSpeed) { ATLASSERT(m_nIndex >= 0 && m_nIndex < LAPSE_PER_MIN); m_aSpeeds[m_nIndex] = lSpeed; if(++m_nIndex >= LAPSE_PER_MIN) m_nIndex = 0; int cnt = 0; int total = 0; for(int i = 0; i < LAPSE_PER_MIN; i++) { if(m_aSpeeds[i] > 0) { cnt++; total += m_aSpeeds[i]; } } if(cnt == 0) return 0; return total / cnt; } const TCHAR kTitle[] = "%d WPM"; const TCHAR kTip[] = "%d WPM (%d average)"; LRESULT CMonitorDlg::OnTimer(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { USES_CONVERSION; // Get the amount of characters it the lapse and reset LONG lSpeed = GetCurSpeedCount(); ClearSpeedCount(); // Multiply it so we don't loose too much detail in // decimals lSpeed *= 10000; // Divide by the word size (The Multiple is used ditto above) lSpeed /= CHARS_PER_WORD; lSpeed *= CHARS_MULTIPLE; // Multiply to get per minute lSpeed *= LAPSE_PER_MIN; // Divide back to get the WPM lSpeed /= 10000; long lAverage = GetAverage(lSpeed); SetDlgItemInt(IDC_VALUE, lSpeed, TRUE); SetDlgItemInt(IDC_AVERAGE, lAverage, TRUE); string sMessage; sMessage.format(kTitle, lSpeed); SetWindowText(sMessage); sMessage.format(kTip, lSpeed, lAverage); if(CreateIcon(lSpeed, lAverage)) m_Icon.SetIcon(m_hIcon, sMessage); return 0; } LRESULT CMonitorDlg::OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { RemoveSpeedHook(); KillTimer(100); SavePosition(m_settings); return 0; } LRESULT CMonitorDlg::OnShow(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled) { ShowWindow(SW_SHOW); SetForegroundWindow(m_hWnd); return 1; } LRESULT CMonitorDlg::OnExit(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled) { PostQuitMessage(0); return 1; }