1 /*
2  * COPYRIGHT:   See COPYING in the top level directory
3  * PROJECT:     ReactOS Sound Volume Control
4  * FILE:        base/applications/sndvol32/advanced.c
5  * PROGRAMMERS: Eric Kohl <eric.kohl@reactos.org>
6  */
7 
8 #include "sndvol32.h"
9 
10 typedef struct _ADVANCED_DATA
11 {
12     PADVANCED_CONTEXT Context;
13 
14     BOOL bEnabled[4];
15     DWORD dwControlId[4];
16 
17     /* Bass and Treble */
18     DWORD dwMaximum[2];
19     DWORD dwMinimum[2];
20 
21 } ADVANCED_DATA, *PADVANCED_DATA;
22 
23 static
24 VOID
25 OnInitDialog(
26     HWND hwndDlg,
27     PADVANCED_DATA pData)
28 {
29     MIXERCONTROLDETAILS_UNSIGNED UnsignedDetails;
30     MIXERCONTROLDETAILS_BOOLEAN BooleanDetails;
31     WCHAR szRawBuffer[256], szCookedBuffer[256];
32     LPMIXERCONTROL Control = NULL;
33     UINT ControlCount = 0, Index;
34     DWORD i, dwStep, dwPosition;
35     DWORD dwOtherControls = 0;
36     RECT rect;
37     LONG dy;
38 
39     /* Set the dialog title */
40     LoadStringW(hAppInstance, IDS_ADVANCED_CONTROLS, szRawBuffer, ARRAYSIZE(szRawBuffer));
41     StringCchPrintfW(szCookedBuffer, ARRAYSIZE(szCookedBuffer), szRawBuffer, pData->Context->LineName);
42     SetWindowTextW(hwndDlg, szCookedBuffer);
43 
44     /* Disable the tone controls */
45     for (i = IDC_ADV_BASS_LOW; i<= IDC_ADV_TREBLE_SLIDER; i++)
46         EnableWindow(GetDlgItem(hwndDlg, i), FALSE);
47 
48     /* Initialize the bass and treble trackbars */
49     SendDlgItemMessageW(hwndDlg, IDC_ADV_BASS_SLIDER, TBM_SETRANGE, (WPARAM)TRUE, (LPARAM)MAKELONG(VOLUME_MIN, VOLUME_MAX));
50     SendDlgItemMessageW(hwndDlg, IDC_ADV_TREBLE_SLIDER, TBM_SETRANGE, (WPARAM)TRUE, (LPARAM)MAKELONG(VOLUME_MIN, VOLUME_MAX));
51     SendDlgItemMessageW(hwndDlg, IDC_ADV_BASS_SLIDER, TBM_SETPAGESIZE, 0, (LPARAM)VOLUME_PAGE_SIZE);
52     SendDlgItemMessageW(hwndDlg, IDC_ADV_TREBLE_SLIDER, TBM_SETPAGESIZE, 0, (LPARAM)VOLUME_PAGE_SIZE);
53     SendDlgItemMessageW(hwndDlg, IDC_ADV_BASS_SLIDER, TBM_SETPOS, (WPARAM)TRUE, (LPARAM)0);
54     SendDlgItemMessageW(hwndDlg, IDC_ADV_TREBLE_SLIDER, TBM_SETPOS, (WPARAM)TRUE, (LPARAM)0);
55 
56     /* Calculate and set ticks */
57     dwStep = (VOLUME_MAX / (VOLUME_TICKS + 1));
58     if (VOLUME_MAX % (VOLUME_TICKS + 1) != 0)
59         dwStep++;
60     for (i = dwStep; i < VOLUME_MAX; i += dwStep)
61     {
62         SendDlgItemMessageW(hwndDlg, IDC_ADV_BASS_SLIDER, TBM_SETTIC, 0, (LPARAM)i);
63         SendDlgItemMessageW(hwndDlg, IDC_ADV_TREBLE_SLIDER, TBM_SETTIC, 0, (LPARAM)i);
64     }
65 
66     /* Hide the other controls */
67     for (i = IDC_ADV_OTHER_CONTROLS; i<= IDC_ADV_OTHER_CHECK2; i++)
68         ShowWindow(GetDlgItem(hwndDlg, i), SW_HIDE);
69 
70     if (SndMixerQueryControls(pData->Context->Mixer, &ControlCount, pData->Context->Line, &Control))
71     {
72         for (Index = 0; Index < ControlCount; Index++)
73         {
74             if (Control[Index].dwControlType == MIXERCONTROL_CONTROLTYPE_BASS)
75             {
76                 /* Bass control */
77 
78                 if (SndMixerGetVolumeControlDetails(pData->Context->Mixer, Control[Index].dwControlID, 1, sizeof(MIXERCONTROLDETAILS_UNSIGNED), (LPVOID)&UnsignedDetails) != -1)
79                 {
80                     for (i = IDC_ADV_BASS_LOW; i<= IDC_ADV_BASS_SLIDER; i++)
81                         EnableWindow(GetDlgItem(hwndDlg, i), TRUE);
82 
83                     dwStep = (Control[Index].Bounds.dwMaximum - Control[Index].Bounds.dwMinimum) / (VOLUME_MAX - VOLUME_MIN);
84                     dwPosition = (UnsignedDetails.dwValue - Control[Index].Bounds.dwMinimum) / dwStep;
85                     SendDlgItemMessageW(hwndDlg, IDC_ADV_BASS_SLIDER, TBM_SETPOS, (WPARAM)TRUE, dwPosition);
86 
87                     pData->bEnabled[0] = TRUE;
88                     pData->dwControlId[0] = Control[Index].dwControlID;
89                     pData->dwMaximum[0] = Control[Index].Bounds.dwMaximum;
90                     pData->dwMinimum[0] = Control[Index].Bounds.dwMinimum;
91                 }
92             }
93             else if (Control[Index].dwControlType == MIXERCONTROL_CONTROLTYPE_TREBLE)
94             {
95                 /* Treble control */
96 
97                 if (SndMixerGetVolumeControlDetails(pData->Context->Mixer, Control[Index].dwControlID, 1, sizeof(MIXERCONTROLDETAILS_UNSIGNED), (LPVOID)&UnsignedDetails) != -1)
98                 {
99                     for (i = IDC_ADV_TREBLE_LOW; i<= IDC_ADV_TREBLE_SLIDER; i++)
100                         EnableWindow(GetDlgItem(hwndDlg, i), TRUE);
101 
102                     dwStep = (Control[Index].Bounds.dwMaximum - Control[Index].Bounds.dwMinimum) / (VOLUME_MAX - VOLUME_MIN);
103                     dwPosition = (UnsignedDetails.dwValue - Control[Index].Bounds.dwMinimum) / dwStep;
104                     SendDlgItemMessageW(hwndDlg, IDC_ADV_TREBLE_SLIDER, TBM_SETPOS, (WPARAM)TRUE, dwPosition);
105 
106                     pData->bEnabled[1] = TRUE;
107                     pData->dwControlId[1] = Control[Index].dwControlID;
108                     pData->dwMaximum[1] = Control[Index].Bounds.dwMaximum;
109                     pData->dwMinimum[1] = Control[Index].Bounds.dwMinimum;
110                 }
111             }
112             else if (((Control[Index].dwControlType & (MIXERCONTROL_CT_CLASS_MASK | MIXERCONTROL_CT_SUBCLASS_MASK | MIXERCONTROL_CT_UNITS_MASK)) == MIXERCONTROL_CONTROLTYPE_BOOLEAN) &&
113                      (Control[Index].dwControlType != MIXERCONTROL_CONTROLTYPE_MUTE))
114             {
115                 /* All boolean controls but the Mute control (Maximum of 2) */
116 
117                 if (dwOtherControls < 2)
118                 {
119                     if (SndMixerGetVolumeControlDetails(pData->Context->Mixer, Control[Index].dwControlID, 1, sizeof(MIXERCONTROLDETAILS_BOOLEAN), (LPVOID)&BooleanDetails) != -1)
120                     {
121                         LoadStringW(hAppInstance, IDS_OTHER_CONTROLS1 + dwOtherControls, szRawBuffer, ARRAYSIZE(szRawBuffer));
122                         StringCchPrintfW(szCookedBuffer, ARRAYSIZE(szCookedBuffer), szRawBuffer, Control[Index].szName);
123                         SetWindowTextW(GetDlgItem(hwndDlg, IDC_ADV_OTHER_CHECK1 + dwOtherControls), szCookedBuffer);
124 
125                         ShowWindow(GetDlgItem(hwndDlg, IDC_ADV_OTHER_CHECK1 + dwOtherControls), SW_SHOWNORMAL);
126 
127                         SendDlgItemMessageW(hwndDlg, IDC_ADV_OTHER_CHECK1 + dwOtherControls, BM_SETCHECK, (WPARAM)BooleanDetails.fValue, 0);
128 
129                         pData->bEnabled[dwOtherControls + 2] = TRUE;
130                         pData->dwControlId[dwOtherControls + 2] = Control[Index].dwControlID;
131 
132                         dwOtherControls++;
133                     }
134                 }
135             }
136         }
137 
138         /* Free controls */
139         HeapFree(GetProcessHeap(), 0, Control);
140     }
141 
142     if (dwOtherControls != 0)
143     {
144         /* Show the 'Other controls' groupbox and text */
145         ShowWindow(GetDlgItem(hwndDlg, IDC_ADV_OTHER_CONTROLS), SW_SHOWNORMAL);
146         ShowWindow(GetDlgItem(hwndDlg, IDC_ADV_OTHER_TEXT), SW_SHOWNORMAL);
147 
148         /* Resize the dialog */
149         GetWindowRect(hwndDlg, &rect);
150 
151         dy = MulDiv((dwOtherControls == 1) ? 73 : (73 + 15), pData->Context->MixerWindow->baseUnit.cy, 8);
152         rect.bottom += dy;
153 
154         SetWindowPos(hwndDlg, HWND_TOP, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, SWP_NOMOVE | SWP_NOZORDER);
155 
156         /* Move the 'Close' button down */
157         GetWindowRect(GetDlgItem(hwndDlg, IDOK), &rect);
158         MapWindowPoints(HWND_DESKTOP, hwndDlg, (LPPOINT)&rect, 2);
159 
160         rect.top += dy;
161         rect.bottom += dy;
162 
163         SetWindowPos(GetDlgItem(hwndDlg, IDOK), HWND_TOP, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, SWP_NOSIZE | SWP_NOZORDER);
164 
165         if (dwOtherControls == 2)
166         {
167             /* Resize the 'Other Controls' groupbox */
168             GetWindowRect(GetDlgItem(hwndDlg, IDC_ADV_OTHER_CONTROLS), &rect);
169             MapWindowPoints(HWND_DESKTOP, hwndDlg, (LPPOINT)&rect, 2);
170 
171             dy = MulDiv(15, pData->Context->MixerWindow->baseUnit.cy, 8);
172             rect.bottom += dy;
173 
174             SetWindowPos(GetDlgItem(hwndDlg, IDC_ADV_OTHER_CONTROLS), HWND_TOP, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, SWP_NOMOVE | SWP_NOZORDER);
175         }
176     }
177 }
178 
179 
180 static
181 VOID
182 OnHScroll(
183     HWND hwndDlg,
184     PADVANCED_DATA pData,
185     DWORD dwCtrlID)
186 {
187     MIXERCONTROLDETAILS_UNSIGNED Details;
188     DWORD dwControlID = 0, dwStep, dwPosition;
189     DWORD dwMaximum, dwMinimum;
190 
191     if (dwCtrlID != IDC_ADV_BASS_SLIDER &&
192         dwCtrlID != IDC_ADV_TREBLE_SLIDER)
193         return;
194 
195     if (dwCtrlID == IDC_ADV_BASS_SLIDER)
196     {
197         if (pData->bEnabled[0] == FALSE)
198             return;
199 
200         dwControlID = pData->dwControlId[0];
201         dwMaximum = pData->dwMaximum[0];
202         dwMinimum = pData->dwMinimum[0];
203     }
204     else if (dwCtrlID == IDC_ADV_TREBLE_SLIDER)
205     {
206         if (pData->bEnabled[1] == FALSE)
207             return;
208 
209         dwControlID = pData->dwControlId[1];
210         dwMaximum = pData->dwMaximum[1];
211         dwMinimum = pData->dwMinimum[1];
212     }
213 
214     dwPosition = (DWORD)SendDlgItemMessage(hwndDlg, dwCtrlID, TBM_GETPOS, 0, 0);
215     dwStep = (dwMaximum - dwMinimum) / (VOLUME_MAX - VOLUME_MIN);
216 
217     Details.dwValue = (dwPosition * dwStep) + dwMinimum;
218 
219     SndMixerSetVolumeControlDetails(pData->Context->Mixer, dwControlID, 1, sizeof(MIXERCONTROLDETAILS_UNSIGNED), &Details);
220 }
221 
222 
223 INT_PTR
224 CALLBACK
225 AdvancedDlgProc(
226     HWND hwndDlg,
227     UINT uMsg,
228     WPARAM wParam,
229     LPARAM lParam)
230 {
231     PADVANCED_DATA pData;
232 
233     pData = (PADVANCED_DATA)GetWindowLongPtr(hwndDlg, DWLP_USER);
234 
235     switch (uMsg)
236     {
237         case WM_INITDIALOG:
238             pData = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(ADVANCED_DATA));
239             if (pData != NULL)
240             {
241                 SetWindowLongPtr(hwndDlg, DWLP_USER, (LONG_PTR)pData);
242                 pData->Context = (PADVANCED_CONTEXT)((LONG_PTR)lParam);
243                 OnInitDialog(hwndDlg, pData);
244             }
245             return TRUE;
246 
247         case WM_COMMAND:
248             switch (LOWORD(wParam))
249             {
250                 case IDOK:
251                     EndDialog(hwndDlg, IDOK);
252                     break;
253             }
254             break;
255 
256         case WM_HSCROLL:
257             if (pData != NULL)
258             {
259                  if (LOWORD(wParam) == TB_THUMBTRACK)
260                     OnHScroll(hwndDlg, pData, GetDlgCtrlID((HWND)lParam));
261             }
262             break;
263 
264         case WM_CLOSE:
265             EndDialog(hwndDlg, IDCANCEL);
266             break;
267 
268         case WM_DESTROY:
269             if (pData != NULL)
270             {
271                 HeapFree(GetProcessHeap(), 0, pData);
272                 SetWindowLongPtr(hwndDlg, DWLP_USER, (LONG_PTR)NULL);
273             }
274             break;
275     }
276 
277     return FALSE;
278 }
279 
280 /* EOF */
281