1 // Copyright 2010-2018, Google Inc.
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
6 // met:
7 //
8 // * Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 // * Redistributions in binary form must reproduce the above
11 // copyright notice, this list of conditions and the following disclaimer
12 // in the documentation and/or other materials provided with the
13 // distribution.
14 // * Neither the name of Google Inc. nor the names of its
15 // contributors may be used to endorse or promote products derived from
16 // this software without specific prior written permission.
17 //
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30 #include "renderer/win32/infolist_window.h"
31
32 #include <windows.h>
33
34 #include <sstream>
35
36 #include "base/coordinates.h"
37 #include "base/logging.h"
38 #include "base/util.h"
39 #include "client/client_interface.h"
40 #include "protocol/renderer_command.pb.h"
41 #include "protocol/renderer_style.pb.h"
42 #include "renderer/renderer_style_handler.h"
43 #include "renderer/table_layout.h"
44 #include "renderer/win32/text_renderer.h"
45 #include "renderer/win_resource.h"
46
47 namespace mozc {
48 namespace renderer {
49 namespace win32 {
50
51 using WTL::CBitmap;
52 using WTL::CDC;
53 using WTL::CDCHandle;
54 using WTL::CMemoryDC;
55 using WTL::CPaintDC;
56 using WTL::CPenHandle;
57 using WTL::CPoint;
58 using WTL::CRect;
59 using WTL::CSize;
60
61 using mozc::commands::Candidates;
62 using mozc::commands::Information;
63 using mozc::commands::InformationList;
64 using mozc::commands::Output;
65 using mozc::commands::SessionCommand;
66 using mozc::renderer::RendererStyle;
67 using mozc::renderer::RendererStyleHandler;
68
69 namespace {
70 const COLORREF kDefaultBackgroundColor = RGB(0xff, 0xff, 0xff);
71 const UINT_PTR kIdDelayShowHideTimer = 100;
72
SendUsageStatsEvent(client::SendCommandInterface * command_sender,const SessionCommand::UsageStatsEvent & event)73 bool SendUsageStatsEvent(client::SendCommandInterface *command_sender,
74 const SessionCommand::UsageStatsEvent &event) {
75 if (command_sender == nullptr) {
76 return false;
77 }
78 SessionCommand command;
79 command.set_type(SessionCommand::USAGE_STATS_EVENT);
80 command.set_usage_stats_event(event);
81 VLOG(2) << "SendUsageStatsEvent " << command.DebugString();
82 Output dummy_output;
83 return command_sender->SendCommand(command, &dummy_output);
84 }
85 } // namespace
86
87
88 // ------------------------------------------------------------------------
89 // InfolistWindow
90 // ------------------------------------------------------------------------
91
InfolistWindow()92 InfolistWindow::InfolistWindow()
93 : candidates_(new commands::Candidates),
94 metrics_changed_(false),
95 text_renderer_(TextRenderer::Create()),
96 style_(new RendererStyle),
97 visible_(false),
98 send_command_interface_(nullptr) {
99 mozc::renderer::RendererStyleHandler::GetRendererStyle(style_.get());
100 }
101
~InfolistWindow()102 InfolistWindow::~InfolistWindow() {}
103
OnDestroy()104 void InfolistWindow::OnDestroy() {
105 // PostQuitMessage may stop the message loop even though other
106 // windows are not closed. WindowManager should close these windows
107 // before process termination.
108 ::PostQuitMessage(0);
109 }
110
OnEraseBkgnd(CDCHandle dc)111 BOOL InfolistWindow::OnEraseBkgnd(CDCHandle dc) {
112 // We do not have to erase background
113 // because all pixels in client area will be drawn in the DoPaint method.
114 return TRUE;
115 }
116
OnGetMinMaxInfo(MINMAXINFO * min_max_info)117 void InfolistWindow::OnGetMinMaxInfo(MINMAXINFO *min_max_info) {
118 // Do not restrict the window size in case the candidate window must be
119 // very small size.
120 min_max_info->ptMinTrackSize.x = 1;
121 min_max_info->ptMinTrackSize.y = 1;
122 SetMsgHandled(TRUE);
123 }
124
OnPaint(CDCHandle dc)125 void InfolistWindow::OnPaint(CDCHandle dc) {
126 CRect client_rect;
127 this->GetClientRect(&client_rect);
128
129 if (dc != nullptr) {
130 CMemoryDC memdc(dc, client_rect);
131 DoPaint(memdc.m_hDC);
132 } else {
133 CPaintDC paint_dc(this->m_hWnd);
134 { // Create a copy of |paint_dc| and render the candidate strings in it.
135 // The image rendered to this |memdc| is to be copied into the original
136 // |paint_dc| in its destructor. So, we don't have to explicitly call
137 // any functions that copy this |memdc| to the |paint_dc| but putting
138 // the following code into a local block.
139 CMemoryDC memdc(paint_dc, client_rect);
140 DoPaint(memdc.m_hDC);
141 }
142 }
143 }
144
OnPrintClient(CDCHandle dc,UINT uFlags)145 void InfolistWindow::OnPrintClient(CDCHandle dc, UINT uFlags) {
146 OnPaint(dc);
147 }
148
DoPaint(CDCHandle dc)149 Size InfolistWindow::DoPaint(CDCHandle dc) {
150 if (dc.m_hDC != nullptr) {
151 dc.SetBkMode(TRANSPARENT);
152 }
153 const RendererStyle::InfolistStyle &infostyle = style_->infolist_style();
154 const InformationList &usages = candidates_->usages();
155
156 int ypos = infostyle.window_border();
157
158 if ((dc.m_hDC != nullptr) && infostyle.has_caption_string()) {
159 const RendererStyle::TextStyle &caption_style =
160 infostyle.caption_style();
161 const int caption_height = infostyle.caption_height();
162 const Rect backgrounnd_rect(infostyle.window_border(), ypos,
163 infostyle.window_width() - infostyle.window_border() * 2,
164 caption_height);
165 const CRect background_crect(
166 backgrounnd_rect.Left(), backgrounnd_rect.Top(),
167 backgrounnd_rect.Right(), backgrounnd_rect.Bottom());
168
169 dc.FillSolidRect(&background_crect,
170 RGB(infostyle.caption_background_color().r(),
171 infostyle.caption_background_color().g(),
172 infostyle.caption_background_color().b()));
173
174 std::wstring caption_str;
175 const Rect caption_rect(
176 infostyle.window_border() + infostyle.caption_padding()
177 + caption_style.left_padding(),
178 ypos + infostyle.caption_padding(),
179 infostyle.window_width() - infostyle.window_border() * 2,
180 caption_height);
181 mozc::Util::UTF8ToWide(infostyle.caption_string(), &caption_str);
182
183 text_renderer_->RenderText(dc,
184 caption_str,
185 caption_rect,
186 TextRenderer::FONTSET_INFOLIST_CAPTION);
187 }
188 ypos += infostyle.caption_height();
189
190 for (int i = 0; i < usages.information_size(); ++i) {
191 Size size = DoPaintRow(dc, i, ypos);
192 ypos += size.height;
193 }
194 ypos += infostyle.window_border();
195
196 if (dc.m_hDC != nullptr) {
197 const CRect rect(0, 0, infostyle.window_width(), ypos);
198 dc.SetDCBrushColor(
199 RGB(infostyle.border_color().r(),
200 infostyle.border_color().g(),
201 infostyle.border_color().b()));
202 dc.FrameRect(&rect,
203 static_cast<HBRUSH>(GetStockObject(DC_BRUSH)));
204 }
205
206
207 return Size(style_->infolist_style().window_width(), ypos);
208 }
209
DoPaintRow(CDCHandle dc,int row,int ypos)210 Size InfolistWindow::DoPaintRow(CDCHandle dc, int row, int ypos) {
211 const RendererStyle::InfolistStyle &infostyle = style_->infolist_style();
212 const InformationList &usages = candidates_->usages();
213 const RendererStyle::TextStyle &title_style = infostyle.title_style();
214 const RendererStyle::TextStyle &desc_style = infostyle.description_style();
215 const int title_width = infostyle.window_width() -
216 title_style.left_padding() - title_style.right_padding() -
217 infostyle.window_border() * 2 -
218 infostyle.row_rect_padding() * 2;
219 const int desc_width = infostyle.window_width() -
220 desc_style.left_padding() - desc_style.right_padding() -
221 infostyle.window_border() * 2 -
222 infostyle.row_rect_padding() * 2;
223 const Information &info = usages.information(row);
224
225 std::wstring title_str;
226 mozc::Util::UTF8ToWide(info.title(), &title_str);
227 const Size title_size = text_renderer_->MeasureStringMultiLine(
228 TextRenderer::FONTSET_INFOLIST_TITLE, title_str, title_width);
229
230 std::wstring desc_str;
231 mozc::Util::UTF8ToWide(info.description(), &desc_str);
232 const Size desc_size = text_renderer_->MeasureStringMultiLine(
233 TextRenderer::FONTSET_INFOLIST_DESCRIPTION, desc_str, desc_width);
234
235 int row_height = title_size.height + desc_size.height +
236 infostyle.row_rect_padding() * 2;
237
238 if (dc.m_hDC == nullptr) {
239 return Size(0, row_height);
240 }
241 const Rect title_rect(
242 infostyle.window_border() + infostyle.row_rect_padding() +
243 title_style.left_padding(),
244 ypos + infostyle.row_rect_padding(),
245 title_width, title_size.height);
246 const Rect desc_rect(
247 infostyle.window_border() + infostyle.row_rect_padding() +
248 desc_style.left_padding(),
249 ypos + infostyle.row_rect_padding() + title_rect.size.height,
250 desc_width, desc_size.height);
251
252 const CRect title_back_crect(infostyle.window_border(), ypos,
253 infostyle.window_width() - infostyle.window_border(),
254 ypos + title_rect.size.height + infostyle.row_rect_padding());
255
256 const CRect desc_back_crect(infostyle.window_border(),
257 ypos + title_rect.size.height + infostyle.row_rect_padding(),
258 infostyle.window_width() - infostyle.window_border(),
259 ypos + title_rect.size.height + infostyle.row_rect_padding() +
260 desc_rect.size.height + infostyle.row_rect_padding());
261
262 if (usages.has_focused_index() && (row == usages.focused_index())) {
263 const CRect selected_rect(infostyle.window_border(), ypos,
264 infostyle.window_width() - infostyle.window_border(),
265 ypos + title_rect.size.height + desc_rect.size.height
266 + infostyle.row_rect_padding() * 2);
267 dc.FillSolidRect(&selected_rect,
268 RGB(infostyle.focused_background_color().r(),
269 infostyle.focused_background_color().g(),
270 infostyle.focused_background_color().b()));
271 dc.SetDCBrushColor(
272 RGB(infostyle.focused_border_color().r(),
273 infostyle.focused_border_color().g(),
274 infostyle.focused_border_color().b()));
275 dc.FrameRect(&selected_rect,
276 static_cast<HBRUSH>(GetStockObject(DC_BRUSH)));
277 } else {
278 if (title_style.has_background_color()) {
279 dc.FillSolidRect(&title_back_crect,
280 RGB(title_style.background_color().r(),
281 title_style.background_color().g(),
282 title_style.background_color().b()));
283 } else {
284 dc.FillSolidRect(&title_back_crect,
285 RGB(255, 255, 255));
286 }
287 if (desc_style.has_background_color()) {
288 dc.FillSolidRect(&desc_back_crect,
289 RGB(title_style.background_color().r(),
290 title_style.background_color().g(),
291 title_style.background_color().b()));
292 } else {
293 dc.FillSolidRect(&desc_back_crect,
294 RGB(255, 255, 255));
295 }
296 }
297
298 text_renderer_->RenderText(dc, title_str, title_rect,
299 TextRenderer::FONTSET_INFOLIST_TITLE);
300 text_renderer_->RenderText(dc, desc_str, desc_rect,
301 TextRenderer::FONTSET_INFOLIST_DESCRIPTION);
302 return Size(0, row_height);
303 }
304
OnSettingChange(UINT uFlags,LPCTSTR)305 void InfolistWindow::OnSettingChange(UINT uFlags, LPCTSTR /*lpszSection*/) {
306 // Since TextRenderer uses dialog font to render,
307 // we monitor font-related parameters to know when the font style is changed.
308 switch (uFlags) {
309 case 0x1049: // = SPI_SETCLEARTYPE
310 case SPI_SETFONTSMOOTHING:
311 case SPI_SETFONTSMOOTHINGCONTRAST:
312 case SPI_SETFONTSMOOTHINGORIENTATION:
313 case SPI_SETFONTSMOOTHINGTYPE:
314 case SPI_SETNONCLIENTMETRICS:
315 metrics_changed_ = true;
316 break;
317 default:
318 // We ignore other changes.
319 break;
320 }
321 }
322
OnTimer(UINT_PTR nIDEvent)323 void InfolistWindow::OnTimer(UINT_PTR nIDEvent) {
324 if (nIDEvent != kIdDelayShowHideTimer) {
325 return;
326 }
327 if (visible_) {
328 DelayShow(0);
329 } else {
330 DelayHide(0);
331 }
332 }
333
DelayShow(UINT mseconds)334 void InfolistWindow::DelayShow(UINT mseconds) {
335 visible_ = true;
336 KillTimer(kIdDelayShowHideTimer);
337 if (mseconds <= 0) {
338 const bool current_visible = (IsWindowVisible() != FALSE);
339 SetWindowPos(HWND_TOPMOST, 0, 0, 0, 0,
340 SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_SHOWWINDOW);
341 SendMessageW(WM_NCACTIVATE, FALSE);
342 if (!current_visible) {
343 SendUsageStatsEvent(send_command_interface_,
344 SessionCommand::INFOLIST_WINDOW_SHOW);
345 }
346 } else {
347 SetTimer(kIdDelayShowHideTimer, mseconds, nullptr);
348 }
349 }
350
DelayHide(UINT mseconds)351 void InfolistWindow::DelayHide(UINT mseconds) {
352 visible_ = false;
353 KillTimer(kIdDelayShowHideTimer);
354 if (mseconds <= 0) {
355 const bool current_visible = (IsWindowVisible() != FALSE);
356 ShowWindow(SW_HIDE);
357 if (current_visible) {
358 SendUsageStatsEvent(send_command_interface_,
359 SessionCommand::INFOLIST_WINDOW_HIDE);
360 }
361 } else {
362 SetTimer(kIdDelayShowHideTimer, mseconds, nullptr);
363 }
364 }
365
UpdateLayout(const commands::Candidates & candidates)366 void InfolistWindow::UpdateLayout(const commands::Candidates &candidates) {
367 candidates_->CopyFrom(candidates);
368
369 // If we detect any change of font parameters, update text renderer
370 if (metrics_changed_) {
371 text_renderer_->OnThemeChanged();
372 metrics_changed_ = false;
373 }
374 }
375
SetSendCommandInterface(client::SendCommandInterface * send_command_interface)376 void InfolistWindow::SetSendCommandInterface(
377 client::SendCommandInterface *send_command_interface) {
378 send_command_interface_ = send_command_interface;
379 }
380
GetLayoutSize()381 Size InfolistWindow::GetLayoutSize() {
382 CDCHandle dmyDc(nullptr);
383 return DoPaint(dmyDc);
384 }
385 } // namespace win32
386 } // namespace renderer
387 } // namespace mozc
388