blob: a8e041e44f9423add28eeef14e85d72f650d60d7 [file] [log] [blame]
// 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 "views/controls/textfield/textfield.h"
#if defined(TOOLKIT_USES_GTK)
#include <gdk/gdkkeysyms.h>
#endif
#include <string>
#include "base/string_util.h"
#include "base/utf_string_conversions.h"
#include "ui/base/accessibility/accessible_view_state.h"
#include "ui/base/ime/text_input_type.h"
#include "ui/base/keycodes/keyboard_codes.h"
#include "ui/base/range/range.h"
#include "ui/gfx/insets.h"
#include "ui/gfx/selection_model.h"
#include "views/controls/native/native_view_host.h"
#include "views/controls/textfield/native_textfield_wrapper.h"
#include "views/controls/textfield/textfield_controller.h"
#include "views/widget/widget.h"
#if defined(OS_LINUX)
#include "ui/base/keycodes/keyboard_code_conversion_gtk.h"
#elif defined(OS_WIN)
#include "base/win/win_util.h"
// TODO(beng): this should be removed when the OS_WIN hack from
// ViewHierarchyChanged is removed.
#include "views/controls/textfield/native_textfield_views.h"
#include "views/controls/textfield/native_textfield_win.h"
#endif
namespace views {
// static
const char Textfield::kViewClassName[] = "views/Textfield";
/////////////////////////////////////////////////////////////////////////////
// Textfield
Textfield::Textfield()
: native_wrapper_(NULL),
controller_(NULL),
style_(STYLE_DEFAULT),
read_only_(false),
default_width_in_chars_(0),
draw_border_(true),
text_color_(SK_ColorBLACK),
use_default_text_color_(true),
background_color_(SK_ColorWHITE),
use_default_background_color_(true),
initialized_(false),
horizontal_margins_were_set_(false),
vertical_margins_were_set_(false),
text_input_type_(ui::TEXT_INPUT_TYPE_TEXT) {
set_focusable(true);
}
Textfield::Textfield(StyleFlags style)
: native_wrapper_(NULL),
controller_(NULL),
style_(style),
read_only_(false),
default_width_in_chars_(0),
draw_border_(true),
text_color_(SK_ColorBLACK),
use_default_text_color_(true),
background_color_(SK_ColorWHITE),
use_default_background_color_(true),
initialized_(false),
horizontal_margins_were_set_(false),
vertical_margins_were_set_(false),
text_input_type_(ui::TEXT_INPUT_TYPE_TEXT) {
set_focusable(true);
if (IsPassword())
SetTextInputType(ui::TEXT_INPUT_TYPE_PASSWORD);
}
Textfield::~Textfield() {
}
void Textfield::SetController(TextfieldController* controller) {
controller_ = controller;
}
TextfieldController* Textfield::GetController() const {
return controller_;
}
void Textfield::SetReadOnly(bool read_only) {
read_only_ = read_only;
if (native_wrapper_) {
native_wrapper_->UpdateReadOnly();
native_wrapper_->UpdateTextColor();
native_wrapper_->UpdateBackgroundColor();
}
}
bool Textfield::IsPassword() const {
return style_ & STYLE_PASSWORD;
}
void Textfield::SetPassword(bool password) {
if (password) {
style_ = static_cast<StyleFlags>(style_ | STYLE_PASSWORD);
SetTextInputType(ui::TEXT_INPUT_TYPE_PASSWORD);
} else {
style_ = static_cast<StyleFlags>(style_ & ~STYLE_PASSWORD);
SetTextInputType(ui::TEXT_INPUT_TYPE_TEXT);
}
if (native_wrapper_)
native_wrapper_->UpdateIsPassword();
}
ui::TextInputType Textfield::GetTextInputType() const {
if (read_only() || !IsEnabled())
return ui::TEXT_INPUT_TYPE_NONE;
return text_input_type_;
}
void Textfield::SetTextInputType(ui::TextInputType type) {
text_input_type_ = type;
bool should_be_password = type == ui::TEXT_INPUT_TYPE_PASSWORD;
if (IsPassword() != should_be_password)
SetPassword(should_be_password);
}
void Textfield::SetText(const string16& text) {
text_ = text;
if (native_wrapper_)
native_wrapper_->UpdateText();
}
void Textfield::AppendText(const string16& text) {
text_ += text;
if (native_wrapper_)
native_wrapper_->AppendText(text);
}
void Textfield::SelectAll() {
if (native_wrapper_)
native_wrapper_->SelectAll();
}
string16 Textfield::GetSelectedText() const {
if (native_wrapper_)
return native_wrapper_->GetSelectedText();
return string16();
}
void Textfield::ClearSelection() const {
if (native_wrapper_)
native_wrapper_->ClearSelection();
}
bool Textfield::HasSelection() const {
ui::Range range;
if (native_wrapper_)
native_wrapper_->GetSelectedRange(&range);
return !range.is_empty();
}
void Textfield::SetTextColor(SkColor color) {
text_color_ = color;
use_default_text_color_ = false;
if (native_wrapper_)
native_wrapper_->UpdateTextColor();
}
void Textfield::UseDefaultTextColor() {
use_default_text_color_ = true;
if (native_wrapper_)
native_wrapper_->UpdateTextColor();
}
void Textfield::SetBackgroundColor(SkColor color) {
background_color_ = color;
use_default_background_color_ = false;
if (native_wrapper_)
native_wrapper_->UpdateBackgroundColor();
}
void Textfield::UseDefaultBackgroundColor() {
use_default_background_color_ = true;
if (native_wrapper_)
native_wrapper_->UpdateBackgroundColor();
}
void Textfield::SetFont(const gfx::Font& font) {
font_ = font;
if (native_wrapper_)
native_wrapper_->UpdateFont();
PreferredSizeChanged();
}
void Textfield::SetHorizontalMargins(int left, int right) {
margins_.Set(margins_.top(), left, margins_.bottom(), right);
horizontal_margins_were_set_ = true;
if (native_wrapper_)
native_wrapper_->UpdateHorizontalMargins();
PreferredSizeChanged();
}
void Textfield::SetVerticalMargins(int top, int bottom) {
margins_.Set(top, margins_.left(), bottom, margins_.right());
vertical_margins_were_set_ = true;
if (native_wrapper_)
native_wrapper_->UpdateVerticalMargins();
PreferredSizeChanged();
}
void Textfield::RemoveBorder() {
if (!draw_border_)
return;
draw_border_ = false;
if (native_wrapper_)
native_wrapper_->UpdateBorder();
}
bool Textfield::GetHorizontalMargins(int* left, int* right) {
if (!horizontal_margins_were_set_)
return false;
*left = margins_.left();
*right = margins_.right();
return true;
}
bool Textfield::GetVerticalMargins(int* top, int* bottom) {
if (!vertical_margins_were_set_)
return false;
*top = margins_.top();
*bottom = margins_.bottom();
return true;
}
void Textfield::UpdateAllProperties() {
if (native_wrapper_) {
native_wrapper_->UpdateText();
native_wrapper_->UpdateTextColor();
native_wrapper_->UpdateBackgroundColor();
native_wrapper_->UpdateReadOnly();
native_wrapper_->UpdateFont();
native_wrapper_->UpdateEnabled();
native_wrapper_->UpdateBorder();
native_wrapper_->UpdateIsPassword();
native_wrapper_->UpdateHorizontalMargins();
native_wrapper_->UpdateVerticalMargins();
}
}
void Textfield::SyncText() {
if (native_wrapper_) {
string16 new_text = native_wrapper_->GetText();
if (new_text != text_) {
text_ = new_text;
if (controller_)
controller_->ContentsChanged(this, text_);
}
}
}
bool Textfield::IsIMEComposing() const {
return native_wrapper_ && native_wrapper_->IsIMEComposing();
}
void Textfield::GetSelectedRange(ui::Range* range) const {
DCHECK(native_wrapper_);
native_wrapper_->GetSelectedRange(range);
}
void Textfield::SelectRange(const ui::Range& range) {
DCHECK(native_wrapper_);
native_wrapper_->SelectRange(range);
}
void Textfield::GetSelectionModel(gfx::SelectionModel* sel) const {
DCHECK(native_wrapper_);
native_wrapper_->GetSelectionModel(sel);
}
void Textfield::SelectSelectionModel(const gfx::SelectionModel& sel) {
DCHECK(native_wrapper_);
native_wrapper_->SelectSelectionModel(sel);
}
size_t Textfield::GetCursorPosition() const {
DCHECK(native_wrapper_);
return native_wrapper_->GetCursorPosition();
}
void Textfield::ApplyStyleRange(const gfx::StyleRange& style) {
DCHECK(native_wrapper_);
return native_wrapper_->ApplyStyleRange(style);
}
void Textfield::ApplyDefaultStyle() {
DCHECK(native_wrapper_);
native_wrapper_->ApplyDefaultStyle();
}
void Textfield::ClearEditHistory() {
DCHECK(native_wrapper_);
native_wrapper_->ClearEditHistory();
}
void Textfield::SetAccessibleName(const string16& name) {
accessible_name_ = name;
}
////////////////////////////////////////////////////////////////////////////////
// Textfield, View overrides:
void Textfield::Layout() {
if (native_wrapper_) {
native_wrapper_->GetView()->SetBoundsRect(GetLocalBounds());
native_wrapper_->GetView()->Layout();
}
}
gfx::Size Textfield::GetPreferredSize() {
gfx::Insets insets;
if (draw_border_ && native_wrapper_)
insets = native_wrapper_->CalculateInsets();
return gfx::Size(font_.GetExpectedTextWidth(default_width_in_chars_) +
insets.width(), font_.GetHeight() + insets.height());
}
bool Textfield::IsFocusable() const {
return View::IsFocusable() && !read_only_;
}
void Textfield::AboutToRequestFocusFromTabTraversal(bool reverse) {
SelectAll();
}
bool Textfield::SkipDefaultKeyEventProcessing(const KeyEvent& e) {
// TODO(hamaji): Figure out which keyboard combinations we need to add here,
// similar to LocationBarView::SkipDefaultKeyEventProcessing.
ui::KeyboardCode key = e.key_code();
if (key == ui::VKEY_BACK)
return true; // We'll handle BackSpace ourselves.
#if defined(USE_AURA)
NOTIMPLEMENTED();
#elif defined(OS_WIN)
// We don't translate accelerators for ALT + NumPad digit on Windows, they are
// used for entering special characters. We do translate alt-home.
if (e.IsAltDown() && (key != ui::VKEY_HOME) &&
NativeTextfieldWin::IsNumPadDigit(key,
(e.flags() & ui::EF_EXTENDED) != 0))
return true;
#endif
return false;
}
void Textfield::OnPaintBackground(gfx::Canvas* canvas) {
// Overridden to be public - gtk_views_entry.cc wants to call it.
View::OnPaintBackground(canvas);
}
void Textfield::OnPaintFocusBorder(gfx::Canvas* canvas) {
if (NativeViewHost::kRenderNativeControlFocus)
View::OnPaintFocusBorder(canvas);
}
bool Textfield::OnKeyPressed(const views::KeyEvent& e) {
return native_wrapper_ && native_wrapper_->HandleKeyPressed(e);
}
bool Textfield::OnKeyReleased(const views::KeyEvent& e) {
return native_wrapper_ && native_wrapper_->HandleKeyReleased(e);
}
void Textfield::OnFocus() {
if (native_wrapper_)
native_wrapper_->HandleFocus();
// Forward the focus to the wrapper if it exists.
if (!native_wrapper_ || !native_wrapper_->SetFocus()) {
// If there is no wrapper or the wrapper didn't take focus, call
// View::Focus to clear the native focus so that we still get
// keyboard messages.
View::OnFocus();
}
}
void Textfield::OnBlur() {
if (native_wrapper_)
native_wrapper_->HandleBlur();
}
void Textfield::GetAccessibleState(ui::AccessibleViewState* state) {
state->role = ui::AccessibilityTypes::ROLE_TEXT;
state->name = accessible_name_;
if (read_only())
state->state |= ui::AccessibilityTypes::STATE_READONLY;
if (IsPassword())
state->state |= ui::AccessibilityTypes::STATE_PROTECTED;
state->value = text_;
DCHECK(native_wrapper_);
ui::Range range;
native_wrapper_->GetSelectedRange(&range);
state->selection_start = range.start();
state->selection_end = range.end();
}
ui::TextInputClient* Textfield::GetTextInputClient() {
return native_wrapper_ ? native_wrapper_->GetTextInputClient() : NULL;
}
void Textfield::OnEnabledChanged() {
View::OnEnabledChanged();
if (native_wrapper_)
native_wrapper_->UpdateEnabled();
}
void Textfield::ViewHierarchyChanged(bool is_add, View* parent, View* child) {
if (is_add && !native_wrapper_ && GetWidget() && !initialized_) {
initialized_ = true;
// The native wrapper's lifetime will be managed by the view hierarchy after
// we call AddChildView.
native_wrapper_ = NativeTextfieldWrapper::CreateWrapper(this);
AddChildView(native_wrapper_->GetView());
// TODO(beng): Move this initialization to NativeTextfieldWin once it
// subclasses NativeControlWin.
UpdateAllProperties();
#if defined(OS_WIN) && !defined(USE_AURA)
if (!views::Widget::IsPureViews()) {
// TODO(beng): remove this once NativeTextfieldWin subclasses
// NativeControlWin. This is currently called to perform post-AddChildView
// initialization for the wrapper. The GTK version subclasses things
// correctly and doesn't need this.
//
// Remove the include for native_textfield_win.h above when you fix this.
static_cast<NativeTextfieldWin*>(native_wrapper_)->AttachHack();
}
#endif
}
}
std::string Textfield::GetClassName() const {
return kViewClassName;
}
} // namespace views