Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code

Side by Side Diff: src/engine/NotificationWindow.cpp

Issue 6505394822184960: Issue 1109 - Support notifications (Closed)
Patch Set: fix obtaining of DPI value of content (NotificationWindow) of NotificationBorderWindow Created Aug. 18, 2015, 9:43 a.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 /*
2 * This file is part of Adblock Plus <https://adblockplus.org/>,
3 * Copyright (C) 2006-2015 Eyeo GmbH
4 *
5 * Adblock Plus is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 3 as
7 * published by the Free Software Foundation.
8 *
9 * Adblock Plus is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 #include <cassert>
19 #include "NotificationWindow.h"
20 #include "../shared/Utils.h"
21 #include <algorithm>
22 #include <fstream>
23 #include "../shared/MsHTMLUtils.h"
24
25 // it is taken from src/plugin/Resource.h
26 #define IDI_ICON_ENABLED 301
27
28 namespace {
29 // DIP = device independant pixels, as DPI is 96
30 const uint32_t kWindowWidth = 400 /*DIP*/;
31 const uint32_t kWindowHeight = 120 /*DIP*/;
32 const uint32_t kIconSize = 64 /*DIP*/;
33 const uint32_t kIconPadding = 10 /*DIP*/;
34
35 // offsets from boundaries of working area of screen
36 const uint32_t kWindowMarginRight = 50 /*DIP*/;
37 const uint32_t kWindowMarginBottom = 20 /*DIP*/;
38
39 std::vector<uint16_t> iconSizes = []()->std::vector<uint16_t>
40 {
41 std::vector<uint16_t> iconSizes;
42 iconSizes.emplace_back(16);
43 iconSizes.emplace_back(19);
44 iconSizes.emplace_back(48);
45 iconSizes.emplace_back(128);
46 iconSizes.emplace_back(256);
47 return iconSizes;
48 }();
49
50 class DCHandle
51 {
52 public:
53 explicit DCHandle(HDC hDC = nullptr) : m_hDC(hDC)
54 {
55 }
56
57 ~DCHandle()
58 {
59 if(m_hDC != nullptr)
60 {
61 ::DeleteDC(m_hDC);
62 m_hDC = nullptr;
63 }
64 }
65
66 operator HDC()
67 {
68 return m_hDC;
69 }
70
71 int GetDeviceCaps(int nIndex) const
72 {
73 ATLASSERT(m_hDC != nullptr);
74 return ::GetDeviceCaps(m_hDC, nIndex);
75 }
76
77 HBITMAP SelectBitmap(HBITMAP value)
78 {
79 return static_cast<HBITMAP>(::SelectObject(m_hDC, value));
80 }
81 private:
82 DCHandle(const DCHandle&);
83 DCHandle& operator=(const DCHandle&);
84 private:
85 HDC m_hDC;
86 };
87
88 /// Case insensitive pattern replacing function.
89 /// ReplaceMulti("Some TeXt <PlaceHolder>Some link</A> and text", "<placeholde r>", ->"<a>")->
90 /// "Some TeXt <a>Some link</A> and text".
91 std::wstring ReplaceMulti(const std::wstring& src, std::wstring placeholder, c onst std::function<std::wstring()>& replacementGenerator)
92 {
93 std::transform(placeholder.begin(), placeholder.end(), placeholder.begin(), ::towlower);
94 std::wstring srcLowerCase = src;
95 std::transform(srcLowerCase.begin(), srcLowerCase.end(), srcLowerCase.begin( ), ::towlower);
96 std::wstring retValue;
97 std::wstring::size_type placeHolderOffset = 0;
98 std::wstring::size_type nextStringChunkOffset = 0;
99 while ((placeHolderOffset = srcLowerCase.find(placeholder, nextStringChunkOf fset)) != std::wstring::npos)
100 {
101 retValue.append(src.substr(nextStringChunkOffset, placeHolderOffset - next StringChunkOffset));
102 retValue.append(replacementGenerator());
103 nextStringChunkOffset = placeHolderOffset + placeholder.length();
104 }
105 retValue.append(src.substr(nextStringChunkOffset));
106 return retValue;
107 }
108 std::wstring ReplaceMulti(const std::wstring& workingString, const std::wstrin g& templ, const std::wstring& replacement)
109 {
110 return ReplaceMulti(workingString, templ, [&replacement]()->std::wstring
111 {
112 return replacement;
113 });
114 }
115 }
116
117 NotificationWindow::NotificationWindow(const AdblockPlus::Notification& notifica tion, const std::wstring& htmlFileDir)
118 {
119 const std::wstring filePath = htmlFileDir + L"NotificationWindow.html";
120 std::wifstream ifs(filePath);
121 assert(ifs.good() && "Cannot open NotificationWindow.html file");
122 if (!ifs.good())
123 {
124 throw std::runtime_error("Cannot read NotificationWindow.html");
125 }
126 m_htmlPage.assign((std::istreambuf_iterator<wchar_t>(ifs)), std::istreambuf_it erator<wchar_t>());
127
128 m_links = ToUtf16Strings(notification.GetLinks());
129 auto body = ToUtf16String(notification.GetMessageString());
130 uint32_t linkIDCounter = 0;
131 body = ReplaceMulti(body, L"<a>", [this, &linkIDCounter]()->std::wstring
132 {
133 return L"<a href=\"#\" data-linkID=\"" + std::to_wstring(linkIDCounter++) + L"\">";
134 });
135 assert(linkIDCounter == m_links.size() && "The amount of links in the text is different from the amount of provided links");
136 m_htmlPage = ReplaceMulti(m_htmlPage, L"<!--Title-->", ToUtf16String(notificat ion.GetTitle()));
137 m_htmlPage = ReplaceMulti(m_htmlPage, L"<!--Body-->", body);
138 }
139
140 NotificationWindow::~NotificationWindow()
141 {
142 }
143
144 LRESULT NotificationWindow::OnCreate(const CREATESTRUCT* /*createStruct*/) {
145 {
146 DCHandle hdc(GetDC());
147 m_dpi = hdc.GetDeviceCaps(LOGPIXELSX);
148 }
149 m_bgColor.CreateSolidBrush(RGB(255, 255, 255));
150
151 CRect iconRect(CPoint(0, 0), CSize(kIconSize + 2 * kIconPadding, kIconSize + 2 * kIconPadding));
152 m_icon.Create(m_hWnd, DPIAware(iconRect), nullptr, WS_CHILD | WS_VISIBLE | SS_ BITMAP | SS_CENTERIMAGE);
153 LoadABPIcon();
154
155 m_axIE.Create(m_hWnd, DPIAware(CRect(CPoint(iconRect.right, 0), CSize(kWindowW idth - iconRect.right, kWindowHeight))),
156 L"", WS_CHILD | WS_VISIBLE, 0, kHTMLDocumentCtrlID);
157 m_axIE.CreateControl((L"mshtml:" + m_htmlPage).c_str());
158 ATL::CComPtr<IAxWinAmbientDispatch> axWinAmbient;
159 if (SUCCEEDED(m_axIE.QueryHost(&axWinAmbient))) {
160 // disable web browser context menu
161 axWinAmbient->put_AllowContextMenu(VARIANT_FALSE);
162 // make web browser DPI aware, so the browser itself sets zoom level and
163 // cares about rendering (not zooming) in the proper size.
164 DWORD docFlags;
165 axWinAmbient->get_DocHostFlags(&docFlags);
166 docFlags |= DOCHOSTUIFLAG_DPI_AWARE;
167 // remove DOCHOSTUIFLAG_SCROLL_NO, so it's scrollable
168 docFlags &= ~DOCHOSTUIFLAG_SCROLL_NO;
169 axWinAmbient->put_DocHostFlags(docFlags);
170 }
171 // kHTMLDocumentCtrlID works here
172 AtlAdviseSinkMap(this, true);
173
174 SetMsgHandled(false);
175 return 0;
176 }
177
178 LRESULT NotificationWindow::OnCtlColor(UINT /*msg*/, WPARAM /*wParam*/, LPARAM l Param, BOOL& handled)
179 {
180 if (reinterpret_cast<HWND>(lParam) != m_icon)
181 {
182 handled = FALSE;
183 }
184 return reinterpret_cast<LRESULT>(static_cast<HBRUSH>(m_bgColor));
185 }
186
187 LRESULT NotificationWindow::OnClick(UINT /*msg*/, WPARAM /*wParam*/, LPARAM /*lP aram*/, BOOL& /*handled*/)
188 {
189 if(m_onClickCallback)
190 m_onClickCallback();
191 return 0;
192 }
193
194 void NotificationWindow::OnSize(uint32_t wParam, CSize size)
195 {
196 if (m_icon)
197 {
198 CRect rect(CPoint(0, 0), DPIAware(CSize(kIconSize + 2 * kIconPadding, kIconS ize + 2 * kIconPadding)));
199 m_icon.SetWindowPos(0, &rect, SWP_NOMOVE);
200 }
201 if (m_axIE)
202 {
203 size.cx -= DPIAware(kIconSize + 2 * kIconPadding);
204 CRect rect(CPoint(0, 0), size);
205 m_axIE.SetWindowPos(0, &rect, SWP_NOMOVE);
206 }
207 SetMsgHandled(false);
208 }
209
210 void NotificationWindow::OnDestroy()
211 {
212 AtlAdviseSinkMap(this, false);
213 // and proceed as usual
214 SetMsgHandled(false);
215 }
216
217 void __stdcall NotificationWindow::OnHTMLDocumentClick(IHTMLEventObj* eventObjec t)
218 {
219 // stop propagating the event since it's handled by us and should not cause an y other actions.
220 if (!eventObject)
221 return;
222 eventObject->put_cancelBubble(VARIANT_TRUE);
223 eventObject->put_returnValue(ATL::CComVariant(false));
224 ATL::CComPtr<IHTMLElement> htmlElement;
225 if (FAILED(eventObject->get_srcElement(&htmlElement)) || !htmlElement) {
226 return;
227 }
228 ATL::CComBSTR tag;
229 htmlElement->get_tagName(&tag);
230 const wchar_t expectedTag[] = { L"a" };
231 if (_wcsnicmp(tag, expectedTag, min(sizeof(expectedTag), tag.Length())) != 0) {
232 return;
233 }
234 auto classAttr = GetHtmlElementAttribute(*htmlElement, ATL::CComBSTR(L"class") );
235 if (classAttr.attributeValue == L"closeButton")
236 {
237 if (m_onCloseCallback)
238 m_onCloseCallback();
239 return;
240 }
241 if (!m_onLinkClickedCallback)
242 {
243 return;
244 }
245 auto linkIDAttr = GetHtmlElementAttribute(*htmlElement, ATL::CComBSTR(L"data-l inkID"));
246 uint32_t linkID = 0;
247 if (!linkIDAttr.attributeValue.empty() && (linkID = std::stoi(linkIDAttr.attri buteValue)) < m_links.size())
248 {
249 m_onLinkClickedCallback(m_links[linkID]);
250 if (m_onCloseCallback)
251 m_onCloseCallback();
252 }
253 }
254
255 void __stdcall NotificationWindow::OnHTMLDocumentSelectStart(IHTMLEventObj* even tObject)
256 {
257 if (!eventObject)
258 return;
259 // disable selecting
260 eventObject->put_cancelBubble(VARIANT_TRUE);
261 eventObject->put_returnValue(ATL::CComVariant(false));
262 }
263
264 void NotificationWindow::LoadABPIcon()
265 {
266 ScopedModule m_adblockPlusDLL;
267 if (!(m_adblockPlusDLL.Open(L"AdblockPlus32.dll", LOAD_LIBRARY_AS_DATAFILE) ||
268 m_adblockPlusDLL.Open(L"AdblockPlus64.dll", LOAD_LIBRARY_AS_DATAFILE) ||
269 // for debug
270 m_adblockPlusDLL.Open(L"AdblockPlus.dll", LOAD_LIBRARY_AS_DATAFILE)))
271 {
272 return;
273 }
274 auto iconSizeIterator = lower_bound(iconSizes.begin(), iconSizes.end(), DPIAwa re(kIconSize));
275 if (iconSizeIterator == iconSizes.end())
276 {
277 iconSizeIterator = iconSizes.rbegin().base();
278 }
279
280 auto hIcon = static_cast<HICON>(::LoadImageW(m_adblockPlusDLL, (L"#" + std::to _wstring(IDI_ICON_ENABLED)).c_str(),
281 IMAGE_ICON, *iconSizeIterator, *iconSizeIterator, LR_SHARED));
282 if (hIcon == nullptr)
283 {
284 return;
285 }
286
287 HDC screenDC = m_icon.GetDC();
288 DCHandle tmpDC(::CreateCompatibleDC(screenDC));
289 ScopedObjectHandle<HBITMAP> bitmap;
290 bitmap = CreateCompatibleBitmap(screenDC, DPIAware(kIconSize), DPIAware(kIconS ize));
291 HBITMAP prevBitmap = tmpDC.SelectBitmap(bitmap);
292 CRect tmpRect(CPoint(0, 0), DPIAware(CSize(kIconSize, kIconSize)));
293 FillRect(tmpDC, &tmpRect, m_bgColor);
294 ::DrawIconEx(tmpDC, 0, 0, hIcon, tmpRect.Width(), tmpRect.Height(), 0, nullptr , DI_NORMAL);
295 m_iconImg.Attach(bitmap.Detach());
296 tmpDC.SelectBitmap(prevBitmap);
297 m_icon.SetBitmap(m_iconImg);
298 }
299
300 POINT NotificationBorderWindow::GetWindowCoordinates() {
301 HMONITOR primaryMonitor = MonitorFromWindow(m_hWnd, MONITOR_DEFAULTTOPRIMARY);
302 {
303 DCHandle hdc(GetDC());
304 m_dpi = hdc.GetDeviceCaps(LOGPIXELSX);
Oleksandr 2015/08/18 11:47:40 Is this still needed, seeing that we now get dpi i
sergei 2015/08/18 11:58:40 It's a little bit odd. `GetWindowCoordinates` is a
305 }
306 MONITORINFO monitorInfo = {};
307 monitorInfo.cbSize = sizeof(monitorInfo);
308 GetMonitorInfo(primaryMonitor, &monitorInfo);
309 int windowX = monitorInfo.rcWork.right - DPIAware(kWindowWidth + kWindowMargin Right);
310 int windowY = monitorInfo.rcWork.bottom - DPIAware(kWindowHeight + kWindowMarg inBottom);
311 POINT coords = {windowX, windowY};
312 return coords;
313 }
314
315 NotificationBorderWindow::NotificationBorderWindow(const AdblockPlus::Notificati on& notification, const std::wstring& htmlFileDir)
316 : m_content(notification, htmlFileDir)
317 {
318 m_content.SetOnClick([this]
319 {
320 PostMessage(WM_CLOSE);
321 });
322 m_content.SetOnClose([this]
323 {
324 PostMessage(WM_CLOSE);
325 });
326 }
327
328 LRESULT NotificationBorderWindow::OnCreate(const CREATESTRUCT* createStruct)
329 {
330 auto windowCoords = GetWindowCoordinates();
331 MoveWindow(windowCoords.x, windowCoords.y, DPIAware(kWindowWidth), DPIAware(kW indowHeight));
332
333 RECT clientRect;
334 GetClientRect(&clientRect);
335 // make one pixel border
336 clientRect.top += 1;
337 clientRect.left += 1;
338 clientRect.bottom -= 1;
339 clientRect.right -= 1;
340 m_content.Create(m_hWnd, clientRect, nullptr, WS_CHILD | WS_VISIBLE);
341 auto err = GetLastError();
342 SetMsgHandled(false);
343 return 0;
344 }
345
346 void NotificationBorderWindow::OnSize(uint32_t wParam, CSize size)
347 {
348 if (m_content.IsWindow())
349 {
350 RECT clientRect;
351 GetClientRect(&clientRect);
352 clientRect.top += 1;
353 clientRect.left += 1;
354 clientRect.bottom -= 1;
355 clientRect.right -= 1;
356 m_content.MoveWindow(&clientRect);
357 }
358 SetMsgHandled(false);
359 }
360
361 LRESULT NotificationBorderWindow::OnClick(UINT /*msg*/, WPARAM /*wParam*/, LPARA M /*lParam*/, BOOL& /*handled*/)
362 {
363 DestroyWindow();
364 return 0;
365 }
366
367 void NotificationBorderWindow::OnFinalMessage(HWND) {
368 if (!!m_onDestroyedCallback) {
369 m_onDestroyedCallback();
370 }
371 }
OLDNEW

Powered by Google App Engine
This is Rietveld