Index: ui/gfx/render_text_win.cc |
diff --git a/ui/gfx/render_text_win.cc b/ui/gfx/render_text_win.cc |
new file mode 100755 |
index 0000000000000000000000000000000000000000..be5fd5cc758fb74215504cf82d86bd595e97273f |
--- /dev/null |
+++ b/ui/gfx/render_text_win.cc |
@@ -0,0 +1,275 @@ |
+// Copyright (c) 2011 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "ui/gfx/render_text_win.h" |
+ |
+// TODO(msw): Remove headers we're not using after Uniscribe impl... |
+#include "base/i18n/break_iterator.h" |
+#include "base/i18n/rtl.h" |
+#include "base/logging.h" |
+#include "ui/gfx/canvas.h" |
+#include "ui/gfx/canvas_skia.h" |
+ |
+namespace { |
+ |
+// TODO(msw): Remove this and use Uniscribe rendering. |
+// Compute the windows flags necessary to implement the provided text Canvas |
+// flags. |
+int ComputeFormatFlags(int flags, const string16& text) { |
+ // Setting the text alignment explicitly in case it hasn't already been set. |
+ // This will make sure that we don't align text to the left on RTL locales |
+ // just because no alignment flag was passed to DrawStringInt(). |
+ if (!(flags & (gfx::Canvas::TEXT_ALIGN_CENTER | |
+ gfx::Canvas::TEXT_ALIGN_RIGHT | |
+ gfx::Canvas::TEXT_ALIGN_LEFT))) { |
+ flags |= gfx::CanvasSkia::DefaultCanvasTextAlignment(); |
+ } |
+ |
+ // horizontal alignment |
+ int f = 0; |
+ if (flags & gfx::Canvas::TEXT_ALIGN_CENTER) |
+ f |= DT_CENTER; |
+ else if (flags & gfx::Canvas::TEXT_ALIGN_RIGHT) |
+ f |= DT_RIGHT; |
+ else |
+ f |= DT_LEFT; |
+ |
+ // vertical alignment |
+ if (flags & gfx::Canvas::TEXT_VALIGN_TOP) |
+ f |= DT_TOP; |
+ else if (flags & gfx::Canvas::TEXT_VALIGN_BOTTOM) |
+ f |= DT_BOTTOM; |
+ else |
+ f |= DT_VCENTER; |
+ |
+ if (flags & gfx::Canvas::MULTI_LINE) { |
+ f |= DT_WORDBREAK; |
+ if (flags & gfx::Canvas::CHARACTER_BREAK) |
+ f |= DT_EDITCONTROL; // Turns on character breaking (not documented) |
+ else if (!(flags & gfx::Canvas::NO_ELLIPSIS)) |
+ f |= DT_WORD_ELLIPSIS; |
+ } else { |
+ f |= DT_SINGLELINE; |
+ } |
+ |
+ if (flags & gfx::Canvas::HIDE_PREFIX) |
+ f |= DT_HIDEPREFIX; |
+ else if ((flags & gfx::Canvas::SHOW_PREFIX) == 0) |
+ f |= DT_NOPREFIX; |
+ |
+ if (!(flags & gfx::Canvas::NO_ELLIPSIS)) |
+ f |= DT_END_ELLIPSIS; |
+ |
+ // In order to make sure RTL/BiDi strings are rendered correctly, we must |
+ // pass the flag DT_RTLREADING to DrawText (when the locale's language is |
+ // a right-to-left language) so that Windows does the right thing. |
+ // |
+ // In addition to correctly displaying text containing both RTL and LTR |
+ // elements (for example, a string containing a telephone number within a |
+ // sentence in Hebrew, or a sentence in Hebrew that contains a word in |
+ // English) this flag also makes sure that if there is not enough space to |
+ // display the entire string, the ellipsis is displayed on the left hand side |
+ // of the truncated string and not on the right hand side. |
+ // |
+ // We make a distinction between Chrome UI strings and text coming from a web |
+ // page. |
+ // |
+ // For text coming from a web page we determine the alignment based on the |
+ // first character with strong directionality. If the directionality of the |
+ // first character with strong directionality in the text is LTR, the |
+ // alignment is set to DT_LEFT, and the directionality should not be set as |
+ // DT_RTLREADING. |
+ // |
+ // This heuristic doesn't work for Chrome UI strings since even in RTL |
+ // locales, some of those might start with English text but we know they're |
+ // localized so we always want them to be right aligned, and their |
+ // directionality should be set as DT_RTLREADING. |
+ // |
+ // Caveat: If the string is purely LTR, don't set DTL_RTLREADING since when |
+ // the flag is set, LRE-PDF don't have the desired effect of rendering |
+ // multiline English-only text as LTR. |
+ // |
+ // Note that if the caller is explicitly requesting displaying the text |
+ // using RTL directionality then we respect that and pass DT_RTLREADING to |
+ // ::DrawText even if the locale is LTR. |
+ if ((flags & gfx::Canvas::FORCE_RTL_DIRECTIONALITY) || |
+ (base::i18n::IsRTL() && |
+ (f & DT_RIGHT) && base::i18n::StringContainsStrongRTLChars(text))) { |
+ f |= DT_RTLREADING; |
+ } |
+ |
+ return f; |
+} |
+ |
+// TODO(msw): Use Uniscribe. |
+// We make sure that LTR text we draw in an RTL context is modified |
+// appropriately to make sure it maintains it LTR orientation. |
+void DoDrawText(HDC hdc, |
+ const string16& text, |
+ RECT* text_bounds, |
+ int flags) { |
+ // Only adjust string directionality if both of the following are true: |
+ // 1. The current locale is RTL. |
+ // 2. The string itself has RTL directionality. |
+ const wchar_t* string_ptr = text.c_str(); |
+ int string_size = static_cast<int>(text.length()); |
+ |
+ string16 localized_text; |
+ if (flags & DT_RTLREADING) { |
+ localized_text = text; |
+ base::i18n::AdjustStringForLocaleDirection(&localized_text); |
+ string_ptr = localized_text.c_str(); |
+ string_size = static_cast<int>(localized_text.length()); |
+ } |
+ |
+ DrawText(hdc, string_ptr, string_size, text_bounds, flags); |
+} |
+ |
+} // namespace |
+ |
+namespace gfx { |
+ |
+RenderTextWin::RenderTextWin() |
+ : RenderText() { |
+} |
+ |
+RenderTextWin::RenderTextWin(const string16& text, |
+ const gfx::Font& font, |
+ const SkColor& color, |
+ const gfx::Rect display_rect, |
+ int flags) |
+ : RenderText(text, font, color, display_rect, flags) { |
+} |
+ |
+RenderTextWin::~RenderTextWin() { |
+} |
+ |
+void RenderTextWin::Draw(HDC drawing_context) const { |
+ // TODO(msw): Use Uniscribe. |
+ gfx::Rect r = get_display_rect(); |
+ RECT text_bounds = { r.x(), r.y(), r.x() + r.width(), r.y() + r.height() }; |
+ int flags = ComputeFormatFlags(get_flags(), text()); |
+ DoDrawText(drawing_context, text(), &text_bounds, flags); |
+} |
+ |
+size_t RenderTextWin::FindCursorPosition(const gfx::Point& point) const { |
+ // TODO(msw): Use Uniscribe. |
+ const gfx::Font& font = GetStyleRanges()[0]->font; |
+ // TODO(msw): gfx::Insets insets = GetInsets(); |
+ int left = 0; |
+ int left_pos = 0; |
+ int right = font.GetStringWidth(text()); |
+ int right_pos = text().length(); |
+ |
+ int x = point.x(); // TODO(msw): - insets.left() - text_offset_; |
+ if (x <= left) return left_pos; |
+ if (x >= right) return right_pos; |
+ // binary searching the cursor position. |
+ // TODO(oshima): use the center of character instead of edge. |
+ // Binary search may not work for language like arabic. |
+ while (std::abs(static_cast<long>(right_pos - left_pos) > 1)) { |
+ int pivot_pos = left_pos + (right_pos - left_pos) / 2; |
+ int pivot = font.GetStringWidth(text().substr(0, pivot_pos)); |
+ if (pivot < x) { |
+ left = pivot; |
+ left_pos = pivot_pos; |
+ } else if (pivot == x) { |
+ return pivot_pos; |
+ } else { |
+ right = pivot; |
+ right_pos = pivot_pos; |
+ } |
+ } |
+ return left_pos; |
+} |
+ |
+std::vector<gfx::Rect> RenderTextWin::GetSubstringBounds( |
+ const ui::Range& range) const { |
+ // TODO(msw): Use Uniscribe. |
+ size_t start = range.GetMin(); |
+ size_t end = range.GetMax(); |
+ const gfx::Font& font = GetStyleRanges()[0]->font; |
+ int start_x = font.GetStringWidth(text().substr(0, start)); |
+ int end_x = font.GetStringWidth(text().substr(0, end)); |
+ std::vector<gfx::Rect> bounds; |
+ bounds.push_back(gfx::Rect(start_x, 0, end_x - start_x, font.GetHeight())); |
+ return bounds; |
+} |
+ |
+size_t RenderTextWin::GetLeftCursorPosition(size_t position, |
+ bool move_by_word) const { |
+ // TODO(msw): Use Uniscribe. |
+ if (!move_by_word) |
+ return position == 0? position : position - 1; |
+ // Notes: We always iterate words from the begining. |
+ // This is probably fast enough for our usage, but we may |
+ // want to modify WordIterator so that it can start from the |
+ // middle of string and advance backwards. |
+ base::i18n::BreakIterator iter(text(), base::i18n::BreakIterator::BREAK_WORD); |
+ bool success = iter.Init(); |
+ DCHECK(success); |
+ if (!success) |
+ return position; |
+ int last = 0; |
+ while (iter.Advance()) { |
+ if (iter.IsWord()) { |
+ size_t begin = iter.pos() - iter.GetString().length(); |
+ if (begin == position) { |
+ // The cursor is at the beginning of a word. |
+ // Move to previous word. |
+ break; |
+ } else if(iter.pos() >= position) { |
+ // The cursor is in the middle or at the end of a word. |
+ // Move to the top of current word. |
+ last = begin; |
+ break; |
+ } else { |
+ last = iter.pos() - iter.GetString().length(); |
+ } |
+ } |
+ } |
+ |
+ return last; |
+} |
+ |
+size_t RenderTextWin::GetRightCursorPosition(size_t position, |
+ bool move_by_word) const { |
+ // TODO(msw): Use Uniscribe. |
+ if (!move_by_word) |
+ return std::min(position + 1, text().length()); |
+ base::i18n::BreakIterator iter(text(), base::i18n::BreakIterator::BREAK_WORD); |
+ bool success = iter.Init(); |
+ DCHECK(success); |
+ if (!success) |
+ return position; |
+ size_t pos = 0; |
+ while (iter.Advance()) { |
+ pos = iter.pos(); |
+ if (iter.IsWord() && pos > position) { |
+ break; |
+ } |
+ } |
+ return pos; |
+} |
+ |
+gfx::Rect RenderTextWin::GetCursorBounds(size_t position, |
+ bool insert_mode) const { |
+ // TODO(msw): Use Uniscribe. |
+ NOTIMPLEMENTED(); |
+ return gfx::Rect(); |
+} |
+ |
+RenderText* RenderText::CreateRenderText() { |
+ return new RenderTextWin; |
+} |
+ |
+RenderText* RenderText::CreateRenderText(const string16& text, |
+ const gfx::Font& font, |
+ const SkColor& color, |
+ const gfx::Rect& display_rect, |
+ int flags) { |
+ return new RenderTextWin(text, font, color, display_rect, flags); |
+} |
+ |
+} // namespace gfx |