/******************************************************************************** Copyright (C) 2012 Hugh Bailey 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, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. ********************************************************************************/ #include "Main.h" struct VolumeControlData { float curVolume; float lastUnmutedVol; DWORD drawColor; bool bDisabled, bHasCapture; long cx,cy; HICON hiconPlay, hiconMute; bool bDrawIcon; void DrawVolumeControl(HDC hDC); }; inline VolumeControlData* GetVolumeControlData(HWND hwnd) { return (VolumeControlData*)GetWindowLongPtr(hwnd, 0); } float ToggleVolumeControlMute(HWND hwnd) { VolumeControlData *control = GetVolumeControlData(hwnd); if(!control) CrashError(TEXT("ToggleVolumeControlMute called on a control that's not a volume control")); if(control->curVolume < EPSILON) { if(control->lastUnmutedVol < EPSILON) control->lastUnmutedVol = 1.0f; control->curVolume = control->lastUnmutedVol; } else { control->lastUnmutedVol = control->curVolume; control->curVolume = 0.0f; } HDC hDC = GetDC(hwnd); control->DrawVolumeControl(hDC); ReleaseDC(hwnd, hDC); return control->curVolume; } float SetVolumeControlValue(HWND hwnd, float fVal) { VolumeControlData *control = GetVolumeControlData(hwnd); if(!control) CrashError(TEXT("SetVolumeControlValue called on a control that's not a volume control")); float lastVal = control->curVolume; control->curVolume = fVal; HDC hDC = GetDC(hwnd); control->DrawVolumeControl(hDC); ReleaseDC(hwnd, hDC); return lastVal; } float GetVolumeControlValue(HWND hwnd) { VolumeControlData *control = GetVolumeControlData(hwnd); if(!control) CrashError(TEXT("GetVolumeControlValue called on a control that's not a volume control")); return control->curVolume; } void SetVolumeControlIcons(HWND hwnd, HICON hiconPlay, HICON hiconMute) { VolumeControlData *control = GetVolumeControlData(hwnd); if(!control) CrashError(TEXT("GetVolumeControlValue called on a control that's not a volume control")); control->hiconPlay = hiconPlay; control->hiconMute = hiconMute; control->bDrawIcon = (control->cy == 32) && control->hiconPlay; HDC hDC = GetDC(hwnd); control->DrawVolumeControl(hDC); ReleaseDC(hwnd, hDC); } LRESULT CALLBACK VolumeControlProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { VolumeControlData *control; switch(message) { case WM_NCCREATE: { CREATESTRUCT *pCreateData = (CREATESTRUCT*)lParam; control = (VolumeControlData*)malloc(sizeof(VolumeControlData)); zero(control, sizeof(VolumeControlData)); SetWindowLongPtr(hwnd, 0, (LONG_PTR)control); control->curVolume = 1.0f; control->bDisabled = ((pCreateData->style & WS_DISABLED) != 0); control->cx = pCreateData->cx; control->cy = pCreateData->cy; return TRUE; } case WM_DESTROY: { control = GetVolumeControlData(hwnd); if(control) free(control); break; } case WM_PAINT: { control = GetVolumeControlData(hwnd); PAINTSTRUCT ps; HDC hDC = BeginPaint(hwnd, &ps); control->DrawVolumeControl(hDC); EndPaint(hwnd, &ps); break; } case WM_LBUTTONDOWN: case WM_LBUTTONUP: { control = GetVolumeControlData(hwnd); short x = short(LOWORD(lParam)); short y = short(HIWORD(lParam)); UINT id = (UINT)GetWindowLongPtr(hwnd, GWLP_ID); if(message == WM_LBUTTONDOWN && !control->bDisabled) { if(control->cy == 32 && x >= (control->cx-32)) { if(control->curVolume < EPSILON) { if(control->lastUnmutedVol < EPSILON) control->lastUnmutedVol = 1.0f; control->curVolume = control->lastUnmutedVol; } else { control->lastUnmutedVol = control->curVolume; control->curVolume = 0.0f; } SendMessage(GetParent(hwnd), WM_COMMAND, MAKEWPARAM(id, VOLN_FINALVALUE), (LPARAM)hwnd); } else { SetCapture(hwnd); control->bHasCapture = true; if(control->curVolume > EPSILON) control->lastUnmutedVol = control->curVolume; int cxAdjust = control->cx; if(control->bDrawIcon) cxAdjust -= 32; control->curVolume = float(x) / cxAdjust; SendMessage(GetParent(hwnd), WM_COMMAND, MAKEWPARAM(id, VOLN_ADJUSTING), (LPARAM)hwnd); } HDC hDC = GetDC(hwnd); control->DrawVolumeControl(hDC); ReleaseDC(hwnd, hDC); } else if(control->bHasCapture) { UINT id = (UINT)GetWindowLongPtr(hwnd, GWLP_ID); SendMessage(GetParent(hwnd), WM_COMMAND, MAKEWPARAM(id, VOLN_FINALVALUE), (LPARAM)hwnd); ReleaseCapture(); control->bHasCapture = false; } break; } case WM_MOUSEMOVE: { control = GetVolumeControlData(hwnd); if(control->bHasCapture) { int cxAdjust = control->cx; if(control->bDrawIcon) cxAdjust -= 32; control->curVolume = float(short(LOWORD(lParam))) / cxAdjust; if(control->curVolume < 0.0f) control->curVolume = 0.0f; else if(control->curVolume > 1.0f) control->curVolume = 1.0f; HDC hDC = GetDC(hwnd); control->DrawVolumeControl(hDC); ReleaseDC(hwnd, hDC); UINT id = (UINT)GetWindowLongPtr(hwnd, GWLP_ID); SendMessage(GetParent(hwnd), WM_COMMAND, MAKEWPARAM(id, VOLN_ADJUSTING), (LPARAM)hwnd); } break; } case WM_ENABLE: { UINT id = (UINT)GetWindowLongPtr(hwnd, GWLP_ID); control = GetVolumeControlData(hwnd); if(control->bDisabled == !wParam) break; control->bDisabled = !control->bDisabled; if(control->bDisabled) { if(control->curVolume > EPSILON) { control->lastUnmutedVol = control->curVolume; control->curVolume = 0.0f; SendMessage(GetParent(hwnd), WM_COMMAND, MAKEWPARAM(id, VOLN_ADJUSTING), (LPARAM)hwnd); } } else { if(control->curVolume < EPSILON) { control->curVolume = control->lastUnmutedVol; SendMessage(GetParent(hwnd), WM_COMMAND, MAKEWPARAM(id, VOLN_ADJUSTING), (LPARAM)hwnd); } } HDC hDC = GetDC(hwnd); control->DrawVolumeControl(hDC); ReleaseDC(hwnd, hDC); break; } case WM_SIZE: { control = GetVolumeControlData(hwnd); control->cx = LOWORD(lParam); control->cy = HIWORD(lParam); control->bDrawIcon = (control->cy == 32) && control->hiconPlay; HDC hDC = GetDC(hwnd); control->DrawVolumeControl(hDC); ReleaseDC(hwnd, hDC); break; } default: return DefWindowProc(hwnd, message, wParam, lParam); } return 0; } void VolumeControlData::DrawVolumeControl(HDC hDC) { HDC hdcTemp = CreateCompatibleDC(hDC); HBITMAP hbmpTemp = CreateCompatibleBitmap(hDC, cx, cy); SelectObject(hdcTemp, hbmpTemp); RECT clientRect = {0, 0, cx, cy}; FillRect(hdcTemp, &clientRect, (HBRUSH)COLOR_WINDOW); HBRUSH hGray = CreateSolidBrush(REVERSE_COLOR(Color_Gray)); HBRUSH hRed = CreateSolidBrush(0x2020bf);//0xff4040); float visualVolume = (bDisabled) ? 0.0f : curVolume; int cxAdjust = cx; if(bDrawIcon) cxAdjust -= 32; const int padding = 1; const float volSliceSize = float(cxAdjust-(padding*9))/10.0f; int volPixelPos = int(visualVolume*float(cxAdjust)); if(volSliceSize > 1.0f) { float ySliceStart, ySliceSize; if(bDrawIcon) { ySliceStart = 4.0f; ySliceSize = float((cy-ySliceStart)/10); } else { ySliceStart = 0.0f; ySliceSize = float(cy/10); } for(int i=0; i<10; i++) { int pos = int(volSliceSize*float(i)) + (padding*i); RECT sliceRect = {pos, int(ySliceStart+((9.0f-float(i))*ySliceSize)), pos+int(volSliceSize), cy}; if(sliceRect.right < volPixelPos) //full FillRect(hdcTemp, &sliceRect, hRed); else if(sliceRect.left < volPixelPos) //half { RECT leftHalf, rightHalf; mcpy(&leftHalf, &sliceRect, sizeof(sliceRect)); mcpy(&rightHalf, &sliceRect, sizeof(sliceRect)); rightHalf.left = leftHalf.right = volPixelPos; FillRect(hdcTemp, &leftHalf, hRed); FillRect(hdcTemp, &rightHalf, hGray); } else //empty FillRect(hdcTemp, &sliceRect, hGray); } if(bDrawIcon) DrawIcon(hdcTemp, cx-32, 0, (visualVolume > EPSILON) ? hiconPlay : hiconMute); } BitBlt(hDC, 0, 0, cx, cy, hdcTemp, 0, 0, SRCCOPY); DeleteObject(hdcTemp); DeleteObject(hbmpTemp); DeleteObject(hGray); DeleteObject(hRed); } void InitVolumeControl() { WNDCLASS wnd; wnd.cbClsExtra = 0; wnd.cbWndExtra = sizeof(LPVOID); wnd.hbrBackground = NULL; wnd.hCursor = LoadCursor(NULL, IDC_ARROW); wnd.hIcon = NULL; wnd.hInstance = hinstMain; wnd.lpfnWndProc = VolumeControlProc; wnd.lpszClassName = VOLUME_CONTROL_CLASS; wnd.lpszMenuName = NULL; wnd.style = CS_PARENTDC | CS_VREDRAW | CS_HREDRAW; if(!RegisterClass(&wnd)) CrashError(TEXT("Could not register volume control class")); }