| // 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 |