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 #ifndef MOZC_RENDERER_WIN32_WIN32_RENDERER_UTIL_H_
31 #define MOZC_RENDERER_WIN32_WIN32_RENDERER_UTIL_H_
32 
33 #include <Windows.h>
34 
35 #include <memory>
36 #include <string>
37 #include <vector>
38 
39 #include "base/port.h"
40 #include "testing/base/public/gunit_prod.h"
41 // for FRIEND_TEST()
42 
43 // TODO(yukawa): Use platform independent primitive types.
44 #ifdef OS_WIN
45 namespace mozc {
46 
47 namespace commands {
48 class RendererCommand;
49 // We cannot use RendererCommand::ApplicationInfo nor
50 // RendererCommand::CandidateForm because C++ does not
51 // allow forward declaration of a nested-type.
52 class RendererCommand_ApplicationInfo;
53 class RendererCommand_CandidateForm;
54 }  // commands
55 
56 namespace renderer {
57 namespace win32 {
58 
59 struct CharacterRange {
60   CharacterRange();
61   int begin;
62   int length;
63 };
64 
65 struct LineLayout {
66   std::wstring text;
67   int line_length;
68   int line_width;
69   int line_start_offset;
70   std::vector<CharacterRange> character_positions;
71   LineLayout();
72 };
73 
74 struct SegmentMarkerLayout {
75   POINT from;
76   POINT to;
77   bool  highlighted;
78   SegmentMarkerLayout();
79 };
80 
81 struct CompositionWindowLayout {
82   RECT window_position_in_screen_coordinate;
83   RECT text_area;
84   RECT caret_rect;
85   POINT base_position;
86   LOGFONT log_font;
87   std::wstring text;
88   std::vector<SegmentMarkerLayout> marker_layouts;
89   CompositionWindowLayout();
90 };
91 
92 // A POD-like class which represents positional information about where the
93 // candidate window should be displayed.
94 // Do not inherit from this class. This class is not designed to be
95 // inheritable.
96 class CandidateWindowLayout {
97  public:
98   // Initializes fields with default values keeping |initialized_| false.
99   CandidateWindowLayout();
100 
101   // Destructor Stub.  Actually do nothing.
102   ~CandidateWindowLayout();
103 
104   // Returns true if this object is initialized with valid parameter.
105   bool initialized() const;
106 
107   // Initializes fields with given target position and sets true
108   // to |initialized_|.
109   void InitializeWithPosition(const POINT &position);
110 
111   // Initializes fields with given target position and exclude region and
112   // sets true to |initialized_|.
113   void InitializeWithPositionAndExcludeRegion(
114       const POINT &position, const RECT &exclude_region);
115 
116   // Clears fields and sets false to |initialized_|.
117   void Clear();
118 
119   // Returns target position in screen coordinate.
120   const POINT &position() const;
121 
122   // Returns exclude region in screen coordinate.  You can call this method
123   // when |has_exclude_region| returns true.
124   const RECT &exclude_region() const;
125 
126   // Returns true if |exclude_region| is available.
127   bool has_exclude_region() const;
128 
129  private:
130   POINT position_;
131   RECT exclude_region_;
132   bool has_exclude_region_;
133   bool initialized_;
134 };
135 
136 struct IndicatorWindowLayout {
137  public:
138   IndicatorWindowLayout();
139   void Clear();
140 
141   RECT window_rect;
142   bool is_vertical;
143 };
144 
145 // This interface is designed to hook API calls for unit test.
146 class SystemPreferenceInterface {
147  public:
~SystemPreferenceInterface()148   virtual ~SystemPreferenceInterface() {}
149 
150   // This method can be used to retrieve some kind of default font
151   // for GUI rendering.
152   // Returns true if succeeds.
153   virtual bool GetDefaultGuiFont(LOGFONTW *log_font) = 0;
154 };
155 
156 // This class implements SystemPreferenceInterface for unit tests.
157 class SystemPreferenceFactory {
158  public:
159   // Returns an instance of WindowPositionEmulator. Caller must delete
160   // the instance.
161   static SystemPreferenceInterface *CreateMock(const LOGFONTW &gui_font);
162 
163  private:
164   DISALLOW_IMPLICIT_CONSTRUCTORS(SystemPreferenceFactory);
165 };
166 
167 // This interface is designed to hook API calls for unit test.
168 class WorkingAreaInterface {
169  public:
~WorkingAreaInterface()170   virtual ~WorkingAreaInterface() {}
171 
172   virtual bool GetWorkingAreaFromPoint(const POINT &point,
173                                        RECT *working_area) = 0;
174 };
175 
176 class WorkingAreaFactory {
177  public:
178   // Returns an instance of WorkingAreaInterface. Caller must delete
179   // the instance.
180   static WorkingAreaInterface *Create();
181 
182   // Returns an instance of WorkingAreaInterface. Caller must delete
183   // the instance.
184   static WorkingAreaInterface *CreateMock(const RECT &working_area);
185 
186  private:
187   DISALLOW_IMPLICIT_CONSTRUCTORS(WorkingAreaFactory);
188 };
189 
190 // This interface is designed to hook API calls for unit test.
191 class WindowPositionInterface {
192  public:
~WindowPositionInterface()193   virtual ~WindowPositionInterface() {}
194 
195   // This method wraps API call of LogicalToPhysicalPoint.
196   // Returns true if this method can convert the given coordinate into
197   // physical space.  We do not declare this method as const method so
198   // that a mock class can implement this method in non-const way.
199   virtual bool LogicalToPhysicalPoint(
200       HWND window_handle, const POINT &logical_coordinate,
201       POINT *physical_coordinate) = 0;
202 
203   // This method wraps API call of GetWindowRect.
204   virtual bool GetWindowRect(HWND window_handle, RECT *rect) = 0;
205 
206   // This method wraps API call of GetClientRect.
207   virtual bool GetClientRect(HWND window_handle, RECT *rect) = 0;
208 
209   // This method wraps API call of ClientToScreen.
210   virtual bool ClientToScreen(HWND window_handle, POINT *point) = 0;
211 
212   // This method wraps API call of IsWindow.
213   virtual bool IsWindow(HWND window_handle) = 0;
214 
215   // This method wraps API call of GetAncestor/GA_ROOT.
216   virtual HWND GetRootWindow(HWND window_handle) = 0;
217 
218   // This method wraps API call of GetClassName.
219   virtual bool GetWindowClassName(HWND window_handle,
220                                   std::wstring *class_name) = 0;
221 };
222 
223 // This class implements WindowPositionInterface and emulates APIs
224 // with positional information registered by RegisterWindow method.
225 class WindowPositionEmulator : public WindowPositionInterface {
226  public:
227   // Returns an instance of WindowPositionEmulator.
228   static WindowPositionEmulator *Create();
229 
230   // Returns a dummy window handle for this emulator.  You can call methods of
231   // WindowPositionInterface with this dummy handle.  You need not to release
232   // the returned handle.
233   virtual HWND RegisterWindow(
234       const std::wstring &class_name, const RECT &window_rect,
235       const POINT &client_area_offset, const SIZE &client_area_size,
236       double scale_factor) = 0;
237 
238   virtual void SetRoot(HWND child_window, HWND root_window) = 0;
239 };
240 
241 enum CompatibilityMode {
242   // This flag represents the position can be calculated with default strategy.
243   COMPATIBILITY_MODE_NONE = 0,
244   // Some applications always keep CandidateForm up-to-date.  When this flag
245   // is specified, CandidateForm is taken into account so that the suggest
246   // window can be shown at more appropriate position.  Qt-based applications
247   // match this category.
248   CAN_USE_CANDIDATE_FORM_FOR_SUGGEST = 1,
249   // Some applications interpret the coordinate system of CandidateForm as
250   // the offset from the upper-left corner of the window as opposed to most
251   // of other applications which simply use local coordinate.  When this flag
252   // is specified, positional fields in CandidateForm are transformed as if
253   // they are the offset from the upper-left corner of the window.  GTK-based
254   // applications and Java AWT-based applications match this category.
255   USE_LOCAL_COORD_FOR_CANDIDATE_FORM = 2,
256   // Some applications such as V2C occasionally create zero-initialized
257   // CANDIDATEFORM and maintain it regardless of the actual position of the
258   // composition. This compatibility flag represents that it is better to
259   // ignore this phantom CANDIDATEFORM.  Currently all the Java AWT-based
260   // applications match this category.
261   IGNORE_DEFAULT_COMPOSITION_FORM = 4,
262   // Some applications such as NTEmacs22 or Meadow3.0 automatically and
263   // frequently generates WM_IME_CONTROL/IMC_SETCOMPOSITIONWINDOW messages.
264   // In this case, the renderer might not be able to show the InfoList
265   // because new rendering messages will come before the InfoList is
266   // displayed with the default delay (~500 msec). This compatibility flag
267   // represents that we should show the InfoList immediately on such
268   // applications. See b/5824433 for details.
269   // TODO(horo, yukawa): Perhapes we can improve the rendering algorithm
270   //     when the renderer is receiving the same messages successively.
271   //     If we can safely ignore such successive messages, this flag
272   //     can be removed.
273   SHOW_INFOLIST_IMMEDIATELY = 8,
274 };
275 
276 class LayoutManager {
277  public:
278   LayoutManager();
279   ~LayoutManager();
280 
281   // A special constructor for unit tests.  You can set a mock object which
282   // emulates native APIs for unit test.  This class is responsible for
283   // deleting the mock objects passed.
284   LayoutManager(SystemPreferenceInterface *mock_system_preference,
285                 WindowPositionInterface *mock_window_position);
286 
287   // Calculates layout of composition windows including preferred position of
288   // the candidate window and text attributes such as underline or text
289   // highlighting in the composition windows.  Returns true when succeeds.
290   // This method is thread-safe.
291   //  command: The renderer commant to be rendered.
292   //  composition_window_layouts: calculated layout for composition windows.
293   //  candidate_form: calculated position for the candidate window to be
294   //    displayed.
295   bool LayoutCompositionWindow(
296       const commands::RendererCommand &command,
297      std::vector<CompositionWindowLayout> *composition_window_layouts,
298       CandidateWindowLayout* candidate_layout) const;
299 
300   // Returns compatibility bits for given target application.
301   int GetCompatibilityMode(
302       const commands::RendererCommand_ApplicationInfo &app_info);
303 
304   // Determines the position where the suggest window should be placed when the
305   // composition window is drawn by the application.  This function does not
306   // take DPI virtualization into account.  In other words, any positional
307   // field in |candidate_form| is calculated in virtualized screen coordinates
308   // for the target application window.
309   bool LayoutCandidateWindowForSuggestion(
310       const commands::RendererCommand_ApplicationInfo &app_info,
311       CandidateWindowLayout *candidate_layout);
312 
313   // Determines the position where the candidate/predict window should be
314   // placed when the composition window is drawn by the application.  This
315   // function does not take DPI virtualization into account.  In other words,
316   // any positional field in |candidate_form| is calculated in virtualized
317   // screen coordinates for the target application window.
318   bool LayoutCandidateWindowForConversion(
319       const commands::RendererCommand_ApplicationInfo &app_info,
320       CandidateWindowLayout *candidate_layout);
321 
322   // Converts a virtualized screen coordinate for the DPI-unaware application
323   // specified by |window_handle| to the universal screen coordinate for
324   // DPI-aware applications.  When the LogicalToPhysicalPoint API is not
325   // available in the target platform, this function simply copies |point|
326   // into |result|.  If LogicalToPhysicalPoint API fails due to some
327   // limitations, this function tries to emulate the result assuming the
328   // scaling factor is one dimension and scaling center is (0, 0).
329   // See remarks of the following document for details about the limitations.
330   // http://msdn.microsoft.com/en-us/library/ms633533.aspx
331   // This method is thread-safe.
332   void GetPointInPhysicalCoords(
333       HWND window_handle, const POINT &point, POINT *result) const;
334 
335   // RECT version of GetPointInPhysicalCoords.  This method is thread-safe.
336   void GetRectInPhysicalCoords(
337       HWND window_handle, const RECT &rect, RECT *result) const;
338 
339   // Converts a local coordinate into a logical screen coordinate assuming
340   // |src_point| is the relative offset from the top-left of the
341   // window specified by the |src_window_handle|.
342   // Returns false if fails.
343   bool LocalPointToScreen(HWND src_window_handle, const POINT &src_point,
344                           POINT *dest_point) const;
345 
346   // Converts a local coordinate into a logical screen coordinate assuming
347   // |src_point| is the relative offset from the top-left of the
348   // window specified by the |src_window_handle|.
349   // Returns false if fails.
350   bool LocalRectToScreen(HWND src_window_handle, const RECT &src_rect,
351                          RECT *dest_rect) const;
352 
353   // Converts a local coordinate into a logical screen coordinate assuming
354   // |src_point| is the client coorinate in the window specified by
355   // |src_window_handle|.
356   // Returns false if fails.
357   bool ClientRectToScreen(HWND src_window_handle, const RECT &src_rect,
358                           RECT *dest_rect) const;
359 
360   // Converts a local coordinate into a logical screen coordinate assuming
361   // |src_point| is the client coorinate in the window specified by
362   // |src_window_handle|.
363   // Returns false if fails.
364   bool ClientPointToScreen(HWND src_window_handle, const POINT &src_point,
365                            POINT *dest_point) const;
366 
367   // Returns true if the client rect of the target window specified by
368   // |src_window_handle| is retrieved in a logical screen coordinate.
369   // Returns false if fails.
370   bool GetClientRect(HWND window_handle, RECT *client_rect) const;
371 
372   // Returns the scaling factor for DPI Virtualization.
373   // Returns 1.0 if any error occurs.
374   double GetScalingFactor(HWND window_handle) const;
375 
376   // Returns true if the default GUI font is retrieved.
377   // Returns false if fails.
378   bool GetDefaultGuiFont(LOGFONTW *logfong) const;
379 
380   // Represents preferred writing direction, especially for composition string.
381   enum WritingDirection {
382     // The writing direction is not specified.
383     WRITING_DIRECTION_UNSPECIFIED = 0,
384     // Horizontal writing is specified.
385     HORIZONTAL_WRITING = 1,
386     // Vertical writing is specified.
387     VERTICAL_WRITING = 2,
388   };
389 
390   // Returns writing direction.
391   static WritingDirection GetWritingDirection(
392       const commands::RendererCommand_ApplicationInfo &app_info);
393 
394   // Returns true when the target rect is successfully obtained.
395   bool LayoutIndicatorWindow(
396       const commands::RendererCommand_ApplicationInfo &app_info,
397       IndicatorWindowLayout *indicator_layout);
398 
399  private:
400   FRIEND_TEST(Win32RendererUtilTest, HorizontalProportional);
401   FRIEND_TEST(Win32RendererUtilTest, VerticalProportional);
402   FRIEND_TEST(Win32RendererUtilTest, HorizontalMonospaced);
403   FRIEND_TEST(Win32RendererUtilTest, VerticalMonospaced);
404   FRIEND_TEST(Win32RendererUtilTest, HorizontalProportionalCompositeGlyph);
405   FRIEND_TEST(Win32RendererUtilTest, VerticalProportionalCompositeGlyph);
406   FRIEND_TEST(Win32RendererUtilTest, HorizontalMonospacedCompositeGlyph);
407   FRIEND_TEST(Win32RendererUtilTest, VerticalMonospacedCompositeGlyph);
408 
409   // Calculates text layout with taking text wrapping into account.  Returns
410   // true when succeeds.
411   //  font: The font information to be used for calculation.
412   //  str: The string to be calculated.  Non-printable characters are not fully
413   //    supported.  For example, this function may not work well if |str|
414   //    contains CR, LF, HT, and VT.
415   //  maximum_line_length: The maximum length which limits the grows of text
416   //    for each line.
417   //  initial_offset: Represents cursor position where the first character in
418   //    the |str| starts from if there is enough space to show the first
419   //    character.
420   //  LineLayout: Layout information for each split line.
421   static bool CalcLayoutWithTextWrapping(
422       const LOGFONTW &font,
423       const std::wstring &text,
424       int maximum_line_length,
425       int initial_offset,
426      std::vector<LineLayout> *line_layouts);
427 
428   std::unique_ptr<SystemPreferenceInterface> system_preference_;
429   std::unique_ptr<WindowPositionInterface> window_position_;
430 
431   DISALLOW_COPY_AND_ASSIGN(LayoutManager);
432 };
433 
434 }  // namespace win32
435 }  // namespace renderer
436 }  // namespace mozc
437 
438 #endif  // OS_WIN
439 #endif  // MOZC_RENDERER_WIN32_WIN32_RENDERER_UTIL_H_
440