RMB-menu for drives in fade-list by Alt+F1 / Alt+F2

Here you can propose new features, make suggestions etc.

Moderators: Hacker, petermad, Stefan2, white

User avatar
MaxX
Power Member
Power Member
Posts: 1189
Joined: 2012-03-23, 18:15 UTC
Location: UA

RMB-menu for drives in fade-list by Alt+F1 / Alt+F2

Post by *MaxX »

That's what I need -> working context menus for all drives in fade-list (list is shown by Alt+F1 / Alt+F2).
Just, we must get sth like this one:
http://savepic.su/1714899.png
Same menus we can get when click RMB on drive-buttons, but drive-buttons are taking too much workspace (when over 10 drive-letters, as I often have), which I can't give (that's why I have drive-buttons disabled).
And I have to run Explorer each time I need to RMB-ckick drive (unmount virtual, eject CDs, other commands from there). :cry:
Working RMB in fade-list is really needed function! Please, add it as soon as you can!

p.s.
We've still have time while beta-testing. :wink:
User avatar
ghisler(Author)
Site Admin
Site Admin
Posts: 50873
Joined: 2003-02-04, 09:46 UTC
Location: Switzerland
Contact:

Post by *ghisler(Author) »

Unfortunately dropdown comboboxes do not support right clicks, sorry.
Author of Total Commander
https://www.ghisler.com
User avatar
MaxX
Power Member
Power Member
Posts: 1189
Joined: 2012-03-23, 18:15 UTC
Location: UA

Post by *MaxX »

Why not?
Ukrainian Total Commander Translator. Feedback and discuss.
User avatar
MVV
Power Member
Power Member
Posts: 8711
Joined: 2008-08-03, 12:51 UTC
Location: Russian Federation

Post by *MVV »

MaxX, it is a question to M$/Borland, not to Mr. Ghisler. :D
BTW, you can choose which buttons TC won't show on drive bar: use wincmd.ini parameter DriveBarHide.

ghisler, maybe you can subclass combo box to support right-click? I think it would be nice and not so hard. You can use GetComboBoxInfo function to get handle of drop-down part of combo box in order to replace its window procedure.
User avatar
MaxX
Power Member
Power Member
Posts: 1189
Joined: 2012-03-23, 18:15 UTC
Location: UA

Post by *MaxX »

2MVV
So, there's nothing difficult, as I see...
Ukrainian Total Commander Translator. Feedback and discuss.
User avatar
Horst.Epp
Power Member
Power Member
Posts: 7014
Joined: 2003-02-06, 17:36 UTC
Location: Germany

Re: RMB-menu for drives in fade-list by Alt+F1 / Alt+F2

Post by *Horst.Epp »

MaxX wrote:That's what I need -> working context menus for all drives in fade-list (list is shown by Alt+F1 / Alt+F2).
Just, we must get sth like this one:
http://savepic.su/1714899.png
Same menus we can get when click RMB on drive-buttons, but drive-buttons are taking too much workspace (when over 10 drive-letters, as I often have), which I can't give (that's why I have drive-buttons disabled).
And I have to run Explorer each time I need to RMB-ckick drive (unmount virtual, eject CDs, other commands from there). :cry:
Working RMB in fade-list is really needed function! Please, add it as soon as you can!

p.s.
We've still have time while beta-testing. :wink:
You don't need Explorer to reach the RMB context menu of drives !
Mapping a button to TC's My Computer shows you all the drives
and you can reach any context for it.
User avatar
MaxX
Power Member
Power Member
Posts: 1189
Joined: 2012-03-23, 18:15 UTC
Location: UA

Post by *MaxX »

2Horst.Epp
But I do NOT need to open any folders. Only direct click needed.
Even more, sometimes I just can't change dir to another due to selections and other reasons.
Ukrainian Total Commander Translator. Feedback and discuss.
User avatar
MarcinW
Power Member
Power Member
Posts: 852
Joined: 2012-01-23, 15:58 UTC
Location: Poland

Post by *MarcinW »

ghisler(Author) is right - combobox control by default doesn't handle right clicks, when dropped down.

However, here comes a solution:

Code: Select all

unit ExtendedComboBox;

{Tested with Delphi 5 and Delphi 2006}
{Tested with Win95, Win98, WinME, WinNT4, Win2k, WinXP}

interface

uses
  Windows, Messages, Forms, Classes, StdCtrls;

type
  TComboBox = class(StdCtrls.TComboBox)
  private
    FObjectInstance : Pointer;
    FDropDownWnd : HWND;
    FDropDownWndProc : Pointer;
    FHandlingDropDownContextMenu : LongWord;
    procedure SubclassedListBoxWndProc(var Message : TMessage);
  protected
    procedure WndProc(var Message : TMessage); override;
  public
    constructor Create(AOwner : TComponent); override;
    destructor Destroy; override;
  end;

implementation

uses
  Menus;

