1 /*******************************************************************************
2  * Copyright (c) 2000, 2008 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  * Port to the D programming language:
11  *     Frank Benoit <benoit@tionex.de>
12  *******************************************************************************/
13 module org.eclipse.swt.ole.win32.OleFrame;
14 
15 import java.util.Vector;
16 
17 import org.eclipse.swt.SWT;
18 import org.eclipse.swt.SWTException;
19 import org.eclipse.swt.internal.ole.win32.COM;
20 import org.eclipse.swt.internal.ole.win32.OLEIDL;
21 import org.eclipse.swt.internal.ole.win32.extras;
22 import org.eclipse.swt.internal.win32.OS;
23 import org.eclipse.swt.widgets.Composite;
24 import org.eclipse.swt.widgets.Control;
25 import org.eclipse.swt.widgets.Display;
26 import org.eclipse.swt.widgets.Event;
27 import org.eclipse.swt.widgets.Listener;
28 import org.eclipse.swt.widgets.Menu;
29 import org.eclipse.swt.widgets.MenuItem;
30 import org.eclipse.swt.widgets.Shell;
31 import org.eclipse.swt.widgets.Widget;
32 
33 import org.eclipse.swt.ole.win32.OleClientSite;
34 import org.eclipse.swt.ole.win32.OLE;
35 
36 import org.eclipse.swt.internal.LONG;
37 
38 import java.lang.all;
39 import java.lang.Runnable;
40 
41 /**
42  *
43  * OleFrame is an OLE Container's top level frame.
44  *
45  * <p>This object implements the OLE Interfaces IUnknown and IOleInPlaceFrame
46  *
47  * <p>OleFrame allows the container to do the following: <ul>
48  *  <li>position and size the ActiveX Control or OLE Document within the application
49  *  <li>insert menu items from the application into the OLE Document's menu
50  *  <li>activate and deactivate the OLE Document's menus
51  *  <li>position the OLE Document's menu in the application
52  *  <li>translate accelerator keystrokes intended for the container's frame</ul>
53  *
54  * <dl>
55  *  <dt><b>Styles</b> <dd>BORDER
56  *  <dt><b>Events</b> <dd>Dispose, Move, Resize
57  * </dl>
58  *
59  * @see <a href="http://www.eclipse.org/swt/snippets/#ole">OLE and ActiveX snippets</a>
60  * @see <a href="http://www.eclipse.org/swt/examples.php">SWT Examples: OLEExample, OleWebBrowser</a>
61  */
62 final public class OleFrame : Composite
63 {
64     // Interfaces for this Ole Client Container
65     //private COMObject iUnknown;
66     private _IOleInPlaceFrameImpl iOleInPlaceFrame;
67 
68     // Access to the embedded/linked Ole Object
69     private IOleInPlaceActiveObject objIOleInPlaceActiveObject;
70 
71     private OleClientSite currentdoc;
72 
73     private int refCount = 0;
74 
75     private MenuItem[] fileMenuItems;
76     private MenuItem[] containerMenuItems;
77     private MenuItem[] windowMenuItems;
78 
79     private Listener listener;
80 
81     private static String CHECK_FOCUS = "OLE_CHECK_FOCUS"; //$NON-NLS-1$
82     private static String HHOOK = "OLE_HHOOK"; //$NON-NLS-1$
83     private static String HHOOKMSG = "OLE_HHOOK_MSG"; //$NON-NLS-1$
84 
85     private static bool ignoreNextKey;
86     private static const short [] ACCENTS = [ cast(short)'~', '`', '\'', '^', '"'];
87 
88     private static const String CONSUME_KEY = "org.eclipse.swt.OleFrame.ConsumeKey"; //$NON-NLS-1$
89 
90 /**
91  * Create an OleFrame child widget using style bits
92  * to select a particular look or set of properties.
93  *
94  * @param parent a composite widget (cannot be null)
95  * @param style the bitwise OR'ing of widget styles
96  *
97  * @exception IllegalArgumentException <ul>
98  *     <li>ERROR_NULL_ARGUMENT when the parent is null
99  * </ul>
100  * @exception SWTException <ul>
101  *     <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread
102  * </ul>
103  *
104  */
105 public this(Composite parent, int style) {
106     super(parent, style);
107 
108     createCOMInterfaces();
109 
110     // setup cleanup proc
111     listener = new class() Listener  {
112         public void handleEvent(Event e) {
113             switch (e.type) {
114             case SWT.Activate :    onActivate(e); break;
115             case SWT.Deactivate :  onDeactivate(e); break;
116             case SWT.Dispose :  onDispose(e); break;
117             case SWT.Resize :
118             case SWT.Move :     onResize(e); break;
119             default :
120                 OLE.error(SWT.ERROR_NOT_IMPLEMENTED);
121             }
122         }
123     };
124 
125 
126     addListener(SWT.Activate, listener);
127     addListener(SWT.Deactivate, listener);
128     addListener(SWT.Dispose, listener);
129 
130     // inform inplaceactiveobject whenever frame resizes
131     addListener(SWT.Resize, listener);
132 
133     // inform inplaceactiveobject whenever frame moves
134     addListener(SWT.Move, listener);
135 
136     // Maintain a reference to yourself so that when
137     // ClientSites close, they don't take the frame away
138     // with them.
139     this.AddRef();
140 
141     // Check for focus change
142     Display display = getDisplay();
143     initCheckFocus(display);
144     initMsgHook(display);
145 }
146 private static void initCheckFocus (Display display_) {
147     if (display_.getData(CHECK_FOCUS) !is null) return;
148     display_.setData(CHECK_FOCUS, new ArrayWrapperString(CHECK_FOCUS));
149     static const int time = 50;
150     auto timer = new class(display_) Runnable {
151         Display display;
152         Control[1] lastFocus;
153         this( Display display){ this.display = display; }
154         public void run() {
155             if (( null !is cast(OleClientSite)lastFocus[0] ) && !lastFocus[0].isDisposed()) {
156                 // ignore popup menus and dialogs
157                 auto hwnd = OS.GetFocus();
158                 while (hwnd !is null) {
159                     auto ownerHwnd = OS.GetWindow(hwnd, OS.GW_OWNER);
160                     if (ownerHwnd !is null) {
161                         display.timerExec(time, this);
162                         return;
163                     }
164                     hwnd = OS.GetParent(hwnd);
165                 }
166             }
167             if (lastFocus[0] is null || lastFocus[0].isDisposed() || !lastFocus[0].isFocusControl()) {
168                 Control currentFocus = display.getFocusControl();
169                 if ( auto frame = cast(OleFrame)currentFocus ) {
170                     currentFocus = frame.getCurrentDocument();
171                 }
172                 if (lastFocus[0] !is currentFocus) {
173                     Event event = new Event();
174                     if (( null !is cast(OleClientSite)lastFocus[0] ) && !lastFocus[0].isDisposed()) {
175                         lastFocus[0].notifyListeners (SWT.FocusOut, event);
176                     }
177                     if (( null !is cast(OleClientSite)currentFocus ) && !currentFocus.isDisposed()) {
178                         currentFocus.notifyListeners(SWT.FocusIn, event);
179                     }
180                 }
181                 lastFocus[0] = currentFocus;
182             }
183             display.timerExec(time, this);
184         }
185     };
186     display_.timerExec(time, timer);
187 }
188 private static void initMsgHook(Display display) {
189     if (display.getData(HHOOK) !is null) return;
190     //final Callback callback = new Callback(OleFrame.class, "getMsgProc", 3); //$NON-NLS-1$
191     //int address = callback.getAddress();
192     //if (address is 0) SWT.error(SWT.ERROR_NO_MORE_CALLBACKS);
193     int threadId = OS.GetCurrentThreadId();
194     auto hHook_ = OS.SetWindowsHookEx(OS.WH_GETMESSAGE, &getMsgProc, null, threadId);
195     if (hHook_ is null) {
196         //callback.dispose();
197         return;
198     }
199     display.setData(HHOOK, new ValueWrapperT!(void*)(hHook_));
200     display.setData(HHOOKMSG, new ValueWrapperT!(MSG*)(new MSG()));
201     display.disposeExec(new class(hHook_) Runnable {
202         void* hHook;
203         this( void* hHook ){ this.hHook = hHook; }
204         public void run() {
205             if (hHook !is null) OS.UnhookWindowsHookEx(hHook);
206             //if (callback !is null) callback.dispose();
207         }
208     });
209 }
210 static extern(Windows) .LRESULT getMsgProc(int code, WPARAM wParam, LPARAM lParam) {
211     Display display = Display.getCurrent();
212     if (display is null) return 0;
213     auto hHook = cast(ValueWrapperT!(void*))display.getData(HHOOK);
214     if (hHook is null) return 0;
215     if (code < 0) {
216         return OS.CallNextHookEx(hHook.value, code, wParam, lParam);
217     }
218     MSG* msg = cast(MSG*)(cast(ValueWrapperT!(MSG*))display.getData(HHOOKMSG)).value;
219     OS.MoveMemory(msg, lParam, MSG.sizeof);
220     int message = msg.message;
221     if (OS.WM_KEYFIRST <= message && message <= OS.WM_KEYLAST) {
222         if (display !is null) {
223             Widget widget = null;
224             auto hwnd = msg.hwnd;
225             while (hwnd !is null) {
226                 widget = display.findWidget (hwnd);
227                 if (widget !is null) break;
228                 hwnd = OS.GetParent (hwnd);
229             }
230             if (widget !is null && (null !is cast(OleClientSite)widget )) {
231                 OleClientSite site = cast(OleClientSite)widget;
232                 if (site.handle is hwnd) {
233                     bool consumed = false;
234                     /* Allow activeX control to translate accelerators except when a menu is active. */
235                     int thread = OS.GetWindowThreadProcessId(msg.hwnd, null);
236                     GUITHREADINFO*  lpgui = new GUITHREADINFO();
237                     lpgui.cbSize = GUITHREADINFO.sizeof;
238                     bool rc = cast(bool) OS.GetGUIThreadInfo(thread, lpgui);
239                     int mask = OS.GUI_INMENUMODE | OS.GUI_INMOVESIZE | OS.GUI_POPUPMENUMODE | OS.GUI_SYSTEMMENUMODE;
240                     if (!rc || (lpgui.flags & mask) is 0) {
241                         OleFrame frame = site.frame;
242                         frame.setData(CONSUME_KEY, null);
243                         consumed = frame.translateOleAccelerator(msg);
244                         if (frame.getData(CONSUME_KEY) !is null) consumed = false;
245                         frame.setData(CONSUME_KEY, null);
246                     }
247                     bool accentKey = false;
248                     switch (msg.message) {
249                         case OS.WM_KEYDOWN:
250                         case OS.WM_SYSKEYDOWN: {
251                             static if (!OS.IsWinCE) {
252                                 switch (msg.wParam) {
253                                     case OS.VK_SHIFT:
254                                     case OS.VK_MENU:
255                                     case OS.VK_CONTROL:
256                                     case OS.VK_CAPITAL:
257                                     case OS.VK_NUMLOCK:
258                                     case OS.VK_SCROLL:
259                                         break;
260                                     default: {
261                                         /*
262                                         * Bug in Windows. The high bit in the result of MapVirtualKey() on
263                                         * Windows NT is bit 32 while the high bit on Windows 95 is bit 16.
264                                         * They should both be bit 32.  The fix is to test the right bit.
265                                         */
266                                         int mapKey = OS.MapVirtualKey (cast(int)/*64bit*/msg.wParam, 2);
267                                         if (mapKey !is 0) {
268                                             accentKey = (mapKey & (OS.IsWinNT ? 0x80000000 : 0x8000)) !is 0;
269                                             if (!accentKey) {
270                                                 for (int i=0; i<ACCENTS.length; i++) {
271                                                     int value = OS.VkKeyScan (ACCENTS [i]);
272                                                     if (value !is -1 && (value & 0xFF) is msg.wParam) {
273                                                         int state = value >> 8;
274                                                         if ((OS.GetKeyState (OS.VK_SHIFT) < 0) is ((state & 0x1) !is 0) &&
275                                                             (OS.GetKeyState (OS.VK_CONTROL) < 0) is ((state & 0x2) !is 0) &&
276                                                             (OS.GetKeyState (OS.VK_MENU) < 0) is ((state & 0x4) !is 0)) {
277                                                                 if ((state & 0x7) !is 0) accentKey = true;
278                                                                 break;
279                                                         }
280                                                     }
281                                                 }
282                                             }
283                                         }
284                                         break;
285                                     }
286                                 }
287                             }
288                             break;
289                         }
290                         default:
291                     }
292                     /* Allow OleClientSite to process key events before activeX control */
293                     if (!consumed && !accentKey && !ignoreNextKey) {
294                         auto hwndOld = msg.hwnd;
295                         msg.hwnd = site.handle;
296                         consumed = OS.DispatchMessage (msg) is 1;
297                         msg.hwnd = hwndOld;
298                     }
299                     switch (msg.message) {
300                         case OS.WM_KEYDOWN:
301                         case OS.WM_SYSKEYDOWN: {
302                             switch (msg.wParam) {
303                                 case OS.VK_SHIFT:
304                                 case OS.VK_MENU:
305                                 case OS.VK_CONTROL:
306                                 case OS.VK_CAPITAL:
307                                 case OS.VK_NUMLOCK:
308                                 case OS.VK_SCROLL:
309                                     break;
310                                 default: {
311                                     ignoreNextKey = accentKey;
312                                     break;
313                                 }
314                             }
315                             break;
316                         }
317                         default:
318                             break;
319                     }
320 
321                     if (consumed) {
322                         // In order to prevent this message from also being processed
323                         // by the application, zero out message, wParam and lParam
324                         msg.message = OS.WM_NULL;
325                         msg.wParam = msg.lParam = 0;
326                         OS.MoveMemory(lParam, msg, MSG.sizeof);
327                         return 0;
328                     }
329                 }
330             }
331         }
332     }
333     return OS.CallNextHookEx( hHook.value, code, wParam, lParam);
334 }
335 /**
336  * Increment the count of references to this instance
337  *
338  * @return the current reference count
339  */
340 int AddRef() {
341     refCount++;
342     return refCount;
343 }
344 private int ContextSensitiveHelp(int fEnterMode) {
345     return COM.S_OK;
346 }
347 private void createCOMInterfaces() {
348     iOleInPlaceFrame = new _IOleInPlaceFrameImpl(this);
349 }
350 private void disposeCOMInterfaces () {
351     iOleInPlaceFrame = null;
352 }
353 private HRESULT GetBorder(LPRECT lprectBorder) {
354     /*
355     The IOleInPlaceUIWindow::GetBorder function, when called on a document or frame window
356     object, returns the outer rectangle (relative to the window) where the object can put
357     toolbars or similar controls.
358     */
359     if (lprectBorder is null) return COM.E_INVALIDARG;
360     RECT* rectBorder = new RECT();
361     // Coordinates must be relative to the window
362     OS.GetClientRect(handle, lprectBorder);
363     return COM.S_OK;
364 }
365 /**
366  *
367  * Returns the application menu items that will appear in the Container location when an OLE Document
368  * is in-place activated.
369  *
370  * <p>When an OLE Document is in-place active, the Document provides its own menus but the application
371  * is given the opportunity to merge some of its menus into the menubar.  The application
372  * is allowed to insert its menus in three locations: File (far left), Container(middle) and Window
373  * (far right just before Help).  The OLE Document retains control of the Edit, Object and Help
374  * menu locations.  Note that an application can insert more than one menu into a single location.
375  *
376  * @return the application menu items that will appear in the Container location when an OLE Document
377  *         is in-place activated.
378  *
379  */
380 public MenuItem[] getContainerMenus(){
381     return containerMenuItems;
382 }
383 /**
384  *
385  * Returns the application menu items that will appear in the File location when an OLE Document
386  * is in-place activated.
387  *
388  * <p>When an OLE Document is in-place active, the Document provides its own menus but the application
389  * is given the opportunity to merge some of its menus into the menubar.  The application
390  * is allowed to insert its menus in three locations: File (far left), Container(middle) and Window
391  * (far right just before Help).  The OLE Document retains control of the Edit, Object and Help
392  * menu locations.  Note that an application can insert more than one menu into a single location.
393  *
394  * @return the application menu items that will appear in the File location when an OLE Document
395  *         is in-place activated.
396  *
397  */
398 public MenuItem[] getFileMenus(){
399     return fileMenuItems;
400 }
401 IOleInPlaceFrame getIOleInPlaceFrame() {
402     return iOleInPlaceFrame;
403 }
404 private ptrdiff_t getMenuItemID(HMENU hMenu, int index) {
405     ptrdiff_t id = 0;
406     MENUITEMINFO lpmii;
407     lpmii.cbSize = OS.MENUITEMINFO_sizeof;
408     lpmii.fMask = OS.MIIM_STATE | OS.MIIM_SUBMENU | OS.MIIM_ID;
409     OS.GetMenuItemInfo(hMenu, index, true, &lpmii);
410     if ((lpmii.fState & OS.MF_POPUP) is OS.MF_POPUP) {
411         id = cast(ptrdiff_t)lpmii.hSubMenu;
412     } else {
413         id = lpmii.wID;
414     }
415     return id;
416 }
417 private int GetWindow(HWND* phwnd) {
418     if (phwnd !is null) {
419         *phwnd = handle;
420     }
421     return COM.S_OK;
422 }
423 /**
424  *
425  * Returns the application menu items that will appear in the Window location when an OLE Document
426  * is in-place activated.
427  *
428  * <p>When an OLE Document is in-place active, the Document provides its own menus but the application
429  * is given the opportunity to merge some of its menus into the menubar.  The application
430  * is allowed to insert its menus in three locations: File (far left), Container(middle) and Window
431  * (far right just before Help).  The OLE Document retains control of the Edit, Object and Help
432  * menu locations.  Note that an application can insert more than one menu into a single location.
433  *
434  * @return the application menu items that will appear in the Window location when an OLE Document
435  *         is in-place activated.
436  *
437  */
438 public MenuItem[] getWindowMenus(){
439     return windowMenuItems;
440 }
441 private HRESULT InsertMenus( HMENU hmenuShared, LPOLEMENUGROUPWIDTHS lpMenuWidths ) {
442     // locate menu bar
443     Menu menubar = getShell().getMenuBar();
444     if (menubar is null || menubar.isDisposed()) {
445         int temp = 0;
446         COM.MoveMemory(lpMenuWidths, &temp, 4);
447         return COM.S_OK;
448     }
449     HMENU hMenu = menubar.handle;
450 
451     // Create a holder for menu information.  This will be passed down to
452     // the OS and the OS will fill in the requested information for each menu.
453     MENUITEMINFO lpmii;
454     auto hHeap = OS.GetProcessHeap();
455     int cch = 128;
456     auto byteCount = cch * TCHAR.sizeof;
457     auto pszText = cast(TCHAR*) OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, byteCount);
458     lpmii.cbSize = OS.MENUITEMINFO_sizeof;
459     lpmii.fMask = OS.MIIM_STATE | OS.MIIM_ID | OS.MIIM_TYPE | OS.MIIM_SUBMENU | OS.MIIM_DATA;
460     lpmii.dwTypeData = pszText;
461     lpmii.cch = cch;
462 
463     // Loop over all "File-like" menus in the menubar and get information about the
464     // item from the OS.
465     int fileMenuCount = 0;
466     int newindex = 0;
467     if (this.fileMenuItems !is null) {
468         for (int i = 0; i < this.fileMenuItems.length; i++) {
469             MenuItem item = this.fileMenuItems[i];
470             if (item !is null) {
471                 int index = item.getParent().indexOf(item);
472                 lpmii.cch = cch;  // lpmii.cch gets updated by GetMenuItemInfo to indicate the
473                                   // exact number of characters in name.  Reset it to our max size
474                                   // before each call.
475                 if (OS.GetMenuItemInfo(hMenu, index, true, &lpmii)) {
476                     if (OS.InsertMenuItem(hmenuShared, newindex, true, &lpmii)) {
477                         // keep track of the number of items
478                         fileMenuCount++;
479                         newindex++;
480                     }
481                 }
482             }
483         }
484     }
485 
486     // copy the menu item count information to the pointer
487     COM.MoveMemory(lpMenuWidths, &fileMenuCount, 4);
488 
489     // Loop over all "Container-like" menus in the menubar and get information about the
490     // item from the OS.
491     int containerMenuCount = 0;
492     if (this.containerMenuItems !is null) {
493         for (int i = 0; i < this.containerMenuItems.length; i++) {
494             MenuItem item = this.containerMenuItems[i];
495             if (item !is null) {
496                 int index = item.getParent().indexOf(item);
497                 lpmii.cch = cch; // lpmii.cch gets updated by GetMenuItemInfo to indicate the
498                                            // exact number of characters in name.  Reset it to a large number
499                                            // before each call.
500                 if (OS.GetMenuItemInfo(hMenu, index, true, &lpmii)) {
501                     if (OS.InsertMenuItem(hmenuShared, newindex, true, &lpmii)) {
502                         // keep track of the number of items
503                         containerMenuCount++;
504                         newindex++;
505                     }
506                 }
507             }
508         }
509     }
510 
511     // copy the menu item count information to the pointer
512     COM.MoveMemory((cast(void*)lpMenuWidths) + 8, &containerMenuCount, 4);
513 
514     // Loop over all "Window-like" menus in the menubar and get information about the
515     // item from the OS.
516     int windowMenuCount = 0;
517     if (this.windowMenuItems !is null) {
518         for (int i = 0; i < this.windowMenuItems.length; i++) {
519             MenuItem item = this.windowMenuItems[i];
520             if (item !is null) {
521                 int index = item.getParent().indexOf(item);
522                 lpmii.cch = cch; // lpmii.cch gets updated by GetMenuItemInfo to indicate the
523                                            // exact number of characters in name.  Reset it to a large number
524                                            // before each call.
525                 if (OS.GetMenuItemInfo(hMenu, index, true, &lpmii)) {
526                     if (OS.InsertMenuItem(hmenuShared, newindex, true, &lpmii)) {
527                         // keep track of the number of items
528                         windowMenuCount++;
529                         newindex++;
530                     }
531                 }
532             }
533         }
534     }
535 
536     // copy the menu item count information to the pointer
537     COM.MoveMemory((cast(void*)lpMenuWidths) + 16, &windowMenuCount, 4);
538 
539     // free resources used in querying the OS
540     if (pszText !is null)
541         OS.HeapFree(hHeap, 0, pszText);
542     return COM.S_OK;
543 }
544 void onActivate(Event e) {
545     if (objIOleInPlaceActiveObject !is null) {
546         objIOleInPlaceActiveObject.OnFrameWindowActivate(true);
547     }
548     if (objIOleInPlaceActiveObject !is null) {
549         objIOleInPlaceActiveObject.OnDocWindowActivate(true);
550     }
551 }
552 void onDeactivate(Event e) {
553     if (objIOleInPlaceActiveObject !is null) {
554         objIOleInPlaceActiveObject.OnFrameWindowActivate(false);
555     }
556     if (objIOleInPlaceActiveObject !is null) {
557         objIOleInPlaceActiveObject.OnDocWindowActivate(false);
558     }
559 }
560 private void onDispose(Event e) {
561 
562     releaseObjectInterfaces();
563     currentdoc = null;
564 
565     this.Release();
566     removeListener(SWT.Activate, listener);
567     removeListener(SWT.Deactivate, listener);
568     removeListener(SWT.Dispose, listener);
569     removeListener(SWT.Resize, listener);
570     removeListener(SWT.Move, listener);
571 }
572 private void onResize(Event e) {
573     if (objIOleInPlaceActiveObject !is null) {
574         RECT lpRect;
575         OS.GetClientRect(handle, &lpRect);
576         objIOleInPlaceActiveObject.ResizeBorder(&lpRect, iOleInPlaceFrame, true);
577     }
578 }
579 private HRESULT QueryInterface(REFCIID riid, void** ppvObject) {
580 //  implements IUnknown, IOleInPlaceFrame, IOleContainer, IOleInPlaceUIWindow
581     if (riid is null || ppvObject is null)
582         return COM.E_INVALIDARG;
583 
584     if (COM.IsEqualGUID(riid, &COM.IIDIUnknown) || COM.IsEqualGUID(riid, &COM.IIDIOleInPlaceFrame) ) {
585         *ppvObject = cast(void*)cast(IOleInPlaceFrame)iOleInPlaceFrame;
586         AddRef();
587         return COM.S_OK;
588     }
589 
590     *ppvObject = null;
591     return COM.E_NOINTERFACE;
592 }
593 /**
594  * Decrement the count of references to this instance
595  *
596  * @return the current reference count
597  */
598 int Release() {
599     refCount--;
600     if (refCount is 0){
601         disposeCOMInterfaces();
602         COM.CoFreeUnusedLibraries();
603     }
604     return refCount;
605 }
606 private void releaseObjectInterfaces() {
607     if (objIOleInPlaceActiveObject !is null) {
608         objIOleInPlaceActiveObject.Release();
609     }
610     objIOleInPlaceActiveObject = null;
611 }
612 private int RemoveMenus(HMENU hmenuShared) {
613 
614     Menu menubar = getShell().getMenuBar();
615     if (menubar is null || menubar.isDisposed()) return COM.S_FALSE;
616 
617     auto hMenu = menubar.handle;
618 
619     Vector ids = new Vector();
620     if (this.fileMenuItems !is null) {
621         for (int i = 0; i < this.fileMenuItems.length; i++) {
622             MenuItem item = this.fileMenuItems[i];
623             if (item !is null && !item.isDisposed()) {
624                 int index = item.getParent().indexOf(item);
625                 // get Id from original menubar
626                 auto id = getMenuItemID(hMenu, index);
627                 ids.addElement(new org.eclipse.swt.internal.LONG.LONG_PTR(id));
628             }
629         }
630     }
631     if (this.containerMenuItems !is null) {
632         for (int i = 0; i < this.containerMenuItems.length; i++) {
633             MenuItem item = this.containerMenuItems[i];
634             if (item !is null && !item.isDisposed()) {
635                 int index = item.getParent().indexOf(item);
636                 auto id = getMenuItemID(hMenu, index);
637                 ids.addElement(new org.eclipse.swt.internal.LONG.LONG_PTR(id));
638             }
639         }
640     }
641     if (this.windowMenuItems !is null) {
642         for (int i = 0; i < this.windowMenuItems.length; i++) {
643             MenuItem item = this.windowMenuItems[i];
644             if (item !is null && !item.isDisposed()) {
645                 int index = item.getParent().indexOf(item);
646                 auto id = getMenuItemID(hMenu, index);
647                 ids.addElement(new org.eclipse.swt.internal.LONG.LONG_PTR(id));
648             }
649         }
650     }
651     int index = OS.GetMenuItemCount(hmenuShared) - 1;
652     for (int i = index; i >= 0; i--) {
653         auto id = getMenuItemID(hmenuShared, i);
654         if (ids.contains(new org.eclipse.swt.internal.LONG.LONG_PTR(id))){
655             OS.RemoveMenu(hmenuShared, i, OS.MF_BYPOSITION);
656         }
657     }
658     return COM.S_OK;
659 }
660 private int RequestBorderSpace(LPCBORDERWIDTHS pborderwidths) {
661     return COM.S_OK;
662 }
663 HRESULT SetActiveObject( LPOLEINPLACEACTIVEOBJECT pActiveObject, LPCOLESTR pszObjName ) {
664     if (objIOleInPlaceActiveObject !is null) {
665         objIOleInPlaceActiveObject.Release();
666         objIOleInPlaceActiveObject = null;
667     }
668     if (pActiveObject !is null) {
669         objIOleInPlaceActiveObject = pActiveObject;
670         objIOleInPlaceActiveObject.AddRef();
671     }
672 
673     return COM.S_OK;
674 }
675 
676 private HRESULT SetBorderSpace( LPCBORDERWIDTHS pborderwidths ) {
677     // A Control/Document can :
678     // Use its own toolbars, requesting border space of a specific size, or,
679     // Use no toolbars, but force the container to remove its toolbars by passing a
680     //   valid BORDERWIDTHS structure containing nothing but zeros in the pborderwidths parameter, or,
681     // Use no toolbars but allow the in-place container to leave its toolbars up by
682     //   passing NULL as the pborderwidths parameter.
683     if (objIOleInPlaceActiveObject is null) return COM.S_OK;
684     RECT* borderwidth = new RECT();
685     if (pborderwidths is null || currentdoc is null ) return COM.S_OK;
686 
687     COM.MoveMemory(borderwidth, pborderwidths, RECT.sizeof);
688     currentdoc.setBorderSpace(borderwidth);
689 
690     return COM.S_OK;
691 }
692 /**
693  *
694  * Specify the menu items that should appear in the Container location when an OLE Document
695  * is in-place activated.
696  *
697  * <p>When an OLE Document is in-place active, the Document provides its own menus but the application
698  * is given the opportunity to merge some of its menus into the menubar.  The application
699  * is allowed to insert its menus in three locations: File (far left), Container(middle) and Window
700  * (far right just before Help).  The OLE Document retains control of the Edit, Object and Help
701  * menu locations.  Note that an application can insert more than one menu into a single location.
702  *
703  * <p>This method must be called before in place activation of the OLE Document.  After the Document
704  * is activated, the menu bar will not be modified until a subsequent activation.
705  *
706  * @param containerMenus an array of top level MenuItems to be inserted into the Container location of
707  *        the menubar
708  */
709 public void setContainerMenus(MenuItem[] containerMenus){
710     containerMenuItems = containerMenus;
711 }
712 OleClientSite getCurrentDocument() {
713     return currentdoc;
714 }
715 void setCurrentDocument(OleClientSite doc) {
716     currentdoc = doc;
717 
718     if (currentdoc !is null && objIOleInPlaceActiveObject !is null) {
719         RECT lpRect;
720         OS.GetClientRect(handle, &lpRect);
721         objIOleInPlaceActiveObject.ResizeBorder(&lpRect, iOleInPlaceFrame, true);
722     }
723 }
724 /**
725  *
726  * Specify the menu items that should appear in the File location when an OLE Document
727  * is in-place activated.
728  *
729  * <p>When an OLE Document is in-place active, the Document provides its own menus but the application
730  * is given the opportunity to merge some of its menus into the menubar.  The application
731  * is allowed to insert its menus in three locations: File (far left), Container(middle) and Window
732  * (far right just before Help).  The OLE Document retains control of the Edit, Object and Help
733  * menu locations.  Note that an application can insert more than one menu into a single location.
734  *
735  * <p>This method must be called before in place activation of the OLE Document.  After the Document
736  * is activated, the menu bar will not be modified until a subsequent activation.
737  *
738  * @param fileMenus an array of top level MenuItems to be inserted into the File location of
739  *        the menubar
740  */
741 public void setFileMenus(MenuItem[] fileMenus){
742     fileMenuItems = fileMenus;
743 }
744 HRESULT SetMenu( HMENU hmenuShared, HOLEMENU holemenu, HWND hwndActiveObject ) {
745     IOleInPlaceActiveObject inPlaceActiveObject;
746     if (objIOleInPlaceActiveObject !is null)
747         inPlaceActiveObject = objIOleInPlaceActiveObject;
748 
749     Menu menubar = getShell().getMenuBar();
750     if (menubar is null || menubar.isDisposed()){
751         return COM.OleSetMenuDescriptor(null, getShell().handle, hwndActiveObject, iOleInPlaceFrame, inPlaceActiveObject);
752     }
753 
754     HWND handle = menubar.getShell().handle;
755 
756     if (hmenuShared is null && holemenu is null) {
757         // re-instate the original menu - this occurs on deactivation
758         hmenuShared = menubar.handle;
759     }
760     if (hmenuShared is null) return COM.E_FAIL;
761 
762     OS.SetMenu(handle, hmenuShared);
763     OS.DrawMenuBar(handle);
764 
765     return COM.OleSetMenuDescriptor(holemenu, handle, hwndActiveObject, iOleInPlaceFrame, inPlaceActiveObject);
766 }
767 
768 /**
769  *
770  * Set the menu items that should appear in the Window location when an OLE Document
771  * is in-place activated.
772  *
773  * <p>When an OLE Document is in-place active, the Document provides its own menus but the application
774  * is given the opportunity to merge some of its menus into the menubar.  The application
775  * is allowed to insert its menus in three locations: File (far left), Container(middle) and Window
776  * (far right just before Help).  The OLE Document retains control of the Edit, Object and Help
777  * menu locations.  Note that an application can insert more than one menu into a single location.
778  *
779  * <p>This method must be called before in place activation of the OLE Document.  After the Document
780  * is activated, the menu bar will not be modified until a subsequent activation.
781  *
782  * @param windowMenus an array of top level MenuItems to be inserted into the Window location of
783  *        the menubar
784  */
785 public void setWindowMenus(MenuItem[] windowMenus){
786     windowMenuItems = windowMenus;
787 }
788 private bool translateOleAccelerator(MSG* msg) {
789     if (objIOleInPlaceActiveObject is null) return false;
790     int result = objIOleInPlaceActiveObject.TranslateAccelerator(msg);
791     return (result != COM.S_FALSE && result != COM.E_NOTIMPL);
792 }
793 
794 HRESULT TranslateAccelerator( LPMSG lpmsg, WORD wID ){
795     Menu menubar = getShell().getMenuBar();
796     if (menubar is null || menubar.isDisposed() || !menubar.isEnabled()) return COM.S_FALSE;
797     if (wID < 0) return COM.S_FALSE;
798 
799     Shell shell = menubar.getShell();
800     HWND hwnd = shell.handle;
801     HACCEL hAccel = cast(HACCEL)OS.SendMessage(hwnd, OS.WM_APP+1, 0, 0);
802     if (hAccel is null) return COM.S_FALSE;
803 
804     MSG msg = *lpmsg;
805     int result = OS.TranslateAccelerator(hwnd, hAccel, &msg);
806     return result == 0 ? COM.S_FALSE : COM.S_OK;
807 }
808 }
809 
810 //  implements IOleInPlaceFrame, IOleInPlaceUIWindow, IOleWindow, IUnknown
811 class _IOleInPlaceFrameImpl : IOleInPlaceFrame {
812 
813     OleFrame parent;
814     this(OleFrame p) { parent = p; }
815 extern (Windows) :
816     // interface of IUnknown
817     HRESULT QueryInterface(REFCIID riid, void ** ppvObject) { return parent.QueryInterface(riid, ppvObject); }
818     ULONG AddRef()  { return parent.AddRef(); }
819     ULONG Release() { return parent.Release(); }
820 
821     // interface IOleWindow
822     HRESULT GetWindow( HWND * phwnd ) { return parent.GetWindow(phwnd); }
823     HRESULT ContextSensitiveHelp( BOOL fEnterMode ){ return COM.S_OK; }
824 
825     //interface IOleInPlaceUIWindow
826     HRESULT GetBorder( LPRECT lprectBorder ) { return parent.GetBorder(lprectBorder); }
827     HRESULT RequestBorderSpace( LPCBORDERWIDTHS pborderwidths ){ return COM.S_OK; }
828     HRESULT SetBorderSpace( LPCBORDERWIDTHS pborderwidths ) { return parent.SetBorderSpace(pborderwidths); }
829     HRESULT SetActiveObject( LPOLEINPLACEACTIVEOBJECT pActiveObject, LPCOLESTR pszObjName ) {
830         return parent.SetActiveObject(pActiveObject, pszObjName);
831     }
832 
833     // interface IOleInPlaceFrame : IOleInPlaceUIWindow
834     HRESULT InsertMenus( HMENU hmenuShared, LPOLEMENUGROUPWIDTHS lpMenuWidths ){
835         return parent.InsertMenus(hmenuShared, lpMenuWidths);
836     }
837     HRESULT SetMenu( HMENU hmenuShared, HOLEMENU holemenu, HWND hwndActiveObject ){
838         return parent.SetMenu(hmenuShared, holemenu, hwndActiveObject);
839     }
840     HRESULT RemoveMenus( HMENU hmenuShared ) {
841         return parent.RemoveMenus(hmenuShared);
842     }
843     HRESULT SetStatusText( LPCOLESTR pszStatusText ) { return COM.E_NOTIMPL; }
844     HRESULT EnableModeless( BOOL fEnable ) { return COM.E_NOTIMPL; }
845     HRESULT TranslateAccelerator( LPMSG lpmsg, WORD wID ) {
846         return parent.TranslateAccelerator(lpmsg, wID);
847     }
848 
849 }
850