| // 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); |
| } |