| // Copyright 2010-2015, Google Inc. |
| // All rights reserved. |
| // |
| // Redistribution and use in source and binary forms, with or without |
| // modification, are permitted provided that the following conditions are |
| // met: |
| // |
| // * Redistributions of source code must retain the above copyright |
| // notice, this list of conditions and the following disclaimer. |
| // * Redistributions in binary form must reproduce the above |
| // copyright notice, this list of conditions and the following disclaimer |
| // in the documentation and/or other materials provided with the |
| // distribution. |
| // * Neither the name of Google Inc. nor the names of its |
| // contributors may be used to endorse or promote products derived from |
| // this software without specific prior written permission. |
| // |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| #include "renderer/unix/window_manager.h" |
| |
| #include "base/logging.h" |
| #include "renderer/renderer_command.pb.h" |
| #include "renderer/unix/gtk_wrapper.h" |
| #include "renderer/window_util.h" |
| |
| namespace mozc { |
| namespace renderer { |
| namespace gtk { |
| |
| WindowManager::WindowManager(GtkWindowInterface *candidate_window, |
| GtkWindowInterface *infolist_window, |
| GtkWrapperInterface *gtk) |
| : candidate_window_(candidate_window), |
| infolist_window_(infolist_window), |
| gtk_(gtk) { |
| } |
| |
| WindowManager::~WindowManager() { |
| } |
| |
| void WindowManager::Initialize() { |
| // Should call ShowWindow function in all window, otherwise each Initialize |
| // function will fail. |
| ShowAllWindows(); |
| HideAllWindows(); |
| candidate_window_->Initialize(); |
| infolist_window_->Initialize(); |
| } |
| |
| void WindowManager::HideAllWindows() { |
| candidate_window_->HideWindow(); |
| infolist_window_->HideWindow(); |
| } |
| |
| void WindowManager::ShowAllWindows() { |
| candidate_window_->ShowWindow(); |
| infolist_window_->ShowWindow(); |
| } |
| |
| // static |
| bool WindowManager::ShouldShowCandidateWindow( |
| const commands::RendererCommand &command) { |
| if (!command.visible()) { |
| return false; |
| } |
| |
| DCHECK(command.has_output()); |
| const commands::Output &output = command.output(); |
| |
| if (!output.has_candidates()) { |
| return false; |
| } |
| |
| const commands::Candidates &candidates = output.candidates(); |
| if (candidates.candidate_size() == 0) { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| Rect WindowManager::UpdateCandidateWindow( |
| const commands::RendererCommand &command) { |
| // TODO(nona): skip update if there are no changes. |
| DCHECK(command.has_output()); |
| DCHECK(command.output().has_candidates()); |
| const commands::Candidates &candidates = command.output().candidates(); |
| DCHECK_LT(0, candidates.candidate_size()); |
| |
| const Size new_window_size = candidate_window_->Update(candidates); |
| |
| Point new_window_pos = candidate_window_->GetWindowPos(); |
| if (candidates.has_window_location()) { |
| if (candidates.window_location() == commands::Candidates::CARET) { |
| DCHECK(candidates.has_caret_rectangle()); |
| new_window_pos.x = candidates.caret_rectangle().x(); |
| new_window_pos.y = candidates.caret_rectangle().y() |
| + candidates.caret_rectangle().height(); |
| } else { |
| DCHECK(candidates.has_composition_rectangle()); |
| new_window_pos.x = candidates.composition_rectangle().x(); |
| new_window_pos.y = candidates.composition_rectangle().y() |
| + candidates.composition_rectangle().height(); |
| } |
| } |
| |
| const Rect working_area = GetMonitorRect(new_window_pos.x, new_window_pos.y); |
| const Point alignment_base_point_in_local_window_coord( |
| candidate_window_->GetCandidateColumnInClientCord().Left(), 0); |
| const Rect caret_rect(candidates.caret_rectangle().x(), |
| candidates.caret_rectangle().y(), |
| candidates.caret_rectangle().width(), |
| candidates.caret_rectangle().height()); |
| // |caret_rect| is not always equal to preedit rect but can be an alternative |
| // in terms of positional calculation, especially for vertical adjustment in |
| // horizontal writing. |
| const Rect expected_window_rect_in_screen_coord = |
| WindowUtil::GetWindowRectForMainWindowFromTargetPointAndPreedit( |
| new_window_pos, |
| caret_rect, |
| new_window_size, |
| alignment_base_point_in_local_window_coord, |
| working_area, |
| false); // GTK+ renderer only support horizontal window. |
| candidate_window_->Move(expected_window_rect_in_screen_coord.origin); |
| candidate_window_->ShowWindow(); |
| |
| return expected_window_rect_in_screen_coord; |
| } |
| |
| // static |
| bool WindowManager::ShouldShowInfolistWindow( |
| const commands::RendererCommand &command) { |
| if (!command.output().has_candidates()) { |
| return false; |
| } |
| |
| const commands::Candidates &candidates = command.output().candidates(); |
| if (candidates.candidate_size() <= 0) { |
| return false; |
| } |
| |
| if (!candidates.has_usages() || !candidates.has_focused_index()) { |
| return false; |
| } |
| |
| if (candidates.usages().information_size() <= 0) { |
| return false; |
| } |
| |
| // Converts candidate's index to column row index. |
| const int focused_row |
| = candidates.focused_index() - candidates.candidate(0).index(); |
| if (candidates.candidate_size() < focused_row) { |
| return false; |
| } |
| |
| if (!candidates.candidate(focused_row).has_information_id()) { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| Rect WindowManager::GetMonitorRect(gint x, gint y) { |
| GtkWidget *window = gtk_->GtkWindowNew(GTK_WINDOW_TOPLEVEL); |
| GdkScreen *screen = gtk_->GtkWindowGetScreen(window); |
| const gint monitor = gtk_->GdkScreenGetMonitorAtPoint(screen, x, y); |
| GdkRectangle screen_rect = {}; |
| gtk_->GdkScreenGetMonitorGeometry(screen, monitor, &screen_rect); |
| return Rect(screen_rect.x, screen_rect.y, |
| screen_rect.width, screen_rect.height); |
| } |
| |
| void WindowManager::UpdateInfolistWindow( |
| const commands::RendererCommand &command, |
| const Rect &candidate_window_rect) { |
| |
| if (!WindowManager::ShouldShowInfolistWindow(command)) { |
| infolist_window_->HideWindow(); |
| return; |
| } |
| |
| const commands::Candidates &candidates = command.output().candidates(); |
| const Size infolist_window_size = infolist_window_->Update(candidates); |
| |
| const Rect screen_rect = GetMonitorRect(candidate_window_rect.Left(), |
| candidate_window_rect.Top()); |
| const Rect infolist_rect = |
| WindowUtil::WindowUtil::GetWindowRectForInfolistWindow( |
| infolist_window_size, candidate_window_rect, screen_rect); |
| infolist_window_->Move(infolist_rect.origin); |
| infolist_window_->ShowWindow(); |
| } |
| |
| void WindowManager::UpdateLayout(const commands::RendererCommand &command) { |
| if (!ShouldShowCandidateWindow(command)) { |
| HideAllWindows(); |
| return; |
| } |
| |
| if (command.has_application_info() && |
| command.application_info().has_pango_font_description()) { |
| const string font_description = |
| command.application_info().pango_font_description(); |
| if (previous_font_description_ != font_description) { |
| DVLOG(1) << "Font description is changed" |
| << " From:" << previous_font_description_ |
| << " To :" << font_description; |
| candidate_window_->ReloadFontConfig(font_description); |
| infolist_window_->ReloadFontConfig(font_description); |
| previous_font_description_.assign(font_description); |
| } |
| } |
| |
| const Rect candidate_window_rect = UpdateCandidateWindow(command); |
| UpdateInfolistWindow(command, candidate_window_rect); |
| } |
| |
| bool WindowManager::Activate() { |
| // TODO(nona): Implement |
| return true; |
| } |
| |
| bool WindowManager::IsAvailable() const { |
| // TODO(nona): Implement |
| return true; |
| } |
| |
| bool WindowManager::SetSendCommandInterface( |
| client::SendCommandInterface *send_command_interface) { |
| send_command_interface_ = send_command_interface; |
| return candidate_window_->SetSendCommandInterface(send_command_interface_) && |
| infolist_window_->SetSendCommandInterface(send_command_interface_); |
| } |
| |
| void WindowManager::SetWindowPos(int x, int y) { |
| candidate_window_->Move(Point(x, y)); |
| } |
| |
| } // namespace gtk |
| } // namespace renderer |
| } // namespace mozc |