4
4
using System . Diagnostics . CodeAnalysis ;
5
5
using System . Drawing ;
6
6
using System . Runtime . InteropServices ;
7
+ using System . Text ;
7
8
using System . Windows . Forms ;
8
9
using Rubberduck . VBEditor ;
9
10
using Rubberduck . VBEditor . WindowsApi ;
@@ -145,7 +146,7 @@ private void RemoveChildControlsFromExposedControl()
145
146
public int /* IOleObject:: */ Close ( [ In ] uint dwSaveOption )
146
147
{
147
148
_logger . Log ( LogLevel . Trace , "IOleObject::Close() called" ) ;
148
- int hr = _userControl . IOleObject . Close ( dwSaveOption ) ;
149
+ var hr = _userControl . IOleObject . Close ( dwSaveOption ) ;
149
150
150
151
// IOleObject::SetClientSite is typically called with pClientSite = null just before calling IOleObject::Close()
151
152
// If it didn't, we release all host COM objects here instead,
@@ -386,7 +387,7 @@ public void AddUserControl(UserControl control, IntPtr vbeHwnd)
386
387
387
388
control . Dock = DockStyle . Fill ;
388
389
_userControl . Controls . Add ( control ) ;
389
-
390
+
390
391
AdjustSize ( ) ;
391
392
}
392
393
@@ -429,14 +430,79 @@ private void AdjustSize()
429
430
}
430
431
}
431
432
433
+ private static void ToggleDockable ( IntPtr hWndVBE )
434
+ {
435
+ NativeMethods . SendMessage ( hWndVBE , 0x1044 , ( IntPtr ) 0xB5 , IntPtr . Zero ) ;
436
+ }
437
+
432
438
[ ComVisible ( false ) ]
433
439
public class ParentWindow : SubclassingWindow
434
440
{
441
+ private readonly Logger _logger = LogManager . GetCurrentClassLogger ( ) ;
442
+
443
+ private const int MF_BYPOSITION = 0x400 ;
444
+
435
445
public event SubClassingWindowEventHandler CallBackEvent ;
436
446
public delegate void SubClassingWindowEventHandler ( object sender , SubClassingWindowEventArgs e ) ;
437
447
438
448
private readonly IntPtr _vbeHwnd ;
439
449
450
+ private IntPtr _containerHwnd ;
451
+ private ToolWindowState _windowState ;
452
+ private IntPtr _menuHandle ;
453
+
454
+ private enum ToolWindowState
455
+ {
456
+ Unknown ,
457
+ Docked ,
458
+ Floating ,
459
+ Undockable
460
+ }
461
+
462
+ private ToolWindowState GetWindowState ( IntPtr containerHwnd )
463
+ {
464
+ var className = new StringBuilder ( 255 ) ;
465
+ if ( NativeMethods . GetClassName ( containerHwnd , className , className . Capacity ) > 0 )
466
+ {
467
+ switch ( className . ToString ( ) )
468
+ {
469
+ case "wndclass_desked_gsk" :
470
+ return ToolWindowState . Docked ;
471
+ case "VBFloatingPalette" :
472
+ return ToolWindowState . Floating ;
473
+ case "DockingView" :
474
+ return ToolWindowState . Undockable ;
475
+ }
476
+ }
477
+
478
+ return ToolWindowState . Unknown ;
479
+ }
480
+
481
+ private void DisplayUndockableContextMenu ( IntPtr handle , IntPtr lParam )
482
+ {
483
+ if ( _menuHandle == IntPtr . Zero )
484
+ {
485
+ _menuHandle = NativeMethods . CreatePopupMenu ( ) ;
486
+
487
+ if ( _menuHandle == IntPtr . Zero )
488
+ {
489
+ _logger . Warn ( "Cannot create menu handle" ) ;
490
+ return ;
491
+ }
492
+
493
+ if ( ! NativeMethods . InsertMenu ( _menuHandle , 0 , MF_BYPOSITION , ( UIntPtr ) WM . RUBBERDUCK_UNDOCKABLE_CONTEXT_MENU , "Dockable" + char . MinValue ) )
494
+ {
495
+ _logger . Warn ( "Failed to insert a menu item for dockable command" ) ;
496
+ }
497
+ }
498
+
499
+ var param = new LParam { Value = ( uint ) lParam } ;
500
+ if ( ! NativeMethods . TrackPopupMenuEx ( _menuHandle , 0x0 , param . LowWord , param . HighWord , handle , IntPtr . Zero ) )
501
+ {
502
+ _logger . Warn ( "Failed to set the context menu for undockable tool windows" ) ;
503
+ } ;
504
+ }
505
+
440
506
private void OnCallBackEvent ( SubClassingWindowEventArgs e )
441
507
{
442
508
CallBackEvent ? . Invoke ( this , e ) ;
@@ -452,6 +518,28 @@ public override int SubClassProc(IntPtr hWnd, IntPtr msg, IntPtr wParam, IntPtr
452
518
{
453
519
switch ( ( uint ) msg )
454
520
{
521
+ case ( uint ) WM . WINDOWPOSCHANGED :
522
+ var containerHwnd = GetParent ( hWnd ) ;
523
+ if ( containerHwnd != _containerHwnd )
524
+ {
525
+ _containerHwnd = containerHwnd ;
526
+ _windowState = GetWindowState ( _containerHwnd ) ;
527
+ }
528
+ break ;
529
+ case ( uint ) WM . CONTEXTMENU :
530
+ if ( _windowState == ToolWindowState . Undockable )
531
+ {
532
+ DisplayUndockableContextMenu ( hWnd , lParam ) ;
533
+ }
534
+ break ;
535
+ case ( uint ) WM . COMMAND :
536
+ switch ( wParam . ToInt32 ( ) )
537
+ {
538
+ case ( int ) WM . RUBBERDUCK_UNDOCKABLE_CONTEXT_MENU :
539
+ ToggleDockable ( _vbeHwnd ) ;
540
+ break ;
541
+ }
542
+ break ;
455
543
case ( uint ) WM . SIZE :
456
544
var args = new SubClassingWindowEventArgs ( lParam ) ;
457
545
if ( ! _closing ) OnCallBackEvent ( args ) ;
@@ -462,6 +550,15 @@ public override int SubClassProc(IntPtr hWnd, IntPtr msg, IntPtr wParam, IntPtr
462
550
case ( uint ) WM . KILLFOCUS :
463
551
if ( ! _closing ) User32 . SendMessage ( _vbeHwnd , WM . RUBBERDUCK_CHILD_FOCUS , Hwnd , IntPtr . Zero ) ;
464
552
break ;
553
+ case ( uint ) WM . DESTROY :
554
+ if ( _menuHandle != IntPtr . Zero )
555
+ {
556
+ if ( ! NativeMethods . DestroyMenu ( _menuHandle ) )
557
+ {
558
+ _logger . Fatal ( $ "Failed to destroy the menu handle { _menuHandle } ") ;
559
+ }
560
+ }
561
+ break ;
465
562
}
466
563
return base . SubClassProc ( hWnd , msg , wParam , lParam , uIdSubclass , dwRefData ) ;
467
564
}
0 commit comments