blob: bfb25b65f6dde7d3fec3821880939b1226b3409c [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.
#ifndef MOZC_RENDERER_WIN32_WIN32_RENDERER_UTIL_H_
#define MOZC_RENDERER_WIN32_WIN32_RENDERER_UTIL_H_
#include <Windows.h>
#include <vector>
#include <string>
#include "base/port.h"
#include "base/scoped_ptr.h"
#include "testing/base/public/gunit_prod.h"
// for FRIEND_TEST()
// TODO(yukawa): Use platform independent primitive types.
#ifdef OS_WIN
namespace mozc {
namespace commands {
class RendererCommand;
// We cannot use RendererCommand::ApplicationInfo nor
// RendererCommand::CandidateForm because C++ does not
// allow forward declaration of a nested-type.
class RendererCommand_ApplicationInfo;
class RendererCommand_CandidateForm;
} // commands
namespace renderer {
namespace win32 {
struct CharacterRange {
CharacterRange();
int begin;
int length;
};
struct LineLayout {
wstring text;
int line_length;
int line_width;
int line_start_offset;
vector<CharacterRange> character_positions;
LineLayout();
};
struct SegmentMarkerLayout {
POINT from;
POINT to;
bool highlighted;
SegmentMarkerLayout();
};
struct CompositionWindowLayout {
RECT window_position_in_screen_coordinate;
RECT text_area;
RECT caret_rect;
POINT base_position;
LOGFONT log_font;
wstring text;
vector<SegmentMarkerLayout> marker_layouts;
CompositionWindowLayout();
};
// A POD-like class which represents positional information about where the
// candidate window should be displayed.
// Do not inherit from this class. This class is not designed to be
// inheritable.
class CandidateWindowLayout {
public:
// Initializes fields with default values keeping |initialized_| false.
CandidateWindowLayout();
// Destructor Stub. Actually do nothing.
~CandidateWindowLayout();
// Returns true if this object is initialized with valid parameter.
bool initialized() const;
// Initializes fields with given target position and sets true
// to |initialized_|.
void InitializeWithPosition(const POINT &position);
// Initializes fields with given target position and exclude region and
// sets true to |initialized_|.
void InitializeWithPositionAndExcludeRegion(
const POINT &position, const RECT &exclude_region);
// Clears fields and sets false to |initialized_|.
void Clear();
// Returns target position in screen coordinate.
const POINT &position() const;
// Returns exclude region in screen coordinate. You can call this method
// when |has_exclude_region| returns true.
const RECT &exclude_region() const;
// Returns true if |exclude_region| is available.
bool has_exclude_region() const;
private:
POINT position_;
RECT exclude_region_;
bool has_exclude_region_;
bool initialized_;
};
struct IndicatorWindowLayout {
public:
IndicatorWindowLayout();
void Clear();
RECT window_rect;
bool is_vertical;
};
// This interface is designed to hook API calls for unit test.
class SystemPreferenceInterface {
public:
virtual ~SystemPreferenceInterface() {}
// This method can be used to retrieve some kind of default font
// for GUI rendering.
// Returns true if succeeds.
virtual bool GetDefaultGuiFont(LOGFONTW *log_font) = 0;
};
// This class implements SystemPreferenceInterface for unit tests.
class SystemPreferenceFactory {
public:
// Returns an instance of WindowPositionEmulator. Caller must delete
// the instance.
static SystemPreferenceInterface *CreateMock(const LOGFONTW &gui_font);
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(SystemPreferenceFactory);
};
// This interface is designed to hook API calls for unit test.
class WorkingAreaInterface {
public:
virtual ~WorkingAreaInterface() {}
virtual bool GetWorkingAreaFromPoint(const POINT &point,
RECT *working_area) = 0;
};
class WorkingAreaFactory {
public:
// Returns an instance of WorkingAreaInterface. Caller must delete
// the instance.
static WorkingAreaInterface *Create();
// Returns an instance of WorkingAreaInterface. Caller must delete
// the instance.
static WorkingAreaInterface *CreateMock(const RECT &working_area);
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(WorkingAreaFactory);
};
// This interface is designed to hook API calls for unit test.
class WindowPositionInterface {
public:
virtual ~WindowPositionInterface() {}
// This method wraps API call of LogicalToPhysicalPoint.
// Returns true if this method can convert the given coordinate into
// physical space. We do not declare this method as const method so
// that a mock class can implement this method in non-const way.
virtual bool LogicalToPhysicalPoint(
HWND window_handle, const POINT &logical_coordinate,
POINT *physical_coordinate) = 0;
// This method wraps API call of GetWindowRect.
virtual bool GetWindowRect(HWND window_handle, RECT *rect) = 0;
// This method wraps API call of GetClientRect.
virtual bool GetClientRect(HWND window_handle, RECT *rect) = 0;
// This method wraps API call of ClientToScreen.
virtual bool ClientToScreen(HWND window_handle, POINT *point) = 0;
// This method wraps API call of IsWindow.
virtual bool IsWindow(HWND window_handle) = 0;
// This method wraps API call of GetAncestor/GA_ROOT.
virtual HWND GetRootWindow(HWND window_handle) = 0;
// This method wraps API call of GetClassName.
virtual bool GetWindowClassName(HWND window_handle, wstring *class_name) = 0;
};
// This class implements WindowPositionInterface and emulates APIs
// with positional information registered by RegisterWindow method.
class WindowPositionEmulator : public WindowPositionInterface {
public:
// Returns an instance of WindowPositionEmulator.
static WindowPositionEmulator *Create();
// Returns a dummy window handle for this emulator. You can call methods of
// WindowPositionInterface with this dummy handle. You need not to release
// the returned handle.
virtual HWND RegisterWindow(
const wstring &class_name, const RECT &window_rect,
const POINT &client_area_offset, const SIZE &client_area_size,
double scale_factor) = 0;
virtual void SetRoot(HWND child_window, HWND root_window) = 0;
};
enum CompatibilityMode {
// This flag represents the position can be calculated with default strategy.
COMPATIBILITY_MODE_NONE = 0,
// Some applications always keep CandidateForm up-to-date. When this flag
// is specified, CandidateForm is taken into account so that the suggest
// window can be shown at more appropriate position. Qt-based applications
// match this category.
CAN_USE_CANDIDATE_FORM_FOR_SUGGEST = 1,
// Some applications interpret the coordinate system of CandidateForm as
// the offset from the upper-left corner of the window as opposed to most
// of other applications which simply use local coordinate. When this flag
// is specified, positional fields in CandidateForm are transformed as if
// they are the offset from the upper-left corner of the window. GTK-based
// applications and Java AWT-based applications match this category.
USE_LOCAL_COORD_FOR_CANDIDATE_FORM = 2,
// Some applications such as V2C occasionally create zero-initialized
// CANDIDATEFORM and maintain it regardless of the actual position of the
// composition. This compatibility flag represents that it is better to
// ignore this phantom CANDIDATEFORM. Currently all the Java AWT-based
// applications match this category.
IGNORE_DEFAULT_COMPOSITION_FORM = 4,
// Some applications such as NTEmacs22 or Meadow3.0 automatically and
// frequently generates WM_IME_CONTROL/IMC_SETCOMPOSITIONWINDOW messages.
// In this case, the renderer might not be able to show the InfoList
// because new rendering messages will come before the InfoList is
// displayed with the default delay (~500 msec). This compatibility flag
// represents that we should show the InfoList immediately on such
// applications. See b/5824433 for details.
// TODO(horo, yukawa): Perhapes we can improve the rendering algorithm
// when the renderer is receiving the same messages successively.
// If we can safely ignore such successive messages, this flag
// can be removed.
SHOW_INFOLIST_IMMEDIATELY = 8,
};
class LayoutManager {
public:
LayoutManager();
~LayoutManager();
// A special constructor for unit tests. You can set a mock object which
// emulates native APIs for unit test. This class is responsible for
// deleting the mock objects passed.
LayoutManager(SystemPreferenceInterface *mock_system_preference,
WindowPositionInterface *mock_window_position);
// Calculates layout of composition windows including preferred position of
// the candidate window and text attributes such as underline or text
// highlighting in the composition windows. Returns true when succeeds.
// This method is thread-safe.
// command: The renderer commant to be rendered.
// composition_window_layouts: calculated layout for composition windows.
// candidate_form: calculated position for the candidate window to be
// displayed.
bool LayoutCompositionWindow(
const commands::RendererCommand &command,
vector<CompositionWindowLayout> *composition_window_layouts,
CandidateWindowLayout* candidate_layout) const;
// Returns compatibility bits for given target application.
int GetCompatibilityMode(
const commands::RendererCommand_ApplicationInfo &app_info);
// Determines the position where the suggest window should be placed when the
// composition window is drawn by the application. This function does not
// take DPI virtualization into account. In other words, any positional
// field in |candidate_form| is calculated in virtualized screen coordinates
// for the target application window.
bool LayoutCandidateWindowForSuggestion(
const commands::RendererCommand_ApplicationInfo &app_info,
CandidateWindowLayout *candidate_layout);
// Determines the position where the candidate/predict window should be
// placed when the composition window is drawn by the application. This
// function does not take DPI virtualization into account. In other words,
// any positional field in |candidate_form| is calculated in virtualized
// screen coordinates for the target application window.
bool LayoutCandidateWindowForConversion(
const commands::RendererCommand_ApplicationInfo &app_info,
CandidateWindowLayout *candidate_layout);
// Converts a virtualized screen coordinate for the DPI-unaware application
// specified by |window_handle| to the universal screen coordinate for
// DPI-aware applications. When the LogicalToPhysicalPoint API is not
// available in the target platform, this function simply copies |point|
// into |result|. If LogicalToPhysicalPoint API fails due to some
// limitations, this function tries to emulate the result assuming the
// scaling factor is one dimension and scaling center is (0, 0).
// See remarks of the following document for details about the limitations.
// http://msdn.microsoft.com/en-us/library/ms633533.aspx
// This method is thread-safe.
void GetPointInPhysicalCoords(
HWND window_handle, const POINT &point, POINT *result) const;
// RECT version of GetPointInPhysicalCoords. This method is thread-safe.
void GetRectInPhysicalCoords(
HWND window_handle, const RECT &rect, RECT *result) const;
// Converts a local coordinate into a logical screen coordinate assuming
// |src_point| is the relative offset from the top-left of the
// window specified by the |src_window_handle|.
// Returns false if fails.
bool LocalPointToScreen(HWND src_window_handle, const POINT &src_point,
POINT *dest_point) const;
// Converts a local coordinate into a logical screen coordinate assuming
// |src_point| is the relative offset from the top-left of the
// window specified by the |src_window_handle|.
// Returns false if fails.
bool LocalRectToScreen(HWND src_window_handle, const RECT &src_rect,
RECT *dest_rect) const;
// Converts a local coordinate into a logical screen coordinate assuming
// |src_point| is the client coorinate in the window specified by
// |src_window_handle|.
// Returns false if fails.
bool ClientRectToScreen(HWND src_window_handle, const RECT &src_rect,
RECT *dest_rect) const;
// Converts a local coordinate into a logical screen coordinate assuming
// |src_point| is the client coorinate in the window specified by
// |src_window_handle|.
// Returns false if fails.
bool ClientPointToScreen(HWND src_window_handle, const POINT &src_point,
POINT *dest_point) const;
// Returns true if the client rect of the target window specified by
// |src_window_handle| is retrieved in a logical screen coordinate.
// Returns false if fails.
bool GetClientRect(HWND window_handle, RECT *client_rect) const;
// Returns the scaling factor for DPI Virtualization.
// Returns 1.0 if any error occurs.
double GetScalingFactor(HWND window_handle) const;
// Returns true if the default GUI font is retrieved.
// Returns false if fails.
bool GetDefaultGuiFont(LOGFONTW *logfong) const;
// Represents preferred writing direction, especially for composition string.
enum WritingDirection {
// The writing direction is not specified.
WRITING_DIRECTION_UNSPECIFIED = 0,
// Horizontal writing is specified.
HORIZONTAL_WRITING = 1,
// Vertical writing is specified.
VERTICAL_WRITING = 2,
};
// Returns writing direction.
static WritingDirection GetWritingDirection(
const commands::RendererCommand_ApplicationInfo &app_info);
// Returns true when the target rect is successfully obtained.
bool LayoutIndicatorWindow(
const commands::RendererCommand_ApplicationInfo &app_info,
IndicatorWindowLayout *indicator_layout);
private:
FRIEND_TEST(Win32RendererUtilTest, HorizontalProportional);
FRIEND_TEST(Win32RendererUtilTest, VerticalProportional);
FRIEND_TEST(Win32RendererUtilTest, HorizontalMonospaced);
FRIEND_TEST(Win32RendererUtilTest, VerticalMonospaced);
FRIEND_TEST(Win32RendererUtilTest, HorizontalProportionalCompositeGlyph);
FRIEND_TEST(Win32RendererUtilTest, VerticalProportionalCompositeGlyph);
FRIEND_TEST(Win32RendererUtilTest, HorizontalMonospacedCompositeGlyph);
FRIEND_TEST(Win32RendererUtilTest, VerticalMonospacedCompositeGlyph);
// Calculates text layout with taking text wrapping into account. Returns
// true when succeeds.
// font: The font information to be used for calculation.
// str: The string to be calculated. Non-printable characters are not fully
// supported. For example, this function may not work well if |str|
// contains CR, LF, HT, and VT.
// maximum_line_length: The maximum length which limits the grows of text
// for each line.
// initial_offset: Represents cursor position where the first character in
// the |str| starts from if there is enough space to show the first
// character.
// LineLayout: Layout information for each split line.
static bool CalcLayoutWithTextWrapping(
const LOGFONTW &font,
const wstring &text,
int maximum_line_length,
int initial_offset,
vector<LineLayout> *line_layouts);
scoped_ptr<SystemPreferenceInterface> system_preference_;
scoped_ptr<WindowPositionInterface> window_position_;
DISALLOW_COPY_AND_ASSIGN(LayoutManager);
};
} // namespace win32
} // namespace renderer
} // namespace mozc
#endif // OS_WIN
#endif // MOZC_RENDERER_WIN32_WIN32_RENDERER_UTIL_H_