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>
12 #include "MainWindow.h"
14 /* DATA *****************************************************/
16 #define BTN_PROPERTIES 0
17 #define BTN_SCAN_HARDWARE 1
18 #define BTN_ENABLE_DRV 2
19 #define BTN_DISABLE_DRV 3
20 #define BTN_UPDATE_DRV 4
21 #define BTN_UNINSTALL_DRV 5
23 #define REFRESH_TIMER 1
25 HINSTANCE g_hThisInstance
= NULL
;
26 HINSTANCE g_hParentInstance
= NULL
;
29 static const MENU_HINT MainMenuHintTable
[] =
32 { IDM_EXIT
, IDS_HINT_EXIT
},
35 { IDM_PROPERTIES
, IDS_HINT_PROPERTIES
},
36 { IDM_SCAN_HARDWARE
, IDS_HINT_SCAN
},
37 { IDM_ENABLE_DRV
, IDS_HINT_ENABLE
},
38 { IDM_DISABLE_DRV
, IDS_HINT_DISABLE
},
39 { IDM_UPDATE_DRV
, IDS_HINT_UPDATE
},
40 { IDM_UNINSTALL_DRV
, IDS_HINT_UNINSTALL
},
41 { IDM_ADD_HARDWARE
, IDS_HINT_ADD
},
44 { IDM_DEVBYTYPE
, IDS_HINT_DEV_BY_TYPE
},
45 { IDM_DEVBYCONN
, IDS_HINT_DEV_BY_CONN
},
46 { IDM_RESBYTYPE
, IDS_HINT_RES_BY_TYPE
},
47 { IDM_RESBYCONN
, IDS_HINT_RES_BY_TYPE
},
48 { IDM_SHOWHIDDEN
, IDS_HINT_SHOW_HIDDEN
},
50 { IDM_ABOUT
, IDS_HINT_ABOUT
}
56 static const MENU_HINT SystemMenuHintTable
[] =
58 {SC_RESTORE
, IDS_HINT_SYS_RESTORE
},
59 {SC_MOVE
, IDS_HINT_SYS_MOVE
},
60 {SC_SIZE
, IDS_HINT_SYS_SIZE
},
61 {SC_MINIMIZE
, IDS_HINT_SYS_MINIMIZE
},
62 {SC_MAXIMIZE
, IDS_HINT_SYS_MAXIMIZE
},
63 {SC_CLOSE
, IDS_HINT_SYS_CLOSE
}
66 static TBBUTTON TbButtons
[] =
68 { BTN_PROPERTIES
, IDM_PROPERTIES
, TBSTATE_ENABLED
, BTNS_BUTTON
, {0}, 0, 0 },
69 { BTN_SCAN_HARDWARE
, IDM_SCAN_HARDWARE
, TBSTATE_ENABLED
, BTNS_BUTTON
, {0}, 0, 0 },
70 { 2, IDC_STATIC
, TBSTATE_ENABLED
, BTNS_SEP
, {0}, 0, 0 },
71 { BTN_ENABLE_DRV
, IDM_ENABLE_DRV
, TBSTATE_ENABLED
, BTNS_BUTTON
, {0}, 0, 0 },
72 { BTN_DISABLE_DRV
, IDM_DISABLE_DRV
, TBSTATE_ENABLED
, BTNS_BUTTON
, {0}, 0, 0 },
73 { BTN_UPDATE_DRV
, IDM_UPDATE_DRV
, TBSTATE_ENABLED
, BTNS_BUTTON
, {0}, 0, 0 },
74 { BTN_UNINSTALL_DRV
, IDM_UNINSTALL_DRV
, TBSTATE_ENABLED
, BTNS_BUTTON
, {0}, 0, 0 }
78 /* PUBLIC METHODS **********************************************/
80 CDeviceManager::CDeviceManager(void) :
85 m_RefreshPending(false)
87 m_szMainWndClass
= L
"DevMgmtWndClass";
90 CDeviceManager::~CDeviceManager(void)
95 CDeviceManager::Create(_In_ HWND
/*hWndParent*/,
97 _In_opt_z_ LPCWSTR
/*lpMachineName*/,
100 CDeviceManager MainWindow
;
101 INITCOMMONCONTROLSEX icex
;
102 CAtlStringW szAppName
;
105 // Store the instances
106 g_hParentInstance
= hInst
;
107 g_hThisInstance
= GetModuleHandleW(L
"devmgr.dll");
109 // Initialize common controls
110 icex
.dwSize
= sizeof(INITCOMMONCONTROLSEX
);
111 icex
.dwICC
= ICC_BAR_CLASSES
| ICC_COOL_CLASSES
;
112 InitCommonControlsEx(&icex
);
114 // Load the application name
115 if (szAppName
.LoadStringW(g_hThisInstance
, IDS_APPNAME
))
117 // Initialize the main window
118 if (MainWindow
.Initialize(szAppName
, nCmdShow
))
120 // Run the application
121 Ret
= MainWindow
.Run();
123 // Uninitialize the main window
124 MainWindow
.Uninitialize();
132 /* PRIVATE METHODS **********************************************/
135 CDeviceManager::Initialize(_In_z_ LPCTSTR lpCaption
,
138 CAtlStringW szCaption
;
139 WNDCLASSEXW wc
= {0};
141 // Store the show window value
142 m_CmdShow
= nCmdShow
;
144 // Setup the window class struct
145 wc
.cbSize
= sizeof(WNDCLASSEXW
);
146 wc
.lpfnWndProc
= MainWndProc
;
147 wc
.hInstance
= g_hThisInstance
;
148 wc
.hIcon
= LoadIcon(g_hThisInstance
, MAKEINTRESOURCEW(IDI_MAIN_ICON
));
149 wc
.hCursor
= LoadCursor(NULL
, IDC_ARROW
);
150 wc
.hbrBackground
= (HBRUSH
)(COLOR_BTNFACE
+ 1);
151 wc
.lpszMenuName
= MAKEINTRESOURCEW(IDM_MAINMENU
);
152 wc
.lpszClassName
= m_szMainWndClass
;
153 wc
.hIconSm
= (HICON
)LoadImage(g_hThisInstance
,
154 MAKEINTRESOURCE(IDI_MAIN_ICON
),
156 GetSystemMetrics(SM_CXSMICON
),
157 GetSystemMetrics(SM_CYSMICON
),
160 // Register the window
161 if (RegisterClassExW(&wc
))
163 // Create the main window and store the object pointer
164 m_hMainWnd
= CreateWindowExW(WS_EX_WINDOWEDGE
,
167 WS_OVERLAPPEDWINDOW
| WS_CLIPCHILDREN
| WS_CLIPSIBLINGS
,
178 // Return creation result
179 return !!(m_hMainWnd
);
183 CDeviceManager::Uninitialize(void)
185 // Unregister the window class
186 UnregisterClassW(m_szMainWndClass
, g_hThisInstance
);
190 CDeviceManager::Run(void)
194 // Pump the message queue
195 while (GetMessageW(&Msg
, NULL
, 0, 0 ) != 0)
197 TranslateMessage(&Msg
);
198 DispatchMessageW(&Msg
);
205 CDeviceManager::MainWndMenuHint(_In_ WORD CmdId
,
206 _In_
const MENU_HINT
*HintArray
,
207 _In_ DWORD HintsCount
,
211 const MENU_HINT
*LastHint
;
212 UINT HintId
= DefHintId
;
214 LastHint
= HintArray
+ HintsCount
;
215 while (HintArray
!= LastHint
)
217 if (HintArray
->CmdId
== CmdId
)
219 HintId
= HintArray
->HintId
;
226 StatusBarLoadString(m_hStatusBar
,
235 CDeviceManager::UpdateStatusBar(_In_
bool InMenuLoop
)
237 SendMessageW(m_hStatusBar
,
244 CDeviceManager::RefreshView(_In_ ViewType Type
,
245 _In_
bool ScanForChanges
)
249 // Refreshed the cached view
250 m_DeviceView
->Refresh(Type
, ScanForChanges
, true);
252 // Get the menu item id
256 CheckId
= IDM_DEVBYTYPE
;
259 case DevicesByConnection
:
260 CheckId
= IDM_DEVBYCONN
;
263 case ResourcesByType
:
264 CheckId
= IDM_RESBYTYPE
;
267 case ResourcesByConnection
:
268 CheckId
= IDM_RESBYCONN
;
276 // Set the new check item
277 CheckMenuRadioItem(m_hMenu
,
287 CDeviceManager::CreateToolBar(void)
289 TBADDBITMAP TbAddBitmap
;
291 DWORD dwStyles
= WS_CHILDWINDOW
| TBSTYLE_FLAT
| TBSTYLE_WRAPABLE
| TBSTYLE_TOOLTIPS
| CCS_NODIVIDER
;
292 DWORD dwExStyles
= WS_EX_LEFT
| WS_EX_LTRREADING
| WS_EX_RIGHTSCROLLBAR
;
294 // Create the toolbar window
295 m_hToolBar
= CreateWindowExW(dwExStyles
,
304 if (m_hToolBar
== NULL
)
307 // Don't show clipped buttons
308 SendMessageW(m_hToolBar
,
311 TBSTYLE_EX_HIDECLIPPEDBUTTONS
);
313 SendMessageW(m_hToolBar
, TB_SETBITMAPSIZE
, 0, MAKELONG(16, 16));
315 // Set the struct size, the toobar needs this...
316 SendMessageW(m_hToolBar
,
321 TbAddBitmap
.hInst
= g_hThisInstance
;
322 TbAddBitmap
.nID
= IDB_TOOLBAR
;
323 SendMessageW(m_hToolBar
, TB_ADDBITMAP
, _countof(TbButtons
), (LPARAM
)&TbAddBitmap
);
325 SendMessageW(m_hToolBar
, TB_ADDBUTTONSW
, _countof(TbButtons
), (LPARAM
)TbButtons
);
326 SendMessageW(m_hToolBar
, TB_AUTOSIZE
, 0, 0);
330 ShowWindow(m_hToolBar
, SW_SHOW
);
337 CDeviceManager::CreateStatusBar(void)
339 int StatWidths
[] = {110, -1}; // widths of status bar
342 // Create the status bar
343 m_hStatusBar
= CreateWindowExW(0,
346 WS_CHILD
| WS_VISIBLE
| SBARS_SIZEGRIP
,
349 (HMENU
)IDC_STATUSBAR
,
355 bRet
= (SendMessageW(m_hStatusBar
,
357 sizeof(StatWidths
) / sizeof(int),
358 (LPARAM
)StatWidths
) != 0);
364 void CDeviceManager::UpdateToolbar()
368 CNode
*Node
= m_DeviceView
->GetSelectedNode();
371 if (Node
->HasProperties())
373 State
= TBSTATE_ENABLED
;
377 State
= TBSTATE_HIDDEN
;
379 SendMessageW(m_hToolBar
, TB_SETSTATE
, IDM_PROPERTIES
, MAKELPARAM(State
, 0));
380 SendMessageW(m_hToolBar
, TB_SETSTATE
, IDM_UPDATE_DRV
, MAKELPARAM(State
, 0)); //hack
381 SendMessageW(m_hToolBar
, TB_SETSTATE
, IDM_UNINSTALL_DRV
, MAKELPARAM(State
, 0)); // hack
383 // enable driver button
384 if (Node
->GetNodeType() == DeviceNode
&&
385 dynamic_cast<CDeviceNode
*>(Node
)->IsDisabled())
387 State
= TBSTATE_ENABLED
;
391 State
= TBSTATE_HIDDEN
;
393 SendMessageW(m_hToolBar
, TB_SETSTATE
, IDM_ENABLE_DRV
, MAKELPARAM(State
, 0));
395 // disable driver button
396 if (Node
->GetNodeType() == DeviceNode
&&
397 dynamic_cast<CDeviceNode
*>(Node
)->CanDisable() &&
398 !dynamic_cast<CDeviceNode
*>(Node
)->IsDisabled())
400 State
= TBSTATE_ENABLED
;
404 State
= TBSTATE_HIDDEN
;
406 SendMessageW(m_hToolBar
, TB_SETSTATE
, IDM_DISABLE_DRV
, MAKELPARAM(State
, 0));
410 CDeviceManager::StatusBarLoadString(_In_ HWND hStatusBar
,
412 _In_ HINSTANCE hInstance
,
415 CAtlStringW szMessage
;
419 if (szMessage
.LoadStringW(hInstance
, uID
))
421 // Show the string on the status bar
422 bRet
= (SendMessageW(hStatusBar
,
425 (LPARAM
)szMessage
.GetBuffer()) != 0);
432 CDeviceManager::OnCreate(_In_ HWND hwnd
)
439 // Store a handle to the main menu
440 m_hMenu
= GetMenu(m_hMainWnd
);
442 // Create the toolbar and statusbar
443 if (CreateToolBar() && CreateStatusBar())
445 // Create the device view object
446 m_DeviceView
= new CDeviceView(m_hMainWnd
);
447 if (m_DeviceView
->Initialize())
449 // Do the initial scan
450 RefreshView(m_DeviceView
->GetCurrentView(), true);
452 // Display the window according to the user request
453 ShowWindow(hwnd
, m_CmdShow
);
462 CDeviceManager::OnSize(void)
464 RECT rcClient
, rcTool
, rcStatus
;
465 INT lvHeight
, iToolHeight
, iStatusHeight
;
467 // Autosize the toolbar
468 SendMessage(m_hToolBar
, TB_AUTOSIZE
, 0, 0);
470 // Get the toolbar rect and save the height
471 GetWindowRect(m_hToolBar
, &rcTool
);
472 iToolHeight
= rcTool
.bottom
- rcTool
.top
;
474 // Resize the status bar
475 SendMessage(m_hStatusBar
, WM_SIZE
, 0, 0);
477 // Get the statusbar rect and save the height
478 GetWindowRect(m_hStatusBar
, &rcStatus
);
479 iStatusHeight
= rcStatus
.bottom
- rcStatus
.top
;
481 // Get the full client rect
482 GetClientRect(m_hMainWnd
, &rcClient
);
484 // Calculate the remaining height for the treeview
485 lvHeight
= rcClient
.bottom
- iToolHeight
- iStatusHeight
;
487 // Resize the device view
488 m_DeviceView
->OnSize(0,
497 CDeviceManager::OnNotify(_In_ LPARAM lParam
)
499 LPNMHDR NmHdr
= (LPNMHDR
)lParam
;
506 HMENU hMenu
= GetSubMenu(m_hMenu
, 1);
507 for (INT i
= GetMenuItemCount(hMenu
) - 1; i
>= 0; i
--)
509 DeleteMenu(hMenu
, i
, MF_BYPOSITION
);
511 m_DeviceView
->CreateActionMenu(hMenu
, true);
518 m_DeviceView
->DisplayPropertySheet();
524 Ret
= m_DeviceView
->OnRightClick(NmHdr
);
530 m_DeviceView
->DisplayPropertySheet();
534 case TTN_GETDISPINFO
:
536 LPTOOLTIPTEXT lpttt
= (LPTOOLTIPTEXT
)lParam
;
537 lpttt
->hinst
= g_hThisInstance
;
539 UINT_PTR idButton
= lpttt
->hdr
.idFrom
;
543 lpttt
->lpszText
= MAKEINTRESOURCEW(IDS_TOOLTIP_PROPERTIES
);
545 case IDM_SCAN_HARDWARE
:
546 lpttt
->lpszText
= MAKEINTRESOURCEW(IDS_TOOLTIP_SCAN
);
549 lpttt
->lpszText
= MAKEINTRESOURCEW(IDS_TOOLTIP_ENABLE
);
551 case IDM_DISABLE_DRV
:
552 lpttt
->lpszText
= MAKEINTRESOURCEW(IDS_TOOLTIP_DISABLE
);
555 lpttt
->lpszText
= MAKEINTRESOURCEW(IDS_TOOLTIP_UPDATE
);
557 case IDM_UNINSTALL_DRV
:
558 lpttt
->lpszText
= MAKEINTRESOURCEW(IDS_TOOLTIP_UNINSTALL
);
569 CDeviceManager::OnContext(_In_ LPARAM lParam
)
571 return m_DeviceView
->OnContextMenu(lParam
);
575 CDeviceManager::OnCommand(_In_ WPARAM wParam
,
576 _In_ LPARAM
/*lParam*/)
582 Msg
= LOWORD(wParam
);
587 case IDM_SCAN_HARDWARE
:
589 case IDM_DISABLE_DRV
:
591 case IDM_UNINSTALL_DRV
:
592 case IDM_ADD_HARDWARE
:
594 m_DeviceView
->OnAction(Msg
);
600 // Create a popup menu with all the actions for the selected node
601 HMENU hMenu
= CreatePopupMenu();
602 m_DeviceView
->CreateActionMenu(hMenu
, true);
604 // Calculate where to put the menu
606 GetMenuItemRect(m_hMainWnd
, m_hMenu
, 1, &rc
);
607 LONG Height
= rc
.bottom
- rc
.top
;
610 TrackPopupMenuEx(hMenu
,
623 RefreshView(DevicesByType
, false);
629 RefreshView(DevicesByConnection
, false);
635 // Get the current state
636 UINT CurCheckState
= GetMenuState(m_hMenu
, IDM_SHOWHIDDEN
, MF_BYCOMMAND
);
637 if (CurCheckState
== MF_CHECKED
)
639 m_DeviceView
->SetHiddenDevices(false);
640 CheckMenuItem(m_hMenu
, IDM_SHOWHIDDEN
, MF_BYCOMMAND
| MF_UNCHECKED
);
642 else if (CurCheckState
== MF_UNCHECKED
)
644 m_DeviceView
->SetHiddenDevices(true);
645 CheckMenuItem(m_hMenu
, IDM_SHOWHIDDEN
, MF_BYCOMMAND
| MF_CHECKED
);
647 // Refresh the device view
648 RefreshView(m_DeviceView
->GetCurrentView(), false);
654 CAtlStringW szAppName
;
655 CAtlStringW szAppAuthors
;
658 if (!szAppName
.LoadStringW(g_hThisInstance
, IDS_APPNAME
))
659 szAppName
= L
"ReactOS Device Manager";
660 if (!szAppAuthors
.LoadStringW(g_hThisInstance
, IDS_APP_AUTHORS
))
662 hIcon
= LoadIconW(g_hThisInstance
, MAKEINTRESOURCEW(IDI_MAIN_ICON
));
663 ShellAboutW(m_hMainWnd
, szAppName
, szAppAuthors
, hIcon
);
667 // Set focus back to the treeview
668 m_DeviceView
->SetFocus();
674 // Post a close message to the window
675 PostMessageW(m_hMainWnd
,
683 // We didn't handle it
692 CDeviceManager::OnActivate(void)
694 m_DeviceView
->SetFocus();
698 CDeviceManager::OnDestroy(void)
700 // Uninitialize the device view
701 m_DeviceView
->Uninitialize();
707 // Clear the user data pointer
708 SetWindowLongPtr(m_hMainWnd
, GWLP_USERDATA
, 0);
710 // Break the message loop
717 CDeviceManager::MainWndProc(_In_ HWND hwnd
,
722 CDeviceManager
*This
;
725 // Get the object pointer from window context
726 This
= (CDeviceManager
*)GetWindowLongPtr(hwnd
, GWLP_USERDATA
);
729 // Check that this isn't a create message
730 if (msg
!= WM_CREATE
)
732 // Don't handle null info pointer
733 goto HandleDefaultMessage
;
741 // Get the object pointer from the create param
742 This
= (CDeviceManager
*)((LPCREATESTRUCT
)lParam
)->lpCreateParams
;
744 // Store the pointer in the window's global user data
745 SetWindowLongPtr(hwnd
, GWLP_USERDATA
, (LONG_PTR
)This
);
747 // Call the create handler
748 RetCode
= This
->OnCreate(hwnd
);
754 RetCode
= This
->OnSize();
760 RetCode
= This
->OnNotify(lParam
);
766 RetCode
= This
->OnContext(lParam
);
772 if (This
->m_hStatusBar
!= NULL
)
774 if (!This
->MainWndMenuHint(LOWORD(wParam
),
776 sizeof(MainMenuHintTable
) / sizeof(MainMenuHintTable
[0]),
779 This
->MainWndMenuHint(LOWORD(wParam
),
781 sizeof(SystemMenuHintTable
) / sizeof(SystemMenuHintTable
[0]),
791 // Handle the command message
792 RetCode
= This
->OnCommand(wParam
, lParam
);
795 // Hand it off to the default message handler
796 goto HandleDefaultMessage
;
801 case WM_DEVICECHANGE
:
803 if (wParam
== DBT_DEVNODES_CHANGED
)
806 // The OS can send multiple change messages in quick succession. To avoid
807 // refreshing multiple times (and to avoid waiting in the message thread)
808 // we set a timer to run in 500ms, which should leave enough time for all
809 // the messages to come through. Wrap so we don't set multiple timers
811 if (InterlockedCompareExchange((LONG
*)&This
->m_RefreshPending
, TRUE
, FALSE
) == FALSE
)
813 SetTimer(hwnd
, REFRESH_TIMER
, 500, NULL
);
821 if (wParam
== REFRESH_TIMER
)
823 // Schedule a refresh (this just creates a thread and returns)
824 This
->RefreshView(This
->m_DeviceView
->GetCurrentView(), true);
827 KillTimer(hwnd
, REFRESH_TIMER
);
829 // Allow more change notifications
830 InterlockedExchange((LONG
*)&This
->m_RefreshPending
, FALSE
);
835 case WM_ENTERMENULOOP
:
837 This
->UpdateStatusBar(true);
841 case WM_EXITMENULOOP
:
843 This
->UpdateStatusBar(false);
849 // Destroy the main window
863 // Call the destroy handler
864 RetCode
= This
->OnDestroy();
870 HandleDefaultMessage
:
871 RetCode
= DefWindowProc(hwnd
, msg
, wParam
, lParam
);