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/window_manager.h"
31
32 #include "base/logging.h"
33 #include "protocol/renderer_command.pb.h"
34 #include "renderer/unix/gtk_wrapper.h"
35 #include "renderer/window_util.h"
36
37 namespace mozc {
38 namespace renderer {
39 namespace gtk {
40
WindowManager(GtkWindowInterface * candidate_window,GtkWindowInterface * infolist_window,GtkWrapperInterface * gtk)41 WindowManager::WindowManager(GtkWindowInterface *candidate_window,
42 GtkWindowInterface *infolist_window,
43 GtkWrapperInterface *gtk)
44 : candidate_window_(candidate_window),
45 infolist_window_(infolist_window),
46 gtk_(gtk) {
47 }
48
~WindowManager()49 WindowManager::~WindowManager() {
50 }
51
Initialize()52 void WindowManager::Initialize() {
53 // Should call ShowWindow function in all window, otherwise each Initialize
54 // function will fail.
55 ShowAllWindows();
56 HideAllWindows();
57 candidate_window_->Initialize();
58 infolist_window_->Initialize();
59 }
60
HideAllWindows()61 void WindowManager::HideAllWindows() {
62 candidate_window_->HideWindow();
63 infolist_window_->HideWindow();
64 }
65
ShowAllWindows()66 void WindowManager::ShowAllWindows() {
67 candidate_window_->ShowWindow();
68 infolist_window_->ShowWindow();
69 }
70
71 // static
ShouldShowCandidateWindow(const commands::RendererCommand & command)72 bool WindowManager::ShouldShowCandidateWindow(
73 const commands::RendererCommand &command) {
74 if (!command.visible()) {
75 return false;
76 }
77
78 DCHECK(command.has_output());
79 const commands::Output &output = command.output();
80
81 if (!output.has_candidates()) {
82 return false;
83 }
84
85 const commands::Candidates &candidates = output.candidates();
86 if (candidates.candidate_size() == 0) {
87 return false;
88 }
89
90 return true;
91 }
92
UpdateCandidateWindow(const commands::RendererCommand & command)93 Rect WindowManager::UpdateCandidateWindow(
94 const commands::RendererCommand &command) {
95 // TODO(nona): skip update if there are no changes.
96 DCHECK(command.has_output());
97 DCHECK(command.output().has_candidates());
98 const commands::Candidates &candidates = command.output().candidates();
99 DCHECK_LT(0, candidates.candidate_size());
100
101 const Size new_window_size = candidate_window_->Update(candidates);
102
103 Point new_window_pos = candidate_window_->GetWindowPos();
104 if (command.has_preedit_rectangle()) {
105 new_window_pos.x = command.preedit_rectangle().left();
106 new_window_pos.y = command.preedit_rectangle().bottom();
107 }
108
109 const Rect working_area = GetMonitorRect(new_window_pos.x, new_window_pos.y);
110 const Point alignment_base_point_in_local_window_coord(
111 candidate_window_->GetCandidateColumnInClientCord().Left(), 0);
112 const auto &preedit_rect = command.preedit_rectangle();
113 const Rect caret_rect(preedit_rect.left(),
114 preedit_rect.top(),
115 preedit_rect.right() - preedit_rect.left(),
116 preedit_rect.bottom() - preedit_rect.top());
117 // |caret_rect| is not always equal to preedit rect but can be an alternative
118 // in terms of positional calculation, especially for vertical adjustment in
119 // horizontal writing.
120 const Rect expected_window_rect_in_screen_coord =
121 WindowUtil::GetWindowRectForMainWindowFromTargetPointAndPreedit(
122 new_window_pos,
123 caret_rect,
124 new_window_size,
125 alignment_base_point_in_local_window_coord,
126 working_area,
127 false); // GTK+ renderer only support horizontal window.
128 candidate_window_->Move(expected_window_rect_in_screen_coord.origin);
129 candidate_window_->ShowWindow();
130
131 return expected_window_rect_in_screen_coord;
132 }
133
134 // static
ShouldShowInfolistWindow(const commands::RendererCommand & command)135 bool WindowManager::ShouldShowInfolistWindow(
136 const commands::RendererCommand &command) {
137 if (!command.output().has_candidates()) {
138 return false;
139 }
140
141 const commands::Candidates &candidates = command.output().candidates();
142 if (candidates.candidate_size() <= 0) {
143 return false;
144 }
145
146 if (!candidates.has_usages() || !candidates.has_focused_index()) {
147 return false;
148 }
149
150 if (candidates.usages().information_size() <= 0) {
151 return false;
152 }
153
154 // Converts candidate's index to column row index.
155 const int focused_row
156 = candidates.focused_index() - candidates.candidate(0).index();
157 if (candidates.candidate_size() < focused_row) {
158 return false;
159 }
160
161 if (!candidates.candidate(focused_row).has_information_id()) {
162 return false;
163 }
164
165 return true;
166 }
167
GetMonitorRect(gint x,gint y)168 Rect WindowManager::GetMonitorRect(gint x, gint y) {
169 GtkWidget *window = gtk_->GtkWindowNew(GTK_WINDOW_TOPLEVEL);
170 GdkScreen *screen = gtk_->GtkWindowGetScreen(window);
171 const gint monitor = gtk_->GdkScreenGetMonitorAtPoint(screen, x, y);
172 GdkRectangle screen_rect = {};
173 gtk_->GdkScreenGetMonitorGeometry(screen, monitor, &screen_rect);
174 return Rect(screen_rect.x, screen_rect.y,
175 screen_rect.width, screen_rect.height);
176 }
177
UpdateInfolistWindow(const commands::RendererCommand & command,const Rect & candidate_window_rect)178 void WindowManager::UpdateInfolistWindow(
179 const commands::RendererCommand &command,
180 const Rect &candidate_window_rect) {
181
182 if (!WindowManager::ShouldShowInfolistWindow(command)) {
183 infolist_window_->HideWindow();
184 return;
185 }
186
187 const commands::Candidates &candidates = command.output().candidates();
188 const Size infolist_window_size = infolist_window_->Update(candidates);
189
190 const Rect screen_rect = GetMonitorRect(candidate_window_rect.Left(),
191 candidate_window_rect.Top());
192 const Rect infolist_rect =
193 WindowUtil::WindowUtil::GetWindowRectForInfolistWindow(
194 infolist_window_size, candidate_window_rect, screen_rect);
195 infolist_window_->Move(infolist_rect.origin);
196 infolist_window_->ShowWindow();
197 }
198
UpdateLayout(const commands::RendererCommand & command)199 void WindowManager::UpdateLayout(const commands::RendererCommand &command) {
200 if (!ShouldShowCandidateWindow(command)) {
201 HideAllWindows();
202 return;
203 }
204
205 if (command.has_application_info() &&
206 command.application_info().has_pango_font_description()) {
207 const string font_description =
208 command.application_info().pango_font_description();
209 if (previous_font_description_ != font_description) {
210 DVLOG(1) << "Font description is changed"
211 << " From:" << previous_font_description_
212 << " To :" << font_description;
213 candidate_window_->ReloadFontConfig(font_description);
214 infolist_window_->ReloadFontConfig(font_description);
215 previous_font_description_.assign(font_description);
216 }
217 }
218
219 const Rect candidate_window_rect = UpdateCandidateWindow(command);
220 UpdateInfolistWindow(command, candidate_window_rect);
221 }
222
Activate()223 bool WindowManager::Activate() {
224 // TODO(nona): Implement
225 return true;
226 }
227
IsAvailable() const228 bool WindowManager::IsAvailable() const {
229 // TODO(nona): Implement
230 return true;
231 }
232
SetSendCommandInterface(client::SendCommandInterface * send_command_interface)233 bool WindowManager::SetSendCommandInterface(
234 client::SendCommandInterface *send_command_interface) {
235 send_command_interface_ = send_command_interface;
236 return candidate_window_->SetSendCommandInterface(send_command_interface_) &&
237 infolist_window_->SetSendCommandInterface(send_command_interface_);
238 }
239
SetWindowPos(int x,int y)240 void WindowManager::SetWindowPos(int x, int y) {
241 candidate_window_->Move(Point(x, y));
242 }
243
244 } // namespace gtk
245 } // namespace renderer
246 } // namespace mozc
247