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: improve ReplaceMulti and change loading of html template Created June 29, 2015, 11:09 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
« no previous file with comments | « src/engine/NotificationWindow.h ('k') | src/plugin/AdblockPlusClient.cpp » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 /// Case insensitive pattern replacing function.
71 /// ReplaceMulti("Some TeXt <PlaceHolder>Some link</A> and text", "<placeholde r>", ->"<a>")->
72 /// "Some TeXt <a>Some link</A> and text".
73 std::wstring ReplaceMulti(const std::wstring& src, std::wstring placeholder, c onst std::function<std::wstring()>& replacementGenerator)
74 {
75 std::transform(placeholder.begin(), placeholder.end(), placeholder.begin(), ::towlower);
76 std::wstring srcLowerCase = src;
77 std::transform(srcLowerCase.begin(), srcLowerCase.end(), srcLowerCase.begin( ), ::towlower);
78 std::wstring retValue;
79 std::wstring::size_type placeHolderOffset = 0;
80 std::wstring::size_type nextStringChunkOffset = 0;
81 while ((placeHolderOffset = srcLowerCase.find(placeholder, nextStringChunkOf fset)) != std::wstring::npos)
82 {
83 retValue.append(src.substr(nextStringChunkOffset, placeHolderOffset - next StringChunkOffset));
84 retValue.append(replacementGenerator());
85 nextStringChunkOffset = placeHolderOffset + placeholder.length();
86 }
87 retValue.append(src.substr(nextStringChunkOffset));
88 return retValue;
89 }
90 std::wstring ReplaceMulti(const std::wstring& workingString, const std::wstrin g& templ, const std::wstring& replacement)
91 {
92 return ReplaceMulti(workingString, templ, [&replacement]()->std::wstring
93 {
94 return replacement;
95 });
96 }
97 }
98
99 NotificationWindow::NotificationWindow(const AdblockPlus::Notification& notifica tion, const std::wstring& htmlFileDir)
100 : m_dpi(96)
101 {
102 const std::wstring filePath = htmlFileDir + L"NotificationWindow.html";
103 std::wifstream ifs(filePath);
104 assert(ifs.good() && "Cannot open NotificationWindow.html file");
105 if (!ifs.good())
106 {
107 throw std::runtime_error("Cannot read NotificationWindow.html");
108 }
109 m_htmlPage.assign((std::istreambuf_iterator<wchar_t>(ifs)), std::istreambuf_it erator<wchar_t>());
110
111 m_links = ToUtf16Strings(notification.GetLinks());
112 auto body = ToUtf16String(notification.GetMessageString());
113 uint32_t linkIDCounter = 0;
114 body = ReplaceMulti(body, L"<a>", [this, &linkIDCounter]()->std::wstring
115 {
116 return L"<a href=\"#\" data-linkID=\"" + std::to_wstring(linkIDCounter++) + L"\">";
117 });
118 assert(linkIDCounter == m_links.size() && "The amount of links in the text is different from the amount of provided links");
119 m_htmlPage = ReplaceMulti(m_htmlPage, L"<!--Title-->", ToUtf16String(notificat ion.GetTitle()));
120 m_htmlPage = ReplaceMulti(m_htmlPage, L"<!--Body-->", body);
121 }
122
123 NotificationWindow::~NotificationWindow()
124 {
125 }
126
127 LRESULT NotificationWindow::OnCreate(const CREATESTRUCT* /*createStruct*/) {
128 m_bgColor.CreateSolidBrush(RGB(255, 255, 255));
129 auto windowCoords = GetWindowCoordinates();
130 MoveWindow(windowCoords.x, windowCoords.y, DPIAware(kWindowWidth), DPIAware(kW indowHeight));
131
132 m_icon.Create(m_hWnd,
133 DPIAware(CRect(CPoint(0, 0), CSize(kIconSize, kIconSize))),
134 nullptr, WS_CHILD | WS_VISIBLE | SS_BITMAP | SS_CENTERIMAGE);
135 LoadABPIcon();
136
137 m_axIE.Create(m_hWnd, DPIAware(CRect(CPoint(kIconSize, 0), CSize(kWindowWidth - kIconSize, kWindowHeight))),
138 L"", WS_CHILD | WS_VISIBLE, 0, kHTMLDocumentCtrlID);
139 m_axIE.CreateControl((L"mshtml:" + m_htmlPage).c_str());
140 ATL::CComPtr<IAxWinAmbientDispatch> axWinAmbient;
141 if (SUCCEEDED(m_axIE.QueryHost(&axWinAmbient))) {
142 // disable web browser context menu
143 axWinAmbient->put_AllowContextMenu(VARIANT_FALSE);
144 // make web browser DPI aware, so the browser itself sets zoom level and
145 // cares about rendering (not zooming) in the proper size.
146 DWORD docFlags;
147 axWinAmbient->get_DocHostFlags(&docFlags);
148 docFlags |= DOCHOSTUIFLAG_DPI_AWARE;
149 // remove DOCHOSTUIFLAG_SCROLL_NO, so it's scrollable
150 docFlags &= ~DOCHOSTUIFLAG_SCROLL_NO;
151 axWinAmbient->put_DocHostFlags(docFlags);
152 }
153 // kHTMLDocumentCtrlID works here
154 AtlAdviseSinkMap(this, true);
155
156 SetMsgHandled(false);
157 return 0;
158 }
159
160 LRESULT NotificationWindow::OnCtlColor(UINT /*msg*/, WPARAM /*wParam*/, LPARAM l Param, BOOL& handled)
161 {
162 if (reinterpret_cast<HWND>(lParam) != m_icon)
163 {
164 handled = FALSE;
165 }
166 return reinterpret_cast<LRESULT>(static_cast<HBRUSH>(m_bgColor));
167 }
168
169 LRESULT NotificationWindow::OnClick(UINT /*msg*/, WPARAM /*wParam*/, LPARAM /*lP aram*/, BOOL& /*handled*/)
170 {
171 DestroyWindow();
172 return 0;
173 }
174
175 void NotificationWindow::OnSize(uint32_t wParam, CSize size)
176 {
177 if (m_axIE)
178 {
179 size.cx -= kIconSize;
180 CRect rect(CPoint(0, 0), size);
181 m_axIE.SetWindowPos(0, &rect, SWP_NOMOVE);
182 }
183 SetMsgHandled(false);
184 }
185
186 void NotificationWindow::OnDestroy()
187 {
188 AtlAdviseSinkMap(this, false);
189 // and proceed as usual
190 SetMsgHandled(false);
191 }
192
193 void __stdcall NotificationWindow::OnHTMLDocumentClick(IHTMLEventObj* eventObjec t)
194 {
195 // stop propagating the event since it's handled by us and should not cause an y other actions.
196 if (!eventObject)
197 return;
198 eventObject->put_cancelBubble(VARIANT_TRUE);
199 eventObject->put_returnValue(ATL::CComVariant(false));
200 ATL::CComPtr<IHTMLElement> htmlElement;
201 if (FAILED(eventObject->get_srcElement(&htmlElement)) || !htmlElement) {
202 return;
203 }
204 ATL::CComBSTR tag;
205 htmlElement->get_tagName(&tag);
206 const wchar_t expectedTag[] = { L"a" };
207 if (_wcsnicmp(tag, expectedTag, min(sizeof(expectedTag), tag.Length())) != 0) {
208 return;
209 }
210 auto classAttr = GetHtmlElementAttribute(*htmlElement, ATL::CComBSTR(L"class") );
211 if (classAttr.attributeValue == L"closeButton")
212 {
213 PostMessage(WM_CLOSE);
214 return;
215 }
216 if (!linkClicked)
217 {
218 return;
219 }
220 auto linkIDAttr = GetHtmlElementAttribute(*htmlElement, ATL::CComBSTR(L"data-l inkID"));
221 uint32_t linkID = 0;
222 if (!linkIDAttr.attributeValue.empty() && (linkID = std::stoi(linkIDAttr.attri buteValue)) < m_links.size())
223 {
224 linkClicked(m_links[linkID]);
225 PostMessage(WM_CLOSE);
226 }
227 }
228
229 void __stdcall NotificationWindow::OnHTMLDocumentSelectStart(IHTMLEventObj* even tObject)
230 {
231 if (!eventObject)
232 return;
233 // disable selecting
234 eventObject->put_cancelBubble(VARIANT_TRUE);
235 eventObject->put_returnValue(ATL::CComVariant(false));
236 }
237
238 void NotificationWindow::OnFinalMessage(HWND) {
239 if (!!destroyed) {
240 destroyed();
241 }
242 }
243
244 void NotificationWindow::LoadABPIcon()
245 {
246 ScopedModule m_adblockPlusDLL;
247 if (!(m_adblockPlusDLL.Open(L"AdblockPlus32.dll", LOAD_LIBRARY_AS_DATAFILE) ||
248 m_adblockPlusDLL.Open(L"AdblockPlus64.dll", LOAD_LIBRARY_AS_DATAFILE) ||
249 // for debug
250 m_adblockPlusDLL.Open(L"AdblockPlus.dll", LOAD_LIBRARY_AS_DATAFILE)))
251 {
252 return;
253 }
254 auto iconSizeIterator = lower_bound(iconSizes.begin(), iconSizes.end(), DPIAwa re(kIconSize));
255 if (iconSizeIterator == iconSizes.end())
256 {
257 iconSizeIterator = iconSizes.rbegin().base();
258 }
259
260 auto hIcon = static_cast<HICON>(::LoadImageW(m_adblockPlusDLL, (L"#" + std::to _wstring(IDI_ICON_ENABLED)).c_str(),
261 IMAGE_ICON, *iconSizeIterator, *iconSizeIterator, LR_SHARED));
262 if (hIcon == nullptr)
263 {
264 return;
265 }
266
267 HDC screenDC = m_icon.GetDC();
268 DCHandle tmpDC(::CreateCompatibleDC(screenDC));
269 ScopedObjectHandle<HBITMAP> bitmap;
270 bitmap = CreateCompatibleBitmap(screenDC, DPIAware(kIconSize), DPIAware(kIconS ize));
271 HBITMAP prevBitmap = tmpDC.SelectBitmap(bitmap);
272 RECT tmpRect = {0, 0, DPIAware(kIconSize), DPIAware(kIconSize)};
273 FillRect(tmpDC, &tmpRect, m_bgColor);
274 ::DrawIconEx(tmpDC, 0, 0, hIcon, DPIAware(kIconSize), DPIAware(kIconSize), 0, nullptr, DI_NORMAL);
275 m_iconImg.Attach(bitmap.Detach());
276 tmpDC.SelectBitmap(prevBitmap);
277 m_icon.SetBitmap(m_iconImg);
278 }
279
280 POINT NotificationWindow::GetWindowCoordinates() {
281 HMONITOR primaryMonitor = MonitorFromWindow(m_hWnd, MONITOR_DEFAULTTOPRIMARY);
282 {
283 DCHandle hdc(GetDC());
284 m_dpi = hdc.GetDeviceCaps(LOGPIXELSX);
285 }
286 MONITORINFO monitorInfo = {};
287 monitorInfo.cbSize = sizeof(monitorInfo);
288 GetMonitorInfo(primaryMonitor, &monitorInfo);
289 int windowX = monitorInfo.rcWork.right - DPIAware(kWindowWidth + kWindowMargin Right);
290 int windowY = monitorInfo.rcWork.bottom - DPIAware(kWindowHeight + kWindowMarg inBottom);
291 POINT coords = {windowX, windowY};
292 return coords;
293 }
OLDNEW
« no previous file with comments | « src/engine/NotificationWindow.h ('k') | src/plugin/AdblockPlusClient.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld