| // 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/win32/composition_window.h" |
| |
| #include <windows.h> |
| #define _ATL_NO_AUTOMATIC_NAMESPACE |
| #define _WTL_NO_AUTOMATIC_NAMESPACE |
| #include <atlbase.h> |
| #include <atlapp.h> |
| #include <atlcrack.h> |
| #include <atlgdi.h> |
| #include <atlmisc.h> |
| #include <atlwin.h> |
| |
| #include <string> |
| #include <vector> |
| |
| #include "base/const.h" |
| #include "base/coordinates.h" |
| #include "base/logging.h" |
| #include "base/util.h" |
| #include "renderer/renderer_command.pb.h" |
| #include "renderer/win32/win32_renderer_util.h" |
| |
| namespace mozc { |
| namespace renderer { |
| namespace win32 { |
| using WTL::CBitmap; |
| using WTL::CDC; |
| using WTL::CDCHandle; |
| using WTL::CFont; |
| using WTL::CFontHandle; |
| using WTL::CLogFont; |
| using WTL::CMemoryDC; |
| using WTL::CPaintDC; |
| using WTL::CPenHandle; |
| using WTL::CPoint; |
| using WTL::CRect; |
| using WTL::CSize; |
| using WTL::CPen; |
| using WTL::CPenHandle; |
| |
| namespace { |
| // As Discussed in b/2317702, UI windows are disabled by default because it is |
| // hard for a user to find out what caused the problem than finding that the |
| // operations seems to be disabled on the UI window when |
| // SPI_GETACTIVEWINDOWTRACKING is enabled. |
| // TODO(yukawa): Support mouse operations before we add a GUI feature which |
| // requires UI interaction by mouse and/or touch. (b/2954874) |
| typedef ATL::CWinTraits< |
| WS_POPUP | WS_DISABLED, |
| WS_EX_TOOLWINDOW | WS_EX_TOPMOST | WS_EX_NOACTIVATE> |
| CompositionLineWindowTraits; |
| |
| // A class which implements an IME composition window for Windows. This class |
| // is derived from an ATL CWindowImpl<T> class, which provides methods for |
| // creating a window and handling windows messages. |
| class CompositionLineWindow |
| : public ATL::CWindowImpl<CompositionLineWindow, |
| ATL::CWindow, |
| CompositionLineWindowTraits> { |
| public: |
| DECLARE_WND_CLASS_EX(kCompositionWindowClassName, CS_SAVEBITS, COLOR_WINDOW); |
| |
| BEGIN_MSG_MAP_EX(CandidateWindow) |
| MSG_WM_CREATE(OnCreate) |
| MSG_WM_ERASEBKGND(OnEraseBkgnd) |
| MSG_WM_PAINT(OnPaint) |
| MSG_WM_PRINTCLIENT(OnPrintClient) |
| END_MSG_MAP() |
| |
| CompositionLineWindow() {} |
| ~CompositionLineWindow() {} |
| |
| LRESULT OnCreate(LPCREATESTRUCT create_struct) { |
| // Make sure the window is disabled for b/2317702. |
| DCHECK(!IsWindowEnabled()) << "The window should no be enabled."; |
| return 0; |
| } |
| BOOL OnEraseBkgnd(WTL::CDCHandle dc) { |
| // We do not have to erase background |
| // because all pixels in client area will be drawn in the DoPaint method. |
| return TRUE; |
| } |
| void OnPaint(CDCHandle dc) { |
| CRect client_rect; |
| this->GetClientRect(&client_rect); |
| |
| if (dc != nullptr) { |
| CMemoryDC memdc(dc, client_rect); |
| DoPaint(memdc.m_hDC); |
| } else { |
| CPaintDC paint_dc(this->m_hWnd); |
| { // Create a copy of |paint_dc| and render the candidate strings in it. |
| // The image rendered to this |memdc| is to be copied into the original |
| // |paint_dc| in its destructor. So, we don't have to explicitly call |
| // any functions that copy this |memdc| to the |paint_dc| but putting |
| // the following code into a local block. |
| CMemoryDC memdc(paint_dc, client_rect); |
| DoPaint(memdc.m_hDC); |
| } |
| } |
| } |
| void OnPrintClient(CDCHandle dc, UINT uFlags) { |
| OnPaint(dc); |
| } |
| void UpdateLayout(const CompositionWindowLayout &layout) { |
| layout_ = layout; |
| font_ = CLogFont(layout.log_font).CreateFontIndirect(); |
| } |
| private: |
| void DoPaint(WTL::CDCHandle dc) { |
| const CFontHandle old_font = dc.SelectFont(font_); |
| CRect client_rect; |
| GetClientRect(&client_rect); |
| dc.SetBkMode(TRANSPARENT); |
| dc.FillSolidRect(&client_rect, RGB(0xff, 0xff, 0xff)); |
| dc.ExtTextOutW( |
| layout_.base_position.x, |
| layout_.base_position.y, |
| 0, |
| &layout_.text_area, |
| layout_.text.c_str(), |
| layout_.text.size()); |
| dc.SelectFont(old_font); |
| dc.SetDCPenColor(RGB(0, 0, 0)); |
| CPenHandle old_pen = dc.GetCurrentPen(); |
| for (size_t i = 0; i < layout_.marker_layouts.size(); ++i) { |
| const SegmentMarkerLayout &marker = layout_.marker_layouts[i]; |
| if (marker.highlighted) { |
| if (highlighted_pen_.IsNull()) { |
| highlighted_pen_.CreatePen(PS_SOLID, 2, RGB(0, 0, 0)); |
| } |
| old_pen = dc.SelectPen(highlighted_pen_); |
| } else { |
| if (dotted_pen_.IsNull()) { |
| const LOGBRUSH logbrush = { |
| BS_SOLID, // lbStyle |
| RGB(0, 0, 0), // lbColor; |
| 0, // lbHatch; |
| }; |
| // TODO(yukawa): Check the following issue on remote desktop. |
| // http://msdn.microsoft.com/en-us/library/dd162705.aspx#1 |
| dotted_pen_.CreatePen(PS_ALTERNATE, 1, &logbrush); |
| } |
| old_pen = dc.SelectPen(dotted_pen_); |
| } |
| dc.MoveTo(marker.from); |
| dc.LineTo(marker.to); |
| } |
| if (!::IsRectEmpty(&layout_.caret_rect)) { |
| dc.FillSolidRect(&layout_.caret_rect, RGB(0, 0, 0)); |
| } |
| if (old_pen) { |
| dc.SelectPen(old_pen); |
| } |
| } |
| CompositionWindowLayout layout_; |
| CFont font_; |
| CPen dotted_pen_; |
| CPen highlighted_pen_; |
| DISALLOW_COPY_AND_ASSIGN(CompositionLineWindow); |
| }; |
| |
| class CompositionWindowListImpl : public CompositionWindowList { |
| public: |
| CompositionWindowListImpl() { |
| } |
| virtual ~CompositionWindowListImpl() { |
| Destroy(); |
| } |
| |
| virtual void UpdateLayout(const vector<CompositionWindowLayout> &layouts) { |
| // Create windows if needed. |
| if (line_windows_.size() < layouts.size()) { |
| const size_t num_windows = layouts.size() - line_windows_.size(); |
| for (size_t i = 0; i < num_windows; ++i) { |
| CompositionLineWindow *window = new CompositionLineWindow(); |
| window->Create(nullptr); |
| line_windows_.push_back(window); |
| } |
| } |
| for (size_t i = 0; i < line_windows_.size(); ++i) { |
| if (i >= layouts.size()) { |
| line_windows_[i]->ShowWindow(SW_HIDE); |
| } else { |
| const CompositionWindowLayout &window_layout = layouts[i]; |
| const CRect rect(window_layout.window_position_in_screen_coordinate); |
| line_windows_[i]->UpdateLayout(window_layout); |
| // We have to ensure the composition window will be placed at the top |
| // most of the TOPMOST layer because the attached window might also be |
| // in the TOPMOST layer. |
| line_windows_[i]->SetWindowPos( |
| HWND_TOPMOST, rect.left, rect.top, rect.Width(), rect.Height(), |
| SWP_NOACTIVATE | SWP_SHOWWINDOW); |
| line_windows_[i]->Invalidate(FALSE); |
| } |
| } |
| } |
| virtual void Initialize() { |
| const int kInitialNumberOfWindows = 3; |
| for (size_t i = 0; i < kInitialNumberOfWindows; ++i) { |
| CompositionLineWindow *window = new CompositionLineWindow(); |
| window->Create(nullptr); |
| line_windows_.push_back(window); |
| } |
| } |
| virtual void AsyncHide() { |
| for (size_t i = 0; i < line_windows_.size(); ++i) { |
| line_windows_[i]->ShowWindow(SW_HIDE); |
| } |
| } |
| virtual void AsyncQuit() { |
| // TODO(yukawa): Implement this. |
| } |
| virtual void Destroy() { |
| for (size_t i = 0; i < line_windows_.size(); ++i) { |
| line_windows_[i]->DestroyWindow(); |
| delete line_windows_[i]; |
| } |
| line_windows_.clear(); |
| } |
| virtual void Hide() { |
| for (size_t i = 0; i < line_windows_.size(); ++i) { |
| line_windows_[i]->ShowWindow(SW_HIDE); |
| } |
| } |
| |
| private: |
| vector<CompositionLineWindow *> line_windows_; |
| DISALLOW_COPY_AND_ASSIGN(CompositionWindowListImpl); |
| }; |
| } // anonymous namespace |
| |
| CompositionWindowList *CompositionWindowList::CreateInstance() { |
| return new CompositionWindowListImpl(); |
| } |
| |
| } // namespace win32 |
| } // namespace renderer |
| } // namespace mozc |