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