blob: 96d0d6ba97be1fe267d167acc1afcca161ce4815 [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/ime/ime_language_bar_menu.h"
#define _ATL_NO_AUTOMATIC_NAMESPACE
#define _WTL_NO_AUTOMATIC_NAMESPACE
#include <atlbase.h>
#include <atlapp.h>
#include <atlmisc.h>
#include <atlgdi.h>
#include <atluser.h>
#include <ole2.h>
#include <olectl.h>
#include <uxtheme.h>
#include <limits>
#include "base/logging.h"
#include "base/win_util.h"
#include "win32/base/text_icon.h"
#include "win32/ime/ime_impl_imm.h"
#include "win32/ime/ime_language_bar.h"
#include "win32/ime/ime_resource.h"
namespace {
using ::WTL::CBitmap;
using ::WTL::CIcon;
using ::WTL::CSize;
using ::WTL::CDC;
using ::mozc::WinUtil;
using ::mozc::win32::TextIcon;
const int kDefaultDPI = 96;
// Represents the cookie for the sink to an ImeLangBarItem object.
const int kImeLangBarMenuCookie = (('M' << 24) |
('o' << 16) |
('z' << 8) |
('c' << 0));
// "MS ゴシック"
const char kTextIconFont[] =
"\xEF\xBC\xAD\xEF\xBC\xB3"
"\x20\xE3\x82\xB4\xE3\x82\xB7\xE3\x83\x83\xE3\x82\xAF";
// TODO(yukawa): Refactor LangBar code so that we can configure following
// settings as a part of initialization.
string GetIconStringIfNecessary(UINT icon_id) {
switch (icon_id) {
case IDI_DIRECT_NT:
return "A";
case IDI_HIRAGANA_NT:
// "あ"
return "\xE3\x81\x82";
case IDI_FULL_KATAKANA_NT:
// "ア"
return "\xE3\x82\xA2";
case IDI_HALF_ALPHANUMERIC_NT:
return "_A";
case IDI_FULL_ALPHANUMERIC_NT:
// "A"
return "\xEF\xBC\xA1";
case IDI_HALF_KATAKANA_NT:
// "_ア"
return "_" "\xEF\xBD\xB1";
}
return string();
}
// Loads an icon which is appropriate for the current theme.
// An icon ID 0 represents "no icon".
HICON LoadIconFromResource(HINSTANCE instance,
UINT icon_id_for_non_theme,
UINT icon_id_for_theme) {
// We use a 32-bpp icon if we can observe the uxtheme is running.
UINT id = icon_id_for_non_theme;
if (icon_id_for_theme != 0) {
const wchar_t kThemeDll[] = L"uxtheme.dll";
typedef BOOL (WINAPI *FPIsThemeActive)();
// mozc::Util::GetSystemModuleHandle is not safe when the specified DLL is
// unloaded by other threads.
// TODO(yukawa): Make a wrapper of GetModuleHandleEx to increment a
// reference count of the theme DLL while we call IsThemeActive API.
const HMODULE theme_dll =
WinUtil::GetSystemModuleHandleAndIncrementRefCount(kThemeDll);
if (theme_dll != nullptr) {
FPIsThemeActive is_thread_active = reinterpret_cast<FPIsThemeActive>(
::GetProcAddress(theme_dll, "IsThemeActive"));
if (is_thread_active != nullptr && is_thread_active()) {
id = icon_id_for_theme;
}
}
if (theme_dll != nullptr) {
::FreeLibrary(theme_dll);
}
}
if (id == 0) {
return nullptr;
}
const auto icon_size = ::GetSystemMetrics(SM_CYSMICON);
// Replace some text icons with on-the-fly image drawn with MS-Gothic.
const auto &icon_text = GetIconStringIfNecessary(id);
if (!icon_text.empty()) {
const COLORREF text_color = ::GetSysColor(COLOR_WINDOWTEXT);
return TextIcon::CreateMonochromeIcon(
icon_size, icon_size, icon_text, kTextIconFont, text_color);
}
return static_cast<HICON>(::LoadImage(
instance, MAKEINTRESOURCE(id),
IMAGE_ICON, icon_size, icon_size, LR_CREATEDIBSECTION));
}
// Retrieves the bitmap handle loaded by using an icon ID.
// Returns true if the specified icons is available as bitmaps.
// Caller can set nullptr for |color| and/or |mask| to represent not to recieve
// the specified handle even if it exists. Caller should releases any returned
// bitmap handle and this function releases any handle which is not recieved
// by the caller.
bool LoadIconAsBitmap(HINSTANCE instance,
UINT icon_id_for_non_theme,
UINT icon_id_for_theme,
HBITMAP *color,
HBITMAP *mask) {
if (color != nullptr) {
*color = nullptr;
}
if (mask != nullptr) {
*mask = nullptr;
}
CIcon icon(LoadIconFromResource(
instance, icon_id_for_non_theme, icon_id_for_theme));
if (icon.IsNull()) {
return false;
}
ICONINFO icon_info = { 0 };
if (!::GetIconInfo(icon, &icon_info)) {
return false;
}
CBitmap color_bitmap(icon_info.hbmColor);
CBitmap mask_bitmap(icon_info.hbmMask);
if (color != nullptr) {
*color = color_bitmap.Detach();
}
if (mask != nullptr) {
*mask = mask_bitmap.Detach();
}
return true;
}
} // namespace
HRESULT ImeLangBarMenuDataArray::Init(
HINSTANCE instance,
const ImeLangBarMenuItem* menu,
int count) {
HRESULT result = S_OK;
// Attach menu texts and icons.
for (int i = 0; i < count; ++i) {
ImeLangBarMenuData data = {};
int length = 0;
HICON icon = nullptr;
if ((menu[i].flags_ & TF_LBMENUF_SEPARATOR) == 0) {
// Retrieve the menu text and button icon.
length = ::LoadString(instance,
menu[i].text_id_,
&data.text_[0],
arraysize(data.text_));
}
data.flags_ = menu[i].flags_;
data.menu_id_ = menu[i].menu_id_;
data.text_id_ = menu[i].text_id_;
data.length_ = length;
data.icon_id_for_non_theme_ = menu[i].icon_id_for_non_theme_;
data.icon_id_for_theme_ = menu[i].icon_id_for_theme_;
data_.push_back(data);
}
return result;
}
size_t ImeLangBarMenuDataArray::size() const {
return data_.size();
}
ImeLangBarMenuData* ImeLangBarMenuDataArray::data(size_t i) {
if (i >= size()) {
return nullptr;
}
return &data_[i];
}
// Implements the constructor of the ImeLangBarMenu class.
ImeLangBarMenu::ImeLangBarMenu(LangBarCallback *langbar_callback,
const GUID& guid,
bool show_in_tray)
: item_sink_(nullptr),
langbar_callback_(nullptr),
status_(0) {
// Initialize its TF_LANGBARITEMINFO object, which contains the properties of
// this item and is copied to the TSF manager in GetInfo() function.
// We set CLSID_NULL because this item is not provided by a text service.
item_info_.clsidService = CLSID_NULL;
item_info_.guidItem = guid;
// The visibility of a langbar-item in the minimized langbar is actually
// controlled by TF_LBI_STYLE_SHOWNINTRAY flag despite the fact that the
// document says "This flag is not currently supported".
// http://msdn.microsoft.com/en-us/library/ms629078.aspx
// http://b/2275633
item_info_.dwStyle = TF_LBI_STYLE_BTN_MENU;
if (show_in_tray) {
item_info_.dwStyle |= TF_LBI_STYLE_SHOWNINTRAY;
}
item_info_.ulSort = 0;
item_info_.szDescription[0] = L'\0';
// Save the LangBarCallback object who owns this button, and increase its
// reference count not to prevent the object from being deleted
langbar_callback_ = langbar_callback;
langbar_callback_->AddRef();
}
// Implements the destructor of the ImeLangBarMenu class.
ImeLangBarMenu::~ImeLangBarMenu() {
// Release the owner LangBarCallback object.
if (langbar_callback_ != nullptr) {
langbar_callback_->Release();
}
langbar_callback_ = nullptr;
}
// Implements the ITfLangBarItem::GetInfo() function.
// This function is called by Windows to update this button menu.
STDAPI ImeLangBarMenu::GetInfo(TF_LANGBARITEMINFO* item_info) {
// Just copies the cached TF_LANGBARITEMINFO object.
*item_info = item_info_;
return S_OK;
}
// Implements the ITfLangBarItem::GetStatus() function.
// This fucntion is called by Windows to retrieve the current status of this
// button menu.
STDAPI ImeLangBarMenu::GetStatus(DWORD* status) {
*status = status_;
return S_OK;
}
// Implements the ITfLangBarItem::Show() function.
// This function is called by Windows to notify the display status of this
// button menu has been updated.
STDAPI ImeLangBarMenu::Show(BOOL show) {
// Just return becasue this button is always shown, i.e. we do not have to
// manage the display state of this button menu.
return E_NOTIMPL;
}
// Implements the ITfLangBarItem::GetTooltipString() function.
// This function is called when Windows to retrieve the tool-tip string of
// this button menu.
STDAPI ImeLangBarMenu::GetTooltipString(BSTR* tooltip) {
// Created a COM string from the description and copy it.
*tooltip = ::SysAllocString(&item_info_.szDescription[0]);
return (*tooltip ? S_OK : E_OUTOFMEMORY);
}
// Implements the ITfLangBarItemButton::OnClick() function.
// This function is not used for a button menu.
STDAPI ImeLangBarMenu::OnClick(TfLBIClick click,
POINT point,
const RECT* rect) {
// Just returns because Windows does not call this function when an
// ITfLangBarItem object is a button menu.
return S_OK;
}
// Implements the ITfLangBarItemButton::GetText() function.
// This function is called by Windows to retrive the text label of this
// button menu.
STDAPI ImeLangBarMenu::GetText(BSTR* text) {
*text = ::SysAllocString(&item_info_.szDescription[0]);
return (*text ? S_OK : E_OUTOFMEMORY);
}
// Implements the ITfSource::AdviseSink() funtion.
STDAPI ImeLangBarMenu::AdviseSink(REFIID interface_id,
IUnknown* unknown,
DWORD* cookie) {
// Return if the caller tries to start advising any events except the
// ITfLangBarItemSink events.
if (!IsEqualIID(IID_ITfLangBarItemSink, interface_id))
return CONNECT_E_CANNOTCONNECT;
// Exit if this object has a sink which advising ITfLangBarItemSink events.
if (item_sink_ != nullptr)
return CONNECT_E_ADVISELIMIT;
// Retrieve the ITfLangBarItemSink interface from the given object and store
// it into |item_sink_|.
HRESULT result = unknown->QueryInterface(IID_ITfLangBarItemSink,
reinterpret_cast<void**>(&item_sink_));
if (result != S_OK) {
item_sink_ = nullptr;
return result;
}
// Return the cookie of this object.
*cookie = kImeLangBarMenuCookie;
return result;
}
STDAPI ImeLangBarMenu::UnadviseSink(DWORD cookie) {
// Return if the given cookie
if (cookie != kImeLangBarMenuCookie || item_sink_ == nullptr)
return CONNECT_E_NOCONNECTION;
// Release the copy of this event.
item_sink_->Release();
item_sink_ = nullptr;
return S_OK;
}
// Initializes an ImeLangBarItem instance.
// This function is called by a text service to provide the information
// required for creating a menu button. A text service MUST call this function
// before calling the ITfLangBarItemMgr::AddItem() function and adding this
// button menu to a language bar.
HRESULT ImeLangBarMenu::Init(HINSTANCE instance,
int string_id,
const ImeLangBarMenuItem* menu,
int count) {
HRESULT result = S_OK;
// Retrieve the text label from the resource.
// This string is also used as a tool-tip text.
::LoadString(instance, string_id, &item_info_.szDescription[0],
arraysize(item_info_.szDescription));
// Create a new ImeLangBarMenuItem object.
menu_data_.Init(instance, menu, count);
return S_OK;
}
HRESULT ImeLangBarMenu::OnUpdate(DWORD update_flag) {
// For some reason, this method might be called when the event sink is not
// available. See b/2977835 for details.
if (item_sink_ == nullptr) {
return E_FAIL;
}
return item_sink_->OnUpdate(update_flag);
}
HRESULT ImeLangBarMenu::SetEnabled(bool enabled) {
HRESULT result = S_OK;
if (enabled) {
status_ &= ~TF_LBI_STATUS_DISABLED;
} else {
status_ |= TF_LBI_STATUS_DISABLED;
}
result = ImeLangBarMenu::OnUpdate(TF_LBI_STATUS);
return result;
}
bool ImeLangBarMenu::CanContextMenuDisplay32bppIcon() {
// We always use a non-theme icon for a context menu icon on the LangBar
// unless the current display mode is 32-bpp. We cannot assume we can
// display a 32-bpp icon for a context menu icon on the LangBar unless the
// current display mode is 32-bpp. See http://b/2260057
CDC display_dc(::GetDC(nullptr));
return !display_dc.IsNull() &&
display_dc.GetDeviceCaps(PLANES) == 1 &&
display_dc.GetDeviceCaps(BITSPIXEL) == 32;
}
ImeLangBarMenuData* ImeLangBarMenu::menu_data(size_t i) {
return menu_data_.data(i);
}
size_t ImeLangBarMenu::menu_data_size() const {
return menu_data_.size();
}
const TF_LANGBARITEMINFO* ImeLangBarMenu::item_info() const {
return &item_info_;
}
// Basic implements the IUnknown::QueryInterface() function.
STDAPI ImeLangBarMenu::QueryInterfaceBase(
REFIID interface_id, void** object) {
if (!object) {
return E_INVALIDARG;
}
// Find a matching interface from the ones implemented by this object
// (i.e. IUnknown, ITfLangBarItem, ITfLangBarItemButton, ITfSource).
if (::IsEqualIID(interface_id, __uuidof(IMozcLangBarMenu))) {
*object = static_cast<IMozcLangBarMenu*>(this);
AddRef();
return S_OK;
}
if (::IsEqualIID(interface_id, IID_IUnknown)) {
*object = static_cast<IUnknown*>(
static_cast<ITfLangBarItemButton*>(this));
AddRef();
return S_OK;
}
if (::IsEqualIID(interface_id, IID_ITfLangBarItem)) {
*object = static_cast<ITfLangBarItem*>(this);
AddRef();
return S_OK;
}
if (::IsEqualIID(interface_id, IID_ITfLangBarItemButton)) {
*object = static_cast<ITfLangBarItemButton*>(this);
AddRef();
return S_OK;
}
if (::IsEqualIID(interface_id, IID_ITfSource)) {
*object = static_cast<ITfSource*>(this);
AddRef();
return S_OK;
}
// This object does not implement the given interface.
*object = nullptr;
return E_NOINTERFACE;
}
ImeIconButtonMenu::ImeIconButtonMenu(LangBarCallback* langbar_callback,
const GUID& guid,
bool show_in_tray)
: ImeLangBarMenu(langbar_callback, guid, show_in_tray),
reference_count_(0),
menu_icon_id_for_theme_(0),
menu_icon_id_for_non_theme_(0) {
}
// Implements the IUnknown::QueryInterface() function.
// This function is used by Windows to retrieve the interfaces implemented by
// this class.
STDAPI ImeIconButtonMenu::QueryInterface(
REFIID interface_id, void** object) {
return QueryInterfaceBase(interface_id, object);
}
// Implements the IUnknown::AddRef() function.
// This function is called by Windows and LangBarCallback instances to notify
// they need to have a copy of this object.
// This implementation just increases the reference count of this object
// to prevent this object from being deleted during it is in use.
STDAPI_(ULONG) ImeIconButtonMenu::AddRef() {
const LONG count = ::InterlockedIncrement(&reference_count_);
if (count < 0) {
DLOG(INFO) << "Reference count is negative.";
return 0;
}
return count;
}
// Implements the IUnknown::Release() function.
// This function is called by Windows and LangBarCallback instances to notify
// they do not need their local copies of this object any longer.
// This implementation just decreases the reference count of this object
// and delete it only if it is not referenced by any objects.
STDAPI_(ULONG) ImeIconButtonMenu::Release() {
const LONG count = ::InterlockedDecrement(&reference_count_);
if (count <= 0) {
delete this;
return 0;
}
return count;
}
STDAPI ImeIconButtonMenu::InitMenu(ITfMenu* menu) {
HRESULT result = S_OK;
// Add the menu items of this object to the given ITfMenu object.
for (size_t i = 0; i < menu_data_size(); ++i) {
const ImeLangBarMenuData* data = menu_data(i);
const UINT icon_id_for_theme =
CanContextMenuDisplay32bppIcon() ? data->icon_id_for_theme_
: data->icon_id_for_non_theme_;
CBitmap bitmap;
CBitmap mask;
// If LoadIconAsBitmap fails, |bitmap.m_hBitmap| and |mask.m_hBitmap|
// remain nullptr bitmap handles.
LoadIconAsBitmap(ImeGetResource(),
data->icon_id_for_non_theme_,
icon_id_for_theme,
&bitmap.m_hBitmap,
&mask.m_hBitmap);
result = menu->AddMenuItem(i, data->flags_, bitmap, mask,
data->text_, data->length_,
nullptr);
if (result != S_OK) {
break;
}
}
return result;
}
STDAPI ImeIconButtonMenu::OnMenuSelect(UINT menu_id) {
// Call the LangBarCallback::OnMenuSelect() function to dispatch the
// given event.
ImeLangBarMenuData* data = menu_data(menu_id);
if (data == nullptr) {
return E_INVALIDARG;
}
if (data->menu_id_ == LangBarCallback::kCancel) {
return S_OK;
}
const HRESULT result = langbar_callback_->OnMenuSelect(
static_cast<LangBarCallback::MenuId>(data->menu_id_));
return result;
}
// Implements the ITfLangBarItem::GetInfo() function.
// This function is called by Windows to update this button menu.
STDAPI ImeIconButtonMenu::GetInfo(TF_LANGBARITEMINFO* item_info) {
HRESULT result = S_OK;
if (item_info == nullptr) {
return E_INVALIDARG;
}
// Just copies the cached TF_LANGBARITEMINFO object.
*item_info = *this->item_info();
return S_OK;
}
STDAPI ImeIconButtonMenu::GetIcon(HICON* icon) {
if (icon == nullptr) {
return E_INVALIDARG;
}
// Excerpt: http://msdn.microsoft.com/en-us/library/ms628718.aspx
// The caller must free this icon when it is no longer required by
// calling DestroyIcon.
// Caveats: ITfLangBarMgr causes GDI handle leak when an icon which consists
// only of mask bitmap (AND bitmap) is returned. |*icon| must have color
// bitmap (XOR bitmap) as well as mask bitmap (XOR bitmap) to avoid GDI
// handle leak.
*icon = LoadIconFromResource(ImeGetResource(),
menu_icon_id_for_non_theme_,
menu_icon_id_for_theme_);
return (*icon ? S_OK : E_FAIL);
}
// Initializes an ImeButtonMenu instance.
// This function allocates resources for an ImeButtonMenu instance.
HRESULT ImeIconButtonMenu::Init(HINSTANCE instance,
UINT string_id,
const ImeLangBarMenuItem* menu,
int count,
UINT menu_icon_id_for_non_theme,
UINT menu_icon_id_for_theme) {
menu_icon_id_for_theme_ = menu_icon_id_for_theme;
menu_icon_id_for_non_theme_ = menu_icon_id_for_non_theme;
return ImeLangBarMenu::Init(instance, string_id, menu, count);
}
ImeToggleButtonMenu::ImeToggleButtonMenu(LangBarCallback* langbar_callback,
const GUID& guid,
bool show_in_tray)
: ImeLangBarMenu(langbar_callback, guid, show_in_tray),
reference_count_(0),
menu_selected_(0) {
}
ImeToggleButtonMenu::~ImeToggleButtonMenu() {
}
// Implements the IUnknown::QueryInterface() function.
// This function is used by Windows to retrieve the interfaces implemented by
// this class.
STDAPI ImeToggleButtonMenu::QueryInterface(
REFIID interface_id, void** object) {
if (!object) {
return E_INVALIDARG;
}
if (::IsEqualIID(interface_id, __uuidof(IMozcToggleButtonMenu))) {
*object = static_cast<IMozcToggleButtonMenu*>(this);
AddRef();
return S_OK;
}
return QueryInterfaceBase(interface_id, object);
}
// Implements the IUnknown::AddRef() function.
// This function is called by Windows and LangBarCallback instances to notify
// they need to have a copy of this object.
// This implementation just increases the reference count of this object
// to prevent this object from being deleted during it is in use.
STDAPI_(ULONG) ImeToggleButtonMenu::AddRef() {
const LONG count = ::InterlockedIncrement(&reference_count_);
if (count < 0) {
DLOG(INFO) << "Reference count is negative.";
return 0;
}
return count;
}
// Implements the IUnknown::Release() function.
// This function is called by Windows and LangBarCallback instances to notify
// they do not need their local copies of this object any longer.
// This implementation just decreases the reference count of this object
// and delete it only if it is not referenced by any objects.
STDAPI_(ULONG) ImeToggleButtonMenu::Release() {
const LONG count = ::InterlockedDecrement(&reference_count_);
if (count <= 0) {
delete this;
return 0;
}
return count;
}
// Implements the ITfLangBarItem::GetInfo() function.
// This function is called by Windows to update this button menu.
STDAPI ImeToggleButtonMenu::GetInfo(TF_LANGBARITEMINFO* item_info) {
HRESULT result = S_OK;
if (item_info == nullptr) {
return E_INVALIDARG;
}
// Just copies the cached TF_LANGBARITEMINFO object.
*item_info = *this->item_info();
if (FAILED(result)) {
return result;
}
return S_OK;
}
STDAPI ImeToggleButtonMenu::InitMenu(ITfMenu* menu) {
HRESULT result = S_OK;
// Add the menu items of this object to the given ITfMenu object.
for (size_t i = 0; i < menu_data_size(); ++i) {
const ImeLangBarMenuData* data = menu_data(i);
result = menu->AddMenuItem(i, data->flags_, nullptr, nullptr,
data->text_, data->length_,
nullptr);
if (result != S_OK) {
break;
}
}
return result;
}
STDAPI ImeToggleButtonMenu::OnMenuSelect(UINT menu_id) {
// Call the LangBarCallback::OnMenuSelect() function to dispatch the
// given event.
ImeLangBarMenuData* data = menu_data(menu_id);
if (data == nullptr) {
return E_INVALIDARG;
}
if (data->menu_id_ == LangBarCallback::kCancel) {
return S_OK;
}
const HRESULT result = langbar_callback_->OnMenuSelect(
static_cast<LangBarCallback::MenuId>(data->menu_id_));
if (result != S_OK) {
return result;
}
ImeLangBarMenuData* selected = menu_data(menu_selected_);
selected->flags_ &= ~TF_LBMENUF_RADIOCHECKED;
data->flags_ |= TF_LBMENUF_RADIOCHECKED;
menu_selected_ = menu_id;
return result;
}
STDAPI ImeToggleButtonMenu::GetIcon(HICON* icon) {
if (icon == nullptr) {
return E_INVALIDARG;
}
const ImeLangBarMenuData* selected = menu_data(menu_selected_);
// Excerpt: http://msdn.microsoft.com/en-us/library/ms628718.aspx
// The caller must free this icon when it is no longer required by
// calling DestroyIcon.
// Caveats: ITfLangBarMgr causes GDI handle leak when an icon which consists
// only of mask bitmap (AND bitmap) is returned. |*icon| must have color
// bitmap (XOR bitmap) as well as mask bitmap (XOR bitmap) to avoid GDI
// handle leak.
*icon = LoadIconFromResource(ImeGetResource(),
selected->icon_id_for_non_theme_,
selected->icon_id_for_theme_);
return (*icon ? S_OK : E_FAIL);
}
HRESULT ImeToggleButtonMenu::Init(HINSTANCE instance,
int string_id,
const ImeLangBarMenuItem* menu,
int count) {
return ImeLangBarMenu::Init(instance, string_id, menu, count);
}
// Implements the IMozcToggleButtonMenu::SelectMenuItem() function.
HRESULT ImeToggleButtonMenu::SelectMenuItem(UINT menu_id) {
// Now SelectMenuItem may be called frequently to update LangbarItem for
// every key input. So we call ImeLangBarMenu::OnUpdate only if any item
// state is updated.
bool item_state_changed = false;
for (size_t i = 0; i < menu_data_size(); ++i) {
ImeLangBarMenuData* data = menu_data(i);
if (data->menu_id_ == menu_id) {
if ((data->flags_ & TF_LBMENUF_RADIOCHECKED) != 0 ||
menu_selected_ != static_cast<UINT>(i)) {
item_state_changed |= true;
}
data->flags_ |= TF_LBMENUF_RADIOCHECKED;
menu_selected_ = static_cast<UINT>(i);
} else {
if (data->flags_ != 0) {
item_state_changed |= true;
}
data->flags_ &= ~TF_LBMENUF_RADIOCHECKED;
}
}
if (item_state_changed) {
ImeLangBarMenu::OnUpdate(TF_LBI_ICON | TF_LBI_STATUS);
}
return S_OK;
}
// Implements the constructor of the ImeSystemLangBarMenu class.
ImeSystemLangBarMenu::ImeSystemLangBarMenu(LangBarCallback *langbar_callback,
const GUID& guid)
: reference_count_(0) {
// Save the LangBarCallback object who owns this button, and increase its
// reference count not to prevent the object from being deleted
langbar_callback_ = langbar_callback;
langbar_callback_->AddRef();
}
// Implements the destructor of the ImeSystemLangBarMenu class.
ImeSystemLangBarMenu::~ImeSystemLangBarMenu() {
// Release the owner LangBarCallback object.
if (langbar_callback_ != nullptr) {
langbar_callback_->Release();
}
langbar_callback_ = nullptr;
}
// Implements the IUnknown::QueryInterface() function.
// This function is used by Windows to retrieve the interfaces implemented by
// this class.
STDAPI ImeSystemLangBarMenu::QueryInterface(REFIID interface_id,
void** object) {
if (!object) {
return E_INVALIDARG;
}
// Find a matching interface from the ones implemented by this object
// (i.e. IUnknown, ITfSystemLangBarItemSink, ITfSource).
if (IsEqualIID(interface_id, IID_IUnknown) ||
IsEqualIID(interface_id, IID_ITfSystemLangBarItemSink)) {
*object = static_cast<ITfSystemLangBarItemSink*>(this);
AddRef();
return S_OK;
} else {
// This object does not implement the given interface.
*object = nullptr;
return E_NOINTERFACE;
}
}
// Implements the IUnknown::AddRef() function.
// This function is called by Windows and LangBarCallback instances to notify
// they need to have a copy of this object.
// This implementation just increases the reference count of this object
// to prevent this object from being deleted during it is in use.
STDAPI_(ULONG) ImeSystemLangBarMenu::AddRef() {
const LONG count = ::InterlockedIncrement(&reference_count_);
if (count < 0) {
DLOG(INFO) << "Reference count is negative.";
return 0;
}
return count;
}
// Implements the IUnknown::Release() function.
// This function is called by Windows and LangBarCallback instances to notify
// they do not need their local copies of this object any longer.
// This implementation just decreases the reference count of this object
// and delete it only if it is not referenced by any objects.
STDAPI_(ULONG) ImeSystemLangBarMenu::Release() {
const LONG count = ::InterlockedDecrement(&reference_count_);
if (count <= 0) {
delete this;
return 0;
}
return count;
}
// Implements the ITfLangBarItemButton::InitMenu() function.
// This function is called by Windows to create the button menu.
STDAPI ImeSystemLangBarMenu::InitMenu(ITfMenu* menu) {
HRESULT result = S_OK;
// Add the menu items of this object to the given ITfMenu object.
for (size_t i = 0; i < menu_data_.size(); ++i) {
const ImeLangBarMenuData* data = menu_data_.data(i);
const UINT icon_id_for_theme =
ImeLangBarMenu::CanContextMenuDisplay32bppIcon()
? data->icon_id_for_theme_
: data->icon_id_for_non_theme_;
CBitmap bitmap;
CBitmap mask;
// If LoadIconAsBitmap fails, |bitmap.m_hBitmap| and |mask.m_hBitmap|
// remain nullptr bitmap handles.
LoadIconAsBitmap(ImeGetResource(),
data->icon_id_for_non_theme_,
icon_id_for_theme,
&bitmap.m_hBitmap,
&mask.m_hBitmap);
result = menu->AddMenuItem(i, data->flags_, bitmap, mask,
data->text_, data->length_,
nullptr);
if (result != S_OK) {
break;
}
}
return result;
}
// Implements the ITfLangBarItemButton::OnMenuSelect() function.
// This function is called by Windows to notify a user selects an item in
// this button menu.
STDAPI ImeSystemLangBarMenu::OnMenuSelect(UINT menu_id) {
// Call the LangBarCallback::OnMenuSelect() function to dispatch the
// given event.
ImeLangBarMenuData* data = menu_data_.data(menu_id);
if (data == nullptr) {
return E_INVALIDARG;
}
if (data->menu_id_ == LangBarCallback::kCancel) {
return S_OK;
}
const HRESULT result = langbar_callback_->OnMenuSelect(
static_cast<LangBarCallback::MenuId>(data->menu_id_));
return S_OK;
}
// Initializes an ImeLangBarItem instance.
// This function is called by a text service to provide the information
// required for creating a menu button. A text service MUST call this function
// before calling the ITfLangBarItemMgr::AddItem() function and adding this
// button menu to a language bar.
HRESULT ImeSystemLangBarMenu::Init(HINSTANCE instance,
const ImeLangBarMenuItem* menu,
int count) {
return menu_data_.Init(instance, menu, count);
}