pyscripter / MustangpeakEasyListView

Mustangpeak EasyListview is a fast and very customizable TListview replacement. It can also be used with the VirtualShellTools component of the MustangPeak Library.
Other
6 stars 2 forks source link

Access Violation on MENU keypress and nothing selected #18

Open Dzomlija opened 4 years ago

Dzomlija commented 4 years ago

An access violation in "procedure TCustomEasyListview.WMContextMenu(var Msg: TMessage);" when there is nothing selected in a TEasyListView and the MENU key is pressed to open a popup menu: c0000005 ACCESS_VIOLATION (Message) c0000005 ACCESS_VIOLATION (Code)

The access violation occurs at the line "Menu.Popup(Msg.LParamLo, Msg.LParamHi);" near the end of the procedure. (Code line no. 17312 in the version I am using).

procedure TCustomEasyListview.WMContextMenu(var Msg: TMessage);
var
  Item: TEasyItem;
  Group: TEasyGroup;
  Pt: TPoint;
  HitInfoGroup: TEasyHitInfoGroup;
  HitInfoItem: TEasyHitInfoItem;
  Menu: TPopupMenu;
  Handled, SkipHitTest, MenuKey: Boolean;
begin
  if not EditManager.Editing and not (Gesture.Enabled and (Gesture.Path <> '')) then
  begin
    Handled := False;
    MenuKey := False;
    if not (ebcsCancelContextMenu in States) then
    begin
      SkipHitTest := False;
      // Support Dual monitors with SmallPointToPoint
      Pt:= SmallPointToPoint(SmallPoint(Msg.LParamLo, Msg.LParamHi));
      if ((Pt.X = 65535) and (Pt.Y = 65535)) or ((Pt.X = -1) and (Pt.Y = -1)) then
      begin
        MenuKey := True;
        Pt := ScreenToClient(Mouse.CursorPos);
        if not PtInRect(ClientRect, Pt) or (Selection.Count = 0) then
        begin
          Pt.X := 0;
          Pt.Y := 0;
          SkipHitTest := True;
        end;
        Pt := ClientToScreen(Pt);
      end;

      if MenuKey and (Selection.Count > 0) then
      begin
        HitInfoItem.Item := Selection.First;
        Pt := ClientToScreen(HitInfoItem.Item.DisplayRect.TopLeft);
        Pt.Y := Pt.Y + Header.RuntimeHeight;
        HitInfoItem.Column := nil;
        HitInfoItem.Group := HitInfoItem.Item.OwnerGroup;
        HitInfoItem.HitInfo := [ehtOnLabel, ehtOnIcon];
        DoItemContextMenu(HitInfoItem, Pt, Menu, Handled)
      end else
      if not SkipHitTest then
      begin
        if IsHeaderMouseMsg(PointToSmallPoint( ScreenToClient(Pt))) then
        begin
          Pt := ClientToScreen(Pt);
          Header.WMContextMenu(Msg);
          Handled := True;
        end else
        begin
          Menu := nil;
          Exclude(FStates, ebcsDragSelectPending);
          Exclude(FStates, ebcsDragPending);

          Handled := False;
          Group := Groups.GroupByPoint(Scrollbars.MapWindowToView(ScreenToClient(Pt)));
          if Assigned(Group) then
          begin
            // The hit was in a group so now see if it was in an item
            Item := Group.ItembyPoint(Scrollbars.MapWindowToView( ScreenToClient(Pt)));
            if Assigned(Item) then
            begin
              if Item.HitTestAt(Scrollbars.MapWindowToView( ScreenToClient(Pt)), HitInfoItem.HitInfo) then
              begin
                HitInfoItem.Column := nil;
                HitInfoItem.Group := Group;
                HitInfoItem.Item := Item;
                DoItemContextMenu(HitInfoItem, Pt, Menu, Handled)
              end
            end;
            if not Assigned(Menu) and not Handled then
            begin
              HitInfoGroup.Group := Group;
              Group.HitTestAt(Scrollbars.MapWindowToView(ScreenToClient(Pt)), HitInfoGroup.HitInfo);
              DoGroupContextMenu(HitInfoGroup, Pt, Menu, Handled)
            end
          end
        end
      end;
      if not Handled then
        DoContextMenu(Pt, Handled);

      if Assigned(Menu) and not Handled then
      begin
        Menu.Popup(Msg.LParamLo, Msg.LParamHi);
        Msg.Result := 1
      end else
      if not Handled then
        inherited  // Use the PopupMenu property from TControl
    end;
  end else
  begin
    Msg.Result := 1;
    inherited
  end;
  Exclude(FStates, ebcsCancelContextMenu);
end;

I resolved the issue by adding "Menu := nil;" near the top of the code block, just after the "Handled" and "MenuKey" vars are initialized.

    Handled := False;
    MenuKey := False;
    Menu := nil;

However, I noticed that regardless of the change, the popup behaves normally when opened with a mouse right-click, but opens at the top left corner of the listview itself when opened with the MENU key.

Should it not open over the currently selected/focused item? It could in this instance be aligned to the bottom-left corner of the item...

TPopupMenu doesn't open over item on menu keypress