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: Created May 11, 2015, 10:01 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 #include <cassert>
2 #include "NotificationWindow.h"
3 #include "../shared/Utils.h"
4 #include <algorithm>
5 #include <fstream>
6 #include "../shared/MsHTMLUtils.h"
7
8 // it is taken from src/plugin/Resource.h
9 #define IDI_ICON_ENABLED 301
10
11 namespace {
12 // DIP = device independant pixels, as DPI is 96
13 const uint32_t kWindowWidth = 400 /*DIP*/;
14 const uint32_t kWindowHeight = 120 /*DIP*/;
15 const uint32_t kIconSize = 64 /*DIP*/;
16
17 // offsets from boundaries of working area of screen
18 const uint32_t kWindowMarginRight = 50 /*DIP*/;
19 const uint32_t kWindowMarginBottom = 20 /*DIP*/;
20
21 std::vector<uint16_t> iconSizes = []()->std::vector<uint16_t>
22 {
23 std::vector<uint16_t> iconSizes;
24 iconSizes.emplace_back(16);
25 iconSizes.emplace_back(19);
26 iconSizes.emplace_back(48);
27 iconSizes.emplace_back(128);
28 iconSizes.emplace_back(256);
29 return iconSizes;
30 }();
31
32 class DCHandle
33 {
34 public:
35 explicit DCHandle(HDC hDC = nullptr) : m_hDC(hDC)
36 {
37 }
38
39 ~DCHandle()
40 {
41 if(m_hDC != nullptr)
42 {
43 ::DeleteDC(m_hDC);
44 m_hDC = nullptr;
45 }
46 }
47
48 operator HDC()
49 {
50 return m_hDC;
51 }
52
53 int GetDeviceCaps(int nIndex) const
54 {
55 ATLASSERT(m_hDC != nullptr);
56 return ::GetDeviceCaps(m_hDC, nIndex);
57 }
58
59 HBITMAP SelectBitmap(HBITMAP value)
60 {
61 return static_cast<HBITMAP>(::SelectObject(m_hDC, value));
62 }
63 private:
64 DCHandle(const DCHandle&);
65 DCHandle& operator=(const DCHandle&);
66 private:
67 HDC m_hDC;
68 };
69
70 std::wstring ReplaceMulti(std::wstring workingString, const std::wstring& plac eholder, const std::function<std::wstring()>& replacementGenerator)
71 {
72 std::wstring::size_type i = 0;
73 while ((i = workingString.find(placeholder, i)) != std::wstring::npos)
74 {
75 const std::wstring dst = replacementGenerator();
76 workingString.replace(i, placeholder.length(), dst);
77 // tiny optimisation to skip already processed part of the string
78 i += dst.length();
79 }
80 return workingString;
81 }
82 std::wstring ReplaceMulti(std::wstring workingString, const std::wstring& temp l, const std::wstring& replacement)
83 {
84 return ReplaceMulti(workingString, templ, [&replacement]()->std::wstring
85 {
86 return replacement;
87 });
88 }
89 }
90
91 NotificationWindow::NotificationWindow(const AdblockPlus::Notification& notifica tion, const std::wstring& htmlFileDir)
92 : m_dpi(96)
93 {
94 m_htmlPage = [](const std::wstring& filePath)->std::wstring
95 {
96 std::wifstream ifs(filePath);
97 assert(ifs.good() && "Cannot open NotificationWindow.html file");
98 if (!ifs.good())
99 {
100 throw std::runtime_error("Cannot read NotificationWindow.html");
101 }
102 std::wstring fileContent;
103 fileContent.assign((std::istreambuf_iterator<wchar_t>(ifs)), std::istreambuf _iterator<wchar_t>());
104 return fileContent;
105 }(htmlFileDir + L"\\NotificationWindow.html");
106
107 m_links = ToUtf16Strings(notification.GetLinks());
108 auto body = ToUtf16String(notification.GetMessageString());
109 uint32_t linkIDCounter = 0;
110 body = ReplaceMulti(body, L"<a>", [this, &linkIDCounter]()->std::wstring
111 {
112 return L"<a href=\"#\" data-linkID=\"" + std::to_wstring(linkIDCounter++) + L"\">";
113 });
114 assert(linkIDCounter == m_links.size() && "The amount of links in the text is different from the amount of provided links");
115 m_htmlPage = ReplaceMulti(m_htmlPage, L"<!--Title-->", ToUtf16String(notificat ion.GetTitle()));
116 m_htmlPage = ReplaceMulti(m_htmlPage, L"<!--Body-->", body);
117 }
118
119 NotificationWindow::~NotificationWindow()
120 {
121 }
122
123 LRESULT NotificationWindow::OnCreate(const CREATESTRUCT* /*createStruct*/) {
124 m_bgColor.CreateSolidBrush(RGB(255, 255, 255));
125 auto windowCoords = GetWindowCoordinates();
126 MoveWindow(windowCoords.x, windowCoords.y, DPIAware(kWindowWidth), DPIAware(kW indowHeight));
127
128 m_icon.Create(m_hWnd,
129 DPIAware(CRect(CPoint(0, 0), CSize(kIconSize, kIconSize))),
130 nullptr, WS_CHILD | WS_VISIBLE | SS_BITMAP | SS_CENTERIMAGE);
131 LoadABPIcon();
132
133 m_axIE.Create(m_hWnd, DPIAware(CRect(CPoint(kIconSize, 0), CSize(kWindowWidth - kIconSize, kWindowHeight))),
134 L"", WS_CHILD | WS_VISIBLE, 0, kHTMLDocumentCtrlID);
135 m_axIE.CreateControl((L"mshtml:" + m_htmlPage).c_str());
136 ATL::CComPtr<IAxWinAmbientDispatch> axWinAmbient;
137 if (SUCCEEDED(m_axIE.QueryHost(&axWinAmbient))) {
138 // disable web browser context menu
139 axWinAmbient->put_AllowContextMenu(VARIANT_FALSE);
140 // make web browser DPI aware, so the browser itself sets zoom level and
141 // cares about rendering (not zooming) in the proper size.
142 DWORD docFlags;
143 axWinAmbient->get_DocHostFlags(&docFlags);
144 docFlags |= DOCHOSTUIFLAG_DPI_AWARE;
145 // remove DOCHOSTUIFLAG_SCROLL_NO, so it's scrollable
146 docFlags &= ~DOCHOSTUIFLAG_SCROLL_NO;
147 axWinAmbient->put_DocHostFlags(docFlags);
148 }
149 // kHTMLDocumentCtrlID works here
150 AtlAdviseSinkMap(this, true);
151
152 SetMsgHandled(false);
153 return 0;
154 }
155
156 LRESULT NotificationWindow::OnCtlColor(UINT /*msg*/, WPARAM /*wParam*/, LPARAM l Param, BOOL& handled)
157 {
158 if (reinterpret_cast<HWND>(lParam) != m_icon)
159 {
160 handled = FALSE;
161 }
162 return reinterpret_cast<LRESULT>(static_cast<HBRUSH>(m_bgColor));
163 }
164
165 LRESULT NotificationWindow::OnClick(UINT /*msg*/, WPARAM /*wParam*/, LPARAM /*lP aram*/, BOOL& /*handled*/)
166 {
167 DestroyWindow();
168 return 0;
169 }
170
171 void NotificationWindow::OnSize(uint32_t wParam, CSize size)
172 {
173 if (m_axIE)
174 {
175 size.cx -= kIconSize;
176 CRect rect(CPoint(0, 0), size);
177 m_axIE.SetWindowPos(0, &rect, SWP_NOMOVE);
178 }
179 SetMsgHandled(false);
180 }
181
182 void NotificationWindow::OnDestroy()
183 {
184 AtlAdviseSinkMap(this, false);
185 // and proceed as usual
186 SetMsgHandled(false);
187 }
188
189 void __stdcall NotificationWindow::OnHTMLDocumentClick(IHTMLEventObj* eventObjec t)
190 {
191 // stop propagating the event since it's handled by us and should not cause an y other actions.
192 if (!eventObject)
193 return;
194 eventObject->put_cancelBubble(VARIANT_TRUE);
195 eventObject->put_returnValue(ATL::CComVariant(false));
196 ATL::CComPtr<IHTMLElement> htmlElement;
197 if (FAILED(eventObject->get_srcElement(&htmlElement)) || !htmlElement) {
198 return;
199 }
200 ATL::CComBSTR tag;
201 htmlElement->get_tagName(&tag);
202 const wchar_t expectedTag[] = { L"a" };
203 if (_wcsnicmp(tag, expectedTag, min(sizeof(expectedTag), tag.Length())) != 0) {
204 return;
205 }
206 auto classAttr = GetHtmlElementAttribute(*htmlElement, ATL::CComBSTR(L"class") );
207 if (classAttr.attributeValue == L"closeButton")
208 {
209 PostMessage(WM_CLOSE);
210 return;
211 }
212 if (!linkClicked)
213 {
214 return;
215 }
216 auto linkIDAttr = GetHtmlElementAttribute(*htmlElement, ATL::CComBSTR(L"data-l inkID"));
217 uint32_t linkID = 0;
218 if (!linkIDAttr.attributeValue.empty() && (linkID = std::stoi(linkIDAttr.attri buteValue)) < m_links.size())
219 {
220 linkClicked(m_links[linkID]);
221 }
222 }
223
224 void __stdcall NotificationWindow::OnHTMLDocumentSelectStart(IHTMLEventObj* even tObject)
225 {
226 if (!eventObject)
227 return;
228 // disable selecting
229 eventObject->put_cancelBubble(VARIANT_TRUE);
230 eventObject->put_returnValue(ATL::CComVariant(false));
231 }
232
233 void NotificationWindow::OnFinalMessage(HWND) {
234 if (!!destroyed) {
235 destroyed();
236 }
237 }
238
239 void NotificationWindow::LoadABPIcon()
240 {
241 ScopedModule m_adblockPlusDLL;
242 if (!(m_adblockPlusDLL.Open(L"AdblockPlus32.dll", LOAD_LIBRARY_AS_DATAFILE) ||
243 m_adblockPlusDLL.Open(L"AdblockPlus64.dll", LOAD_LIBRARY_AS_DATAFILE) ||
244 // for debug
245 m_adblockPlusDLL.Open(L"AdblockPlus.dll", LOAD_LIBRARY_AS_DATAFILE)))
246 {
247 return;
248 }
249 auto iconSizeIterator = lower_bound(iconSizes.begin(), iconSizes.end(), DPIAwa re(kIconSize));
250 if (iconSizeIterator == iconSizes.end())
251 {
252 iconSizeIterator = iconSizes.rbegin().base();
253 }
254
255 auto hIcon = static_cast<HICON>(::LoadImageW(m_adblockPlusDLL, (L"#" + std::to _wstring(IDI_ICON_ENABLED)).c_str(),
256 IMAGE_ICON, *iconSizeIterator, *iconSizeIterator, LR_SHARED));
257 if (hIcon == nullptr)
258 {
259 return;
260 }
261
262 HDC screenDC = m_icon.GetDC();
263 DCHandle tmpDC(::CreateCompatibleDC(screenDC));
264 ScopedObjectHandle<HBITMAP> bitmap;
265 bitmap = CreateCompatibleBitmap(screenDC, DPIAware(kIconSize), DPIAware(kIconS ize));
266 HBITMAP prevBitmap = tmpDC.SelectBitmap(bitmap);
267 RECT tmpRect = {0, 0, DPIAware(kIconSize), DPIAware(kIconSize)};
268 FillRect(tmpDC, &tmpRect, m_bgColor);
269 ::DrawIconEx(tmpDC, 0, 0, hIcon, DPIAware(kIconSize), DPIAware(kIconSize), 0, nullptr, DI_NORMAL);
270 m_iconImg.Attach(bitmap.Detach());
271 tmpDC.SelectBitmap(prevBitmap);
272 m_icon.SetBitmap(m_iconImg);
273 }
274
275 POINT NotificationWindow::GetWindowCoordinates() {
276 HMONITOR primaryMonitor = MonitorFromWindow(m_hWnd, MONITOR_DEFAULTTOPRIMARY);
277 {
278 DCHandle hdc(GetDC());
279 m_dpi = hdc.GetDeviceCaps(LOGPIXELSX);
280 }
281 MONITORINFO monitorInfo = {};
282 monitorInfo.cbSize = sizeof(monitorInfo);
283 GetMonitorInfo(primaryMonitor, &monitorInfo);
284 int windowX = monitorInfo.rcWork.right - DPIAware(kWindowWidth + kWindowMargin Right);
285 int windowY = monitorInfo.rcWork.bottom - DPIAware(kWindowHeight + kWindowMarg inBottom);
286 POINT coords = {windowX, windowY};
287 return coords;
288 }
OLDNEW

Powered by Google App Engine
This is Rietveld