constructor TComboBox.Create(AOwner : TComponent);
begin
  FObjectInstance:=MakeObjectInstance(SubclassedListBoxWndProc);
  inherited;
end;

{Every combobox window has it's own listbox window, however, listbox window
 is not a child of combobox window. So we can't be sure that listbox window
 will be destroyed before destroying combobox - we must un-subclass listbox
 window before destroying combobox}
destructor TComboBox.Destroy;
begin
  if FDropDownWnd <> 0 then
    SetWindowLong(FDropDownWnd,GWL_WNDPROC,Integer(FDropDownWndProc));
  if FObjectInstance <> nil then
    FreeObjectInstance(FObjectInstance);
  inherited;
end;

{In order to override WM_CTLCOLORLISTBOX handler, we must override WndProc -
 because of special handling of WM_CTLCOLORLISTBOX by Delphi, "message
 WM_CTLCOLORLISTBOX" handler doesn't work}
procedure TComboBox.WndProc(var Message: TMessage);
begin
  inherited;
  with Message do
  begin
    {Grab handle of the listbox}
    if Msg = WM_CTLCOLORLISTBOX then
    if FDropDownWnd = 0 then
    begin
      FDropDownWnd:=LParam;
      FDropDownWndProc:=Pointer(SetWindowLong(FDropDownWnd,GWL_WNDPROC,Integer(FObjectInstance)));
    end;
  end;
end;

{Subclassed window procedure for listbox}
procedure TComboBox.SubclassedListBoxWndProc(var Message : TMessage);
var
  SaveDropDownWnd : HWND;
  Pt : TPoint;
  TempPt : TPoint;
  R : TRect;
  Handled : Boolean;
  PopupMenu : TPopupMenu;
  TempMsg : TMsg;
begin
  with Message do
  begin
    {Don't close listbox when displaying a popup menu}
    if Msg = WM_CAPTURECHANGED then
    if FHandlingDropDownContextMenu > 0 then
    begin
      Result:=0;
      Exit;
    end;

    SaveDropDownWnd:=FDropDownWnd;

    {Un-subclass listbox}
    if Msg = WM_DESTROY then
    begin
      SetWindowLong(FDropDownWnd,GWL_WNDPROC,Integer(FDropDownWndProc));
      FDropDownWnd:=0;
    end;

    Result:=CallWindowProc(FDropDownWndProc,SaveDropDownWnd,Msg,WParam,LParam);

    {Handle right clicks on the listbox (on the basis of TControl.WMContextMenu)}
    if Msg = WM_CONTEXTMENU then
    if Result = 0 then {if not handled yet}
    try
      Inc(FHandlingDropDownContextMenu);

      Pt:=SmallPointToPoint(TWMContextMenu(Message).Pos);
      TempPt:=Pt;
      if Pt.X >= 0 then {if not InvalidPoint(Pt) then}
      begin
        {Listbox has mouse captured, so we get mouse clicks from the whole
         screen - so don't allow to display popup menu outside listbox}
        if Windows.ScreenToClient(FDropDownWnd,TempPt) then
        if Windows.GetClientRect(FDropDownWnd,R) then
        if not PtInRect(R,TempPt) then
          Exit;
        TempPt:=ScreenToClient(Pt);
      end;

      Handled:=False;
      DoContextPopup(TempPt,Handled);
      Result:=Ord(Handled);

      if not Handled then
      begin
        PopupMenu:=GetPopupMenu;
        if PopupMenu <> nil then
        if PopupMenu.AutoPopup then
        begin
          PopupMenu.PopupComponent:=Self;
          if Pt.X < 0 then {if InvalidPoint(Pt) then}
            Pt:=ClientToScreen(Point(0,0));
          PopupMenu.Popup(Pt.X,Pt.Y);
          Result:=1;
        end;
      end;

      if GetCapture <> FDropDownWnd then {if popup menu has been displayed}
      begin
        SetCapture(FDropDownWnd);

        {Remove all waiting WM_LBUTTONDOWN messages - so closing popup menu by
         clicking left mouse button on the listbox will not close the listbox}
        while PeekMessage(TempMsg,FDropDownWnd,WM_LBUTTONDOWN,WM_LBUTTONDOWN,PM_REMOVE) do;
      end;
    finally
      Dec(FHandlingDropDownContextMenu);
    end;
  end;
end;

(******************************************************************************)

type
  TPopupListEx = class(TPopupList)
  protected
    procedure WndProc(var Message : TMessage); override;
  end;

{If the component, which is displaying a popup menu, is a combobox,
 close its listbox when the popup menu item has been executed}
procedure TPopupListEx.WndProc(var Message : TMessage);
var
  I : Integer;
begin
  with Message do
  begin
    if Msg = WM_COMMAND then
      Result:=1; {not handled}

    inherited;

    if Result = 0 then
      Exit;
    Result:=0;

    for I:=0 to Count-1 do
    with TPopupMenu(Items[I]) do
    if FindItem(wParam,fkCommand) <> nil then
    begin
      if PopupComponent is TComboBox then
      with TComboBox(PopupComponent) do
        DroppedDown:=False;
      Break;
    end;
  end;
end;

var
  PopupListEx : TPopupListEx;
initialization
  {Replace PopupList global variable with TPopupListEx object}
  PopupListEx:=TPopupListEx.Create;
  while PopupList.Count > 0 do
  try
    PopupListEx.Add(PopupList[0]);
  finally
    PopupList.Remove(PopupList[0]); {Force DeallocateHWnd call for the last item}
  end;
  PopupList.Free;
  PopupList:=TPopupList(PopupListEx);
end.
Usage example: attach this unit _after_ StdCtrls, create combobox, assign a popup menu to the combobox and try:

Code: Select all

procedure TForm1.ComboBox1ContextPopup(Sender: TObject; MousePos: TPoint; var Handled: Boolean);
begin
  with Sender as TComboBox do
  if PopupMenu <> nil then
  if PopupMenu.Items.Count > 0 then
    PopupMenu.Items[0].Caption:=Text;
end;
or:

Code: Select all

procedure TForm1.ComboBox1ContextPopup(Sender: TObject; MousePos: TPoint; var Handled: Boolean);
begin
  with Sender as TComboBox do
  if PopupMenu <> nil then
  if PopupMenu.Items.Count > 0 then
  begin
    PopupMenu.Items[0].Caption:=Text;
    MousePos:=ClientToScreen(MousePos);
    PopupMenu.PopupComponent:=TComboBox(Sender); {important!}
    PopupMenu.Popup(MousePos.X,MousePos.Y);
    Handled:=True;
  end;
end;
User avatar
Sir_SiLvA
Power Member
Power Member
Posts: 3381
Joined: 2003-05-06, 11:46 UTC

Post by *Sir_SiLvA »

2MarcinW
NICE, now rewrite that for delphi 2 wich is used...
AND for Lazerus for TC64Bit...
Hoecker sie sind raus!
User avatar
MVV
Power Member
Power Member
Posts: 8711
Joined: 2008-08-03, 12:51 UTC
Location: Russian Federation

Post by *MVV »

I think it would be simpler to write just Windows subclass procedure and use SetWindowLongPtr function to override it and send some message to parent window on mouse events... And it will be compatible with both 32/64.
User avatar
MarcinW
Power Member
Power Member
Posts: 852
Joined: 2012-01-23, 15:58 UTC
Location: Poland

Post by *MarcinW »

MVV wrote:I think it would be simpler to write just Windows subclass procedure and use SetWindowLongPtr function to override it
Have you looked at the code above for more than 2 seconds? 2/3 of this code is just subclassed window procedure, and the code installing and uninstalling it. There are many comments there - everything is explained. If you have any working code, that is simpler than the code above - feel free to put it here...
MVV wrote:"send some message to parent window on mouse events"
Sending "some messages" directly to combobox will not work - control will NOT display a context menu, when mouse click has been fired outside the control. But we want to display context menu in the place of drop-down list (which is not a part of combobox window), so we must handle context menu manually - see code marked as "Handle right clicks on the listbox".
User avatar
MarcinW
Power Member
Power Member
Posts: 852
Joined: 2012-01-23, 15:58 UTC
Location: Poland

Post by *MarcinW »

Changes for Delphi 2:

1) change LongWord to Cardinal

2) declare:

Code: Select all

type
  TWMContextMenu = packed record
    Msg: Cardinal;
    hWnd: HWND;
    case Integer of
      0: (
        XPos: Smallint;
        YPos: Smallint);
      1: (
        Pos: TSmallPoint;
        Result: Longint);
  end;
3) comment like this:

