summaryrefslogtreecommitdiffstats
path: root/chrome_frame/in_place_menu.h
blob: d6b368a4850a012ab115895a71fe7474bafbbacb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
// 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_