blob: d6b368a4850a012ab115895a71fe7474bafbbacb [file] [log] [blame]
// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_FRAME_IN_PLACE_MENU_H_
#define CHROME_FRAME_IN_PLACE_MENU_H_
// in_place_menu.h : menu merging implementation
//
// This file is a modified version of the menu.h file, which is
// part of the ActiveDoc MSDN sample. The modifications are largely
// conversions to Google coding guidelines. Below is the original header
// from the file.
// This is a part of the Active Template Library.
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// This source code is only intended as a supplement to the
// Active Template Library Reference and related
// electronic documentation provided with the library.
// See these sources for detailed information regarding the
// Active Template Library product.
#include "base/logging.h"
#include "base/scoped_comptr_win.h"
template <class T>
class InPlaceMenu {
public:
InPlaceMenu() : shared_menu_(NULL), ole_menu_(NULL), our_menu_(NULL) {
}
~InPlaceMenu() {
InPlaceMenuDestroy();
}
HRESULT InPlaceMenuCreate(LPCWSTR menu_name) {
// We might already have an in-place menu set, because we set menus
// IOleDocumentView::UIActivate as well as in
// IOleInPlaceActiveObject::OnDocWindowActivate. If we have already
// done our work, just return silently
if (ole_menu_ || shared_menu_)
return S_OK;
ScopedComPtr<IOleInPlaceFrame> in_place_frame;
GetInPlaceFrame(in_place_frame.Receive());
// We have no IOleInPlaceFrame, no menu merging possible
if (!in_place_frame) {
NOTREACHED();
return E_FAIL;
}
// Create a blank menu and ask the container to add
// its menus into the OLEMENUGROUPWIDTHS structure
shared_menu_ = ::CreateMenu();
OLEMENUGROUPWIDTHS mgw = {0};
HRESULT hr = in_place_frame->InsertMenus(shared_menu_, &mgw);
if (FAILED(hr)) {
::DestroyMenu(shared_menu_);
shared_menu_ = NULL;
return hr;
}
// Insert our menus
our_menu_ = LoadMenu(_AtlBaseModule.GetResourceInstance(),menu_name);
MergeMenus(shared_menu_, our_menu_, &mgw.width[0], 1);
// Send the menu to the client
ole_menu_ = (HMENU)OleCreateMenuDescriptor(shared_menu_, &mgw);
T* t = static_cast<T*>(this);
in_place_frame->SetMenu(shared_menu_, ole_menu_, t->m_hWnd);
return S_OK;
}
HRESULT InPlaceMenuDestroy() {
ScopedComPtr<IOleInPlaceFrame> in_place_frame;
GetInPlaceFrame(in_place_frame.Receive());
if (in_place_frame) {
in_place_frame->RemoveMenus(shared_menu_);
in_place_frame->SetMenu(NULL, NULL, NULL);
}
if (ole_menu_) {
OleDestroyMenuDescriptor(ole_menu_);
ole_menu_ = NULL;
}
if (shared_menu_) {
UnmergeMenus(shared_menu_, our_menu_);
DestroyMenu(shared_menu_);
shared_menu_ = NULL;
}
if (our_menu_) {
DestroyMenu(our_menu_);
our_menu_ = NULL;
}
return S_OK;
}
void MergeMenus(HMENU shared_menu, HMENU source_menu, LONG* menu_widths,
unsigned int width_index) {
// Copy the popups from the source menu
// Insert at appropriate spot depending on width_index
DCHECK(width_index == 0 || width_index == 1);
int position = 0;
if (width_index == 1)
position = (int)menu_widths[0];
int group_width = 0;
int menu_items = GetMenuItemCount(source_menu);
for (int index = 0; index < menu_items; index++) {
// Get the HMENU of the popup
HMENU popup_menu = ::GetSubMenu(source_menu, index);
// Separators move us to next group
UINT state = GetMenuState(source_menu, index, MF_BYPOSITION);
if (!popup_menu && (state & MF_SEPARATOR)) {
// Servers should not touch past 5
DCHECK(width_index <= 5);
menu_widths[width_index] = group_width;
group_width = 0;
if (width_index < 5)
position += static_cast<int>(menu_widths[width_index+1]);
width_index += 2;
} else {
// Get the menu item text
TCHAR item_text[256] = {0};
int text_length = GetMenuString(source_menu, index, item_text,
ARRAYSIZE(item_text), MF_BYPOSITION);
// Popups are handled differently than normal menu items
if (popup_menu) {
if (::GetMenuItemCount(popup_menu) != 0) {
// Strip the HIBYTE because it contains a count of items
state = LOBYTE(state) | MF_POPUP; // Must be popup
// Non-empty popup -- add it to the shared menu bar
InsertMenu(shared_menu, position, state|MF_BYPOSITION,
reinterpret_cast<UINT_PTR>(popup_menu), item_text);
++position;
++group_width;
}
} else if (text_length > 0) {
// only non-empty items are added
DCHECK(item_text[0] != 0);
// here the state does not contain a count in the HIBYTE
InsertMenu(shared_menu, position, state|MF_BYPOSITION,
GetMenuItemID(source_menu, index), item_text);
++position;
++group_width;
}
}
}
}
void UnmergeMenus(HMENU shared_menu, HMENU source_menu) {
int our_item_count = GetMenuItemCount(source_menu);
int shared_item_count = GetMenuItemCount(shared_menu);
for (int index = shared_item_count - 1; index >= 0; index--) {
// Check the popup menus
HMENU popup_menu = ::GetSubMenu(shared_menu, index);
if (popup_menu) {
// If it is one of ours, remove it from the shared menu
for (int sub_index = 0; sub_index < our_item_count; sub_index++) {
if (::GetSubMenu(source_menu, sub_index) == popup_menu) {
// Remove the menu from hMenuShared
RemoveMenu(shared_menu, index, MF_BYPOSITION);
break;
}
}
}
}
}
protected:
HRESULT GetInPlaceFrame(IOleInPlaceFrame** in_place_frame) {
if (!in_place_frame) {
NOTREACHED();
return E_POINTER;
}
T* t = static_cast<T*>(this);
HRESULT hr = E_FAIL;
if (S_OK != t->GetInPlaceFrame(in_place_frame)) {
// We weren't given an IOleInPlaceFrame pointer, so
// we'll have to get it ourselves.
if (t->m_spInPlaceSite) {
t->frame_info_.cb = sizeof(OLEINPLACEFRAMEINFO);
ScopedComPtr<IOleInPlaceUIWindow> in_place_ui_window;
RECT position_rect = {0};
RECT clip_rect = {0};
hr = t->m_spInPlaceSite->GetWindowContext(in_place_frame,
in_place_ui_window.Receive(),
&position_rect, &clip_rect,
&t->frame_info_);
}
}
return hr;
}
protected:
// The OLE menu descriptor created by the OleCreateMenuDescriptor
HMENU ole_menu_;
// The shared menu that we pass to IOleInPlaceFrame::SetMenu
HMENU shared_menu_;
// Our menu resource that we want to insert
HMENU our_menu_;
};
#endif // CHROME_FRAME_IN_PLACE_MENU_H_