blob: 7a2f65872f88937bda7ab73ea33b9dd7546a5c40 [file] [log] [blame]
// 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