xref: /reactos/dll/shellext/deskmon/deskmon.c (revision 2b82fe44)
1 #include "precomp.h"
2 
3 #define NDEBUG
4 #include <debug.h>
5 
6 static HINSTANCE hInstance;
7 
8 #ifdef UNICODE
9 typedef INT_PTR (WINAPI *PDEVICEPROPERTIES)(HWND,LPCWSTR,LPCWSTR,BOOL);
10 #define FUNC_DEVICEPROPERTIES "DevicePropertiesW"
11 #else
12 typedef INT_PTR (WINAPI *PDEVICEPROPERTIES)(HWND,LPCSTR,LPCSTR,BOOL);
13 #define FUNC_DEVICEPROPERTIES "DevicePropertiesA"
14 #endif
15 
16 static LPTSTR
17 GetMonitorDevInstID(LPCTSTR lpDeviceID)
18 {
19     /* FIXME: Implement, allocate returned string with LocalAlloc! */
20     return NULL;
21 }
22 
23 static VOID
24 ShowMonitorProperties(PDESKMONITOR This)
25 {
26     HMODULE hDevMgr;
27     PDEVICEPROPERTIES pDeviceProperties;
28     LPTSTR lpDevInstID;
29 
30     if (This->SelMonitor != NULL)
31     {
32         lpDevInstID = GetMonitorDevInstID(This->SelMonitor->dd.DeviceID);
33         if (lpDevInstID != NULL)
34         {
35             hDevMgr = LoadLibrary(TEXT("devmgr.dll"));
36             if (hDevMgr != NULL)
37             {
38                 pDeviceProperties = (PDEVICEPROPERTIES)GetProcAddress(hDevMgr,
39                                                                       FUNC_DEVICEPROPERTIES);
40                 if (pDeviceProperties != NULL)
41                 {
42                     pDeviceProperties(This->hwndDlg,
43                                        NULL,
44                                        This->SelMonitor->dd.DeviceID,
45                                        FALSE);
46                 }
47 
48                 FreeLibrary(hDevMgr);
49             }
50 
51             LocalFree((HLOCAL)lpDevInstID);
52         }
53     }
54 }
55 
56 static VOID
57 UpdateMonitorSelection(PDESKMONITOR This)
58 {
59     INT i;
60 
61     if (This->dwMonitorCount > 1)
62     {
63         This->SelMonitor = NULL;
64 
65         i = (INT)SendDlgItemMessage(This->hwndDlg,
66                                     IDC_MONITORLIST,
67                                     LB_GETCURSEL,
68                                     0,
69                                     0);
70         if (i >= 0)
71         {
72             This->SelMonitor = (PDESKMONINFO)SendDlgItemMessage(This->hwndDlg,
73                                                                 IDC_MONITORLIST,
74                                                                 LB_GETITEMDATA,
75                                                                 (WPARAM)i,
76                                                                 0);
77         }
78     }
79     else
80         This->SelMonitor = This->Monitors;
81 
82     EnableWindow(GetDlgItem(This->hwndDlg,
83                             IDC_MONITORPROPERTIES),
84                  This->SelMonitor != NULL);
85 }
86 
87 static VOID
88 UpdatePruningControls(PDESKMONITOR This)
89 {
90     EnableWindow(GetDlgItem(This->hwndDlg,
91                             IDC_PRUNINGCHECK),
92                  This->bModesPruned && !This->bKeyIsReadOnly);
93     CheckDlgButton(This->hwndDlg,
94                    IDC_PRUNINGCHECK,
95                    (This->bModesPruned && This->bPruningOn) ? BST_CHECKED : BST_UNCHECKED);
96 }
97 
98 static VOID
99 GetPruningSettings(PDESKMONITOR This)
100 {
101     BOOL bModesPruned = FALSE, bKeyIsReadOnly = FALSE, bPruningOn = FALSE;
102 
103     if (This->DeskExtInterface != NULL)
104     {
105         This->DeskExtInterface->GetPruningMode(This->DeskExtInterface->Context,
106                                                &bModesPruned,
107                                                &bKeyIsReadOnly,
108                                                &bPruningOn);
109     }
110 
111     /* Check the boolean values against zero before assigning it to the bitfields! */
112     This->bModesPruned = (bModesPruned != FALSE);
113     This->bKeyIsReadOnly = (bKeyIsReadOnly != FALSE);
114     This->bPruningOn = (bPruningOn != FALSE);
115 
116     UpdatePruningControls(This);
117 }
118 
119 static VOID
120 UpdateRefreshFrequencyList(PDESKMONITOR This)
121 {
122     PDEVMODEW lpCurrentMode, lpMode;
123     TCHAR szBuffer[64];
124     DWORD dwIndex;
125     INT i;
126     BOOL bHasDef = FALSE;
127     BOOL bAdded = FALSE;
128 
129     /* Fill the refresh rate combo box */
130     SendDlgItemMessage(This->hwndDlg,
131                        IDC_REFRESHRATE,
132                        CB_RESETCONTENT,
133                        0,
134                        0);
135 
136     lpCurrentMode = This->DeskExtInterface->GetCurrentMode(This->DeskExtInterface->Context);
137     dwIndex = 0;
138 
139     do
140     {
141         lpMode = This->DeskExtInterface->EnumAllModes(This->DeskExtInterface->Context,
142                                                       dwIndex++);
143         if (lpMode != NULL &&
144             lpMode->dmBitsPerPel == lpCurrentMode->dmBitsPerPel &&
145             lpMode->dmPelsWidth == lpCurrentMode->dmPelsWidth &&
146             lpMode->dmPelsHeight == lpCurrentMode->dmPelsHeight)
147         {
148             /* We're only interested in refresh rates for the current resolution and color depth */
149 
150             if (lpMode->dmDisplayFrequency <= 1)
151             {
152                 /* Default hardware frequency */
153                 if (bHasDef)
154                     continue;
155 
156                 bHasDef = TRUE;
157 
158                 if (!LoadString(hInstance,
159                                 IDS_USEDEFFRQUENCY,
160                                 szBuffer,
161                                 sizeof(szBuffer) / sizeof(szBuffer[0])))
162                 {
163                     szBuffer[0] = TEXT('\0');
164                 }
165             }
166             else
167             {
168                 TCHAR szFmt[64];
169 
170                 if (!LoadString(hInstance,
171                                 IDS_FREQFMT,
172                                 szFmt,
173                                 sizeof(szFmt) / sizeof(szFmt[0])))
174                 {
175                     szFmt[0] = TEXT('\0');
176                 }
177 
178                 _sntprintf(szBuffer,
179                            sizeof(szBuffer) / sizeof(szBuffer[0]),
180                            szFmt,
181                            lpMode->dmDisplayFrequency);
182             }
183 
184             i = (INT)SendDlgItemMessage(This->hwndDlg,
185                                         IDC_REFRESHRATE,
186                                         CB_ADDSTRING,
187                                         0,
188                                         (LPARAM)szBuffer);
189             if (i >= 0)
190             {
191                 bAdded = TRUE;
192 
193                 SendDlgItemMessage(This->hwndDlg,
194                                    IDC_REFRESHRATE,
195                                    CB_SETITEMDATA,
196                                    (WPARAM)i,
197                                    (LPARAM)lpMode);
198 
199                 if (lpMode->dmDisplayFrequency == lpCurrentMode->dmDisplayFrequency)
200                 {
201                     SendDlgItemMessage(This->hwndDlg,
202                                        IDC_REFRESHRATE,
203                                        CB_SETCURSEL,
204                                        (WPARAM)i,
205                                        0);
206                 }
207             }
208         }
209 
210     } while (lpMode != NULL);
211 
212     EnableWindow(GetDlgItem(This->hwndDlg,
213                             IDS_MONITORSETTINGSGROUP),
214                  bAdded);
215     EnableWindow(GetDlgItem(This->hwndDlg,
216                             IDS_REFRESHRATELABEL),
217                  bAdded);
218     EnableWindow(GetDlgItem(This->hwndDlg,
219                             IDC_REFRESHRATE),
220                  bAdded);
221 
222     GetPruningSettings(This);
223 }
224 
225 static VOID
226 InitMonitorDialog(PDESKMONITOR This)
227 {
228     PDESKMONINFO pmi, pminext, *pmilink;
229     DISPLAY_DEVICE dd;
230     BOOL bRet;
231     INT i;
232     DWORD dwIndex;
233 
234     /* Free all allocated monitors */
235     pmi = This->Monitors;
236     This->Monitors = NULL;
237     while (pmi != NULL)
238     {
239         pminext = pmi->Next;
240         LocalFree((HLOCAL)pmi);
241         pmi = pminext;
242     }
243 
244     This->SelMonitor = NULL;
245     This->dwMonitorCount = 0;
246 
247     if (This->lpDisplayDevice != NULL)
248         LocalFree((HLOCAL)This->lpDisplayDevice);
249 
250     This->lpDisplayDevice = QueryDeskCplString(This->pdtobj,
251                                                RegisterClipboardFormat(DESK_EXT_DISPLAYDEVICE));
252 
253     if (This->DeskExtInterface != NULL)
254     {
255         if (This->lpDisplayDevice != NULL)
256         {
257             /* Enumerate all monitors */
258             dwIndex = 0;
259             pmilink = &This->Monitors;
260 
261             do
262             {
263                 dd.cb = sizeof(dd);
264                 bRet = EnumDisplayDevices(This->lpDisplayDevice,
265                                           dwIndex++,
266                                           &dd,
267                                           0);
268                 if (bRet)
269                 {
270                     pmi = LocalAlloc(LMEM_FIXED,
271                                      sizeof(*pmi));
272                     if (pmi != NULL)
273                     {
274                         CopyMemory(&pmi->dd,
275                                    &dd,
276                                    sizeof(dd));
277                         pmi->Next = NULL;
278                         *pmilink = pmi;
279                         pmilink = &pmi->Next;
280 
281                         This->dwMonitorCount++;
282                     }
283                 }
284             } while (bRet);
285         }
286 
287         This->lpDevModeOnInit = This->DeskExtInterface->GetCurrentMode(This->DeskExtInterface->Context);
288     }
289     else
290         This->lpDevModeOnInit = NULL;
291 
292     This->lpSelDevMode = This->lpDevModeOnInit;
293 
294     /* Setup the UI depending on how many monitors are attached */
295     if (This->dwMonitorCount == 0)
296     {
297         LPTSTR lpMonitorName;
298 
299         /* This is a fallback, let's hope that desk.cpl can provide us with a monitor name */
300         lpMonitorName = QueryDeskCplString(This->pdtobj,
301                                            RegisterClipboardFormat(DESK_EXT_MONITORNAME));
302 
303         SetDlgItemText(This->hwndDlg,
304                        IDC_MONITORNAME,
305                        lpMonitorName);
306 
307         if (lpMonitorName != NULL)
308             LocalFree((HLOCAL)lpMonitorName);
309     }
310     else if (This->dwMonitorCount == 1)
311     {
312         This->SelMonitor = This->Monitors;
313         SetDlgItemText(This->hwndDlg,
314                        IDC_MONITORNAME,
315                        This->Monitors->dd.DeviceString);
316     }
317     else
318     {
319         SendDlgItemMessage(This->hwndDlg,
320                            IDC_MONITORLIST,
321                            LB_RESETCONTENT,
322                            0,
323                            0);
324 
325         pmi = This->Monitors;
326         while (pmi != NULL)
327         {
328             i = (INT)SendDlgItemMessage(This->hwndDlg,
329                                         IDC_MONITORLIST,
330                                         LB_ADDSTRING,
331                                         0,
332                                         (LPARAM)pmi->dd.DeviceString);
333             if (i >= 0)
334             {
335                 SendDlgItemMessage(This->hwndDlg,
336                                    IDC_MONITORLIST,
337                                    LB_SETITEMDATA,
338                                    (WPARAM)i,
339                                    (LPARAM)pmi);
340 
341                 if (This->SelMonitor == NULL)
342                 {
343                     SendDlgItemMessage(This->hwndDlg,
344                                        IDC_MONITORLIST,
345                                        LB_SETCURSEL,
346                                        (WPARAM)i,
347                                        0);
348 
349                     This->SelMonitor = pmi;
350                 }
351             }
352 
353             pmi = pmi->Next;
354         }
355     }
356 
357     /* Show/Hide controls */
358     ShowWindow(GetDlgItem(This->hwndDlg,
359                           IDC_MONITORNAME),
360                (This->dwMonitorCount <= 1 ? SW_SHOW : SW_HIDE));
361     ShowWindow(GetDlgItem(This->hwndDlg,
362                           IDC_MONITORLIST),
363                (This->dwMonitorCount > 1 ? SW_SHOW : SW_HIDE));
364 
365     UpdateRefreshFrequencyList(This);
366     UpdateMonitorSelection(This);
367 }
368 
369 static VOID
370 UpdatePruningSelection(PDESKMONITOR This)
371 {
372     BOOL bPruningOn;
373 
374     if (This->DeskExtInterface != NULL && This->bModesPruned && !This->bKeyIsReadOnly)
375     {
376         bPruningOn = IsDlgButtonChecked(This->hwndDlg,
377                                         IDC_PRUNINGCHECK) != BST_UNCHECKED;
378 
379         if (bPruningOn != This->bPruningOn)
380         {
381             /* Tell desk.cpl to turn on/off pruning mode */
382             This->bPruningOn = bPruningOn;
383             This->DeskExtInterface->SetPruningMode(This->DeskExtInterface->Context,
384                                                    bPruningOn);
385 
386             /* Fill the refresh rate combobox again, we now receive a filtered
387                or unfiltered device mode list from desk.cpl (depending on whether
388                pruning is active or not) */
389             UpdateRefreshFrequencyList(This);
390 
391             (void)PropSheet_Changed(GetParent(This->hwndDlg),
392                                     This->hwndDlg);
393         }
394     }
395 }
396 
397 static VOID
398 UpdateRefreshRateSelection(PDESKMONITOR This)
399 {
400     PDEVMODEW lpCurrentDevMode;
401     INT i;
402 
403     if (This->DeskExtInterface != NULL)
404     {
405         i = (INT)SendDlgItemMessage(This->hwndDlg,
406                                     IDC_REFRESHRATE,
407                                     CB_GETCURSEL,
408                                     0,
409                                     0);
410         if (i >= 0)
411         {
412             lpCurrentDevMode = This->lpSelDevMode;
413             This->lpSelDevMode = (PDEVMODEW)SendDlgItemMessage(This->hwndDlg,
414                                                                IDC_REFRESHRATE,
415                                                                CB_GETITEMDATA,
416                                                                (WPARAM)i,
417                                                                0);
418 
419             if (This->lpSelDevMode != NULL && This->lpSelDevMode != lpCurrentDevMode)
420             {
421                 This->DeskExtInterface->SetCurrentMode(This->DeskExtInterface->Context,
422                                                        This->lpSelDevMode);
423 
424                 UpdateRefreshFrequencyList(This);
425 
426                 (void)PropSheet_Changed(GetParent(This->hwndDlg),
427                                         This->hwndDlg);
428             }
429         }
430     }
431 }
432 
433 static LONG
434 ApplyMonitorChanges(PDESKMONITOR This)
435 {
436     LONG lChangeRet;
437 
438     if (This->DeskExtInterface != NULL)
439     {
440         /* Change the display settings through desk.cpl */
441         lChangeRet = DeskCplExtDisplaySaveSettings(This->DeskExtInterface,
442                                                    This->hwndDlg);
443         if (lChangeRet == DISP_CHANGE_SUCCESSFUL)
444         {
445             /* Save the new mode */
446             This->lpDevModeOnInit = This->DeskExtInterface->GetCurrentMode(This->DeskExtInterface->Context);
447             This->lpSelDevMode = This->lpDevModeOnInit;
448             return PSNRET_NOERROR;
449         }
450         else if (lChangeRet == DISP_CHANGE_RESTART)
451         {
452             /* Notify desk.cpl that the user needs to reboot */
453             PropSheet_RestartWindows(GetParent(This->hwndDlg));
454             return PSNRET_NOERROR;
455         }
456     }
457 
458     InitMonitorDialog(This);
459 
460     return PSNRET_INVALID_NOCHANGEPAGE;
461 }
462 
463 static VOID
464 ResetMonitorChanges(PDESKMONITOR This)
465 {
466     if (This->DeskExtInterface != NULL && This->lpDevModeOnInit != NULL)
467     {
468         This->DeskExtInterface->SetCurrentMode(This->DeskExtInterface->Context,
469                                                This->lpDevModeOnInit);
470     }
471 }
472 
473 static INT_PTR CALLBACK
474 MonitorDlgProc(HWND hwndDlg,
475                UINT uMsg,
476                WPARAM wParam,
477                LPARAM lParam)
478 {
479     PDESKMONITOR This;
480     INT_PTR Ret = 0;
481 
482     if (uMsg != WM_INITDIALOG)
483     {
484         This = (PDESKMONITOR)GetWindowLongPtr(hwndDlg,
485                                               DWL_USER);
486     }
487 
488     switch (uMsg)
489     {
490         case WM_INITDIALOG:
491             This = (PDESKMONITOR)((LPCPROPSHEETPAGE)lParam)->lParam;
492             This->hwndDlg = hwndDlg;
493             SetWindowLongPtr(hwndDlg,
494                              DWL_USER,
495                              (LONG_PTR)This);
496 
497             InitMonitorDialog(This);
498             Ret = TRUE;
499             break;
500 
501         case WM_COMMAND:
502             switch (LOWORD(wParam))
503             {
504                 case IDC_MONITORPROPERTIES:
505                     ShowMonitorProperties(This);
506                     break;
507 
508                 case IDC_MONITORLIST:
509                     if (HIWORD(wParam) == LBN_SELCHANGE)
510                         UpdateMonitorSelection(This);
511                     break;
512 
513                 case IDC_PRUNINGCHECK:
514                     if (HIWORD(wParam) == BN_CLICKED)
515                         UpdatePruningSelection(This);
516                     break;
517 
518                 case IDC_REFRESHRATE:
519                     if (HIWORD(wParam) == CBN_SELCHANGE)
520                         UpdateRefreshRateSelection(This);
521                     break;
522             }
523             break;
524 
525         case WM_NOTIFY:
526         {
527             NMHDR *nmh = (NMHDR *)lParam;
528 
529             switch (nmh->code)
530             {
531                 case PSN_APPLY:
532                 {
533                     SetWindowLongPtr(hwndDlg,
534                                      DWL_MSGRESULT,
535                                      ApplyMonitorChanges(This));
536                     break;
537                 }
538 
539                 case PSN_RESET:
540                     ResetMonitorChanges(This);
541                     break;
542 
543                 case PSN_SETACTIVE:
544                     UpdateRefreshFrequencyList(This);
545                     break;
546             }
547             break;
548         }
549     }
550 
551     return Ret;
552 }
553 
554 static VOID
555 IDeskMonitor_Destroy(PDESKMONITOR This)
556 {
557     PDESKMONINFO pmi, pminext;
558 
559     if (This->pdtobj != NULL)
560     {
561         IDataObject_Release(This->pdtobj);
562         This->pdtobj = NULL;
563     }
564 
565     if (This->DeskExtInterface != NULL)
566     {
567         LocalFree((HLOCAL)This->DeskExtInterface);
568         This->DeskExtInterface = NULL;
569     }
570 
571     if (This->lpDisplayDevice != NULL)
572     {
573         LocalFree((HLOCAL)This->lpDisplayDevice);
574         This->lpDisplayDevice = NULL;
575     }
576 
577     /* Free all monitors */
578     pmi = This->Monitors;
579     This->Monitors = NULL;
580     while (pmi != NULL)
581     {
582         pminext = pmi->Next;
583         LocalFree((HLOCAL)pmi);
584         pmi = pminext;
585     }
586 }
587 
588 ULONG
589 IDeskMonitor_AddRef(PDESKMONITOR This)
590 {
591     ULONG ret;
592 
593     ret = InterlockedIncrement((PLONG)&This->ref);
594     if (ret == 1)
595         InterlockedIncrement(&dll_refs);
596 
597     return ret;
598 }
599 
600 ULONG
601 IDeskMonitor_Release(PDESKMONITOR This)
602 {
603     ULONG ret;
604 
605     ret = InterlockedDecrement((PLONG)&This->ref);
606     if (ret == 0)
607     {
608         IDeskMonitor_Destroy(This);
609         InterlockedDecrement(&dll_refs);
610 
611         HeapFree(GetProcessHeap(),
612                  0,
613                  This);
614     }
615 
616     return ret;
617 }
618 
619 HRESULT STDMETHODCALLTYPE
620 IDeskMonitor_QueryInterface(PDESKMONITOR This,
621                             REFIID iid,
622                             PVOID *pvObject)
623 {
624     *pvObject = NULL;
625 
626     if (IsEqualIID(iid,
627                    &IID_IShellPropSheetExt) ||
628         IsEqualIID(iid,
629                    &IID_IUnknown))
630     {
631         *pvObject = impl_to_interface(This, IShellPropSheetExt);
632     }
633     else if (IsEqualIID(iid,
634                         &IID_IShellExtInit))
635     {
636         *pvObject = impl_to_interface(This, IShellExtInit);
637     }
638     else if (IsEqualIID(iid,
639                         &IID_IClassFactory))
640     {
641         *pvObject = impl_to_interface(This, IClassFactory);
642     }
643     else
644     {
645         DPRINT1("IDeskMonitor::QueryInterface(%p,%p): E_NOINTERFACE\n", iid, pvObject);
646         return E_NOINTERFACE;
647     }
648 
649     IDeskMonitor_AddRef(This);
650     return S_OK;
651 }
652 
653 HRESULT
654 IDeskMonitor_Initialize(PDESKMONITOR This,
655                         LPCITEMIDLIST pidlFolder,
656                         IDataObject *pdtobj,
657                         HKEY hkeyProgID)
658 {
659     DPRINT1("IDeskMonitor::Initialize(%p,%p,%p)\n", pidlFolder, pdtobj, hkeyProgID);
660 
661     if (pdtobj != NULL)
662     {
663         IDataObject_AddRef(pdtobj);
664         This->pdtobj = pdtobj;
665 
666         /* Get a copy of the desk.cpl extension interface */
667         This->DeskExtInterface = QueryDeskCplExtInterface(This->pdtobj);
668         if (This->DeskExtInterface != NULL)
669             return S_OK;
670     }
671 
672     return S_FALSE;
673 }
674 
675 HRESULT
676 IDeskMonitor_AddPages(PDESKMONITOR This,
677                       LPFNADDPROPSHEETPAGE pfnAddPage,
678                       LPARAM lParam)
679 {
680     HPROPSHEETPAGE hpsp;
681     PROPSHEETPAGE psp;
682 
683     DPRINT1("IDeskMonitor::AddPages(%p,%p)\n", pfnAddPage, lParam);
684 
685     psp.dwSize = sizeof(psp);
686     psp.dwFlags = PSP_DEFAULT;
687     psp.hInstance = hInstance;
688     psp.pszTemplate = MAKEINTRESOURCE(IDD_MONITOR);
689     psp.pfnDlgProc = MonitorDlgProc;
690     psp.lParam = (LPARAM)This;
691 
692     hpsp = CreatePropertySheetPage(&psp);
693     if (hpsp != NULL && pfnAddPage(hpsp, lParam))
694         return S_OK;
695 
696     return S_FALSE;
697 }
698 
699 HRESULT
700 IDeskMonitor_ReplacePage(PDESKMONITOR This,
701                          EXPPS uPageID,
702                          LPFNADDPROPSHEETPAGE pfnReplacePage,
703                          LPARAM lParam)
704 {
705     DPRINT1("IDeskMonitor::ReplacePage(%u,%p,%p)\n", uPageID, pfnReplacePage, lParam);
706     return E_NOTIMPL;
707 }
708 
709 HRESULT
710 IDeskMonitor_Constructor(REFIID riid,
711                          LPVOID *ppv)
712 {
713     PDESKMONITOR This;
714     HRESULT hRet = E_OUTOFMEMORY;
715 
716     DPRINT1("IDeskMonitor::Constructor(%p,%p)\n", riid, ppv);
717 
718     This = HeapAlloc(GetProcessHeap(),
719                      0,
720                      sizeof(*This));
721     if (This != NULL)
722     {
723         ZeroMemory(This,
724                    sizeof(*This));
725 
726         IDeskMonitor_InitIface(This);
727 
728         hRet = IDeskMonitor_QueryInterface(This,
729                                            riid,
730                                            ppv);
731         if (!SUCCEEDED(hRet))
732             IDeskMonitor_Release(This);
733     }
734 
735     return hRet;
736 }
737 
738 BOOL WINAPI
739 DllMain(HINSTANCE hinstDLL,
740         DWORD dwReason,
741         LPVOID lpvReserved)
742 {
743     switch (dwReason)
744     {
745         case DLL_PROCESS_ATTACH:
746             hInstance = hinstDLL;
747             DisableThreadLibraryCalls(hInstance);
748             break;
749     }
750 
751     return TRUE;
752 }
753