Left: | ||
Right: |
LEFT | RIGHT |
---|---|
1 #include <sstream> | 1 #include <sstream> |
2 #include <string> | 2 #include <string> |
3 #include <Windows.h> | 3 #include <Windows.h> |
4 | 4 |
5 #include "../shared/Dictionary.h" | 5 #include "../shared/Dictionary.h" |
6 #include "../shared/Utils.h" | 6 #include "../shared/Utils.h" |
7 #include "UpdateInstallDialog.h" | 7 #include "UpdateInstallDialog.h" |
8 | 8 |
9 namespace | 9 namespace |
10 { | 10 { |
11 LRESULT CALLBACK ProcessWindowMessage(HWND window, UINT message, | 11 LRESULT CALLBACK ProcessWindowMessage(HWND window, UINT message, |
12 WPARAM wParam, LPARAM lParam) | 12 WPARAM wParam, LPARAM lParam) |
13 { | 13 { |
14 switch (message) | 14 switch (message) |
15 { | 15 { |
16 case WM_CLOSE: | 16 case WM_CLOSE: |
17 PostQuitMessage(0); | 17 PostQuitMessage(1); |
18 return 0; | 18 return 0; |
19 case WM_COMMAND: | 19 case WM_COMMAND: |
20 switch (LOWORD(wParam)) | 20 switch (LOWORD(wParam)) |
21 { | 21 { |
22 case IDYES: | 22 case IDYES: |
23 PostQuitMessage(1); | 23 PostQuitMessage(0); |
24 return 0; | 24 return 0; |
25 case IDNO: | 25 case IDNO: |
26 PostQuitMessage(0); | 26 PostQuitMessage(1); |
27 return 0; | 27 return 0; |
28 } | 28 } |
29 break; | 29 break; |
30 } | 30 } |
31 return DefWindowProc(window, message, wParam, lParam); | 31 return DefWindowProc(window, message, wParam, lParam); |
32 } | 32 } |
33 | |
34 HWND CreateDialogWindow() | |
35 { | |
36 const std::wstring windowClassName = L"ABP_UPDATE_INSTALL_DIALOG"; | |
37 WNDCLASSEXW windowClass = {}; | |
38 windowClass.cbSize = sizeof(windowClass); | |
39 windowClass.lpfnWndProc = ProcessWindowMessage; | |
40 windowClass.hbrBackground = reinterpret_cast<HBRUSH>(COLOR_MENU + 1); | |
41 windowClass.lpszClassName = windowClassName.c_str(); | |
42 RegisterClassEx(&windowClass); | |
43 | |
44 Dictionary* dictionary = Dictionary::GetInstance(); | |
45 std::wstring title = | |
46 dictionary->Lookup("updater", "install-question-title"); | |
47 | |
48 return CreateWindowExW(WS_EX_TOOLWINDOW | WS_EX_TOPMOST, | |
49 windowClassName.c_str(), title.c_str(), WS_SYSMENU, | |
50 0, 0, 0, 0, 0, 0, 0, 0); | |
51 } | |
52 | |
53 int GetAverageCharacterWidth(HDC deviceContext) | |
54 { | |
55 std::wstring alphabet; | |
56 for (wchar_t letter = L'A'; letter != 'Z'; letter++) | |
57 alphabet += letter; | |
58 RECT alphabetRect = {}; | |
59 DrawTextW(deviceContext, alphabet.c_str(), -1, &alphabetRect, | |
60 DT_CALCRECT | DT_NOPREFIX); | |
61 return alphabetRect.right / alphabet.length(); | |
62 } | |
63 | |
64 HWND CreateText(HWND parent) | |
65 { | |
66 Dictionary* dictionary = Dictionary::GetInstance(); | |
67 std::wstring text = dictionary->Lookup("updater", "install-question-text"); | |
68 | |
69 HDC deviceContext = GetDC(parent); | |
70 const int characterWidth = GetAverageCharacterWidth(deviceContext); | |
71 const int lineLength = 40; | |
72 const int textWidth = lineLength * characterWidth; | |
73 | |
74 RECT textRect = {}; | |
75 textRect.right = textWidth; | |
76 DrawTextW(deviceContext, text.c_str(), -1, &textRect, | |
77 DT_CALCRECT | DT_NOPREFIX | DT_WORDBREAK); | |
78 const int textHeight = textRect.bottom; | |
79 | |
80 return CreateWindowW(L"STATIC", text.c_str(), | |
81 WS_CHILD | WS_VISIBLE| SS_LEFT, 0, 0, | |
82 textWidth, textHeight, parent, 0, 0, 0); | |
83 } | |
84 | |
85 HWND CreateButtons(HWND parent) | |
86 { | |
87 const std::wstring panelClassName = L"ABP_PANEL"; | |
88 WNDCLASSW panelClass = {}; | |
89 panelClass.lpfnWndProc = | |
90 reinterpret_cast<WNDPROC>(GetWindowLongPtr(parent, GWLP_WNDPROC)); | |
91 panelClass.lpszClassName = panelClassName.c_str(); | |
92 RegisterClass(&panelClass); | |
93 | |
94 Dictionary* dictionary = Dictionary::GetInstance(); | |
95 const std::wstring yesLabel = dictionary->Lookup("general", "button-yes"); | |
96 const std::wstring noLabel = dictionary->Lookup("general", "button-no"); | |
97 | |
98 HDC deviceContext = GetDC(parent); | |
99 RECT yesButtonRect = {}; | |
100 DrawTextW(deviceContext, yesLabel.c_str(), -1, &yesButtonRect, | |
101 DT_CALCRECT | DT_NOPREFIX); | |
102 RECT noButtonRect = {}; | |
103 DrawTextW(deviceContext, noLabel.c_str(), -1, &noButtonRect, | |
104 DT_CALCRECT | DT_NOPREFIX); | |
105 | |
106 const int minButtonWidth = 120; | |
107 const int minButtonHeight = 30; | |
108 const int buttonPadding = 5; | |
109 int yesButtonWidth = yesButtonRect.right + buttonPadding * 2; | |
110 yesButtonWidth = max(yesButtonWidth, minButtonWidth); | |
111 int noButtonWidth = noButtonRect.right + buttonPadding * 2; | |
112 noButtonWidth = max(noButtonWidth, minButtonWidth); | |
113 int buttonHeight = max(yesButtonRect.bottom, noButtonRect.bottom) | |
114 + buttonPadding * 2; | |
115 buttonHeight = max(buttonHeight, minButtonHeight); | |
116 | |
117 const int gap = 10; | |
118 const int panelWidth = yesButtonWidth + gap + noButtonWidth; | |
119 const int panelHeight = buttonHeight; | |
120 | |
121 const DWORD flags = WS_CHILD | WS_VISIBLE; | |
122 HWND buttons = CreateWindowW(panelClassName.c_str(), 0, flags, 0, 0, | |
123 panelWidth, panelHeight, parent, 0, 0, 0); | |
124 CreateWindowW(L"BUTTON", yesLabel.c_str(), flags, | |
125 0, 0, yesButtonWidth, buttonHeight, | |
126 buttons, reinterpret_cast<HMENU>(IDYES), 0, 0); | |
127 CreateWindowW(L"BUTTON", noLabel.c_str(), flags, | |
128 yesButtonWidth + gap, 0, noButtonWidth, buttonHeight, | |
129 buttons, reinterpret_cast<HMENU>(IDNO), 0, 0); | |
130 return buttons; | |
131 } | |
132 | |
133 void UpdateSizes(HWND window, const HWND text, const HWND buttons) | |
134 { | |
135 RECT textRect; | |
136 GetWindowRect(text, &textRect); | |
137 | |
138 RECT buttonsRect; | |
139 GetWindowRect(buttons, &buttonsRect); | |
140 | |
141 const int padding = 10; | |
142 const int margin = 10; | |
143 const int gap = 10; | |
144 const int contentWidth = max(textRect.right, buttonsRect.right); | |
145 const int contentHeight = textRect.bottom + buttonsRect.bottom + margin; | |
146 const int windowWidth = contentWidth + padding * 2; | |
147 const int windowHeight = contentHeight + padding * 2; | |
148 | |
149 RECT workArea; | |
150 SystemParametersInfo(SPI_GETWORKAREA, 0, &workArea, 0); | |
151 const int windowX = workArea.right - windowWidth - margin; | |
152 const int windowY = workArea.bottom - windowHeight - margin; | |
153 SetWindowPos(window, 0, windowX, windowY, windowWidth, windowHeight, 0); | |
154 | |
155 const int textX = padding; | |
156 const int textY = padding; | |
157 SetWindowPos(text, 0, padding, padding, 0, 0, SWP_NOSIZE); | |
158 | |
159 const int buttonsX = windowWidth - buttonsRect.right - padding; | |
160 const int buttonsY = windowHeight - buttonsRect.bottom - padding; | |
161 SetWindowPos(buttons, 0, buttonsX, buttonsY, 0, 0, SWP_NOSIZE); | |
162 } | |
33 } | 163 } |
34 | 164 |
35 UpdateInstallDialog::UpdateInstallDialog() | 165 UpdateInstallDialog::UpdateInstallDialog() |
36 { | 166 { |
37 const std::wstring windowClassName = L"UPDATE_INSTALL_DIALOG"; | 167 window = CreateDialogWindow(); |
38 WNDCLASSEX windowClass = {}; | 168 HWND text = CreateText(window); |
Wladimir Palant
2013/08/01 09:58:24
We should use WNDCLASSEXW explicitly.
| |
39 windowClass.cbSize = sizeof(WNDCLASSEX); | 169 HWND buttons = CreateButtons(window); |
Wladimir Palant
2013/08/01 09:58:24
sizeof(windowClass) to avoid repeating the type na
Felix Dahlke
2013/08/02 10:48:01
Done.
| |
40 windowClass.lpfnWndProc = ProcessWindowMessage; | 170 UpdateSizes(window, text, buttons); |
41 windowClass.hbrBackground = reinterpret_cast<HBRUSH>(COLOR_MENU + 1); | |
42 windowClass.lpszClassName = windowClassName.c_str(); | |
Oleksandr
2013/08/01 07:05:12
Maybe use hIcon here with ABP logo? A default wind
| |
43 if (!RegisterClassEx(&windowClass)) | |
44 { | |
45 std::stringstream stream; | |
46 stream << "Failed to register window class " | |
47 << ToUtf8String(windowClassName) << ": " << GetLastError(); | |
48 throw std::runtime_error(stream.str()); | |
49 } | |
50 | |
51 Dictionary* dictionary = Dictionary::GetInstance(); | |
52 std::wstring title = dictionary->Lookup("updater", "install-question-title"); | |
Oleksandr
2013/07/31 22:16:59
This is fine only for when download is ready. How
Felix Dahlke
2013/08/01 07:21:17
Like I said earlier, I think it's better to focus
| |
53 | |
54 RECT workArea; | |
55 SystemParametersInfo(SPI_GETWORKAREA, 0, &workArea, 0); | |
56 | |
57 const int width = 300; | |
58 const int height = 150; | |
Wladimir Palant
2013/08/01 09:58:24
Where do these numbers come from? What will happen
Felix Dahlke
2013/08/02 10:48:01
Done.
| |
59 const int margin = 10; | |
60 | |
61 window = CreateWindowExW(WS_EX_TOOLWINDOW | WS_EX_TOPMOST, | |
62 windowClassName.c_str(), title.c_str(), WS_SYSMENU, | |
63 workArea.right - width - margin, | |
64 workArea.bottom - height - margin, | |
65 width, height, 0, 0, 0, 0); | |
Oleksandr
2013/08/01 07:05:12
Should we do something like:
SetForegroundWindow(
| |
66 CreateText(); | |
67 CreateButtons(); | |
Oleksandr
2013/07/31 22:16:59
No CreateButtons() in the overridden constructor I
Felix Dahlke
2013/08/01 07:21:17
As I said above, let's make this more generic when
| |
68 } | 171 } |
69 | 172 |
70 UpdateInstallDialog::~UpdateInstallDialog() | 173 UpdateInstallDialog::~UpdateInstallDialog() |
71 { | 174 { |
72 DestroyWindow(window); | 175 DestroyWindow(window); |
73 } | 176 } |
74 | 177 |
75 bool UpdateInstallDialog::Show() | 178 bool UpdateInstallDialog::Show() |
76 { | 179 { |
77 ShowWindow(window, SW_SHOW); | 180 ShowWindow(window, SW_SHOW); |
78 MSG message; | 181 MSG message; |
79 while(GetMessage(&message, 0, 0, 0) > 0) | 182 while(GetMessage(&message, 0, 0, 0) > 0) |
80 { | 183 { |
81 TranslateMessage(&message); | 184 TranslateMessage(&message); |
82 DispatchMessage(&message); | 185 DispatchMessage(&message); |
83 } | 186 } |
84 ShowWindow(window, SW_HIDE); | 187 ShowWindow(window, SW_HIDE); |
85 return message.wParam; | 188 return message.wParam == 0; |
Wladimir Palant
2013/08/01 09:58:24
Doesn't this cause a warning? I would rather cast
Felix Dahlke
2013/08/02 10:48:01
Done. Opted for message.wParam == 0 in case of yes
| |
86 } | 189 } |
87 | |
88 void UpdateInstallDialog::CreateText() | |
89 { | |
90 Dictionary* dictionary = Dictionary::GetInstance(); | |
91 std::wstring text = dictionary->Lookup("updater", "install-question-text"); | |
92 | |
93 RECT clientRect; | |
94 GetClientRect(window, &clientRect); | |
95 | |
96 const int margin = 10; | |
97 const int width = clientRect.right - margin * 2; | |
98 const int height = 60; | |
99 | |
100 CreateWindowW(L"STATIC", text.c_str(), WS_CHILD | WS_VISIBLE| SS_LEFT, | |
101 margin, margin, width, height, window, 0, 0, 0); | |
102 } | |
103 | |
104 void UpdateInstallDialog::CreateButtons() | |
105 { | |
106 Dictionary* dictionary = Dictionary::GetInstance(); | |
107 const std::wstring yesLabel = dictionary->Lookup("general", "button-yes"); | |
108 const std::wstring noLabel = dictionary->Lookup("general", "button-no"); | |
109 | |
110 const DWORD buttonFlags = WS_CHILD | WS_VISIBLE; | |
111 | |
112 RECT clientRect; | |
113 GetClientRect(window, &clientRect); | |
114 | |
115 const int buttonWidth = 100; | |
116 const int buttonHeight = 30; | |
Felix Dahlke
2013/08/02 10:48:01
Done.
| |
117 const int margin = 10; | |
118 const int combinedWidth = buttonWidth * 2 + margin; | |
119 const int yesButtonX = clientRect.right - combinedWidth - margin; | |
120 const int noButtonX = yesButtonX + buttonWidth + margin; | |
121 const int buttonY = clientRect.bottom - buttonHeight - margin; | |
122 | |
123 CreateWindowW(L"BUTTON", yesLabel.c_str(), buttonFlags, | |
124 yesButtonX, buttonY, buttonWidth, buttonHeight, | |
125 window, reinterpret_cast<HMENU>(IDYES), 0, 0); | |
126 CreateWindowW(L"BUTTON", noLabel.c_str(), buttonFlags, | |
127 noButtonX, buttonY, buttonWidth, buttonHeight, | |
128 window, reinterpret_cast<HMENU>(IDNO), 0, 0); | |
129 } | |
LEFT | RIGHT |