/////////////////////////////////////////////////////////////////////////////////////////////////////////// // // KNOB.CPP // // Multimedia Knob Control class; helper functions // // Copyright (c) Microsoft Corporation 1997 // // 12/18/97 David Stewart / dstewart // /////////////////////////////////////////////////////////////////////////////////////////////////////////// #include "knob.h" #include "windowsx.h" #include #include "resource.h" #include "dib.h" #include "math.h" #include "mmfw.h" #ifdef UNICODE #define WC_KNOB L"DES_KnobClass" #else #define WC_KNOB "DES_KnobClass" #endif //externals extern HPALETTE hpalMain; extern int g_nColorMode; #define LIGHT_OFFSET 9 #define RADIAN_45DEG 0.785398163397448309615 #define RADIAN_90DEG 1.57079632679489661923 #define RADIAN_135DEG 2.356194490192344899999999999925 #define DEGREE_CONVERTER 57.295779513082320876846364344191 #define RADIAN_CONVERTER 0.017453292519943295769222222222222 #define TRACK_TICK 5 #define FAST_TRACK_TICK 1 #define TRACK_DEGREES_PER_TICK 10 #define FLASH_TICK 150 #define KEYBOARD_STEP 3000 //static data members ... these control the Window Class HINSTANCE CKnob::m_hInst = NULL; DWORD CKnob::m_dwKnobClassRef = 0; ATOM CKnob::m_KnobAtom = NULL; HANDLE CKnob::m_hbmpKnob = NULL; HANDLE CKnob::m_hbmpKnobTab = NULL; HANDLE CKnob::m_hbmpLight = NULL; HANDLE CKnob::m_hbmpLightBright = NULL; HANDLE CKnob::m_hbmpLightMask = NULL; int CKnob::m_nLightWidth = 0; int CKnob::m_nLightHeight = 0; CKnob* CreateKnob(DWORD dwWindowStyle, DWORD dwRange, DWORD dwInitialPosition, int x, int y, int width, int height, HWND hwndParent, int nID, HINSTANCE hInst) { if (CKnob::m_KnobAtom == NULL) { CKnob::InitKnobs(hInst); } CKnob* pKnob = new CKnob; //ensure this is a child window dwWindowStyle = dwWindowStyle|WS_CHILD; TCHAR szCaption[MAX_PATH]; LoadString(hInst,IDB_TT_VOLUME,szCaption,sizeof(szCaption)/sizeof(TCHAR)); HWND hwnd = CreateWindowEx(0, WC_KNOB, szCaption, dwWindowStyle, x, y, width, height, hwndParent, (HMENU)IntToPtr(nID), hInst, NULL); if (hwnd == NULL) { //if we can't create the window, nuke it and fail DWORD dwErr = GetLastError(); delete pKnob; return NULL; } SetWindowLongPtr(hwnd,0,(LONG_PTR)pKnob); pKnob->m_hwnd = hwnd; pKnob->m_nID = nID; pKnob->SetRange(dwRange); pKnob->SetPosition(dwInitialPosition,FALSE); return (pKnob); } BOOL CKnob::InitKnobs(HINSTANCE hInst) { m_hInst = hInst; if (m_KnobAtom == NULL) { WNDCLASSEX wc; ZeroMemory(&wc,sizeof(wc)); wc.cbSize = sizeof(wc); wc.lpszClassName = WC_KNOB; wc.lpfnWndProc = CKnob::KnobProc; wc.hInstance = hInst; wc.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS; wc.hIcon = LoadIcon(hInst, MAKEINTRESOURCE(IDI_MMFW)); wc.hCursor = LoadCursor(hInst, MAKEINTRESOURCE(IDC_VOLHAND)); wc.hbrBackground = (HBRUSH)(CTLCOLOR_DLG+1); wc.cbWndExtra = sizeof(CKnob*); m_KnobAtom = RegisterClassEx(&wc); } int nBitmap = IDB_KNOB; switch (g_nColorMode) { case COLOR_16 : nBitmap = IDB_KNOB_16; break; case COLOR_HICONTRAST : nBitmap = IDB_KNOB_HI; break; } HBITMAP hbmpTemp = (HBITMAP)LoadImage(hInst,MAKEINTRESOURCE(nBitmap),IMAGE_BITMAP,0,0,LR_CREATEDIBSECTION); CKnob::m_hbmpKnob = DibFromBitmap((HBITMAP)hbmpTemp,0,0,NULL,0); DeleteObject(hbmpTemp); nBitmap = IDB_KNOB_TABSTATE; switch (g_nColorMode) { case COLOR_16 : nBitmap = IDB_KNOB_TABSTATE_16; break; case COLOR_HICONTRAST : nBitmap = IDB_KNOB_TABSTATE_HI; break; } hbmpTemp = (HBITMAP)LoadImage(hInst,MAKEINTRESOURCE(nBitmap),IMAGE_BITMAP,0,0,LR_CREATEDIBSECTION); CKnob::m_hbmpKnobTab = DibFromBitmap((HBITMAP)hbmpTemp,0,0,NULL,0); DeleteObject(hbmpTemp); nBitmap = IDB_KNOB_LIGHT_DIM; switch (g_nColorMode) { case COLOR_16 : nBitmap = IDB_KNOB_LIGHT_16; break; case COLOR_HICONTRAST : nBitmap = IDB_KNOB_LIGHT_16; break; } hbmpTemp = (HBITMAP)LoadImage(hInst,MAKEINTRESOURCE(nBitmap),IMAGE_BITMAP,0,0,LR_CREATEDIBSECTION); CKnob::m_hbmpLight = DibFromBitmap((HBITMAP)hbmpTemp,0,0,NULL,0); BITMAP bm; GetObject(hbmpTemp,sizeof(bm),&bm); CKnob::m_nLightWidth = bm.bmWidth; CKnob::m_nLightHeight = bm.bmHeight; DeleteObject(hbmpTemp); nBitmap = IDB_KNOB_LIGHT; switch (g_nColorMode) { case COLOR_16 : nBitmap = IDB_KNOB_LIGHT_16; break; case COLOR_HICONTRAST : nBitmap = IDB_KNOB_LIGHT_HI; break; } hbmpTemp = (HBITMAP)LoadImage(hInst,MAKEINTRESOURCE(nBitmap),IMAGE_BITMAP,0,0,LR_CREATEDIBSECTION); CKnob::m_hbmpLightBright = DibFromBitmap((HBITMAP)hbmpTemp,0,0,NULL,0); DeleteObject(hbmpTemp); m_hbmpLightMask = (HBITMAP)LoadImage(hInst,MAKEINTRESOURCE(IDB_KNOB_LIGHTMASK),IMAGE_BITMAP,0,0,LR_MONOCHROME); return (m_KnobAtom != NULL); } void CKnob::UninitKnobs() { UnregisterClass(WC_KNOB,m_hInst); DeleteObject(CKnob::m_hbmpLightMask); GlobalFree(CKnob::m_hbmpLight); GlobalFree(CKnob::m_hbmpKnob); GlobalFree(CKnob::m_hbmpKnobTab); GlobalFree(CKnob::m_hbmpLightBright); m_KnobAtom = NULL; CKnob::m_hbmpLight = NULL; CKnob::m_hbmpKnob = NULL; CKnob::m_hbmpKnobTab = NULL; CKnob::m_hbmpLightMask = NULL; CKnob::m_hbmpLightBright = NULL; } //Given a parent window and a control ID, return the CMButton object CKnob* GetKnobFromID(HWND hwndParent, int nID) { HWND hwnd = GetDlgItem(hwndParent, nID); return (GetKnobFromHWND(hwnd)); } //Given the window handle of the button, return the CMButton object CKnob* GetKnobFromHWND(HWND hwnd) { CKnob* pKnob = (CKnob*)GetWindowLongPtr(hwnd, 0); return (pKnob); } CKnob::CKnob() { m_dwKnobClassRef++; m_hwnd = NULL; m_nID = -1; m_nLightX = 0; m_nLightY = 0; m_dwRange = 100; m_dwPosition = 0; m_dwCurPosition = 0; m_fDim = TRUE; } CKnob::~CKnob() { m_dwKnobClassRef--; if (m_dwKnobClassRef==0) { UninitKnobs(); } } void CALLBACK CKnob::TrackProc(HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime) { CKnob* pKnob = (CKnob*)UIntToPtr(idEvent); //idEvent holds pointer to knob object that created timer if (pKnob!=NULL) { pKnob->OnTimer(); } } void CALLBACK CKnob::FlashProc(HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime) { CKnob* pKnob = (CKnob*)GetWindowLongPtr(hwnd, 0); if (pKnob!=NULL) { pKnob->OnFlashTimer(); } } LRESULT CALLBACK CKnob::KnobProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam) { CKnob* pKnob = (CKnob*)GetWindowLongPtr(hwnd, 0); if (pKnob != NULL) { switch (iMsg) { case WM_SETFOCUS : { SendMessage(GetParent(pKnob->m_hwnd),DM_SETDEFID,pKnob->m_nID,0); pKnob->m_fDim = FALSE; InvalidateRect(hwnd,NULL,FALSE); UpdateWindow(hwnd); pKnob->m_uFlashTimerID = SetTimer(hwnd,(UINT_PTR)hwnd,FLASH_TICK,(TIMERPROC)CKnob::FlashProc); } break; case WM_KILLFOCUS : { KillTimer(hwnd,pKnob->m_uFlashTimerID); pKnob->m_fDim = TRUE; InvalidateRect(hwnd,NULL,FALSE); UpdateWindow(hwnd); } break; case WM_GETDLGCODE : { return (DLGC_WANTARROWS); } break; case WM_KEYDOWN : { int nVirtKey = (int)wParam; DWORD dwCurrent = pKnob->GetPosition(); switch (nVirtKey) { case VK_LEFT : case VK_DOWN : { if (dwCurrent - KEYBOARD_STEP > 65535) { dwCurrent = KEYBOARD_STEP; } pKnob->SetPosition(dwCurrent - KEYBOARD_STEP,TRUE); NMHDR nmhdr; nmhdr.hwndFrom = pKnob->m_hwnd; nmhdr.idFrom = pKnob->m_nID; nmhdr.code = TRUE; SendMessage(GetParent(pKnob->m_hwnd),WM_NOTIFY,(WPARAM)pKnob->m_nID,(LPARAM)&nmhdr); } break; case VK_RIGHT : case VK_UP : { if (dwCurrent + KEYBOARD_STEP > 65535) { dwCurrent = 65535 - KEYBOARD_STEP; } pKnob->SetPosition(dwCurrent + KEYBOARD_STEP,TRUE); NMHDR nmhdr; nmhdr.hwndFrom = pKnob->m_hwnd; nmhdr.idFrom = pKnob->m_nID; nmhdr.code = TRUE; SendMessage(GetParent(pKnob->m_hwnd),WM_NOTIFY,(WPARAM)pKnob->m_nID,(LPARAM)&nmhdr); } break; default: { //not a key we want ... tell our parent about it SendMessage(GetParent(hwnd),WM_KEYDOWN,wParam,lParam); } break; } //end switch } break; case WM_ERASEBKGND : { pKnob->Draw((HDC)wParam); return TRUE; } case WM_PAINT : { HDC hdc; PAINTSTRUCT ps; hdc = BeginPaint( hwnd, &ps ); pKnob->Draw(hdc); EndPaint(hwnd,&ps); return 0; } break; case WM_RBUTTONDOWN : { pKnob->m_fFastKnob = TRUE; pKnob->OnButtonDown(LOWORD(lParam),HIWORD(lParam)); pKnob->OnMouseMove(LOWORD(lParam),HIWORD(lParam)); } break; case WM_RBUTTONUP : { pKnob->OnButtonUp(); } break; case WM_LBUTTONDOWN : { pKnob->m_fFastKnob = FALSE; pKnob->OnButtonDown(LOWORD(lParam),HIWORD(lParam)); pKnob->OnMouseMove(LOWORD(lParam),HIWORD(lParam)); } break; case WM_LBUTTONUP : { pKnob->OnButtonUp(); } break; case WM_MOUSEMOVE : { pKnob->OnMouseMove(LOWORD(lParam),HIWORD(lParam)); } break; } //end switch } //end if class pointer assigned LRESULT lResult = DefWindowProc(hwnd, iMsg, wParam, lParam); if (iMsg == WM_DESTROY) { //auto-delete the knob class SetWindowLongPtr(hwnd,0,0); if (pKnob) { delete pKnob; } pKnob = NULL; } return (lResult); } void CKnob::OnButtonDown(int x, int y) { SetCapture(m_hwnd); m_fDim = FALSE; InvalidateRect(m_hwnd,NULL,FALSE); UpdateWindow(m_hwnd); m_uFlashTimerID = SetTimer(m_hwnd,(UINT_PTR)m_hwnd,FLASH_TICK,(TIMERPROC)CKnob::FlashProc); } void CKnob::OnButtonUp() { KillTimer(m_hwnd,m_uTrackTimerID); KillTimer(m_hwnd,m_uFlashTimerID); //we want to be sure the light is dim when we're done if (!m_fDim) { m_fDim = TRUE; InvalidateRect(m_hwnd,NULL,FALSE); UpdateWindow(m_hwnd); } ReleaseCapture(); } void CKnob::OnFlashTimer() { m_fDim = !m_fDim; InvalidateRect(m_hwnd,NULL,FALSE); UpdateWindow(m_hwnd); } void CKnob::OnTimer() { RECT rect; GetClientRect(m_hwnd,&rect); int nWidth = rect.right - rect.left; int radius = (nWidth / 2) - LIGHT_OFFSET; double degree = ((double)m_dwPosition / m_dwRange) * 270; degree = degree + 135; if (abs((int)m_trackdegree-(int)degree) < TRACK_DEGREES_PER_TICK) { m_trackdegree = degree; KillTimer(m_hwnd,m_uTrackTimerID); } else { if (m_trackdegree > degree) { m_trackdegree -= TRACK_DEGREES_PER_TICK; } else { m_trackdegree += TRACK_DEGREES_PER_TICK; } } double angle = m_trackdegree * RADIAN_CONVERTER; double fLightX = radius * cos(angle); double fLightY = radius * sin(angle); //convert to proper gdi coordinates m_nLightX = ((int)fLightX) - (m_nLightWidth / 2) + (nWidth / 2); m_nLightY = ((int)fLightY) - (m_nLightHeight / 2) + (nWidth / 2); InvalidateRect(m_hwnd,NULL,FALSE); UpdateWindow(m_hwnd); degree = m_trackdegree - 135; if (degree < 0) degree = degree + 360; double percentage = degree / 270; m_dwCurPosition = (DWORD)(m_dwRange * percentage); NMHDR nmhdr; nmhdr.hwndFrom = m_hwnd; nmhdr.idFrom = m_nID; nmhdr.code = TRUE; SendMessage(GetParent(m_hwnd),WM_NOTIFY,(WPARAM)m_nID,(LPARAM)&nmhdr); } BOOL CKnob::ComputeCursor(int deltaX, int deltaY, int maxdist) { double distance = sqrt(double((deltaX * deltaX) + (deltaY * deltaY))); double degrees = -((atan2(deltaX,deltaY) * DEGREE_CONVERTER) - double(180.0)); BOOL fDeadZone = FALSE; if (distance < double(4)) { fDeadZone = TRUE; } if (distance <= maxdist) { SetCursor(LoadCursor(m_hInst, MAKEINTRESOURCE(IDC_VOLHAND))); } else { int volcur; if ((degrees < double( 22.5) || degrees > double(337.5)) || (degrees > double(157.5) && degrees < double(202.5))) { volcur = IDC_VOLHORZ; } else if ((degrees > double( 22.5) && degrees < double( 67.5)) || (degrees > double(202.5) && degrees < double(247.5))) { volcur = IDC_VOLDNEG; } else if ((degrees > double( 67.5) && degrees < double(112.5)) || (degrees > double(247.5) && degrees < double(292.5))) { volcur = IDC_VOLVERT; } else { volcur = IDC_VOLDPOS; } SetCursor(LoadCursor(m_hInst, MAKEINTRESOURCE(volcur))); } return fDeadZone; } void CKnob::OnMouseMove(int x, int y) { if (GetCapture()==m_hwnd) { //do the calculations as if 0,0 were the center of the control, //then translate to gdi coordinates later (0,0 = top left of control in gdi) RECT rect; GetClientRect(m_hwnd,&rect); int nWidth = rect.right - rect.left; int nHeight = rect.bottom - rect.top; int maxdist = (nWidth / 2) + 3; int radius = (nWidth / 2) - LIGHT_OFFSET; //convert to short to force negative numbers for coordinates short sx = (short)x; short sy = (short)y; int deltaX = sx - (nWidth / 2); int deltaY = sy - (nHeight / 2); ComputeCursor(deltaX, deltaY, maxdist); double angle = atan2(deltaY,deltaX); double degrees = angle * DEGREE_CONVERTER; degrees = degrees + 225; if (degrees < 0) degrees = 0; if (degrees >= 360) { degrees = degrees - 360; } double percentage = degrees / 270; m_dwPosition = (DWORD)(m_dwRange * percentage); //special-case the "dead zone" if ((degrees >= 270) && (degrees <= 315)) { m_dwPosition = m_dwRange; } if (degrees > 315) { m_dwPosition = 0; } if (m_fFastKnob) { m_uTrackTimerID = SetTimer(m_hwnd,(UINT_PTR)this,FAST_TRACK_TICK,(TIMERPROC)CKnob::TrackProc); } else { m_uTrackTimerID = SetTimer(m_hwnd,(UINT_PTR)this,TRACK_TICK,(TIMERPROC)CKnob::TrackProc); } } } void CKnob::SetPosition(DWORD dwPosition, BOOL fNotify) { if (GetCapture()==m_hwnd) { //we're in a feedback loop, return immediately return; } m_dwPosition = dwPosition; m_dwCurPosition = dwPosition; RECT rect; GetClientRect(m_hwnd,&rect); int nWidth = rect.right - rect.left; int radius = (nWidth / 2) - LIGHT_OFFSET; double degree = ((double)m_dwPosition / m_dwRange) * 270; degree = degree + 135; m_trackdegree = degree; //instantly track when position is set programmatically double angle = degree * RADIAN_CONVERTER; double fLightX = radius * cos(angle); double fLightY = radius * sin(angle); //convert to proper gdi coordinates m_nLightX = ((int)fLightX) - (m_nLightWidth / 2) + (nWidth / 2); m_nLightY = ((int)fLightY) - (m_nLightHeight / 2) + (nWidth / 2); InvalidateRect(m_hwnd,NULL,FALSE); UpdateWindow(m_hwnd); if (fNotify) { NMHDR nmhdr; nmhdr.hwndFrom = m_hwnd; nmhdr.idFrom = m_nID; nmhdr.code = FALSE; SendMessage(GetParent(m_hwnd),WM_NOTIFY,(WPARAM)m_nID,(LPARAM)&nmhdr); } } //kmaskblt -- cuz MaskBlt doesn't work on all platforms. This is all it does anyway. // uses same params as MaskBlt, ignoring the flags part as dwDummy void CKnob::KMaskBlt(HDC hdcDest, int x, int y, int width, int height, HDC hdcSource, int xs, int ys, HBITMAP hMask, int xm, int ym, DWORD dwDummy) { HDC hdcMask = CreateCompatibleDC(hdcDest); HBITMAP holdbmp = (HBITMAP)SelectObject(hdcMask,hMask); BitBlt(hdcDest, x, y, width, height, hdcSource, xs, ys, SRCINVERT); BitBlt(hdcDest, x, y, width, height, hdcMask, xm, ym, SRCAND); BitBlt(hdcDest, x, y, width, height, hdcSource, xs, ys, SRCINVERT); SelectObject(hdcMask,holdbmp); DeleteDC(hdcMask); } void CKnob::Draw(HDC hdc) { RECT rect; GetClientRect(m_hwnd,&rect); int nWidth = rect.right - rect.left; int nHeight = rect.bottom - rect.top; HPALETTE hpalOld = SelectPalette(hdc,hpalMain,FALSE); RealizePalette(hdc); HDC memDC = CreateCompatibleDC(hdc); HPALETTE hpalmemOld = SelectPalette(memDC,hpalMain,FALSE); RealizePalette(memDC); HBITMAP hbmp = CreateCompatibleBitmap(hdc,nWidth,nHeight); HBITMAP hbmpOld = (HBITMAP)SelectObject(memDC,hbmp); HDC maskmemDC = CreateCompatibleDC(hdc); HBITMAP hmaskbmp = CreateCompatibleBitmap(hdc,nWidth,nHeight); HBITMAP hmaskbmpOld = (HBITMAP)SelectObject(maskmemDC,hmaskbmp); if (GetFocus()==m_hwnd) { DibBlt(memDC, 0, 0, -1, -1, m_hbmpKnobTab, 0, 0, SRCCOPY, 0); } else { DibBlt(memDC, 0, 0, -1, -1, m_hbmpKnob, 0, 0, SRCCOPY, 0); } DibBlt(maskmemDC, 0, 0, -1, -1, m_fDim ? m_hbmpLight : m_hbmpLightBright, 0, 0, SRCCOPY, 0); KMaskBlt(memDC, m_nLightX, m_nLightY, m_nLightWidth, m_nLightHeight, maskmemDC, 0, 0, (HBITMAP)m_hbmpLightMask, 0, 0, MAKEROP4(SRCAND,SRCCOPY)); BitBlt(hdc,0,0,nWidth,nHeight,memDC,0,0,SRCCOPY); SelectObject(memDC,hbmpOld); SelectPalette(memDC,hpalmemOld,TRUE); RealizePalette(memDC); DeleteObject(hbmp); DeleteDC(memDC); SelectObject(maskmemDC, hmaskbmpOld); DeleteObject(hmaskbmp); DeleteDC(maskmemDC); SelectPalette(hdc,hpalOld,TRUE); RealizePalette(hdc); }