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"
15 /* DATA *****************************************************/
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
24 #define REFRESH_TIMER 1
26 HINSTANCE g_hThisInstance
= NULL
;
27 HINSTANCE g_hParentInstance
= NULL
;
30 static const MENU_HINT MainMenuHintTable
[] =
33 { IDC_EXIT
, IDS_HINT_EXIT
},
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
},
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
},
52 { IDC_ABOUT
, IDS_HINT_ABOUT
}
58 static const MENU_HINT SystemMenuHintTable
[] =
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
}
68 static TBBUTTON TbButtons
[] =
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 }
80 /* PUBLIC METHODS **********************************************/
82 CDeviceManager::CDeviceManager(void) :
87 m_RefreshPending(false)
89 m_szMainWndClass
= L
"DevMgmtWndClass";
92 CDeviceManager::~CDeviceManager(void)
97 CDeviceManager::Create(_In_ HWND
/*hWndParent*/,
99 _In_opt_z_ LPCWSTR
/*lpMachineName*/,
102 CDeviceManager MainWindow
;
103 INITCOMMONCONTROLSEX icex
;
104 CAtlStringW szAppName
;
107 // Store the instances
108 g_hParentInstance
= hInst
;
109 g_hThisInstance
= GetModuleHandleW(L
"devmgr.dll");
111 // Initialize common controls
112 icex
.dwSize
= sizeof(INITCOMMONCONTROLSEX
);
113 icex
.dwICC
= ICC_BAR_CLASSES
| ICC_COOL_CLASSES
;
114 InitCommonControlsEx(&icex
);
116 // Load the application name
117 if (szAppName
.LoadStringW(g_hThisInstance
, IDS_APPNAME
))
119 // Initialize the main window
120 if (MainWindow
.Initialize(szAppName
, nCmdShow
))
122 // Run the application
123 Ret
= MainWindow
.Run();
125 // Uninitialize the main window
126 MainWindow
.Uninitialize();
134 /* PRIVATE METHODS **********************************************/
137 CDeviceManager::Initialize(_In_z_ LPCTSTR lpCaption
,
140 CAtlStringW szCaption
;
141 WNDCLASSEXW wc
= {0};
143 // Store the show window value
144 m_CmdShow
= nCmdShow
;
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
),
158 GetSystemMetrics(SM_CXSMICON
),
159 GetSystemMetrics(SM_CYSMICON
),
162 // Register the window
163 if (RegisterClassExW(&wc
))
165 // Create the main window and store the object pointer
166 m_hMainWnd
= CreateWindowExW(WS_EX_WINDOWEDGE
,
169 WS_OVERLAPPEDWINDOW
| WS_CLIPCHILDREN
| WS_CLIPSIBLINGS
,
180 // Return creation result
181 return !!(m_hMainWnd
);
185 CDeviceManager::Uninitialize(void)
187 // Unregister the window class
188 UnregisterClassW(m_szMainWndClass
, g_hThisInstance
);
192 CDeviceManager::Run(void)
196 // Pump the message queue
197 while (GetMessageW(&Msg
, NULL
, 0, 0 ) != 0)
199 TranslateMessage(&Msg
);
200 DispatchMessageW(&Msg
);
207 CDeviceManager::MainWndMenuHint(_In_ WORD CmdId
,
208 _In_
const MENU_HINT
*HintArray
,
209 _In_ DWORD HintsCount
,
213 const MENU_HINT
*LastHint
;
214 UINT HintId
= DefHintId
;
216 LastHint
= HintArray
+ HintsCount
;
217 while (HintArray
!= LastHint
)
219 if (HintArray
->CmdId
== CmdId
)
221 HintId
= HintArray
->HintId
;
228 StatusBarLoadString(m_hStatusBar
,
237 CDeviceManager::UpdateStatusBar(_In_
bool InMenuLoop
)
239 SendMessageW(m_hStatusBar
,
246 CDeviceManager::RefreshView(_In_ ViewType Type
,
247 _In_
bool ScanForChanges
)
251 // Refreshed the cached view
252 m_DeviceView
->Refresh(Type
, ScanForChanges
, true);
254 // Get the menu item id
258 CheckId
= IDC_DEVBYTYPE
;
261 case DevicesByConnection
:
262 CheckId
= IDC_DEVBYCONN
;
265 case ResourcesByType
:
266 CheckId
= IDC_RESBYTYPE
;
269 case ResourcesByConnection
:
270 CheckId
= IDC_RESBYCONN
;
278 // Set the new check item
279 CheckMenuRadioItem(m_hMenu
,
289 CDeviceManager::CreateToolBar(void)
291 TBADDBITMAP TbAddBitmap
;
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
;
296 // Create the toolbar window
297 m_hToolBar
= CreateWindowExW(dwExStyles
,
306 if (m_hToolBar
== NULL
)
309 // Don't show clipped buttons
310 SendMessageW(m_hToolBar
,
313 TBSTYLE_EX_HIDECLIPPEDBUTTONS
);
315 SendMessageW(m_hToolBar
, TB_SETBITMAPSIZE
, 0, MAKELONG(16, 16));
317 // Set the struct size, the toobar needs this...
318 SendMessageW(m_hToolBar
,
323 TbAddBitmap
.hInst
= g_hThisInstance
;
324 TbAddBitmap
.nID
= IDB_TOOLBAR
;
325 SendMessageW(m_hToolBar
, TB_ADDBITMAP
, _countof(TbButtons
), (LPARAM
)&TbAddBitmap
);
327 SendMessageW(m_hToolBar
, TB_ADDBUTTONSW
, _countof(TbButtons
), (LPARAM
)TbButtons
);
328 SendMessageW(m_hToolBar
, TB_AUTOSIZE
, 0, 0);
332 ShowWindow(m_hToolBar
, SW_SHOW
);
339 CDeviceManager::CreateStatusBar(void)
341 int StatWidths
[] = {110, -1}; // widths of status bar
344 // Create the status bar
345 m_hStatusBar
= CreateWindowExW(0,
348 WS_CHILD
| WS_VISIBLE
| SBARS_SIZEGRIP
,
351 (HMENU
)IDC_STATUSBAR
,
357 bRet
= (SendMessageW(m_hStatusBar
,
359 sizeof(StatWidths
) / sizeof(int),
360 (LPARAM
)StatWidths
) != 0);
366 void CDeviceManager::UpdateToolbar()
370 CNode
*Node
= m_DeviceView
->GetSelectedNode();
373 if (Node
->HasProperties())
375 State
= TBSTATE_ENABLED
;
379 State
= TBSTATE_HIDDEN
;
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
385 // enable driver button
386 if (Node
->GetNodeType() == DeviceNode
&&
387 dynamic_cast<CDeviceNode
*>(Node
)->IsDisabled())
389 State
= TBSTATE_ENABLED
;
393 State
= TBSTATE_HIDDEN
;
395 SendMessageW(m_hToolBar
, TB_SETSTATE
, IDC_ENABLE_DRV
, MAKELPARAM(State
, 0));
397 // disable driver button
398 if (Node
->GetNodeType() == DeviceNode
&&
399 dynamic_cast<CDeviceNode
*>(Node
)->CanDisable() &&
400 !dynamic_cast<CDeviceNode
*>(Node
)->IsDisabled())
402 State
= TBSTATE_ENABLED
;
406 State
= TBSTATE_HIDDEN
;
408 SendMessageW(m_hToolBar
, TB_SETSTATE
, IDC_DISABLE_DRV
, MAKELPARAM(State
, 0));
412 CDeviceManager::StatusBarLoadString(_In_ HWND hStatusBar
,
414 _In_ HINSTANCE hInstance
,
417 CAtlStringW szMessage
;
421 if (szMessage
.LoadStringW(hInstance
, uID
))
423 // Show the string on the status bar
424 bRet
= (SendMessageW(hStatusBar
,
427 (LPARAM
)szMessage
.GetBuffer()) != 0);
434 CDeviceManager::OnCreate(_In_ HWND hwnd
)
441 // Store a handle to the main menu
442 m_hMenu
= GetMenu(m_hMainWnd
);
444 // Create the toolbar and statusbar
445 if (CreateToolBar() && CreateStatusBar())
447 // Create the device view object
448 m_DeviceView
= new CDeviceView(m_hMainWnd
);
449 if (m_DeviceView
->Initialize())
451 // Do the initial scan
452 RefreshView(m_DeviceView
->GetCurrentView(), true);
454 // Display the window according to the user request
455 ShowWindow(hwnd
, m_CmdShow
);
464 CDeviceManager::OnSize(void)
466 RECT rcClient
, rcTool
, rcStatus
;
467 INT lvHeight
, iToolHeight
, iStatusHeight
;
469 // Autosize the toolbar
470 SendMessage(m_hToolBar
, TB_AUTOSIZE
, 0, 0);
472 // Get the toolbar rect and save the height
473 GetWindowRect(m_hToolBar
, &rcTool
);
474 iToolHeight
= rcTool
.bottom
- rcTool
.top
;
476 // Resize the status bar
477 SendMessage(m_hStatusBar
, WM_SIZE
, 0, 0);
479 // Get the statusbar rect and save the height
480 GetWindowRect(m_hStatusBar
, &rcStatus
);
481 iStatusHeight
= rcStatus
.bottom
- rcStatus
.top
;
483 // Get the full client rect
484 GetClientRect(m_hMainWnd
, &rcClient
);
486 // Calculate the remaining height for the treeview
487 lvHeight
= rcClient
.bottom
- iToolHeight
- iStatusHeight
;
489 // Resize the device view
490 m_DeviceView
->OnSize(0,
499 CDeviceManager::OnNotify(_In_ LPARAM lParam
)
501 LPNMHDR NmHdr
= (LPNMHDR
)lParam
;
514 m_DeviceView
->DisplayPropertySheet();
520 Ret
= m_DeviceView
->OnRightClick(NmHdr
);
526 m_DeviceView
->DisplayPropertySheet();
530 case TTN_GETDISPINFO
:
532 LPTOOLTIPTEXT lpttt
= (LPTOOLTIPTEXT
)lParam
;
533 lpttt
->hinst
= g_hThisInstance
;
535 UINT_PTR idButton
= lpttt
->hdr
.idFrom
;
539 lpttt
->lpszText
= MAKEINTRESOURCEW(IDS_TOOLTIP_PROPERTIES
);
541 case IDC_SCAN_HARDWARE
:
542 lpttt
->lpszText
= MAKEINTRESOURCEW(IDS_TOOLTIP_SCAN
);
545 lpttt
->lpszText
= MAKEINTRESOURCEW(IDS_TOOLTIP_ENABLE
);
547 case IDC_DISABLE_DRV
:
548 lpttt
->lpszText
= MAKEINTRESOURCEW(IDS_TOOLTIP_DISABLE
);
551 lpttt
->lpszText
= MAKEINTRESOURCEW(IDS_TOOLTIP_UPDATE
);
553 case IDC_UNINSTALL_DRV
:
554 lpttt
->lpszText
= MAKEINTRESOURCEW(IDS_TOOLTIP_UNINSTALL
);
565 CDeviceManager::OnContext(_In_ LPARAM lParam
)
567 return m_DeviceView
->OnContextMenu(lParam
);
571 CDeviceManager::OnCommand(_In_ WPARAM wParam
,
572 _In_ LPARAM
/*lParam*/)
578 Msg
= LOWORD(wParam
);
583 case IDC_SCAN_HARDWARE
:
585 case IDC_DISABLE_DRV
:
587 case IDC_UNINSTALL_DRV
:
588 case IDC_ADD_HARDWARE
:
590 m_DeviceView
->OnAction(Msg
);
596 // Create a popup menu with all the actions for the selected node
597 HMENU hMenu
= CreatePopupMenu();
598 m_DeviceView
->CreateActionMenu(hMenu
, true);
600 // Calculate where to put the menu
602 GetMenuItemRect(m_hMainWnd
, m_hMenu
, 1, &rc
);
603 LONG Height
= rc
.bottom
- rc
.top
;
606 TrackPopupMenuEx(hMenu
,
619 RefreshView(DevicesByType
, false);
625 RefreshView(DevicesByConnection
, false);
631 // Get the current state
632 UINT CurCheckState
= GetMenuState(m_hMenu
, IDC_SHOWHIDDEN
, MF_BYCOMMAND
);
633 if (CurCheckState
== MF_CHECKED
)
635 m_DeviceView
->SetHiddenDevices(false);
636 CheckMenuItem(m_hMenu
, IDC_SHOWHIDDEN
, MF_BYCOMMAND
| MF_UNCHECKED
);
638 else if (CurCheckState
== MF_UNCHECKED
)
640 m_DeviceView
->SetHiddenDevices(true);
641 CheckMenuItem(m_hMenu
, IDC_SHOWHIDDEN
, MF_BYCOMMAND
| MF_CHECKED
);
643 // Refresh the device view
644 RefreshView(m_DeviceView
->GetCurrentView(), false);
651 MessageBoxW(m_hMainWnd
,
652 L
"ReactOS Device Manager\r\nCopyright Ged Murphy 2015",
654 MB_OK
| MB_APPLMODAL
);
656 // Set focus back to the treeview
657 m_DeviceView
->SetFocus();
663 // Post a close message to the window
664 PostMessageW(m_hMainWnd
,
672 // We didn't handle it
681 CDeviceManager::OnActivate(void)
683 m_DeviceView
->SetFocus();
687 CDeviceManager::OnDestroy(void)
689 // Uninitialize the device view
690 m_DeviceView
->Uninitialize();
696 // Clear the user data pointer
697 SetWindowLongPtr(m_hMainWnd
, GWLP_USERDATA
, 0);
699 // Break the message loop
706 CDeviceManager::MainWndProc(_In_ HWND hwnd
,
711 CDeviceManager
*This
;
714 // Get the object pointer from window context
715 This
= (CDeviceManager
*)GetWindowLongPtr(hwnd
, GWLP_USERDATA
);
718 // Check that this isn't a create message
719 if (msg
!= WM_CREATE
)
721 // Don't handle null info pointer
722 goto HandleDefaultMessage
;
730 // Get the object pointer from the create param
731 This
= (CDeviceManager
*)((LPCREATESTRUCT
)lParam
)->lpCreateParams
;
733 // Store the pointer in the window's global user data
734 SetWindowLongPtr(hwnd
, GWLP_USERDATA
, (LONG_PTR
)This
);
736 // Call the create handler
737 RetCode
= This
->OnCreate(hwnd
);
743 RetCode
= This
->OnSize();
749 RetCode
= This
->OnNotify(lParam
);
755 RetCode
= This
->OnContext(lParam
);
761 if (This
->m_hStatusBar
!= NULL
)
763 if (!This
->MainWndMenuHint(LOWORD(wParam
),
765 sizeof(MainMenuHintTable
) / sizeof(MainMenuHintTable
[0]),
768 This
->MainWndMenuHint(LOWORD(wParam
),
770 sizeof(SystemMenuHintTable
) / sizeof(SystemMenuHintTable
[0]),
780 // Handle the command message
781 RetCode
= This
->OnCommand(wParam
, lParam
);
784 // Hand it off to the default message handler
785 goto HandleDefaultMessage
;
790 case WM_DEVICECHANGE
:
792 if (wParam
== DBT_DEVNODES_CHANGED
)
795 // The OS can send multiple change messages in quick succession. To avoid
796 // refreshing multiple times (and to avoid waiting in the message thread)
797 // we set a timer to run in 500ms, which should leave enough time for all
798 // the messages to come through. Wrap so we don't set multiple timers
800 if (InterlockedCompareExchange((LONG
*)&This
->m_RefreshPending
, TRUE
, FALSE
) == FALSE
)
802 SetTimer(hwnd
, REFRESH_TIMER
, 500, NULL
);
810 if (wParam
== REFRESH_TIMER
)
812 // Schedule a refresh (this just creates a thread and returns)
813 This
->RefreshView(This
->m_DeviceView
->GetCurrentView(), true);
816 KillTimer(hwnd
, REFRESH_TIMER
);
818 // Allow more change notifications
819 InterlockedExchange((LONG
*)&This
->m_RefreshPending
, FALSE
);
824 case WM_ENTERMENULOOP
:
826 This
->UpdateStatusBar(true);
830 case WM_EXITMENULOOP
:
832 This
->UpdateStatusBar(false);
838 // Destroy the main window
852 // Call the destroy handler
853 RetCode
= This
->OnDestroy();
859 HandleDefaultMessage
:
860 RetCode
= DefWindowProc(hwnd
, msg
, wParam
, lParam
);