[DEVMGR] Fix wonky action menu
[reactos.git] / dll / win32 / devmgr / devmgmt / MainWindow.cpp
1 /*
2 * PROJECT: ReactOS Device Manager
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: dll/win32/devmgr/devmgmt/MainWindow.cpp
5 * PURPOSE: Implements the main container window for the device view
6 * COPYRIGHT: Copyright 2014 - 2015 Ged Murphy <gedmurphy@reactos.org>
7 */
8
9
10 #include "precomp.h"
11 #include "devmgmt.h"
12 #include "MainWindow.h"
13
14
15 /* DATA *****************************************************/
16
17 #define BTN_PROPERTIES 0
18 #define BTN_SCAN_HARDWARE 1
19 #define BTN_ENABLE_DRV 2
20 #define BTN_DISABLE_DRV 3
21 #define BTN_UPDATE_DRV 4
22 #define BTN_UNINSTALL_DRV 5
23
24 #define REFRESH_TIMER 1
25
26 HINSTANCE g_hThisInstance = NULL;
27 HINSTANCE g_hParentInstance = NULL;
28
29 // menu hints
30 static const MENU_HINT MainMenuHintTable[] =
31 {
32 // File Menu
33 { IDC_EXIT, IDS_HINT_EXIT },
34
35 // Action Menu
36 { IDC_PROPERTIES, IDS_HINT_PROPERTIES },
37 { IDC_SCAN_HARDWARE, IDS_HINT_SCAN },
38 { IDC_ENABLE_DRV, IDS_HINT_ENABLE },
39 { IDC_DISABLE_DRV, IDS_HINT_DISABLE },
40 { IDC_UPDATE_DRV, IDS_HINT_UPDATE },
41 { IDC_UNINSTALL_DRV, IDS_HINT_UNINSTALL },
42 { IDC_ADD_HARDWARE, IDS_HINT_ADD },
43
44
45 // View Menu
46 { IDC_DEVBYTYPE, IDS_HINT_DEV_BY_TYPE},
47 { IDC_DEVBYCONN, IDS_HINT_DEV_BY_CONN},
48 { IDC_RESBYTYPE, IDS_HINT_RES_BY_TYPE},
49 { IDC_RESBYCONN, IDS_HINT_RES_BY_TYPE},
50 { IDC_SHOWHIDDEN, IDS_HINT_SHOW_HIDDEN },
51
52 { IDC_ABOUT, IDS_HINT_ABOUT }
53
54 };
55
56
57 // system menu hints
58 static const MENU_HINT SystemMenuHintTable[] =
59 {
60 {SC_RESTORE, IDS_HINT_SYS_RESTORE},
61 {SC_MOVE, IDS_HINT_SYS_MOVE},
62 {SC_SIZE, IDS_HINT_SYS_SIZE},
63 {SC_MINIMIZE, IDS_HINT_SYS_MINIMIZE},
64 {SC_MAXIMIZE, IDS_HINT_SYS_MAXIMIZE},
65 {SC_CLOSE, IDS_HINT_SYS_CLOSE}
66 };
67
68 static TBBUTTON TbButtons[] =
69 {
70 { BTN_PROPERTIES, IDC_PROPERTIES, TBSTATE_ENABLED, BTNS_BUTTON, {0}, 0, 0 },
71 { BTN_SCAN_HARDWARE, IDC_SCAN_HARDWARE, TBSTATE_ENABLED, BTNS_BUTTON, {0}, 0, 0 },
72 { 2, IDC_STATIC, TBSTATE_ENABLED, BTNS_SEP, {0}, 0, 0 },
73 { BTN_ENABLE_DRV, IDC_ENABLE_DRV, TBSTATE_ENABLED, BTNS_BUTTON, {0}, 0, 0 },
74 { BTN_DISABLE_DRV, IDC_DISABLE_DRV, TBSTATE_ENABLED, BTNS_BUTTON, {0}, 0, 0 },
75 { BTN_UPDATE_DRV, IDC_UPDATE_DRV, TBSTATE_ENABLED, BTNS_BUTTON, {0}, 0, 0 },
76 { BTN_UNINSTALL_DRV, IDC_UNINSTALL_DRV, TBSTATE_ENABLED, BTNS_BUTTON, {0}, 0, 0 }
77 };
78
79
80 /* PUBLIC METHODS **********************************************/
81
82 CDeviceManager::CDeviceManager(void) :
83 m_hMainWnd(NULL),
84 m_hStatusBar(NULL),
85 m_hToolBar(NULL),
86 m_CmdShow(0),
87 m_RefreshPending(false)
88 {
89 m_szMainWndClass = L"DevMgmtWndClass";
90 }
91
92 CDeviceManager::~CDeviceManager(void)
93 {
94 }
95
96 bool
97 CDeviceManager::Create(_In_ HWND /*hWndParent*/,
98 _In_ HINSTANCE hInst,
99 _In_opt_z_ LPCWSTR /*lpMachineName*/,
100 _In_ int nCmdShow)
101 {
102 CDeviceManager MainWindow;
103 INITCOMMONCONTROLSEX icex;
104 CAtlStringW szAppName;
105 int Ret = 1;
106
107 // Store the instances
108 g_hParentInstance = hInst;
109 g_hThisInstance = GetModuleHandleW(L"devmgr.dll");
110
111 // Initialize common controls
112 icex.dwSize = sizeof(INITCOMMONCONTROLSEX);
113 icex.dwICC = ICC_BAR_CLASSES | ICC_COOL_CLASSES;
114 InitCommonControlsEx(&icex);
115
116 // Load the application name
117 if (szAppName.LoadStringW(g_hThisInstance, IDS_APPNAME))
118 {
119 // Initialize the main window
120 if (MainWindow.Initialize(szAppName, nCmdShow))
121 {
122 // Run the application
123 Ret = MainWindow.Run();
124
125 // Uninitialize the main window
126 MainWindow.Uninitialize();
127 }
128 }
129
130 return (Ret == 0);
131 }
132
133
134 /* PRIVATE METHODS **********************************************/
135
136 bool
137 CDeviceManager::Initialize(_In_z_ LPCTSTR lpCaption,
138 _In_ int nCmdShow)
139 {
140 CAtlStringW szCaption;
141 WNDCLASSEXW wc = {0};
142
143 // Store the show window value
144 m_CmdShow = nCmdShow;
145
146 // Setup the window class struct
147 wc.cbSize = sizeof(WNDCLASSEXW);
148 wc.lpfnWndProc = MainWndProc;
149 wc.hInstance = g_hThisInstance;
150 wc.hIcon = LoadIcon(g_hThisInstance, MAKEINTRESOURCEW(IDI_MAIN_ICON));
151 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
152 wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
153 wc.lpszMenuName = MAKEINTRESOURCEW(IDR_MAINMENU);
154 wc.lpszClassName = m_szMainWndClass;
155 wc.hIconSm = (HICON)LoadImage(g_hThisInstance,
156 MAKEINTRESOURCE(IDI_MAIN_ICON),
157 IMAGE_ICON,
158 GetSystemMetrics(SM_CXSMICON),
159 GetSystemMetrics(SM_CYSMICON),
160 0);
161
162 // Register the window
163 if (RegisterClassExW(&wc))
164 {
165 // Create the main window and store the object pointer
166 m_hMainWnd = CreateWindowExW(WS_EX_WINDOWEDGE,
167 m_szMainWndClass,
168 lpCaption,
169 WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
170 CW_USEDEFAULT,
171 CW_USEDEFAULT,
172 550,
173 500,
174 NULL,
175 NULL,
176 g_hThisInstance,
177 this);
178 }
179
180 // Return creation result
181 return !!(m_hMainWnd);
182 }
183
184 void
185 CDeviceManager::Uninitialize(void)
186 {
187 // Unregister the window class
188 UnregisterClassW(m_szMainWndClass, g_hThisInstance);
189 }
190
191 int
192 CDeviceManager::Run(void)
193 {
194 MSG Msg;
195
196 // Pump the message queue
197 while (GetMessageW(&Msg, NULL, 0, 0 ) != 0)
198 {
199 TranslateMessage(&Msg);
200 DispatchMessageW(&Msg);
201 }
202
203 return 0;
204 }
205
206 bool
207 CDeviceManager::MainWndMenuHint(_In_ WORD CmdId,
208 _In_ const MENU_HINT *HintArray,
209 _In_ DWORD HintsCount,
210 _In_ UINT DefHintId)
211 {
212 bool Found = false;
213 const MENU_HINT *LastHint;
214 UINT HintId = DefHintId;
215
216 LastHint = HintArray + HintsCount;
217 while (HintArray != LastHint)
218 {
219 if (HintArray->CmdId == CmdId)
220 {
221 HintId = HintArray->HintId;
222 Found = true;
223 break;
224 }
225 HintArray++;
226 }
227
228 StatusBarLoadString(m_hStatusBar,
229 SB_SIMPLEID,
230 g_hThisInstance,
231 HintId);
232
233 return Found;
234 }
235
236 void
237 CDeviceManager::UpdateStatusBar(_In_ bool InMenuLoop)
238 {
239 SendMessageW(m_hStatusBar,
240 SB_SIMPLE,
241 (WPARAM)InMenuLoop,
242 0);
243 }
244
245 bool
246 CDeviceManager::RefreshView(_In_ ViewType Type,
247 _In_ bool ScanForChanges)
248 {
249 UINT CheckId = 0;
250
251 // Refreshed the cached view
252 m_DeviceView->Refresh(Type, ScanForChanges, true);
253
254 // Get the menu item id
255 switch (Type)
256 {
257 case DevicesByType:
258 CheckId = IDC_DEVBYTYPE;
259 break;
260
261 case DevicesByConnection:
262 CheckId = IDC_DEVBYCONN;
263 break;
264
265 case ResourcesByType:
266 CheckId = IDC_RESBYTYPE;
267 break;
268
269 case ResourcesByConnection:
270 CheckId = IDC_RESBYCONN;
271 break;
272
273 default:
274 ATLASSERT(FALSE);
275 break;
276 }
277
278 // Set the new check item
279 CheckMenuRadioItem(m_hMenu,
280 IDC_DEVBYTYPE,
281 IDC_RESBYCONN,
282 CheckId,
283 MF_BYCOMMAND);
284
285 return true;
286 }
287
288 bool
289 CDeviceManager::CreateToolBar(void)
290 {
291 TBADDBITMAP TbAddBitmap;
292
293 DWORD dwStyles = WS_CHILDWINDOW | TBSTYLE_FLAT | TBSTYLE_WRAPABLE | TBSTYLE_TOOLTIPS | CCS_NODIVIDER;
294 DWORD dwExStyles = WS_EX_LEFT | WS_EX_LTRREADING | WS_EX_RIGHTSCROLLBAR;
295
296 // Create the toolbar window
297 m_hToolBar = CreateWindowExW(dwExStyles,
298 TOOLBARCLASSNAME,
299 NULL,
300 dwStyles,
301 0, 0, 0, 0,
302 m_hMainWnd,
303 (HMENU)IDC_TOOLBAR,
304 g_hThisInstance,
305 NULL);
306 if (m_hToolBar == NULL)
307 return FALSE;
308
309 // Don't show clipped buttons
310 SendMessageW(m_hToolBar,
311 TB_SETEXTENDEDSTYLE,
312 0,
313 TBSTYLE_EX_HIDECLIPPEDBUTTONS);
314
315 SendMessageW(m_hToolBar, TB_SETBITMAPSIZE, 0, MAKELONG(16, 16));
316
317 // Set the struct size, the toobar needs this...
318 SendMessageW(m_hToolBar,
319 TB_BUTTONSTRUCTSIZE,
320 sizeof(TBBUTTON),
321 0);
322
323 TbAddBitmap.hInst = g_hThisInstance;
324 TbAddBitmap.nID = IDB_TOOLBAR;
325 SendMessageW(m_hToolBar, TB_ADDBITMAP, _countof(TbButtons), (LPARAM)&TbAddBitmap);
326
327 SendMessageW(m_hToolBar, TB_ADDBUTTONSW, _countof(TbButtons), (LPARAM)TbButtons);
328 SendMessageW(m_hToolBar, TB_AUTOSIZE, 0, 0);
329
330 if (TRUE)
331 {
332 ShowWindow(m_hToolBar, SW_SHOW);
333 }
334
335 return TRUE;
336 }
337
338 bool
339 CDeviceManager::CreateStatusBar(void)
340 {
341 int StatWidths[] = {110, -1}; // widths of status bar
342 bool bRet = FALSE;
343
344 // Create the status bar
345 m_hStatusBar = CreateWindowExW(0,
346 STATUSCLASSNAME,
347 NULL,
348 WS_CHILD | WS_VISIBLE | SBARS_SIZEGRIP,
349 0, 0, 0, 0,
350 m_hMainWnd,
351 (HMENU)IDC_STATUSBAR,
352 g_hThisInstance,
353 NULL);
354 if (m_hStatusBar)
355 {
356 // Set the width
357 bRet = (SendMessageW(m_hStatusBar,
358 SB_SETPARTS,
359 sizeof(StatWidths) / sizeof(int),
360 (LPARAM)StatWidths) != 0);
361 }
362
363 return bRet;
364 }
365
366 void CDeviceManager::UpdateToolbar()
367 {
368 WORD State;
369
370 CNode *Node = m_DeviceView->GetSelectedNode();
371
372 // properties button
373 if (Node->HasProperties())
374 {
375 State = TBSTATE_ENABLED;
376 }
377 else
378 {
379 State = TBSTATE_HIDDEN;
380 }
381 SendMessageW(m_hToolBar, TB_SETSTATE, IDC_PROPERTIES, MAKELPARAM(State, 0));
382 SendMessageW(m_hToolBar, TB_SETSTATE, IDC_UPDATE_DRV, MAKELPARAM(State, 0)); //hack
383 SendMessageW(m_hToolBar, TB_SETSTATE, IDC_UNINSTALL_DRV, MAKELPARAM(State, 0)); // hack
384
385 // enable driver button
386 if (Node->GetNodeType() == DeviceNode &&
387 dynamic_cast<CDeviceNode *>(Node)->IsDisabled())
388 {
389 State = TBSTATE_ENABLED;
390 }
391 else
392 {
393 State = TBSTATE_HIDDEN;
394 }
395 SendMessageW(m_hToolBar, TB_SETSTATE, IDC_ENABLE_DRV, MAKELPARAM(State, 0));
396
397 // disable driver button
398 if (Node->GetNodeType() == DeviceNode &&
399 dynamic_cast<CDeviceNode *>(Node)->CanDisable() &&
400 !dynamic_cast<CDeviceNode *>(Node)->IsDisabled())
401 {
402 State = TBSTATE_ENABLED;
403 }
404 else
405 {
406 State = TBSTATE_HIDDEN;
407 }
408 SendMessageW(m_hToolBar, TB_SETSTATE, IDC_DISABLE_DRV, MAKELPARAM(State, 0));
409 }
410
411 bool
412 CDeviceManager::StatusBarLoadString(_In_ HWND hStatusBar,
413 _In_ INT PartId,
414 _In_ HINSTANCE hInstance,
415 _In_ UINT uID)
416 {
417 CAtlStringW szMessage;
418 bool bRet = false;
419
420 // Load the string
421 if (szMessage.LoadStringW(hInstance, uID))
422 {
423 // Show the string on the status bar
424 bRet = (SendMessageW(hStatusBar,
425 SB_SETTEXT,
426 (WPARAM)PartId,
427 (LPARAM)szMessage.GetBuffer()) != 0);
428 }
429
430 return bRet;
431 }
432
433 LRESULT
434 CDeviceManager::OnCreate(_In_ HWND hwnd)
435 {
436 LRESULT RetCode;
437
438 RetCode = -1;
439 m_hMainWnd = hwnd;
440
441 // Store a handle to the main menu
442 m_hMenu = GetMenu(m_hMainWnd);
443
444 // Create the toolbar and statusbar
445 if (CreateToolBar() && CreateStatusBar())
446 {
447 // Create the device view object
448 m_DeviceView = new CDeviceView(m_hMainWnd);
449 if (m_DeviceView->Initialize())
450 {
451 // Do the initial scan
452 RefreshView(m_DeviceView->GetCurrentView(), true);
453
454 // Display the window according to the user request
455 ShowWindow(hwnd, m_CmdShow);
456 RetCode = 0;
457 }
458 }
459
460 return RetCode;
461 }
462
463 LRESULT
464 CDeviceManager::OnSize(void)
465 {
466 RECT rcClient, rcTool, rcStatus;
467 INT lvHeight, iToolHeight, iStatusHeight;
468
469 // Autosize the toolbar
470 SendMessage(m_hToolBar, TB_AUTOSIZE, 0, 0);
471
472 // Get the toolbar rect and save the height
473 GetWindowRect(m_hToolBar, &rcTool);
474 iToolHeight = rcTool.bottom - rcTool.top;
475
476 // Resize the status bar
477 SendMessage(m_hStatusBar, WM_SIZE, 0, 0);
478
479 // Get the statusbar rect and save the height
480 GetWindowRect(m_hStatusBar, &rcStatus);
481 iStatusHeight = rcStatus.bottom - rcStatus.top;
482
483 // Get the full client rect
484 GetClientRect(m_hMainWnd, &rcClient);
485
486 // Calculate the remaining height for the treeview
487 lvHeight = rcClient.bottom - iToolHeight - iStatusHeight;
488
489 // Resize the device view
490 m_DeviceView->OnSize(0,
491 iToolHeight,
492 rcClient.right,
493 lvHeight);
494
495 return 0;
496 }
497
498 LRESULT
499 CDeviceManager::OnNotify(_In_ LPARAM lParam)
500 {
501 LPNMHDR NmHdr = (LPNMHDR)lParam;
502 LRESULT Ret = 0;
503
504 switch (NmHdr->code)
505 {
506 case TVN_SELCHANGED:
507 {
508 HMENU hMenu = GetSubMenu(m_hMenu, 1);
509 for (INT i = GetMenuItemCount(hMenu) - 1; i >= 0; i--)
510 {
511 DeleteMenu(hMenu, i, MF_BYPOSITION);
512 }
513 m_DeviceView->CreateActionMenu(hMenu, true);
514 UpdateToolbar();
515 break;
516 }
517
518 case NM_DBLCLK:
519 {
520 m_DeviceView->DisplayPropertySheet();
521 break;
522 }
523
524 case NM_RCLICK:
525 {
526 Ret = m_DeviceView->OnRightClick(NmHdr);
527 break;
528 }
529
530 case NM_RETURN:
531 {
532 m_DeviceView->DisplayPropertySheet();
533 break;
534 }
535
536 case TTN_GETDISPINFO:
537 {
538 LPTOOLTIPTEXT lpttt = (LPTOOLTIPTEXT)lParam;
539 lpttt->hinst = g_hThisInstance;
540
541 UINT_PTR idButton = lpttt->hdr.idFrom;
542 switch (idButton)
543 {
544 case IDC_PROPERTIES:
545 lpttt->lpszText = MAKEINTRESOURCEW(IDS_TOOLTIP_PROPERTIES);
546 break;
547 case IDC_SCAN_HARDWARE:
548 lpttt->lpszText = MAKEINTRESOURCEW(IDS_TOOLTIP_SCAN);
549 break;
550 case IDC_ENABLE_DRV:
551 lpttt->lpszText = MAKEINTRESOURCEW(IDS_TOOLTIP_ENABLE);
552 break;
553 case IDC_DISABLE_DRV:
554 lpttt->lpszText = MAKEINTRESOURCEW(IDS_TOOLTIP_DISABLE);
555 break;
556 case IDC_UPDATE_DRV:
557 lpttt->lpszText = MAKEINTRESOURCEW(IDS_TOOLTIP_UPDATE);
558 break;
559 case IDC_UNINSTALL_DRV:
560 lpttt->lpszText = MAKEINTRESOURCEW(IDS_TOOLTIP_UNINSTALL);
561 break;
562 }
563 break;
564 }
565 }
566
567 return Ret;
568 }
569
570 LRESULT
571 CDeviceManager::OnContext(_In_ LPARAM lParam)
572 {
573 return m_DeviceView->OnContextMenu(lParam);
574 }
575
576 LRESULT
577 CDeviceManager::OnCommand(_In_ WPARAM wParam,
578 _In_ LPARAM /*lParam*/)
579 {
580 LRESULT RetCode = 0;
581 WORD Msg;
582
583 // Get the message
584 Msg = LOWORD(wParam);
585
586 switch (Msg)
587 {
588 case IDC_PROPERTIES:
589 case IDC_SCAN_HARDWARE:
590 case IDC_ENABLE_DRV:
591 case IDC_DISABLE_DRV:
592 case IDC_UPDATE_DRV:
593 case IDC_UNINSTALL_DRV:
594 case IDC_ADD_HARDWARE:
595 {
596 m_DeviceView->OnAction(Msg);
597 break;
598 }
599
600 case IDC_ACTIONMENU:
601 {
602 // Create a popup menu with all the actions for the selected node
603 HMENU hMenu = CreatePopupMenu();
604 m_DeviceView->CreateActionMenu(hMenu, true);
605
606 // Calculate where to put the menu
607 RECT rc;
608 GetMenuItemRect(m_hMainWnd, m_hMenu, 1, &rc);
609 LONG Height = rc.bottom - rc.top;
610
611 // Display the menu
612 TrackPopupMenuEx(hMenu,
613 TPM_RIGHTBUTTON,
614 rc.left,
615 rc.top + Height,
616 m_hMainWnd,
617 NULL);
618
619 DestroyMenu(hMenu);
620 break;
621 }
622
623 case IDC_DEVBYTYPE:
624 {
625 RefreshView(DevicesByType, false);
626 break;
627 }
628
629 case IDC_DEVBYCONN:
630 {
631 RefreshView(DevicesByConnection, false);
632 break;
633 }
634
635 case IDC_SHOWHIDDEN:
636 {
637 // Get the current state
638 UINT CurCheckState = GetMenuState(m_hMenu, IDC_SHOWHIDDEN, MF_BYCOMMAND);
639 if (CurCheckState == MF_CHECKED)
640 {
641 m_DeviceView->SetHiddenDevices(false);
642 CheckMenuItem(m_hMenu, IDC_SHOWHIDDEN, MF_BYCOMMAND | MF_UNCHECKED);
643 }
644 else if (CurCheckState == MF_UNCHECKED)
645 {
646 m_DeviceView->SetHiddenDevices(true);
647 CheckMenuItem(m_hMenu, IDC_SHOWHIDDEN, MF_BYCOMMAND | MF_CHECKED);
648 }
649 // Refresh the device view
650 RefreshView(m_DeviceView->GetCurrentView(), false);
651 break;
652 }
653
654 case IDC_ABOUT:
655 {
656 // Apportion blame
657 MessageBoxW(m_hMainWnd,
658 L"ReactOS Device Manager\r\nCopyright Ged Murphy 2015",
659 L"About",
660 MB_OK | MB_APPLMODAL);
661
662 // Set focus back to the treeview
663 m_DeviceView->SetFocus();
664 break;
665 }
666
667 case IDC_EXIT:
668 {
669 // Post a close message to the window
670 PostMessageW(m_hMainWnd,
671 WM_CLOSE,
672 0,
673 0);
674 break;
675 }
676
677 default:
678 // We didn't handle it
679 RetCode = -1;
680 break;
681 }
682
683 return RetCode;
684 }
685
686 void
687 CDeviceManager::OnActivate(void)
688 {
689 m_DeviceView->SetFocus();
690 }
691
692 LRESULT
693 CDeviceManager::OnDestroy(void)
694 {
695 // Uninitialize the device view
696 m_DeviceView->Uninitialize();
697
698 // Kill the object
699 delete m_DeviceView;
700 m_DeviceView = NULL;
701
702 // Clear the user data pointer
703 SetWindowLongPtr(m_hMainWnd, GWLP_USERDATA, 0);
704
705 // Break the message loop
706 PostQuitMessage(0);
707
708 return 0;
709 }
710
711 LRESULT CALLBACK
712 CDeviceManager::MainWndProc(_In_ HWND hwnd,
713 _In_ UINT msg,
714 _In_ WPARAM wParam,
715 _In_ LPARAM lParam)
716 {
717 CDeviceManager *This;
718 LRESULT RetCode = 0;
719
720 // Get the object pointer from window context
721 This = (CDeviceManager *)GetWindowLongPtr(hwnd, GWLP_USERDATA);
722 if (This == NULL)
723 {
724 // Check that this isn't a create message
725 if (msg != WM_CREATE)
726 {
727 // Don't handle null info pointer
728 goto HandleDefaultMessage;
729 }
730 }
731
732 switch(msg)
733 {
734 case WM_CREATE:
735 {
736 // Get the object pointer from the create param
737 This = (CDeviceManager *)((LPCREATESTRUCT)lParam)->lpCreateParams;
738
739 // Store the pointer in the window's global user data
740 SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)This);
741
742 // Call the create handler
743 RetCode = This->OnCreate(hwnd);
744 break;
745 }
746
747 case WM_SIZE:
748 {
749 RetCode = This->OnSize();
750 break;
751 }
752
753 case WM_NOTIFY:
754 {
755 RetCode = This->OnNotify(lParam);
756 break;
757 }
758
759 case WM_CONTEXTMENU:
760 {
761 RetCode = This->OnContext(lParam);
762 break;
763 }
764
765 case WM_MENUSELECT:
766 {
767 if (This->m_hStatusBar != NULL)
768 {
769 if (!This->MainWndMenuHint(LOWORD(wParam),
770 MainMenuHintTable,
771 sizeof(MainMenuHintTable) / sizeof(MainMenuHintTable[0]),
772 IDS_HINT_BLANK))
773 {
774 This->MainWndMenuHint(LOWORD(wParam),
775 SystemMenuHintTable,
776 sizeof(SystemMenuHintTable) / sizeof(SystemMenuHintTable[0]),
777 IDS_HINT_BLANK);
778 }
779 }
780
781 break;
782 }
783
784 case WM_COMMAND:
785 {
786 // Handle the command message
787 RetCode = This->OnCommand(wParam, lParam);
788 if (RetCode == -1)
789 {
790 // Hand it off to the default message handler
791 goto HandleDefaultMessage;
792 }
793 break;
794 }
795
796 case WM_DEVICECHANGE:
797 {
798 if (wParam == DBT_DEVNODES_CHANGED)
799 {
800 //
801 // The OS can send multiple change messages in quick succession. To avoid
802 // refreshing multiple times (and to avoid waiting in the message thread)
803 // we set a timer to run in 500ms, which should leave enough time for all
804 // the messages to come through. Wrap so we don't set multiple timers
805 //
806 if (InterlockedCompareExchange((LONG *)&This->m_RefreshPending, TRUE, FALSE) == FALSE)
807 {
808 SetTimer(hwnd, REFRESH_TIMER, 500, NULL);
809 }
810 }
811 break;
812 }
813
814 case WM_TIMER:
815 {
816 if (wParam == REFRESH_TIMER)
817 {
818 // Schedule a refresh (this just creates a thread and returns)
819 This->RefreshView(This->m_DeviceView->GetCurrentView(), true);
820
821 // Cleanup the timer
822 KillTimer(hwnd, REFRESH_TIMER);
823
824 // Allow more change notifications
825 InterlockedExchange((LONG *)&This->m_RefreshPending, FALSE);
826 }
827 break;
828 }
829
830 case WM_ENTERMENULOOP:
831 {
832 This->UpdateStatusBar(true);
833 break;
834 }
835
836 case WM_EXITMENULOOP:
837 {
838 This->UpdateStatusBar(false);
839 break;
840 }
841
842 case WM_CLOSE:
843 {
844 // Destroy the main window
845 DestroyWindow(hwnd);
846 break;
847 }
848
849 case WM_ACTIVATE:
850 {
851 if (LOWORD(hwnd))
852 This->OnActivate();
853 break;
854 }
855
856 case WM_DESTROY:
857 {
858 // Call the destroy handler
859 RetCode = This->OnDestroy();
860 break;
861 }
862
863 default:
864 {
865 HandleDefaultMessage:
866 RetCode = DefWindowProc(hwnd, msg, wParam, lParam);
867 break;
868 }
869 }
870
871 return RetCode;
872 }