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 ;
@@ -429,46 +430,76 @@ private void AdjustSize()
429
430
}
430
431
}
431
432
432
- private const int WM_SYSCOMMAND = 0x112 ;
433
- private const int MF_BYPOSITION = 0x400 ;
434
- private const int ToggleDockableMenuId = 1000 ;
433
+ private static void ToggleDockable ( IntPtr hWndVBE )
434
+ {
435
+ NativeMethods . SendMessage ( hWndVBE , 0x1044 , ( IntPtr ) 0xB5 , IntPtr . Zero ) ;
436
+ }
437
+
438
+ [ ComVisible ( false ) ]
439
+ public class ParentWindow : SubclassingWindow
440
+ {
441
+ private const int MF_BYPOSITION = 0x400 ;
435
442
436
- [ DllImport ( "user32.dll" ) ]
437
- private static extern IntPtr GetSystemMenu ( IntPtr hWnd , bool bRevert ) ;
438
- [ DllImport ( "user32.dll" ) ]
439
- private static extern bool InsertMenu ( IntPtr hMenu , int wPosition , int wFlags , int wIDNewItem , string lpNewItem ) ;
443
+ public event SubClassingWindowEventHandler CallBackEvent ;
444
+ public delegate void SubClassingWindowEventHandler ( object sender , SubClassingWindowEventArgs e ) ;
440
445
441
- [ DllImport ( "user32.dll" , CharSet = CharSet . Auto ) ]
442
- public static extern IntPtr SendMessage ( IntPtr hWnd , int Msg , int wParam , IntPtr lParam ) ;
446
+ private readonly IntPtr _vbeHwnd ;
443
447
444
- private static void InsertDockableToggle ( IntPtr handle )
445
- {
446
- var menuHandle = GetSystemMenu ( handle , false ) ;
448
+ private IntPtr _containerHwnd ;
449
+ private ToolWindowState _windowState ;
450
+ private IntPtr _menuHandle ;
447
451
448
- if ( menuHandle == IntPtr . Zero )
452
+ private enum ToolWindowState
449
453
{
450
- Debug . Print ( "No menu handle" ) ;
451
- return ;
454
+ Unknown ,
455
+ Docked ,
456
+ Floating ,
457
+ Undockable
452
458
}
453
459
454
- if ( ! InsertMenu ( menuHandle , 5 , MF_BYPOSITION , ToggleDockableMenuId , "Dockable" ) )
460
+ private ToolWindowState GetWindowState ( IntPtr containerHwnd )
455
461
{
456
- Debug . Print ( "Failed to insert a menu item for dockable command" ) ;
462
+ var className = new StringBuilder ( 255 ) ;
463
+ if ( NativeMethods . GetClassName ( containerHwnd , className , className . Capacity ) > 0 )
464
+ {
465
+ switch ( className . ToString ( ) )
466
+ {
467
+ case "wndclass_desked_gsk" :
468
+ return ToolWindowState . Docked ;
469
+ case "VBFloatingPalette" :
470
+ return ToolWindowState . Floating ;
471
+ case "DockingView" :
472
+ return ToolWindowState . Undockable ;
473
+ }
474
+ }
475
+
476
+ return ToolWindowState . Unknown ;
457
477
}
458
- }
459
478
460
- private static void ToggleDockable ( IntPtr hWndVBE )
461
- {
462
- SendMessage ( hWndVBE , 0x1044 , 0xB5 , IntPtr . Zero ) ;
463
- }
479
+ private void DisplayUndockableContextMenu ( IntPtr handle , IntPtr lParam )
480
+ {
481
+ if ( _menuHandle == IntPtr . Zero )
482
+ {
483
+ _menuHandle = NativeMethods . CreatePopupMenu ( ) ;
464
484
465
- [ ComVisible ( false ) ]
466
- public class ParentWindow : SubclassingWindow
467
- {
468
- public event SubClassingWindowEventHandler CallBackEvent ;
469
- public delegate void SubClassingWindowEventHandler ( object sender , SubClassingWindowEventArgs e ) ;
485
+ if ( _menuHandle == IntPtr . Zero )
486
+ {
487
+ Debug . Print ( "Cannot create menu handle" ) ;
488
+ return ;
489
+ }
470
490
471
- private readonly IntPtr _vbeHwnd ;
491
+ if ( ! NativeMethods . InsertMenu ( _menuHandle , 0 , MF_BYPOSITION , ( UIntPtr ) WM . RUBBERDUCK_UNDOCKABLE_CONTEXT_MENU , "Dockable" + char . MinValue ) )
492
+ {
493
+ Debug . Print ( "Failed to insert a menu item for dockable command" ) ;
494
+ }
495
+ }
496
+
497
+ var param = new LParam { Value = ( uint ) lParam } ;
498
+ if ( ! NativeMethods . TrackPopupMenuEx ( _menuHandle , 0x0 , param . LowWord , param . HighWord , handle , IntPtr . Zero ) )
499
+ {
500
+ Debug . Print ( "Failed to set the context menu for undockable tool windows" ) ;
501
+ } ;
502
+ }
472
503
473
504
private void OnCallBackEvent ( SubClassingWindowEventArgs e )
474
505
{
@@ -485,10 +516,24 @@ public override int SubClassProc(IntPtr hWnd, IntPtr msg, IntPtr wParam, IntPtr
485
516
{
486
517
switch ( ( uint ) msg )
487
518
{
488
- case ( uint ) WM . SYSCOMMAND :
519
+ case ( uint ) WM . WINDOWPOSCHANGED :
520
+ var containerHwnd = GetParent ( hWnd ) ;
521
+ if ( containerHwnd != _containerHwnd )
522
+ {
523
+ _containerHwnd = containerHwnd ;
524
+ _windowState = GetWindowState ( _containerHwnd ) ;
525
+ }
526
+ break ;
527
+ case ( uint ) WM . CONTEXTMENU :
528
+ if ( _windowState == ToolWindowState . Undockable )
529
+ {
530
+ DisplayUndockableContextMenu ( hWnd , lParam ) ;
531
+ }
532
+ break ;
533
+ case ( uint ) WM . COMMAND :
489
534
switch ( wParam . ToInt32 ( ) )
490
535
{
491
- case ToggleDockableMenuId :
536
+ case ( int ) WM . RUBBERDUCK_UNDOCKABLE_CONTEXT_MENU :
492
537
ToggleDockable ( _vbeHwnd ) ;
493
538
break ;
494
539
}
@@ -503,6 +548,15 @@ public override int SubClassProc(IntPtr hWnd, IntPtr msg, IntPtr wParam, IntPtr
503
548
case ( uint ) WM . KILLFOCUS :
504
549
if ( ! _closing ) User32 . SendMessage ( _vbeHwnd , WM . RUBBERDUCK_CHILD_FOCUS , Hwnd , IntPtr . Zero ) ;
505
550
break ;
551
+ case ( uint ) WM . DESTROY :
552
+ if ( _menuHandle != IntPtr . Zero )
553
+ {
554
+ if ( ! NativeMethods . DestroyMenu ( _menuHandle ) )
555
+ {
556
+ Debug . Print ( $ "Failed to destroy the menu handle { _menuHandle } ") ;
557
+ }
558
+ }
559
+ break ;
506
560
}
507
561
return base . SubClassProc ( hWnd , msg , wParam , lParam , uIdSubclass , dwRefData ) ;
508
562
}
0 commit comments