Code: Select all

      Handled:=False;
//      DoContextPopup(TempPt,Handled);
//      Result:=Ord(Handled);
4) update Menus.pas: move TPopupList type and PopupList global variable to the interface section. Yes, I know that editing Delphi sources is not a good practice. However it's not a problem in our case:
ghisler(Author) wrote:I made many changes already in the Delphi 2 VCL (e.g. to support Unicode windows)

http://ghisler.ch/board/viewtopic.php?p=221418#221418
Regards!
User avatar
MaxX
Power Member
Power Member
Posts: 1189
Joined: 2012-03-23, 18:15 UTC
Location: UA

Post by *MaxX »

So, solution is found.
How soon we'll see this feature in TC?
Ukrainian Total Commander Translator. Feedback and discuss.
User avatar
Sir_SiLvA
Power Member
Power Member
Posts: 3381
Joined: 2003-05-06, 11:46 UTC

Post by *Sir_SiLvA »

I hope more important things have more priority
(apart from the fact where is the Lazerus Solution wich is needed for TC64 :?:)
Hoecker sie sind raus!
User avatar
MaxX
Power Member
Power Member
Posts: 1189
Joined: 2012-03-23, 18:15 UTC
Location: UA

Post by *MaxX »

2Sir_SiLvA
What can be more imprortant than this daily-needed feature?
Post Reply