blob: 6c795625a6fdf0c8377e7777a130612eab127eb0 [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_lang_bar.h"
#include "base/logging.h"
#include "base/system_util.h"
#include "base/win_util.h"
#include "session/commands.pb.h"
#include "win32/tip/tip_dll_module.h"
#include "win32/tip/tip_lang_bar_menu.h"
#include "win32/tip/tip_resource.h"
namespace mozc {
namespace win32 {
namespace tsf {
namespace {
using ATL::CComPtr;
using ATL::CComQIPtr;
// The GUID of the help menu in the system language bar.
// {ED9D5450-EBE6-4255-8289-F8A31E687228}
const GUID kSystemLangBarHelpMenu = {
0xED9D5450, 0xEBE6, 0x4255, {0x82, 0x89, 0xF8, 0xA3, 0x1E, 0x68, 0x72, 0x28}
};
// For Windows 8
// {2C77A81E-41CC-4178-A3A7-5F8A987568E6} == GUID_LBI_INPUTMODE
const GUID kSystemInputMode = {
0x2C77A81E, 0x41CC, 0x4178, {0xA3, 0xA7, 0x5F, 0x8A, 0x98, 0x75, 0x68, 0xE6}
};
#ifdef GOOGLE_JAPANESE_INPUT_BUILD
// {D8C8D5EB-8213-47CE-95B7-BA3F67757F94}
const GUID kTipLangBarItem_Button= {
0xd8c8d5eb, 0x8213, 0x47ce, {0x95, 0xb7, 0xba, 0x3f, 0x67, 0x75, 0x7f, 0x94}
};
// {0EAB48C4-F798-4CC8-91FA-087B24F520A8}
const GUID kTipLangBarItem_ToolButton = {
0xeab48c4, 0xf798, 0x4cc8, {0x91, 0xfa, 0x8, 0x7b, 0x24, 0xf5, 0x20, 0xa8}
};
// {6D46F0F2-2924-4666-9B89-4F23699B2203}
const GUID kTipLangBarItem_HelpMenu = {
0x6d46f0f2, 0x2924, 0x4666, {0x9b, 0x89, 0x4f, 0x23, 0x69, 0x9b, 0x22, 0x3}
};
#else
// {FC8E2486-F5BA-4863-91C3-8D166B454604}
const GUID kTipLangBarItem_Button = {
0xfc8e2486, 0xf5ba, 0x4863, {0x91, 0xc3, 0x8d, 0x16, 0x6b, 0x45, 0x46, 0x4}
};
// {1BA637CA-7521-4F21-B51E-6516271A9FE3}
const GUID kTipLangBarItem_ToolButton = {
0x1ba637ca, 0x7521, 0x4f21, {0xb5, 0x1e, 0x65, 0x16, 0x27, 0x1a, 0x9f, 0xe3}
};
// {F78AD6B1-49D3-400E-8218-896F22A70011}
const GUID kTipLangBarItem_HelpMenu = {
0xf78ad6b1, 0x49d3, 0x400e, {0x82, 0x18, 0x89, 0x6f, 0x22, 0xa7, 0x0, 0x11}
};
#endif
const bool kShowInTaskbar = true;
CComPtr<ITfLangBarItemMgr> GetLangBarItemMgr() {
// "msctf.dll" is not always available. For example, Windows XP can disable
// TSF completely. In this case, the "msctf.dll" is not loaded.
// Note that "msctf.dll" never be unloaded when it exists because we
// increments its reference count here. This prevents weired crashes such as
// b/4322508.
const HMODULE module =
WinUtil::GetSystemModuleHandleAndIncrementRefCount(L"msctf.dll");
if (module == nullptr) {
return nullptr;
}
void *function = ::GetProcAddress(module, "TF_CreateLangBarItemMgr");
if (function == nullptr) {
return nullptr;
}
typedef HRESULT (WINAPI *FPTF_CreateLangBarItemMgr)(
ITfLangBarItemMgr **pplbim);
CComPtr<ITfLangBarItemMgr> ptr;
const HRESULT result = reinterpret_cast<FPTF_CreateLangBarItemMgr>(function)(
&ptr);
if (FAILED(result)) {
return nullptr;
}
return ptr;
}
TipLangBarCallback::ItemId GetItemId(DWORD composition_mode) {
switch (composition_mode) {
case commands::DIRECT:
return TipLangBarCallback::kDirect;
case commands::HIRAGANA:
return TipLangBarCallback::kHiragana;
case commands::FULL_KATAKANA:
return TipLangBarCallback::kFullKatakana;
case commands::HALF_ASCII:
return TipLangBarCallback::kHalfAlphanumeric;
case commands::FULL_ASCII:
return TipLangBarCallback::kFullAlphanumeric;
case commands::HALF_KATAKANA:
return TipLangBarCallback::kHalfKatakana;
default:
LOG(ERROR) << "Unknown composition mode: " << composition_mode;
return TipLangBarCallback::kDirect;
}
}
} // namespace
TipLangBarCallback::~TipLangBarCallback() {}
TipLangBar::TipLangBar()
: tool_button_menu_(nullptr),
help_menu_(nullptr),
help_menu_cookie_(TF_INVALID_COOKIE) {}
TipLangBar::~TipLangBar() {}
// Initializes button menus in the language bar.
HRESULT TipLangBar::InitLangBar(TipLangBarCallback *text_service) {
HRESULT result = S_OK;
// TODO(yukawa): Optimize this method. We do not need to obtain an instance of
// ITfLangBarItemMgr unless there remains something to be initialized for
// LangBar.
// A workaround to satisfy both b/6106437 and b/6641460.
// On Windows 8, keep the instance into |lang_bar_item_mgr_for_win8_|.
// On prior OSes, always instantiate new LangBarItemMgr object.
CComPtr<ITfLangBarItemMgr> item;
if (SystemUtil::IsWindows8OrLater()) {
if (!lang_bar_item_mgr_for_win8_) {
lang_bar_item_mgr_for_win8_ = GetLangBarItemMgr();
}
item = lang_bar_item_mgr_for_win8_;
} else {
item = GetLangBarItemMgr();
}
if (!item) {
return E_FAIL;
}
const TipLangBarMenuItem kInputMenuDisabled = {
kTipLangBarItemTypeDefault, 0, IDS_DISABLED,
IDI_DISABLED_NT, IDI_DISABLED
};
if (input_button_menu_ == nullptr) {
// Add the "Input Mode" button.
const TipLangBarMenuItem kInputMenu[] = {
{kTipLangBarItemTypeDefault,
TipLangBarCallback::kHiragana,
IDS_HIRAGANA, IDI_HIRAGANA_NT, IDI_HIRAGANA},
{kTipLangBarItemTypeDefault,
TipLangBarCallback::kFullKatakana,
IDS_FULL_KATAKANA, IDI_FULL_KATAKANA_NT, IDI_FULL_KATAKANA},
{kTipLangBarItemTypeDefault,
TipLangBarCallback::kFullAlphanumeric,
IDS_FULL_ALPHANUMERIC, IDI_FULL_ALPHANUMERIC_NT, IDI_FULL_ALPHANUMERIC},
{kTipLangBarItemTypeDefault,
TipLangBarCallback::kHalfKatakana,
IDS_HALF_KATAKANA, IDI_HALF_KATAKANA_NT, IDI_HALF_KATAKANA},
{kTipLangBarItemTypeDefault,
TipLangBarCallback::kHalfAlphanumeric,
IDS_HALF_ALPHANUMERIC, IDI_HALF_ALPHANUMERIC_NT, IDI_HALF_ALPHANUMERIC},
{kTipLangBarItemTypeRadioChecked,
TipLangBarCallback::kDirect,
IDS_DIRECT, IDI_DIRECT_NT,
IDI_DIRECT},
{kTipLangBarItemTypeSeparator, 0, 0, 0, 0},
{kTipLangBarItemTypeDefault,
TipLangBarCallback::kCancel,
IDS_CANCEL, 0, 0},
};
const bool kMenuButton = true;
CComPtr<TipLangBarToggleButton> input_button_menu(
new TipLangBarToggleButton(text_service, kTipLangBarItem_Button,
kMenuButton, kShowInTaskbar));
if (input_button_menu == nullptr) {
return E_OUTOFMEMORY;
}
result = input_button_menu->Init(TipDllModule::module_handle(),
IDS_INPUTMODE,
&kInputMenu[0],
arraysize(kInputMenu),
kInputMenuDisabled);
if (result != S_OK) {
return result;
}
item->AddItem(input_button_menu);
input_button_menu.QueryInterface(&input_button_menu_);
}
if (SystemUtil::IsWindows8OrLater() && !input_mode_button_for_win8_) {
// Add the "Input Mode" button.
const TipLangBarMenuItem kInputMenu[] = {
{kTipLangBarItemTypeDefault,
TipLangBarCallback::kHiragana,
IDS_HIRAGANA, IDI_HIRAGANA_NT, IDI_HIRAGANA},
{kTipLangBarItemTypeDefault,
TipLangBarCallback::kFullKatakana,
IDS_FULL_KATAKANA, IDI_FULL_KATAKANA_NT, IDI_FULL_KATAKANA},
{kTipLangBarItemTypeDefault,
TipLangBarCallback::kFullAlphanumeric,
IDS_FULL_ALPHANUMERIC, IDI_FULL_ALPHANUMERIC_NT, IDI_FULL_ALPHANUMERIC},
{kTipLangBarItemTypeDefault,
TipLangBarCallback::kHalfKatakana,
IDS_HALF_KATAKANA, IDI_HALF_KATAKANA_NT, IDI_HALF_KATAKANA},
{kTipLangBarItemTypeDefault,
TipLangBarCallback::kHalfAlphanumeric,
IDS_HALF_ALPHANUMERIC, IDI_HALF_ALPHANUMERIC_NT, IDI_HALF_ALPHANUMERIC},
{kTipLangBarItemTypeDefault,
TipLangBarCallback::kDirect,
IDS_DIRECT, IDI_DIRECT_NT, IDI_DIRECT},
{kTipLangBarItemTypeSeparator, 0, 0, 0, 0},
{kTipLangBarItemTypeDefault,
TipLangBarCallback::kHandWriting,
IDS_HAND_WRITING, 0, 0},
{kTipLangBarItemTypeDefault,
TipLangBarCallback::kCharacterPalette,
IDS_CHARACTER_PALETTE, 0, 0},
{kTipLangBarItemTypeDefault,
TipLangBarCallback::kDictionary,
IDS_DICTIONARY, IDI_DICTIONARY_NT, IDI_DICTIONARY},
{kTipLangBarItemTypeDefault,
TipLangBarCallback::kWordRegister,
IDS_WORD_REGISTER, IDI_DICTIONARY_NT, IDI_DICTIONARY},
{kTipLangBarItemTypeDefault,
TipLangBarCallback::kProperty,
IDS_PROPERTY, IDI_PROPERTY_NT, IDI_PROPERTY},
{kTipLangBarItemTypeSeparator, 0, 0, 0, 0},
{kTipLangBarItemTypeDefault,
TipLangBarCallback::kAbout,
IDS_ABOUT, 0, 0},
{kTipLangBarItemTypeDefault, TipLangBarCallback::kHelp, IDS_HELP, 0, 0},
};
const bool kNonMenuButton = false;
CComPtr<TipLangBarToggleButton> input_mode_menu(
new TipLangBarToggleButton(text_service, kSystemInputMode,
kNonMenuButton, kShowInTaskbar));
if (input_mode_menu == nullptr) {
return E_OUTOFMEMORY;
}
result = input_mode_menu->Init(TipDllModule::module_handle(),
IDS_WIN8_TRAY_ITEM,
kInputMenu, arraysize(kInputMenu),
kInputMenuDisabled);
if (FAILED(result)) {
return result;
}
result = item->AddItem(input_mode_menu);
input_mode_menu.QueryInterface(&input_mode_button_for_win8_);
}
if (tool_button_menu_ == nullptr) {
// Add the "Tool" button.
// TODO(yukawa): Make an Icon for kWordRegister.
const TipLangBarMenuItem kToolMenu[] = {
{kTipLangBarItemTypeDefault,
TipLangBarCallback::kHandWriting, IDS_HAND_WRITING, 0, 0},
{kTipLangBarItemTypeDefault,
TipLangBarCallback::kCharacterPalette, IDS_CHARACTER_PALETTE, 0, 0},
{kTipLangBarItemTypeDefault,
TipLangBarCallback::kDictionary, IDS_DICTIONARY,
IDI_DICTIONARY_NT, IDI_DICTIONARY},
{kTipLangBarItemTypeDefault,
TipLangBarCallback::kWordRegister, IDS_WORD_REGISTER,
IDI_DICTIONARY_NT, IDI_DICTIONARY}, // Use Dictionary icon temporarily
{kTipLangBarItemTypeDefault, TipLangBarCallback::kProperty, IDS_PROPERTY,
IDI_PROPERTY_NT, IDI_PROPERTY},
{kTipLangBarItemTypeSeparator, 0, 0, 0, 0},
{kTipLangBarItemTypeDefault,
TipLangBarCallback::kCancel,
IDS_CANCEL, 0, 0},
};
// Always show the tool icon so that a user can find the icon.
// This setting is different from that of MS-IME but we believe this is
// more friendly. See b/2275683
CComPtr<TipLangBarMenuButton> tool_button(new TipLangBarMenuButton(
text_service, kTipLangBarItem_ToolButton, kShowInTaskbar));
if (tool_button == nullptr) {
return E_OUTOFMEMORY;
}
result = tool_button->Init(TipDllModule::module_handle(), IDS_TOOL,
&kToolMenu[0], arraysize(kToolMenu),
IDI_TOOL_NT, IDI_TOOL);
if (result != S_OK) {
return result;
}
item->AddItem(tool_button);
tool_button.QueryInterface(&tool_button_menu_);
}
if (help_menu_ == nullptr) {
// Add the "Help" items to the system language bar help menu.
const TipLangBarMenuItem kHelpMenu[] = {
{0, TipLangBarCallback::kAbout, IDS_ABOUT, 0, 0},
{0, TipLangBarCallback::kHelp, IDS_HELP, 0, 0},
};
CComPtr<TipSystemLangBarMenu> help_menu(
new TipSystemLangBarMenu(text_service, kTipLangBarItem_HelpMenu));
if (help_menu == nullptr) {
return E_OUTOFMEMORY;
}
result = help_menu->Init(TipDllModule::module_handle(),
&kHelpMenu[0],
arraysize(kHelpMenu));
if (result != S_OK) {
return result;
}
CComPtr<ITfLangBarItem> help_menu_item;
result = item->GetItem(kSystemLangBarHelpMenu, &help_menu_item);
if (result != S_OK) {
return result;
}
CComPtr<ITfSource> source;
result = help_menu_item.QueryInterface(&source);
if (result != S_OK) {
return result;
}
result = source->AdviseSink(
IID_ITfSystemLangBarItemSink,
static_cast<ITfSystemLangBarItemSink*>(help_menu),
&help_menu_cookie_);
if (result != S_OK) {
return result;
}
help_menu_ = help_menu;
}
return result;
}
// IMPORTANT: See b/6106437 and b/6641460 before you change this method.
HRESULT TipLangBar::UninitLangBar() {
HRESULT result = S_OK;
// A workaround to satisfy both b/6106437 and b/6641460.
// On Windows 8, retrieves the instance from |lang_bar_item_mgr_for_win8_|.
// On prior OSes, always instantiates new LangBarItemMgr object.
CComPtr<ITfLangBarItemMgr> item;
if (SystemUtil::IsWindows8OrLater()) {
// Move the ownership.
item = lang_bar_item_mgr_for_win8_;
lang_bar_item_mgr_for_win8_.Release();
} else {
item = GetLangBarItemMgr();
}
if (!item) {
return E_FAIL;
}
if (input_mode_button_for_win8_ != nullptr) {
item->RemoveItem(input_mode_button_for_win8_);
input_mode_button_for_win8_ = nullptr;
}
if (input_button_menu_ != nullptr) {
item->RemoveItem(input_button_menu_);
input_button_menu_ = nullptr;
}
if (tool_button_menu_ != nullptr) {
item->RemoveItem(tool_button_menu_);
tool_button_menu_ = nullptr;
}
if ((help_menu_ != nullptr) && (help_menu_cookie_ != TF_INVALID_COOKIE)) {
CComPtr<ITfLangBarItem> help_menu_item;
result = item->GetItem(
kSystemLangBarHelpMenu, &help_menu_item);
if (result == S_OK) {
CComPtr<ITfSource> source;
result = help_menu_item.QueryInterface(&source);
if (result == S_OK) {
result = source->UnadviseSink(help_menu_cookie_);
if (result == S_OK) {
help_menu_cookie_ = TF_INVALID_COOKIE;
help_menu_ = nullptr;
}
}
}
}
return result;
}
HRESULT TipLangBar::UpdateMenu(bool enabled, uint32 composition_mode) {
HRESULT result = S_OK;
const UINT menu_id = GetItemId(composition_mode);
{
CComQIPtr<IMozcLangBarToggleItem> mode_menu(input_button_menu_);
if (mode_menu) {
mode_menu->SelectMenuItem(menu_id);
}
CComQIPtr<IMozcLangBarToggleItem> mode_button(input_mode_button_for_win8_);
if (mode_button) {
mode_button->SelectMenuItem(menu_id);
}
}
{
CComQIPtr<IMozcLangBarItem> mode_menu_item(input_button_menu_);
if (mode_menu_item) {
mode_menu_item->SetEnabled(enabled);
}
CComQIPtr<IMozcLangBarItem> tool_menu_item(tool_button_menu_);
if (tool_menu_item) {
tool_menu_item->SetEnabled(enabled);
}
CComQIPtr<IMozcLangBarItem> mode_button_item(input_mode_button_for_win8_);
if (mode_button_item) {
mode_button_item->SetEnabled(enabled);
}
}
return result;
}
} // namespace tsf
} // namespace win32
} // namespace mozc