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/unix/infolist_window.h"
31 
32 #include "base/logging.h"
33 #include "base/mutex.h"
34 #include "protocol/renderer_command.pb.h"
35 #include "protocol/renderer_style.pb.h"
36 #include "renderer/renderer_style_handler.h"
37 #include "renderer/unix/cairo_wrapper.h"
38 #include "renderer/unix/const.h"
39 #include "renderer/unix/draw_tool.h"
40 #include "renderer/unix/font_spec.h"
41 #include "renderer/unix/text_renderer.h"
42 
43 namespace mozc {
44 namespace renderer {
45 namespace gtk {
46 
47 using mozc::commands::Candidates;
48 using mozc::commands::Information;
49 using mozc::commands::InformationList;
50 using mozc::commands::Output;
51 using mozc::commands::SessionCommand;
52 using mozc::renderer::RendererStyle;
53 using mozc::renderer::RendererStyleHandler;
54 
55 namespace {
StyleColorToRGBA(const RendererStyle::RGBAColor & rgbacolor)56 RGBA StyleColorToRGBA(const RendererStyle::RGBAColor &rgbacolor) {
57   const RGBA rgba = { static_cast<uint8>(rgbacolor.r()),
58                       static_cast<uint8>(rgbacolor.g()),
59                       static_cast<uint8>(rgbacolor.b()),
60                       0xFF };
61   return rgba;
62 }
63 }  // namespace
64 
InfolistWindow(TextRendererInterface * text_renderer,DrawToolInterface * draw_tool,GtkWrapperInterface * gtk,CairoFactoryInterface * cairo_factory)65 InfolistWindow::InfolistWindow(
66     TextRendererInterface *text_renderer,
67     DrawToolInterface *draw_tool,
68     GtkWrapperInterface *gtk,
69     CairoFactoryInterface *cairo_factory)
70     : GtkWindowBase(gtk),
71       text_renderer_(text_renderer),
72       style_(new RendererStyle()),
73       draw_tool_(draw_tool),
74       cairo_factory_(cairo_factory) {
75   mozc::renderer::RendererStyleHandler::GetRendererStyle(style_.get());
76 }
77 
Update(const commands::Candidates & candidates)78 Size InfolistWindow::Update(const commands::Candidates &candidates) {
79   candidates_.CopyFrom(candidates);
80 
81   const RendererStyle::InfolistStyle &infostyle = style_->infolist_style();
82   const InformationList &usages = candidates_.usages();
83 
84   int ypos = infostyle.window_border();
85   ypos += infostyle.caption_height();
86 
87   for (int i = 0; i < usages.information_size(); ++i) {
88     ypos += GetRowRects(i, ypos).whole_rect.Height();
89   }
90   ypos += infostyle.window_border();
91 
92   const Size result_size(style_->infolist_style().window_width(), ypos);
93   Resize(result_size);
94   Redraw();
95   return result_size;
96 }
97 
98 // We do not use this function. So following code makes no sense.
GetCandidateColumnInClientCord() const99 Rect InfolistWindow::GetCandidateColumnInClientCord() const {
100   DCHECK(false) << "Do not call this function without CandidateWindow.";
101   return Rect(0, 0, 0, 0);
102 }
103 
OnPaint(GtkWidget * widget,GdkEventExpose * event)104 bool InfolistWindow::OnPaint(GtkWidget *widget, GdkEventExpose *event) {
105   draw_tool_->Reset(cairo_factory_->CreateCairoInstance(
106       GetCanvasWidget()->window));
107   Draw();
108   return true;
109 }
110 
DrawCaption()111 int InfolistWindow::DrawCaption() {
112   const RendererStyle::InfolistStyle &infostyle = style_->infolist_style();
113   if (!infostyle.has_caption_string()) {
114     return 0;
115   }
116   const RendererStyle::TextStyle &caption_style = infostyle.caption_style();
117   const int caption_height = infostyle.caption_height();
118   const Rect background_rect(
119       infostyle.window_border(),
120       infostyle.window_border(),
121       infostyle.window_width() - infostyle.window_border() * 2,
122       caption_height);
123 
124   const RGBA bgcolor = StyleColorToRGBA(infostyle.caption_background_color());
125   draw_tool_->FillRect(background_rect, bgcolor);
126 
127   const Rect caption_rect(
128       background_rect.Left()
129           + infostyle.caption_padding() + caption_style.left_padding(),
130       background_rect.Top() + infostyle.caption_padding(),
131       background_rect.Width()
132           - infostyle.caption_padding() - caption_style.left_padding(),
133       caption_height - infostyle.caption_padding());
134   text_renderer_->RenderText(infostyle.caption_string(), caption_rect,
135                              FontSpec::FONTSET_INFOLIST_CAPTION);
136   return infostyle.caption_height();
137 }
138 
DrawFrame(int height)139 void InfolistWindow::DrawFrame(int height) {
140   const RendererStyle::InfolistStyle &infostyle = style_->infolist_style();
141   const Rect rect(0, 0, infostyle.window_width(), height);
142   const RGBA framecolor = StyleColorToRGBA(infostyle.border_color());
143   draw_tool_->FrameRect(rect, framecolor, 1);
144 }
145 
Draw()146 void InfolistWindow::Draw() {
147   const RendererStyle::InfolistStyle &infostyle = style_->infolist_style();
148   const InformationList &usages = candidates_.usages();
149   int ypos = infostyle.window_border();
150   ypos += DrawCaption();
151   for (int i = 0; i < usages.information_size(); ++i) {
152     ypos += DrawRow(i, ypos);
153   }
154   DrawFrame(ypos);
155 }
156 
GetRenderingRects(const renderer::RendererStyle::TextStyle & style,const string & text,FontSpec::FONT_TYPE font_type,int top,Rect * background_rect,Rect * textarea_rect)157 void InfolistWindow::GetRenderingRects(
158     const renderer::RendererStyle::TextStyle &style,
159     const string &text,
160     FontSpec::FONT_TYPE font_type,
161     int top,
162     Rect *background_rect,
163     Rect *textarea_rect) {
164   const RendererStyle::InfolistStyle &infostyle = style_->infolist_style();
165   const int text_width = infostyle.window_width()
166       - style.left_padding() - style.right_padding()
167       - infostyle.window_border() * 2 - infostyle.row_rect_padding() * 2;
168 
169   const Size text_size = text_renderer_->GetMultiLinePixelSize(font_type,
170                                                                text,
171                                                                text_width);
172 
173   background_rect->origin.x = infostyle.window_border();
174   background_rect->origin.y = top;
175   background_rect->size.width =
176       infostyle.window_width() - infostyle.window_border() * 2;
177   background_rect->size.height =
178       text_size.height + infostyle.row_rect_padding() * 2;
179 
180   *textarea_rect = *background_rect;
181 
182   textarea_rect->origin.x +=
183       infostyle.row_rect_padding() + style.left_padding();
184   textarea_rect->origin.y += infostyle.row_rect_padding();
185   textarea_rect->size.width = text_width;
186   textarea_rect->size.height = text_size.height;
187 }
188 
GetRowRects(int row,int ypos)189 InfolistWindow::RenderingRowRects InfolistWindow::GetRowRects(int row,
190                                                               int ypos) {
191   const RendererStyle::InfolistStyle &infostyle = style_->infolist_style();
192   const InformationList &usages = candidates_.usages();
193   const RendererStyle::TextStyle &title_style = infostyle.title_style();
194   const RendererStyle::TextStyle &desc_style = infostyle.description_style();
195   DCHECK_GT(usages.information_size(), row);
196   const Information &info = usages.information(row);
197 
198   RenderingRowRects result;
199 
200   GetRenderingRects(title_style,
201                     info.title(),
202                     FontSpec::FONTSET_INFOLIST_TITLE,
203                     ypos,
204                     &result.title_back_rect,
205                     &result.title_rect);
206   ypos += result.title_back_rect.size.height;
207   GetRenderingRects(desc_style,
208                     info.description(),
209                     FontSpec::FONTSET_INFOLIST_DESCRIPTION,
210                     ypos,
211                     &result.desc_back_rect,
212                     &result.desc_rect);
213   result.whole_rect = result.title_back_rect;
214   result.whole_rect.size.height += result.desc_back_rect.Height();
215   return result;
216 }
217 
DrawRow(int row,int ypos)218 int InfolistWindow::DrawRow(int row, int ypos) {
219   const RendererStyle::InfolistStyle &infostyle = style_->infolist_style();
220   const InformationList &usages = candidates_.usages();
221   const RendererStyle::TextStyle &title_style = infostyle.title_style();
222   const RendererStyle::TextStyle &desc_style = infostyle.description_style();
223   DCHECK_GT(usages.information_size(), row);
224   const Information &info = usages.information(row);
225   const RenderingRowRects row_rects = GetRowRects(row, ypos);
226 
227   if (usages.has_focused_index() && (row == usages.focused_index())) {
228     const RGBA bgcol = StyleColorToRGBA(infostyle.focused_background_color());
229     draw_tool_->FillRect(row_rects.whole_rect, bgcol);
230 
231     const RGBA frcol = StyleColorToRGBA(infostyle.focused_border_color());
232     draw_tool_->FrameRect(row_rects.whole_rect, frcol, 1);
233   } else {
234     if (title_style.has_background_color()) {
235       draw_tool_->FillRect(row_rects.title_back_rect,
236                            StyleColorToRGBA(title_style.background_color()));
237     } else {
238       draw_tool_->FillRect(row_rects.title_back_rect, kWhite);
239     }
240     if (desc_style.has_background_color()) {
241       draw_tool_->FillRect(row_rects.desc_back_rect,
242                            StyleColorToRGBA(desc_style.background_color()));
243     } else {
244       draw_tool_->FillRect(row_rects.desc_back_rect, kWhite);
245     }
246   }
247 
248   text_renderer_->RenderText(info.title(), row_rects.title_rect,
249                              FontSpec::FONTSET_INFOLIST_TITLE);
250   text_renderer_->RenderText(info.description(), row_rects.desc_rect,
251                              FontSpec::FONTSET_INFOLIST_DESCRIPTION);
252   return row_rects.whole_rect.Height();
253 }
254 
Initialize()255 void InfolistWindow::Initialize() {
256   text_renderer_->Initialize(GetCanvasWidget()->window);
257 }
258 
ReloadFontConfig(const string & font_description)259 void InfolistWindow::ReloadFontConfig(const string &font_description) {
260   text_renderer_->ReloadFontConfig(font_description);
261 }
262 
263 }  // namespace gtk
264 }  // namespace renderer
265 }  // namespace mozc
266