blob: 2902fb86428e9c452870249bc0fa6f9df04e26d6 [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.
#include "win32/tip/tip_input_mode_manager.h"
#include <algorithm>
#include "win32/base/conversion_mode_util.h"
#include "win32/base/indicator_visibility_tracker.h"
namespace mozc {
typedef ::mozc::commands::CompositionMode CompositionMode;
namespace win32 {
namespace tsf {
namespace {
template <typename T>
void Dedup(vector<T> *container) {
sort(container->begin(), container->end());
auto new_end = unique(container->begin(), container->end());
container->erase(new_end, container->end());
}
} // namespace
TipInputModeManagerImpl::StatePair::StatePair()
: open_close(false),
conversion_mode(kHiragana) {}
TipInputModeManagerImpl::StatePair::StatePair(
bool open_close, ConversionMode conversion_mode)
: open_close(open_close),
conversion_mode(conversion_mode) {}
TipInputModeManagerImpl::StatePair TipInputModeManagerImpl::GetOverriddenState(
const StatePair &base_state,
const vector<InputScope> &input_scopes) {
if (input_scopes.empty()) {
return base_state;
}
vector<ConversionMode> states;
for (size_t i = 0; i < input_scopes.size(); ++i) {
const InputScope input_scope = input_scopes[i];
switch (input_scope) {
// Some InputStope can be mapped to commands::Context::InputFieldType.
// TODO(yukawa): Pass context information to the converter.
case IS_URL:
case IS_EMAIL_USERNAME:
case IS_EMAIL_SMTPEMAILADDRESS:
case IS_DIGITS:
case IS_NUMBER:
case IS_PASSWORD:
case IS_TELEPHONE_FULLTELEPHONENUMBER:
case IS_TELEPHONE_COUNTRYCODE:
case IS_TELEPHONE_AREACODE:
case IS_TELEPHONE_LOCALNUMBER:
case IS_TIME_FULLTIME:
case IS_TIME_HOUR:
case IS_TIME_MINORSEC:
states.push_back(kDirect);
break;
case IS_HIRAGANA:
states.push_back(kHiragana);
break;
case IS_ALPHANUMERIC_HALFWIDTH:
states.push_back(kHalfAscii);
break;
case IS_NUMBER_FULLWIDTH:
case IS_ALPHANUMERIC_FULLWIDTH:
states.push_back(kFullAscii);
break;
case IS_KATAKANA_HALFWIDTH:
states.push_back(kHalfKatakana);
break;
case IS_KATAKANA_FULLWIDTH:
states.push_back(kFullKatakana);
break;
default:
break;
}
}
if (states.empty()) {
return base_state;
}
Dedup(&states);
if (states.size() > 1) {
// Multiple mode found.
// TODO(yukawa): consider this case.
return base_state;
}
if (states[0] == kDirect) {
return StatePair(false, base_state.conversion_mode);
}
return StatePair(true, states[0]);
}
TipInputModeManager::Config::Config()
: use_global_mode(false) {}
class TipInputModeManager::InternalState {
public:
InternalState()
: use_global_mode(false) {}
bool use_global_mode;
StatePair mozc_state;
StatePair tsf_state;
IndicatorVisibilityTracker indicator_visibility_tracker;
vector<InputScope> input_scope;
};
// For Mode Indicator.
TipInputModeManager::Action TipInputModeManager::OnDissociateContext() {
const IndicatorVisibilityTracker::Action action =
state_->indicator_visibility_tracker.OnDissociateContext();
switch (action) {
case IndicatorVisibilityTracker::kUpdateUI:
return kUpdateUI;
default:
return kDoNothing;
}
}
TipInputModeManager::Action TipInputModeManager::OnTestKey(
const VirtualKey &key, bool is_down, bool eaten) {
const IndicatorVisibilityTracker::Action action =
state_->indicator_visibility_tracker.OnTestKey(key, is_down, eaten);
switch (action) {
case IndicatorVisibilityTracker::kUpdateUI:
return kUpdateUI;
default:
return kDoNothing;
}
}
TipInputModeManager::Action TipInputModeManager::OnKey(
const VirtualKey &key, bool is_down, bool eaten) {
const IndicatorVisibilityTracker::Action action =
state_->indicator_visibility_tracker.OnKey(key, is_down, eaten);
switch (action) {
case IndicatorVisibilityTracker::kUpdateUI:
return kUpdateUI;
default:
return kDoNothing;
}
}
TipInputModeManager::Action TipInputModeManager::OnMoveFocusedWindow() {
const IndicatorVisibilityTracker::Action action =
state_->indicator_visibility_tracker.OnMoveFocusedWindow();
switch (action) {
case IndicatorVisibilityTracker::kUpdateUI:
return kUpdateUI;
default:
return kDoNothing;
}
}
TipInputModeManager::TipInputModeManager(const Config &config)
: state_(new InternalState) {
state_->use_global_mode = config.use_global_mode;
}
TipInputModeManager::~TipInputModeManager() {}
TipInputModeManager::NotifyActionSet TipInputModeManager::OnReceiveCommand(
bool mozc_open_close_mode,
DWORD mozc_logical_mode,
DWORD mozc_visible_mode) {
const StatePair prev_tsf_state = state_->tsf_state;
state_->tsf_state.open_close = mozc_open_close_mode;
state_->tsf_state.conversion_mode =
static_cast<ConversionMode>(mozc_logical_mode);
state_->mozc_state.open_close = mozc_open_close_mode;
state_->mozc_state.conversion_mode =
static_cast<ConversionMode>(mozc_visible_mode);
NotifyActionSet action_set = kNotifyNothing;
if (prev_tsf_state.open_close != state_->tsf_state.open_close) {
action_set |= kNotifySystemOpenClose;
}
if (prev_tsf_state.conversion_mode != state_->tsf_state.conversion_mode) {
action_set |= kNotifySystemConversionMode;
}
if (action_set != kNotifyNothing) {
state_->indicator_visibility_tracker.OnChangeInputMode();
}
return action_set;
}
void TipInputModeManager::OnInitialize(bool system_open_close_mode,
DWORD system_conversion_mode) {
state_->mozc_state.open_close = system_open_close_mode;
state_->tsf_state.open_close = system_open_close_mode;
if (state_->use_global_mode) {
return;
}
CompositionMode mozc_mode = commands::HIRAGANA;
if (ConversionModeUtil::ToMozcMode(system_conversion_mode, &mozc_mode)) {
state_->tsf_state.conversion_mode =
static_cast<ConversionMode>(mozc_mode);
}
state_->mozc_state.conversion_mode = state_->tsf_state.conversion_mode;
}
TipInputModeManager::Action TipInputModeManager::OnSetFocus(
bool system_open_close_mode,
DWORD system_conversion_mode,
const vector<InputScope> &input_scopes) {
const StatePair prev_effective = state_->mozc_state;
state_->indicator_visibility_tracker.OnMoveFocusedWindow();
vector<InputScope> new_input_scopes = input_scopes;
Dedup(&new_input_scopes);
state_->tsf_state.open_close = system_open_close_mode;
CompositionMode mozc_mode = commands::HIRAGANA;
if (!state_->use_global_mode) {
if (ConversionModeUtil::ToMozcMode(system_conversion_mode, &mozc_mode)) {
state_->tsf_state.conversion_mode =
static_cast<ConversionMode>(mozc_mode);
}
}
if (new_input_scopes.size() > 0 &&
(new_input_scopes == state_->input_scope)) {
// The same input scope is specified. Use the previous mode.
return kDoNothing;
}
swap(state_->input_scope, new_input_scopes);
state_->mozc_state = GetOverriddenState(state_->tsf_state, input_scopes);
if ((state_->mozc_state.open_close != prev_effective.open_close) ||
(state_->mozc_state.conversion_mode !=
prev_effective.conversion_mode)) {
state_->indicator_visibility_tracker.OnChangeInputMode();
return kUpdateUI;
}
return kDoNothing;
}
TipInputModeManager::Action
TipInputModeManager::OnChangeOpenClose(bool new_open_close_mode) {
const bool prev_open = state_->mozc_state.open_close; // effective on/off
state_->tsf_state.open_close = new_open_close_mode;
if (prev_open != new_open_close_mode) {
state_->mozc_state.open_close = new_open_close_mode;
state_->indicator_visibility_tracker.OnChangeInputMode();
return kUpdateUI;
}
return kDoNothing;
}
TipInputModeManager::Action
TipInputModeManager::OnChangeConversionMode(DWORD new_conversion_mode) {
const StatePair prev_effective = state_->mozc_state;
if (state_->use_global_mode) {
// Ignore mode change.
return kDoNothing;
}
CompositionMode mozc_mode = commands::HIRAGANA;
if (ConversionModeUtil::ToMozcMode(new_conversion_mode, &mozc_mode)) {
state_->tsf_state.conversion_mode =
static_cast<ConversionMode>(mozc_mode);
state_->mozc_state.conversion_mode =
static_cast<ConversionMode>(mozc_mode);
}
if (prev_effective.conversion_mode !=
state_->mozc_state.conversion_mode) {
state_->indicator_visibility_tracker.OnChangeInputMode();
return kUpdateUI;
}
return kDoNothing;
}
TipInputModeManager::Action
TipInputModeManager::OnChangeInputScope(
const vector<InputScope> &input_scopes) {
const StatePair prev_effective = state_->mozc_state;
vector<InputScope> new_input_scopes = input_scopes;
Dedup(&new_input_scopes);
if (new_input_scopes == state_->input_scope) {
// The same input scope is specified. Use the previous mode.
return kDoNothing;
}
swap(state_->input_scope, new_input_scopes);
state_->mozc_state = GetOverriddenState(state_->tsf_state, input_scopes);
if ((state_->mozc_state.open_close != prev_effective.open_close) ||
(state_->mozc_state.conversion_mode !=
prev_effective.conversion_mode)) {
state_->indicator_visibility_tracker.OnChangeInputMode();
return kUpdateUI;
}
return kDoNothing;
}
bool TipInputModeManager::GetEffectiveOpenClose() const {
return state_->mozc_state.open_close;
}
bool TipInputModeManager::GetTsfOpenClose() const {
return state_->tsf_state.open_close;
}
TipInputModeManagerImpl::ConversionMode
TipInputModeManager::GetEffectiveConversionMode() const {
return state_->mozc_state.conversion_mode;
}
TipInputModeManagerImpl::ConversionMode
TipInputModeManager::GetTsfConversionMode() const {
return state_->tsf_state.conversion_mode;
}
bool TipInputModeManager::IsIndicatorVisible() const {
return state_->indicator_visibility_tracker.IsVisible();
}
} // namespace tsf
} // namespace win32
} // namespace mozc