xref: /reactos/dll/cpl/desk/settings.c (revision dad056e0)
1 /*
2  * COPYRIGHT:       See COPYING in the top level directory
3  * PROJECT:         ReactOS Display Control Panel
4  * FILE:            dll/cpl/desk/settings.c
5  * PURPOSE:         Settings property page
6  *
7  * PROGRAMMERS:     Trevor McCort (lycan359@gmail.com)
8  *                  Hervé Poussineau (hpoussin@reactos.org)
9  */
10 
11 #include "desk.h"
12 
13 typedef struct _SETTINGS_DATA
14 {
15     PDISPLAY_DEVICE_ENTRY DisplayDeviceList;
16     PDISPLAY_DEVICE_ENTRY CurrentDisplayDevice;
17     HBITMAP hSpectrumBitmaps[NUM_SPECTRUM_BITMAPS];
18     int cxSource[NUM_SPECTRUM_BITMAPS];
19     int cySource[NUM_SPECTRUM_BITMAPS];
20 } SETTINGS_DATA, *PSETTINGS_DATA;
21 
22 typedef struct _TIMEOUTDATA
23 {
24     TCHAR szRawBuffer[256];
25     TCHAR szCookedBuffer[256];
26     INT nTimeout;
27 } TIMEOUTDATA, *PTIMEOUTDATA;
28 
29 static VOID
30 UpdateDisplay(IN HWND hwndDlg, PSETTINGS_DATA pData, IN BOOL bUpdateThumb)
31 {
32     TCHAR Buffer[64];
33     TCHAR Pixel[64];
34     DWORD index;
35     HWND hwndMonSel;
36     MONSL_MONINFO info;
37 
38     LoadString(hApplet, IDS_PIXEL, Pixel, sizeof(Pixel) / sizeof(TCHAR));
39     _stprintf(Buffer, Pixel, pData->CurrentDisplayDevice->CurrentSettings->dmPelsWidth, pData->CurrentDisplayDevice->CurrentSettings->dmPelsHeight, Pixel);
40     SendDlgItemMessage(hwndDlg, IDC_SETTINGS_RESOLUTION_TEXT, WM_SETTEXT, 0, (LPARAM)Buffer);
41 
42     for (index = 0; index < pData->CurrentDisplayDevice->ResolutionsCount; index++)
43     {
44         if (pData->CurrentDisplayDevice->Resolutions[index].dmPelsWidth == pData->CurrentDisplayDevice->CurrentSettings->dmPelsWidth &&
45             pData->CurrentDisplayDevice->Resolutions[index].dmPelsHeight == pData->CurrentDisplayDevice->CurrentSettings->dmPelsHeight)
46         {
47             if (bUpdateThumb)
48                 SendDlgItemMessage(hwndDlg, IDC_SETTINGS_RESOLUTION, TBM_SETPOS, TRUE, index);
49             break;
50         }
51     }
52     if (LoadString(hApplet, (2900 + pData->CurrentDisplayDevice->CurrentSettings->dmBitsPerPel), Buffer, sizeof(Buffer) / sizeof(TCHAR)))
53         SendDlgItemMessage(hwndDlg, IDC_SETTINGS_BPP, CB_SELECTSTRING, (WPARAM)-1, (LPARAM)Buffer);
54 
55     hwndMonSel = GetDlgItem(hwndDlg, IDC_SETTINGS_MONSEL);
56     index = (INT)SendMessage(hwndMonSel, MSLM_GETCURSEL, 0, 0);
57     if (index != (DWORD)-1 && SendMessage(hwndMonSel, MSLM_GETMONITORINFO, index, (LPARAM)&info))
58     {
59         info.Size.cx = pData->CurrentDisplayDevice->CurrentSettings->dmPelsWidth;
60         info.Size.cy = pData->CurrentDisplayDevice->CurrentSettings->dmPelsHeight;
61         SendMessage(hwndMonSel, MSLM_SETMONITORINFO, index, (LPARAM)&info);
62     }
63 }
64 
65 static int
66 CompareSettings(PSETTINGS_ENTRY Entry, DWORD dmPelsWidth, DWORD dmPelsHeight,
67                 DWORD dmBitsPerPel, DWORD dmDisplayFrequency)
68 {
69     if (Entry->dmPelsWidth  == dmPelsWidth  &&
70         Entry->dmPelsHeight == dmPelsHeight &&
71         Entry->dmBitsPerPel == dmBitsPerPel &&
72         Entry->dmDisplayFrequency == dmDisplayFrequency)
73     {
74         return 0;
75     }
76     else
77     if ((Entry->dmPelsWidth  < dmPelsWidth) ||
78         (Entry->dmPelsWidth == dmPelsWidth && Entry->dmPelsHeight < dmPelsHeight) ||
79         (Entry->dmPelsWidth == dmPelsWidth && Entry->dmPelsHeight == dmPelsHeight &&
80          Entry->dmBitsPerPel < dmBitsPerPel))
81     {
82         return 1;
83     }
84     return -1;
85 }
86 
87 static PSETTINGS_ENTRY
88 GetPossibleSettings(IN LPCTSTR DeviceName, OUT DWORD* pSettingsCount, OUT PSETTINGS_ENTRY* CurrentSettings)
89 {
90     DEVMODE devmode;
91     DWORD NbSettings = 0;
92     DWORD iMode = 0;
93     DWORD dwFlags = 0;
94     PSETTINGS_ENTRY Settings = NULL;
95     HDC hDC;
96     PSETTINGS_ENTRY Current;
97     DWORD bpp, xres, yres;
98     DWORD curDispFreq;
99 
100     /* Get current settings */
101     *CurrentSettings = NULL;
102     hDC = CreateIC(NULL, DeviceName, NULL, NULL);
103     bpp = GetDeviceCaps(hDC, PLANES);
104     bpp *= GetDeviceCaps(hDC, BITSPIXEL);
105     xres = GetDeviceCaps(hDC, HORZRES);
106     yres = GetDeviceCaps(hDC, VERTRES);
107     DeleteDC(hDC);
108 
109     /* List all settings */
110     devmode.dmSize = (WORD)sizeof(devmode);
111     devmode.dmDriverExtra = 0;
112 
113     if (!EnumDisplaySettingsEx(DeviceName, ENUM_CURRENT_SETTINGS, &devmode, dwFlags))
114         return NULL;
115 
116     curDispFreq = devmode.dmDisplayFrequency;
117 
118     while (EnumDisplaySettingsEx(DeviceName, iMode, &devmode, dwFlags))
119     {
120         iMode++;
121 
122         if (devmode.dmPelsWidth < 640 ||
123             devmode.dmPelsHeight < 480 ||
124             devmode.dmDisplayFrequency != curDispFreq ||
125             (devmode.dmBitsPerPel != 4 &&
126              devmode.dmBitsPerPel != 8 &&
127              devmode.dmBitsPerPel != 16 &&
128              devmode.dmBitsPerPel != 24 &&
129              devmode.dmBitsPerPel != 32))
130         {
131             continue;
132         }
133 
134         Current = HeapAlloc(GetProcessHeap(), 0, sizeof(SETTINGS_ENTRY));
135         if (Current != NULL)
136         {
137             /* Sort resolutions by increasing height, and BPP */
138             PSETTINGS_ENTRY Previous = NULL;
139             PSETTINGS_ENTRY Next = Settings;
140             Current->dmPelsWidth = devmode.dmPelsWidth;
141             Current->dmPelsHeight = devmode.dmPelsHeight;
142             Current->dmBitsPerPel = devmode.dmBitsPerPel;
143             Current->dmDisplayFrequency = devmode.dmDisplayFrequency;
144             while (Next != NULL &&
145                    CompareSettings(Next, devmode.dmPelsWidth,
146                                    devmode.dmPelsHeight, devmode.dmBitsPerPel,
147                                    devmode.dmDisplayFrequency) > 0)
148             {
149                 Previous = Next;
150                 Next = Next->Flink;
151             }
152             Current->Blink = Previous;
153             Current->Flink = Next;
154             if (Previous == NULL)
155                 Settings = Current;
156             else
157                 Previous->Flink = Current;
158             if (Next != NULL)
159                 Next->Blink = Current;
160             if (devmode.dmPelsWidth == xres && devmode.dmPelsHeight == yres && devmode.dmBitsPerPel == bpp)
161             {
162                 *CurrentSettings = Current;
163             }
164             NbSettings++;
165         }
166     }
167 
168     *pSettingsCount = NbSettings;
169     return Settings;
170 }
171 
172 static BOOL
173 AddDisplayDevice(IN PSETTINGS_DATA pData, IN const DISPLAY_DEVICE *DisplayDevice)
174 {
175     PDISPLAY_DEVICE_ENTRY newEntry = NULL;
176     LPTSTR description = NULL;
177     LPTSTR name = NULL;
178     LPTSTR key = NULL;
179     LPTSTR devid = NULL;
180     SIZE_T descriptionSize, nameSize, keySize, devidSize;
181     PSETTINGS_ENTRY Current;
182     DWORD ResolutionsCount = 1;
183     DWORD i;
184 
185     newEntry = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(DISPLAY_DEVICE_ENTRY));
186     if (!newEntry) goto ByeBye;
187 
188     newEntry->Settings = GetPossibleSettings(DisplayDevice->DeviceName, &newEntry->SettingsCount, &newEntry->CurrentSettings);
189     if (!newEntry->Settings) goto ByeBye;
190 
191     newEntry->InitialSettings.dmPelsWidth = newEntry->CurrentSettings->dmPelsWidth;
192     newEntry->InitialSettings.dmPelsHeight = newEntry->CurrentSettings->dmPelsHeight;
193     newEntry->InitialSettings.dmBitsPerPel = newEntry->CurrentSettings->dmBitsPerPel;
194     newEntry->InitialSettings.dmDisplayFrequency = newEntry->CurrentSettings->dmDisplayFrequency;
195 
196     /* Count different resolutions */
197     for (Current = newEntry->Settings; Current != NULL; Current = Current->Flink)
198     {
199         if (Current->Flink != NULL &&
200             ((Current->dmPelsWidth != Current->Flink->dmPelsWidth) ||
201              (Current->dmPelsHeight != Current->Flink->dmPelsHeight)))
202         {
203             ResolutionsCount++;
204         }
205     }
206 
207     newEntry->Resolutions = HeapAlloc(GetProcessHeap(), 0, ResolutionsCount * sizeof(RESOLUTION_INFO));
208     if (!newEntry->Resolutions) goto ByeBye;
209 
210     newEntry->ResolutionsCount = ResolutionsCount;
211 
212     /* Fill resolutions infos */
213     for (Current = newEntry->Settings, i = 0; Current != NULL; Current = Current->Flink)
214     {
215         if (Current->Flink == NULL ||
216             (Current->Flink != NULL &&
217             ((Current->dmPelsWidth != Current->Flink->dmPelsWidth) ||
218              (Current->dmPelsHeight != Current->Flink->dmPelsHeight))))
219         {
220             newEntry->Resolutions[i].dmPelsWidth = Current->dmPelsWidth;
221             newEntry->Resolutions[i].dmPelsHeight = Current->dmPelsHeight;
222             i++;
223         }
224     }
225     descriptionSize = (_tcslen(DisplayDevice->DeviceString) + 1) * sizeof(TCHAR);
226     description = HeapAlloc(GetProcessHeap(), 0, descriptionSize);
227     if (!description) goto ByeBye;
228 
229     nameSize = (_tcslen(DisplayDevice->DeviceName) + 1) * sizeof(TCHAR);
230     name = HeapAlloc(GetProcessHeap(), 0, nameSize);
231     if (!name) goto ByeBye;
232 
233     keySize = (_tcslen(DisplayDevice->DeviceKey) + 1) * sizeof(TCHAR);
234     key = HeapAlloc(GetProcessHeap(), 0, keySize);
235     if (!key) goto ByeBye;
236 
237     devidSize = (_tcslen(DisplayDevice->DeviceID) + 1) * sizeof(TCHAR);
238     devid = HeapAlloc(GetProcessHeap(), 0, devidSize);
239     if (!devid) goto ByeBye;
240 
241     memcpy(description, DisplayDevice->DeviceString, descriptionSize);
242     memcpy(name, DisplayDevice->DeviceName, nameSize);
243     memcpy(key, DisplayDevice->DeviceKey, keySize);
244     memcpy(devid, DisplayDevice->DeviceID, devidSize);
245     newEntry->DeviceDescription = description;
246     newEntry->DeviceName = name;
247     newEntry->DeviceKey = key;
248     newEntry->DeviceID = devid;
249     newEntry->DeviceStateFlags = DisplayDevice->StateFlags;
250     newEntry->Flink = pData->DisplayDeviceList;
251     pData->DisplayDeviceList = newEntry;
252     return TRUE;
253 
254 ByeBye:
255     if (newEntry != NULL)
256     {
257         if (newEntry->Settings != NULL)
258         {
259             Current = newEntry->Settings;
260             while (Current != NULL)
261             {
262                 PSETTINGS_ENTRY Next = Current->Flink;
263                 HeapFree(GetProcessHeap(), 0, Current);
264                 Current = Next;
265             }
266         }
267         if (newEntry->Resolutions != NULL)
268             HeapFree(GetProcessHeap(), 0, newEntry->Resolutions);
269         HeapFree(GetProcessHeap(), 0, newEntry);
270     }
271     if (description != NULL)
272         HeapFree(GetProcessHeap(), 0, description);
273     if (name != NULL)
274         HeapFree(GetProcessHeap(), 0, name);
275     if (key != NULL)
276         HeapFree(GetProcessHeap(), 0, key);
277     return FALSE;
278 }
279 
280 static VOID
281 OnDisplayDeviceChanged(IN HWND hwndDlg, IN PSETTINGS_DATA pData, IN PDISPLAY_DEVICE_ENTRY pDeviceEntry)
282 {
283     PSETTINGS_ENTRY Current;
284     DWORD index;
285 
286     pData->CurrentDisplayDevice = pDeviceEntry; /* Update variable */
287 
288     /* Fill color depths combo box */
289     SendDlgItemMessage(hwndDlg, IDC_SETTINGS_BPP, CB_RESETCONTENT, 0, 0);
290     for (Current = pDeviceEntry->Settings; Current != NULL; Current = Current->Flink)
291     {
292         TCHAR Buffer[64];
293         if (LoadString(hApplet, (2900 + Current->dmBitsPerPel), Buffer, sizeof(Buffer) / sizeof(TCHAR)))
294         {
295             index = (DWORD) SendDlgItemMessage(hwndDlg, IDC_SETTINGS_BPP, CB_FINDSTRINGEXACT, (WPARAM)-1, (LPARAM)Buffer);
296             if (index == (DWORD)CB_ERR)
297             {
298                 index = (DWORD) SendDlgItemMessage(hwndDlg, IDC_SETTINGS_BPP, CB_ADDSTRING, 0, (LPARAM)Buffer);
299                 SendDlgItemMessage(hwndDlg, IDC_SETTINGS_BPP, CB_SETITEMDATA, index, Current->dmBitsPerPel);
300             }
301         }
302     }
303 
304     /* Fill device description */
305     SendDlgItemMessage(hwndDlg, IDC_SETTINGS_DEVICE, WM_SETTEXT, 0, (LPARAM)pDeviceEntry->DeviceDescription);
306 
307     /* Fill resolutions slider */
308     SendDlgItemMessage(hwndDlg, IDC_SETTINGS_RESOLUTION, TBM_CLEARTICS, TRUE, 0);
309     SendDlgItemMessage(hwndDlg, IDC_SETTINGS_RESOLUTION, TBM_SETRANGE, TRUE, MAKELONG(0, pDeviceEntry->ResolutionsCount - 1));
310 
311     UpdateDisplay(hwndDlg, pData, TRUE);
312 }
313 
314 static VOID
315 SettingsOnInitDialog(IN HWND hwndDlg)
316 {
317     BITMAP bitmap;
318     DWORD Result = 0;
319     DWORD iDevNum = 0;
320     DWORD i;
321     DISPLAY_DEVICE displayDevice;
322     PSETTINGS_DATA pData;
323 
324     pData = HeapAlloc(GetProcessHeap(), 0, sizeof(SETTINGS_DATA));
325     if (pData == NULL)
326         return;
327 
328     SetWindowLongPtr(hwndDlg, DWLP_USER, (LONG_PTR)pData);
329 
330     /* Get video cards list */
331     pData->DisplayDeviceList = NULL;
332     displayDevice.cb = sizeof(displayDevice);
333     while (EnumDisplayDevices(NULL, iDevNum, &displayDevice, EDD_GET_DEVICE_INTERFACE_NAME))
334     {
335         if ((displayDevice.StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP) != 0)
336         {
337             if (AddDisplayDevice(pData, &displayDevice))
338                 Result++;
339         }
340         iDevNum++;
341     }
342 
343     if (Result == 0)
344     {
345         /* No adapter found */
346         EnableWindow(GetDlgItem(hwndDlg, IDC_SETTINGS_BPP), FALSE);
347         EnableWindow(GetDlgItem(hwndDlg, IDC_SETTINGS_RESOLUTION), FALSE);
348         EnableWindow(GetDlgItem(hwndDlg, IDC_SETTINGS_RESOLUTION_TEXT), FALSE);
349         EnableWindow(GetDlgItem(hwndDlg, IDC_SETTINGS_ADVANCED), FALSE);
350         ShowWindow(GetDlgItem(hwndDlg, IDC_SETTINGS_SPECTRUM), SW_HIDE);
351 
352         /* Do not initialize the color spectrum bitmaps */
353         memset(pData->hSpectrumBitmaps, 0, sizeof(pData->hSpectrumBitmaps));
354         return;
355     }
356     else if (Result == 1)
357     {
358         MONSL_MONINFO monitors;
359 
360         /* Single video adapter */
361         OnDisplayDeviceChanged(hwndDlg, pData, pData->DisplayDeviceList);
362 
363         ShowWindow(GetDlgItem(hwndDlg, IDC_SETTINGS_MONTEXT), SW_HIDE);
364         ShowWindow(GetDlgItem(hwndDlg, IDC_SETTINGS_MONSEL), SW_HIDE);
365 
366         monitors.Position.x = monitors.Position.y = 0;
367         monitors.Size.cx = pData->CurrentDisplayDevice->CurrentSettings->dmPelsWidth;
368         monitors.Size.cy = pData->CurrentDisplayDevice->CurrentSettings->dmPelsHeight;
369         monitors.Flags = 0;
370         SendDlgItemMessage(hwndDlg,
371                    IDC_SETTINGS_MONSEL,
372                    MSLM_SETMONITORSINFO,
373                    1,
374                    (LPARAM)&monitors);
375     }
376     else /* FIXME: Incomplete! */
377     {
378         PMONSL_MONINFO pMonitors;
379         DWORD i;
380 
381         OnDisplayDeviceChanged(hwndDlg, pData, pData->DisplayDeviceList);
382 
383         ShowWindow(GetDlgItem(hwndDlg, IDC_RESOLUTION_PREVIEW), SW_HIDE);
384 
385         pMonitors = (PMONSL_MONINFO)HeapAlloc(GetProcessHeap(), 0, sizeof(MONSL_MONINFO) * Result);
386         if (pMonitors)
387         {
388             DWORD hack = 1280;
389             for (i = 0; i < Result; i++)
390             {
391                 pMonitors[i].Position.x = hack * i;
392                 pMonitors[i].Position.y = 0;
393                 pMonitors[i].Size.cx = pData->DisplayDeviceList->CurrentSettings->dmPelsWidth;
394                 pMonitors[i].Size.cy = pData->DisplayDeviceList->CurrentSettings->dmPelsHeight;
395                 pMonitors[i].Flags = 0;
396             }
397 
398             SendDlgItemMessage(hwndDlg,
399                        IDC_SETTINGS_MONSEL,
400                        MSLM_SETMONITORSINFO,
401                        Result,
402                        (LPARAM)pMonitors);
403 
404             HeapFree(GetProcessHeap(), 0, pMonitors);
405         }
406     }
407 
408     /* Initialize the color spectrum bitmaps */
409     for (i = 0; i < NUM_SPECTRUM_BITMAPS; i++)
410     {
411         pData->hSpectrumBitmaps[i] = LoadImageW(hApplet, MAKEINTRESOURCEW(IDB_SPECTRUM_4 + i), IMAGE_BITMAP, 0, 0, LR_DEFAULTCOLOR);
412 
413         if (pData->hSpectrumBitmaps[i] != NULL)
414         {
415             if (GetObjectW(pData->hSpectrumBitmaps[i], sizeof(BITMAP), &bitmap) != 0)
416             {
417                 pData->cxSource[i] = bitmap.bmWidth;
418                 pData->cySource[i] = bitmap.bmHeight;
419             }
420             else
421             {
422                 pData->cxSource[i] = 0;
423                 pData->cySource[i] = 0;
424             }
425         }
426     }
427 }
428 
429 static VOID
430 ShowResolutionPreview(IN LPDRAWITEMSTRUCT draw, IN PSETTINGS_DATA pData)
431 {
432     HBRUSH hBrush;
433     HDC hDC;
434     HGDIOBJ hOldObj;
435     RECT rcItem = {
436         MONITOR_LEFT,
437         MONITOR_TOP,
438         MONITOR_RIGHT,
439         MONITOR_BOTTOM
440     };
441 
442     hDC = CreateCompatibleDC(draw->hDC);
443     hOldObj = SelectObject(hDC, g_GlobalData.hMonitorBitmap);
444 
445     /* FIXME: Draw resolution preview bitmap inside monitor. */
446     hBrush = CreateSolidBrush(g_GlobalData.desktop_color);
447     FillRect(hDC, &rcItem, hBrush);
448     DeleteObject(hBrush);
449 
450     GdiTransparentBlt(draw->hDC,
451                       draw->rcItem.left, draw->rcItem.top,
452                       draw->rcItem.right - draw->rcItem.left + 1,
453                       draw->rcItem.bottom - draw->rcItem.top + 1,
454                       hDC,
455                       0, 0,
456                       g_GlobalData.bmMonWidth, g_GlobalData.bmMonHeight,
457                       MONITOR_ALPHA);
458 
459     SelectObject(hDC, hOldObj);
460     DeleteDC(hDC);
461 }
462 
463 /* Get the ID for SETTINGS_DATA::hSpectrumBitmaps */
464 static VOID
465 ShowColorSpectrum(IN HDC hDC, IN LPRECT client, IN DWORD BitsPerPel, IN PSETTINGS_DATA pData)
466 {
467     HDC hdcMem;
468     INT iBitmap;
469 
470     hdcMem = CreateCompatibleDC(hDC);
471 
472     if (!hdcMem)
473         return;
474 
475     switch(BitsPerPel)
476     {
477         case 4:  iBitmap = 0; break;
478         case 8:  iBitmap = 1; break;
479         default: iBitmap = 2;
480     }
481 
482     if (SelectObject(hdcMem, pData->hSpectrumBitmaps[iBitmap]))
483     {
484         StretchBlt(hDC,
485                client->left, client->top,
486                client->right - client->left,
487                client->bottom - client->top,
488                hdcMem, 0, 0,
489                pData->cxSource[iBitmap],
490                pData->cySource[iBitmap], SRCCOPY);
491     }
492 
493     DeleteDC(hdcMem);
494 }
495 
496 static VOID
497 OnBPPChanged(IN HWND hwndDlg, IN PSETTINGS_DATA pData)
498 {
499     /* If new BPP is not compatible with resolution:
500      * 1) try to find the nearest smaller matching resolution
501      * 2) otherwise, get the nearest bigger resolution
502      */
503     PSETTINGS_ENTRY Current;
504     DWORD dmNewBitsPerPel;
505     DWORD index;
506     HDC hSpectrumDC;
507     HWND hSpectrumControl;
508     RECT client;
509 
510     index = (DWORD) SendDlgItemMessage(hwndDlg, IDC_SETTINGS_BPP, CB_GETCURSEL, 0, 0);
511     dmNewBitsPerPel = (DWORD) SendDlgItemMessage(hwndDlg, IDC_SETTINGS_BPP, CB_GETITEMDATA, index, 0);
512 
513     /* Show a new spectrum bitmap */
514     hSpectrumControl = GetDlgItem(hwndDlg, IDC_SETTINGS_SPECTRUM);
515     hSpectrumDC = GetDC(hSpectrumControl);
516     if (hSpectrumDC == NULL)
517         return;
518 
519     GetClientRect(hSpectrumControl, &client);
520     ShowColorSpectrum(hSpectrumDC, &client, dmNewBitsPerPel, pData);
521     ReleaseDC(hSpectrumControl, hSpectrumDC);
522 
523     /* Find if new parameters are valid */
524     Current = pData->CurrentDisplayDevice->CurrentSettings;
525     if (dmNewBitsPerPel == Current->dmBitsPerPel)
526     {
527         /* No change */
528         return;
529     }
530 
531     PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
532 
533     if (dmNewBitsPerPel < Current->dmBitsPerPel)
534     {
535         Current = Current->Blink;
536         while (Current != NULL)
537         {
538             if (Current->dmBitsPerPel == dmNewBitsPerPel
539              && Current->dmPelsHeight == pData->CurrentDisplayDevice->CurrentSettings->dmPelsHeight
540              && Current->dmPelsWidth == pData->CurrentDisplayDevice->CurrentSettings->dmPelsWidth)
541             {
542                 pData->CurrentDisplayDevice->CurrentSettings = Current;
543                 UpdateDisplay(hwndDlg, pData, TRUE);
544                 return;
545             }
546             Current = Current->Blink;
547         }
548     }
549     else
550     {
551         Current = Current->Flink;
552         while (Current != NULL)
553         {
554             if (Current->dmBitsPerPel == dmNewBitsPerPel
555              && Current->dmPelsHeight == pData->CurrentDisplayDevice->CurrentSettings->dmPelsHeight
556              && Current->dmPelsWidth == pData->CurrentDisplayDevice->CurrentSettings->dmPelsWidth)
557             {
558                 pData->CurrentDisplayDevice->CurrentSettings = Current;
559                 UpdateDisplay(hwndDlg, pData, TRUE);
560                 return;
561             }
562             Current = Current->Flink;
563         }
564     }
565 
566     /* Search smaller resolution compatible with current color depth */
567     Current = pData->CurrentDisplayDevice->CurrentSettings->Blink;
568     while (Current != NULL)
569     {
570         if (Current->dmBitsPerPel == dmNewBitsPerPel)
571         {
572             pData->CurrentDisplayDevice->CurrentSettings = Current;
573             UpdateDisplay(hwndDlg, pData, TRUE);
574             return;
575         }
576         Current = Current->Blink;
577     }
578 
579     /* Search bigger resolution compatible with current color depth */
580     Current = pData->CurrentDisplayDevice->CurrentSettings->Flink;
581     while (Current != NULL)
582     {
583         if (Current->dmBitsPerPel == dmNewBitsPerPel)
584         {
585             pData->CurrentDisplayDevice->CurrentSettings = Current;
586             UpdateDisplay(hwndDlg, pData, TRUE);
587             return;
588         }
589         Current = Current->Flink;
590     }
591 
592     /* We shouldn't go there */
593 }
594 
595 static VOID
596 OnResolutionChanged(IN HWND hwndDlg, IN PSETTINGS_DATA pData, IN DWORD NewPosition,
597                     IN BOOL bUpdateThumb)
598 {
599     /* If new resolution is not compatible with color depth:
600      * 1) try to find the nearest bigger matching color depth
601      * 2) otherwise, get the nearest smaller color depth
602      */
603     PSETTINGS_ENTRY Current;
604     DWORD dmNewPelsHeight = pData->CurrentDisplayDevice->Resolutions[NewPosition].dmPelsHeight;
605     DWORD dmNewPelsWidth = pData->CurrentDisplayDevice->Resolutions[NewPosition].dmPelsWidth;
606     DWORD dmBitsPerPel;
607     DWORD dmDisplayFrequency;
608 
609     /* Find if new parameters are valid */
610     Current = pData->CurrentDisplayDevice->CurrentSettings;
611     if (dmNewPelsHeight == Current->dmPelsHeight && dmNewPelsWidth == Current->dmPelsWidth)
612     {
613         /* No change */
614         return;
615     }
616 
617     PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
618     InvalidateRect(GetDlgItem(hwndDlg, IDC_RESOLUTION_PREVIEW), NULL, TRUE);
619 
620     dmBitsPerPel = Current->dmBitsPerPel;
621     dmDisplayFrequency = Current->dmDisplayFrequency;
622 
623     if (CompareSettings(Current, dmNewPelsWidth,
624                         dmNewPelsHeight, dmBitsPerPel,
625                         dmDisplayFrequency) < 0)
626     {
627         Current = Current->Blink;
628         while (Current != NULL)
629         {
630             if (Current->dmPelsHeight == dmNewPelsHeight
631              && Current->dmPelsWidth  == dmNewPelsWidth
632              && Current->dmBitsPerPel == dmBitsPerPel)
633             {
634                 pData->CurrentDisplayDevice->CurrentSettings = Current;
635                 UpdateDisplay(hwndDlg, pData, bUpdateThumb);
636                 return;
637             }
638             Current = Current->Blink;
639         }
640     }
641     else
642     {
643         Current = Current->Flink;
644         while (Current != NULL)
645         {
646             if (Current->dmPelsHeight == dmNewPelsHeight
647              && Current->dmPelsWidth  == dmNewPelsWidth
648              && Current->dmBitsPerPel == dmBitsPerPel)
649             {
650                 pData->CurrentDisplayDevice->CurrentSettings = Current;
651                 UpdateDisplay(hwndDlg, pData, bUpdateThumb);
652                 return;
653             }
654             Current = Current->Flink;
655         }
656     }
657 
658     /* Search bigger color depth compatible with current resolution */
659     Current = pData->CurrentDisplayDevice->CurrentSettings->Flink;
660     while (Current != NULL)
661     {
662         if (dmNewPelsHeight == Current->dmPelsHeight && dmNewPelsWidth == Current->dmPelsWidth)
663         {
664             pData->CurrentDisplayDevice->CurrentSettings = Current;
665             UpdateDisplay(hwndDlg, pData, bUpdateThumb);
666             return;
667         }
668         Current = Current->Flink;
669     }
670 
671     /* Search smaller color depth compatible with current resolution */
672     Current = pData->CurrentDisplayDevice->CurrentSettings->Blink;
673     while (Current != NULL)
674     {
675         if (dmNewPelsHeight == Current->dmPelsHeight && dmNewPelsWidth == Current->dmPelsWidth)
676         {
677             pData->CurrentDisplayDevice->CurrentSettings = Current;
678             UpdateDisplay(hwndDlg, pData, bUpdateThumb);
679             return;
680         }
681         Current = Current->Blink;
682     }
683 
684     /* We shouldn't go there */
685 }
686 
687 /* Property sheet page callback */
688 UINT CALLBACK
689 SettingsPageCallbackProc(HWND hwnd, UINT uMsg, LPPROPSHEETPAGE ppsp)
690 {
691     UINT Ret = 0;
692 
693     switch (uMsg)
694     {
695         case PSPCB_CREATE:
696             Ret = RegisterMonitorSelectionControl(hApplet);
697             break;
698 
699         case PSPCB_RELEASE:
700             UnregisterMonitorSelectionControl(hApplet);
701             break;
702     }
703 
704     return Ret;
705 }
706 
707 static INT_PTR CALLBACK
708 ConfirmDlgProc(IN HWND hwndDlg, IN UINT uMsg, IN WPARAM wParam, IN LPARAM lParam)
709 {
710     PTIMEOUTDATA pData;
711 
712     pData = (PTIMEOUTDATA)GetWindowLongPtr(hwndDlg, DWLP_USER);
713 
714     switch(uMsg)
715     {
716         case WM_INITDIALOG:
717             /* Allocate the local dialog data */
718             pData = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TIMEOUTDATA));
719             if (pData == NULL)
720                 return FALSE;
721 
722             /* Link the dialog data to the dialog */
723             SetWindowLongPtr(hwndDlg, DWLP_USER, (LONG_PTR)pData);
724 
725             /* Timeout in seconds */
726             pData->nTimeout = 15;
727 
728             /* Load the raw timeout string */
729             LoadString(hApplet, IDS_TIMEOUTTEXT, pData->szRawBuffer, ARRAYSIZE(pData->szRawBuffer));
730 
731             /* Cook the timeout string and show it */
732             _stprintf(pData->szCookedBuffer, pData->szRawBuffer, pData->nTimeout);
733             SetDlgItemText(hwndDlg, IDC_TIMEOUTTEXT, pData->szCookedBuffer);
734 
735             /* Start the timer (ticks every second)*/
736             SetTimer(hwndDlg, 1, 1000, NULL);
737             break;
738 
739         case WM_TIMER:
740             /* Update the timepout value */
741             pData->nTimeout--;
742 
743             /* Update the timeout text */
744             _stprintf(pData->szCookedBuffer, pData->szRawBuffer, pData->nTimeout);
745             SetDlgItemText(hwndDlg, IDC_TIMEOUTTEXT, pData->szCookedBuffer);
746 
747             /* Kill the timer and return a 'No', if we ran out of time */
748             if (pData->nTimeout == 0)
749             {
750                 KillTimer(hwndDlg, 1);
751                 EndDialog(hwndDlg, IDNO);
752             }
753             break;
754 
755         case WM_COMMAND:
756             /* Kill the timer and return the clicked button id */
757             KillTimer(hwndDlg, 1);
758             EndDialog(hwndDlg, LOWORD(wParam));
759             break;
760 
761         case WM_DESTROY:
762             /* Free the local dialog data */
763             HeapFree(GetProcessHeap(), 0, pData);
764             break;
765     }
766 
767     return FALSE;
768 }
769 
770 BOOL
771 SwitchDisplayMode(HWND hwndDlg, PWSTR DeviceName, PSETTINGS_ENTRY seInit, PSETTINGS_ENTRY seNew, OUT PLONG rc)
772 {
773     TCHAR Message[1024], Title[256];
774     DEVMODE devmode;
775 
776     RtlZeroMemory(&devmode, sizeof(devmode));
777     devmode.dmSize = (WORD)sizeof(devmode);
778     devmode.dmPelsWidth = seNew->dmPelsWidth;
779     devmode.dmPelsHeight = seNew->dmPelsHeight;
780     devmode.dmBitsPerPel = seNew->dmBitsPerPel;
781     devmode.dmDisplayFrequency = seNew->dmDisplayFrequency;
782     devmode.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL | DM_DISPLAYFREQUENCY;
783 
784     *rc = ChangeDisplaySettingsEx(DeviceName,
785                                   &devmode,
786                                   NULL,
787                                   CDS_UPDATEREGISTRY,
788                                   NULL);
789     switch (*rc)
790     {
791         case DISP_CHANGE_SUCCESSFUL:
792             break;
793 
794         case DISP_CHANGE_RESTART:
795             LoadString(hApplet, IDS_DISPLAY_SETTINGS, Title, sizeof(Title) / sizeof(TCHAR));
796             LoadString(hApplet, IDS_APPLY_NEEDS_RESTART, Message, sizeof(Message) / sizeof (TCHAR));
797             MessageBox(hwndDlg, Message, Title, MB_OK | MB_ICONINFORMATION);
798             return FALSE;
799 
800         case DISP_CHANGE_FAILED:
801         default:
802             LoadString(hApplet, IDS_DISPLAY_SETTINGS, Title, sizeof(Title) / sizeof(TCHAR));
803             LoadString(hApplet, IDS_APPLY_FAILED, Message, sizeof(Message) / sizeof (TCHAR));
804             MessageBox(hwndDlg, Message, Title, MB_OK | MB_ICONSTOP);
805             return FALSE;
806     }
807 
808     if (DialogBox(hApplet, MAKEINTRESOURCE(IDD_CONFIRMSETTINGS), hwndDlg, ConfirmDlgProc) == IDYES)
809     {
810         return TRUE;
811     }
812     else
813     {
814         devmode.dmPelsWidth = seInit->dmPelsWidth;
815         devmode.dmPelsHeight = seInit->dmPelsHeight;
816         devmode.dmBitsPerPel = seInit->dmBitsPerPel;
817         devmode.dmDisplayFrequency = seInit->dmDisplayFrequency;
818 
819         *rc = ChangeDisplaySettingsEx(DeviceName,
820                                       &devmode,
821                                       NULL,
822                                       CDS_UPDATEREGISTRY,
823                                       NULL);
824         switch (*rc)
825         {
826             case DISP_CHANGE_SUCCESSFUL:
827                 return FALSE;
828 
829             case DISP_CHANGE_RESTART:
830                 LoadString(hApplet, IDS_DISPLAY_SETTINGS, Title, sizeof(Title) / sizeof(TCHAR));
831                 LoadString(hApplet, IDS_APPLY_NEEDS_RESTART, Message, sizeof(Message) / sizeof (TCHAR));
832                 MessageBox(hwndDlg, Message, Title, MB_OK | MB_ICONINFORMATION);
833                 return FALSE;
834 
835             case DISP_CHANGE_FAILED:
836             default:
837                 LoadString(hApplet, IDS_DISPLAY_SETTINGS, Title, sizeof(Title) / sizeof(TCHAR));
838                 LoadString(hApplet, IDS_APPLY_FAILED, Message, sizeof(Message) / sizeof (TCHAR));
839                 MessageBox(hwndDlg, Message, Title, MB_OK | MB_ICONSTOP);
840                 return FALSE;
841         }
842     }
843 }
844 
845 static
846 PSETTINGS_ENTRY
847 FindBestElement(
848     _In_ PDISPLAY_DEVICE_ENTRY pDevice)
849 {
850     PSETTINGS_ENTRY Request = &pDevice->InitialSettings, BestEntry = NULL, Current;
851     LONG Distance, NearestDistance = MAXLONG;
852 
853     /* Find the best entry in the list */
854     for (Current = pDevice->Settings; Current; Current = Current->Flink)
855     {
856         Distance = 0x100000 * labs(Current->dmBitsPerPel       - Request->dmBitsPerPel      ) +
857                       0x100 * labs(Current->dmPelsWidth        - Request->dmPelsWidth       ) +
858                       0x100 * labs(Current->dmPelsHeight       - Request->dmPelsHeight      ) +
859                               labs(Current->dmDisplayFrequency - Request->dmDisplayFrequency);
860         if (Distance == 0)
861             return Current;
862 
863         if (Distance < NearestDistance)
864         {
865             BestEntry = Current;
866             NearestDistance = Distance;
867         }
868     }
869 
870     return BestEntry;
871 }
872 
873 static VOID
874 ApplyDisplaySettings(HWND hwndDlg, PSETTINGS_DATA pData)
875 {
876     BOOL Ret;
877     LONG rc;
878 
879     Ret = SwitchDisplayMode(hwndDlg,
880                             pData->CurrentDisplayDevice->DeviceName,
881                             &pData->CurrentDisplayDevice->InitialSettings,
882                             pData->CurrentDisplayDevice->CurrentSettings,
883                             &rc);
884 
885     if (rc != DISP_CHANGE_SUCCESSFUL)
886         return;
887 
888     if (Ret)
889     {
890         pData->CurrentDisplayDevice->InitialSettings.dmPelsWidth = pData->CurrentDisplayDevice->CurrentSettings->dmPelsWidth;
891         pData->CurrentDisplayDevice->InitialSettings.dmPelsHeight = pData->CurrentDisplayDevice->CurrentSettings->dmPelsHeight;
892         pData->CurrentDisplayDevice->InitialSettings.dmBitsPerPel = pData->CurrentDisplayDevice->CurrentSettings->dmBitsPerPel;
893         pData->CurrentDisplayDevice->InitialSettings.dmDisplayFrequency = pData->CurrentDisplayDevice->CurrentSettings->dmDisplayFrequency;
894     }
895     else
896     {
897         pData->CurrentDisplayDevice->CurrentSettings = FindBestElement(pData->CurrentDisplayDevice);
898         UpdateDisplay(hwndDlg, pData, TRUE);
899     }
900 }
901 
902 /* Property page dialog callback */
903 INT_PTR CALLBACK
904 SettingsPageProc(IN HWND hwndDlg, IN UINT uMsg, IN WPARAM wParam, IN LPARAM lParam)
905 {
906     PSETTINGS_DATA pData;
907 
908     pData = (PSETTINGS_DATA)GetWindowLongPtr(hwndDlg, DWLP_USER);
909 
910     switch(uMsg)
911     {
912         case WM_INITDIALOG:
913         {
914             SettingsOnInitDialog(hwndDlg);
915             break;
916         }
917         case WM_DRAWITEM:
918         {
919             LPDRAWITEMSTRUCT lpDrawItem;
920             lpDrawItem = (LPDRAWITEMSTRUCT)lParam;
921 
922             switch (lpDrawItem->CtlID)
923             {
924                 case IDC_RESOLUTION_PREVIEW:
925                 {
926                     ShowResolutionPreview(lpDrawItem, pData);
927                     break;
928                 }
929                 case IDC_SETTINGS_SPECTRUM:
930                 {
931                     ShowColorSpectrum(lpDrawItem->hDC, &lpDrawItem->rcItem, pData->CurrentDisplayDevice->CurrentSettings->dmBitsPerPel, pData);
932                     break;
933                 }
934             }
935             break;
936         }
937         case WM_COMMAND:
938         {
939             DWORD controlId = LOWORD(wParam);
940             DWORD command   = HIWORD(wParam);
941 
942             if (controlId == IDC_SETTINGS_ADVANCED && command == BN_CLICKED)
943             {
944                 if (DisplayAdvancedSettings(hwndDlg, pData->CurrentDisplayDevice))
945                 {
946                     DEVMODE devmode;
947                     ZeroMemory(&devmode, sizeof(devmode));
948                     devmode.dmSize = (WORD)sizeof(devmode);
949                     if (EnumDisplaySettingsExW(pData->CurrentDisplayDevice->DeviceName,
950                                                ENUM_CURRENT_SETTINGS, &devmode, 0))
951                     {
952                         PSETTINGS_ENTRY pInitialSettings = &pData->CurrentDisplayDevice->InitialSettings;
953                         pInitialSettings->dmPelsWidth = devmode.dmPelsWidth;
954                         pInitialSettings->dmPelsHeight = devmode.dmPelsHeight;
955                         pInitialSettings->dmBitsPerPel = devmode.dmBitsPerPel;
956                         pInitialSettings->dmDisplayFrequency = devmode.dmDisplayFrequency;
957                         pData->CurrentDisplayDevice->CurrentSettings =
958                             FindBestElement(pData->CurrentDisplayDevice);
959                         UpdateDisplay(hwndDlg, pData, TRUE);
960                     }
961                 }
962             }
963             else if (controlId == IDC_SETTINGS_BPP && command == CBN_SELCHANGE)
964                 OnBPPChanged(hwndDlg, pData);
965             break;
966         }
967         case WM_HSCROLL:
968         {
969             switch (LOWORD(wParam))
970             {
971                 case TB_LINEUP:
972                 case TB_LINEDOWN:
973                 case TB_PAGEUP:
974                 case TB_PAGEDOWN:
975                 case TB_TOP:
976                 case TB_BOTTOM:
977                 case TB_ENDTRACK:
978                 {
979                     DWORD newPosition = (DWORD) SendDlgItemMessage(hwndDlg, IDC_SETTINGS_RESOLUTION, TBM_GETPOS, 0, 0);
980                     OnResolutionChanged(hwndDlg, pData, newPosition, TRUE);
981                     break;
982                 }
983 
984                 case TB_THUMBTRACK:
985                     OnResolutionChanged(hwndDlg, pData, HIWORD(wParam), FALSE);
986                     break;
987             }
988             break;
989         }
990         case WM_NOTIFY:
991         {
992             LPNMHDR lpnm = (LPNMHDR)lParam;
993             if (lpnm->code == (UINT)PSN_APPLY)
994             {
995                 if (pData->CurrentDisplayDevice->CurrentSettings->dmPelsWidth != pData->CurrentDisplayDevice->InitialSettings.dmPelsWidth
996                  || pData->CurrentDisplayDevice->CurrentSettings->dmPelsHeight != pData->CurrentDisplayDevice->InitialSettings.dmPelsHeight
997                  || pData->CurrentDisplayDevice->CurrentSettings->dmBitsPerPel != pData->CurrentDisplayDevice->InitialSettings.dmBitsPerPel)
998                 {
999                     /* Apply new settings */
1000                     ApplyDisplaySettings(hwndDlg, pData);
1001                 }
1002             }
1003             else if (lpnm->code == MSLN_MONITORCHANGED)
1004             {
1005                 PMONSL_MONNMMONITORCHANGING lpnmi = (PMONSL_MONNMMONITORCHANGING)lParam;
1006                 PDISPLAY_DEVICE_ENTRY Current = pData->DisplayDeviceList;
1007                 ULONG i;
1008                 for (i = 0; i < lpnmi->hdr.Index; i++)
1009                     Current = Current->Flink;
1010                 OnDisplayDeviceChanged(hwndDlg, pData, Current);
1011             }
1012             break;
1013         }
1014 
1015         case WM_CONTEXTMENU:
1016         {
1017             HWND hwndMonSel;
1018             HMENU hPopup;
1019             UINT uiCmd;
1020             POINT pt, ptClient;
1021             INT Index;
1022 
1023             pt.x = (SHORT)LOWORD(lParam);
1024             pt.y = (SHORT)HIWORD(lParam);
1025 
1026             hwndMonSel = GetDlgItem(hwndDlg,
1027                                     IDC_SETTINGS_MONSEL);
1028             if ((HWND)wParam == hwndMonSel)
1029             {
1030                 if (pt.x == -1 && pt.y == -1)
1031                 {
1032                     RECT rcMon;
1033 
1034                     Index = (INT)SendMessage(hwndMonSel,
1035                                              MSLM_GETCURSEL,
1036                                              0,
1037                                              0);
1038 
1039                     if (Index >= 0 &&
1040                         (INT)SendMessage(hwndMonSel,
1041                                          MSLM_GETMONITORRECT,
1042                                          Index,
1043                                          (LPARAM)&rcMon) > 0)
1044                     {
1045                         pt.x = rcMon.left + ((rcMon.right - rcMon.left) / 2);
1046                         pt.y = rcMon.top + ((rcMon.bottom - rcMon.top) / 2);
1047                     }
1048                     else
1049                         pt.x = pt.y = 0;
1050 
1051                     MapWindowPoints(hwndMonSel,
1052                                     NULL,
1053                                     &pt,
1054                                     1);
1055                 }
1056                 else
1057                 {
1058                     ptClient = pt;
1059                     MapWindowPoints(NULL,
1060                                     hwndMonSel,
1061                                     &ptClient,
1062                                     1);
1063 
1064                     Index = (INT)SendMessage(hwndMonSel,
1065                                              MSLM_HITTEST,
1066                                              (WPARAM)&ptClient,
1067                                              0);
1068                 }
1069 
1070                 if (Index >= 0)
1071                 {
1072                     hPopup = LoadPopupMenu(hApplet,
1073                                            MAKEINTRESOURCE(IDM_MONITOR_MENU));
1074                     if (hPopup != NULL)
1075                     {
1076                         /* FIXME: Enable/Disable menu items */
1077                         EnableMenuItem(hPopup,
1078                                        ID_MENU_ATTACHED,
1079                                        MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
1080                         EnableMenuItem(hPopup,
1081                                        ID_MENU_PRIMARY,
1082                                        MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
1083                         EnableMenuItem(hPopup,
1084                                        ID_MENU_IDENTIFY,
1085                                        MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
1086                         EnableMenuItem(hPopup,
1087                                        ID_MENU_PROPERTIES,
1088                                        MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
1089 
1090                         uiCmd = (UINT)TrackPopupMenu(hPopup,
1091                                                      TPM_RETURNCMD | TPM_RIGHTBUTTON,
1092                                                      pt.x,
1093                                                      pt.y,
1094                                                      0,
1095                                                      hwndDlg,
1096                                                      NULL);
1097 
1098                         switch (uiCmd)
1099                         {
1100                             case ID_MENU_ATTACHED:
1101                             case ID_MENU_PRIMARY:
1102                             case ID_MENU_IDENTIFY:
1103                             case ID_MENU_PROPERTIES:
1104                                 /* FIXME: Implement */
1105                                 break;
1106                         }
1107 
1108                         DestroyMenu(hPopup);
1109                     }
1110                 }
1111             }
1112             break;
1113         }
1114 
1115         case WM_DESTROY:
1116         {
1117             DWORD i;
1118             PDISPLAY_DEVICE_ENTRY Current = pData->DisplayDeviceList;
1119 
1120             while (Current != NULL)
1121             {
1122                 PDISPLAY_DEVICE_ENTRY Next = Current->Flink;
1123                 PSETTINGS_ENTRY CurrentSettings = Current->Settings;
1124                 while (CurrentSettings != NULL)
1125                 {
1126                     PSETTINGS_ENTRY NextSettings = CurrentSettings->Flink;
1127                     HeapFree(GetProcessHeap(), 0, CurrentSettings);
1128                     CurrentSettings = NextSettings;
1129                 }
1130                 HeapFree(GetProcessHeap(), 0, Current);
1131                 Current = Next;
1132             }
1133 
1134             for (i = 0; i < NUM_SPECTRUM_BITMAPS; i++)
1135             {
1136                 if (pData->hSpectrumBitmaps[i])
1137                     DeleteObject(pData->hSpectrumBitmaps[i]);
1138             }
1139 
1140             HeapFree(GetProcessHeap(), 0, pData);
1141         }
1142     }
1143     return FALSE;
1144 }
1145