Recent

Author Topic: StringGrid: which is "current" row/col when PopupMenu pops up?  (Read 542 times)

Bart

  • Hero Member
  • *****
  • Posts: 5727
    • Bart en Mariska's Webstek
Hi,

I have a TStringGrid (Name: SGrid).
It has a PopUp menu.
When it pops up I need to know the "current" row and column.
As long as the grid has an editor showing, this equals to SGrid.Row and SGrid.Col.

When no editor is showing, it can be determined using MouseToCell.

There's a caveat though.
See attached picture.

No editor is showing (SGrid.Editor.Visible is False), but you can see a focusrect drawn around the cell with "4" in it.
SGrid.Row and SGrid.Col in this exampe, are [0,0], which obviously is not where the focusrect is.
Also, in this case the Popup can be invoked from any point (right mouse click) in the grid, so MouseToCell doesn't give the correct Row/Col either.
How can I determine wether or not a focusrect is drawn when Editor.Visible = False?
And in which cell this is drawn.

I intend to use the popup for (a.o.) "Delete current Row/Col", so in this example (screenshot) the user won't be happy if e.g. the first row is deleted, because, the user will perceive the cell with the focusrect as the current active row/column.

Bart

zeljko

  • Hero Member
  • *****
  • Posts: 1951
    • http://wiki.lazarus.freepascal.org/User:Zeljan
Re: StringGrid: which is "current" row/col when PopupMenu pops up?
« Reply #1 on: May 14, 2026, 07:48:02 pm »
Maybe grid have an option to select cell with right mouse click ?

valdir.marcos

  • Hero Member
  • *****
  • Posts: 1285
Re: StringGrid: which is "current" row/col when PopupMenu pops up?
« Reply #2 on: May 14, 2026, 09:27:05 pm »
Hi,

I have a TStringGrid (Name: SGrid).
It has a PopUp menu.
When it pops up I need to know the "current" row and column.
As long as the grid has an editor showing, this equals to SGrid.Row and SGrid.Col.

When no editor is showing, it can be determined using MouseToCell.

There's a caveat though.
See attached picture.

No editor is showing (SGrid.Editor.Visible is False), but you can see a focusrect drawn around the cell with "4" in it.
SGrid.Row and SGrid.Col in this exampe, are [0,0], which obviously is not where the focusrect is.
Also, in this case the Popup can be invoked from any point (right mouse click) in the grid, so MouseToCell doesn't give the correct Row/Col either.
How can I determine wether or not a focusrect is drawn when Editor.Visible = False?
And in which cell this is drawn.

I intend to use the popup for (a.o.) "Delete current Row/Col", so in this example (screenshot) the user won't be happy if e.g. the first row is deleted, because, the user will perceive the cell with the focusrect as the current active row/column.

Bart

When a TStringGrid's PopupMenu pops up (typically via right-click), the Row and Col properties often return -1 or stale values because the right-click does not automatically select the cell as the left-click does.

To reliably get the current row and column when the context menu appears, you must capture the cell coordinates before the menu opens.  The most effective solution is to handle the OnMouseDown event.


Recommended Solution:

Use the OnMouseDown event to simulate a left-click when the right mouse button is pressed. This forces the grid to update its Row and Col properties to the cell under the cursor.

Code: Pascal  [Select][+][-]
  1. procedure TForm1.StringGrid1MouseDown(Sender: TObject; Button: TMouseButton;
  2.   Shift: TShiftState; X, Y: Integer);
  3. begin
  4.   if Button = mbRight then
  5.   begin
  6.     // Simulate a left-click to update Row/Col to the cell under the mouse
  7.     StringGrid1.Perform(WM_LBUTTONDOWN, 0, MakeLParam(Word(X), Word(Y)));
  8.   end;
  9. end;

After this code executes, StringGrid1.Row and StringGrid1.Col will contain the correct coordinates. You can then access these values in your PopupMenu.OnClick event handlers.



Alternative: Using Selection Properties:

If goRowSelect is enabled or you need to handle complex selections, relying on Row and Col can be buggy. Instead, use the Selection property:

Single Cell/Row: StringGrid1.Selection.Top and StringGrid1.Selection.Left give the top-left of the selected area.
Note: Selection represents the highlighted area, whereas Row/Col represent the focused cell. If goRowSelect=True, Row may not accurately reflect the selected row's data.



Key Distinction:

Left-Click: Automatically updates Row and Col.

Right-Click: Does not update Row and Col by default; it only opens the menu.  The OnMouseDown trick is required to synchronize the grid's state with the mouse position.

jcmontherock

  • Sr. Member
  • ****
  • Posts: 356
Re: StringGrid: which is "current" row/col when PopupMenu pops up?
« Reply #3 on: May 15, 2026, 11:21:00 am »
What I am using with a right click in StringGrid1MouseDown:

Code: Pascal  [Select][+][-]
  1.       pTopLeft := Mouse.CursorPos;
  2.       PopupMenu2.Popup(pTopLeft.X, pTopLeft.Y - StringGrid1.RowHeights[aRow]);
Windows 11 UTF8-64 - Lazarus 4.6-64 - FPC 3.2.2

Bart

  • Hero Member
  • *****
  • Posts: 5727
    • Bart en Mariska's Webstek
Re: StringGrid: which is "current" row/col when PopupMenu pops up?
« Reply #4 on: May 15, 2026, 11:17:59 pm »
I use OnMouseDown to cache the mouse coördinates where the mouse went down.
From that I can calculate Row/Col.
Notice that OnMouseDown isn't fired when you click on an active (visible) editor.

As I said: there are situations where what the user would see as active row/col (cell) I cannot distinguish that.

Currently I use the following logic:
If an editor is visible, then just query SGrid.Col/Row. It''l match what the user sees to be the active cell.
If no editor is visible, use cached mousedown values and determine which cell was clicked.
In the attached image (first post) there's a mismatch between what the user sees (interprets) and what the logic then spits out.

I've tried with goAlwaysShowEditro, but then it becomes even more difficult.
There will be situations where you cannot see that there is an editor visible, but the grid says there is one.

Bart

valdir.marcos

  • Hero Member
  • *****
  • Posts: 1285
Re: StringGrid: which is "current" row/col when PopupMenu pops up?
« Reply #5 on: May 16, 2026, 07:52:01 am »
I use OnMouseDown to cache the mouse coördinates where the mouse went down.
From that I can calculate Row/Col.
Notice that OnMouseDown isn't fired when you click on an active (visible) editor.

As I said: there are situations where what the user would see as active row/col (cell) I cannot distinguish that.

Currently I use the following logic:
If an editor is visible, then just query SGrid.Col/Row. It''l match what the user sees to be the active cell.
If no editor is visible, use cached mousedown values and determine which cell was clicked.
In the attached image (first post) there's a mismatch between what the user sees (interprets) and what the logic then spits out.

I've tried with goAlwaysShowEditro, but then it becomes even more difficult.
There will be situations where you cannot see that there is an editor visible, but the grid says there is one.

Bart

Right-click anywhere on the stringgrid to test. Then, click on the button to check the last cell clicked information.

I have tried to simulate your problem to provide a viable solution.

Have I succeeded?

Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs, Grids, LMessages,
  9.   StdCtrls, ExtCtrls;
  10.  
  11. type
  12.  
  13.   { TForm1 }
  14.  
  15.   TForm1 = class(TForm)
  16.     Panel1: TPanel;
  17.     Button1: TButton;
  18.     StringGrid1: TStringGrid;
  19.     procedure Button1Click(Sender: TObject);
  20.     procedure FormShow(Sender: TObject);
  21.     procedure StringGrid1MouseDown(Sender: TObject; Button: TMouseButton;
  22.       Shift: TShiftState; X, Y: Integer);
  23.   private
  24.     GridLastCol, GridLastRow: Integer;
  25.  
  26.   public
  27.  
  28.   end;
  29.  
  30. var
  31.   Form1: TForm1;
  32.  
  33. implementation
  34.  
  35. {$R *.lfm}
  36.  
  37. { TForm1 }
  38.  
  39. procedure TForm1.FormShow(Sender: TObject);
  40. var
  41.   i, j: Integer;
  42. begin
  43.   Caption := 'TStringGrid - OnMouseDown Test';
  44.   Height  := 500;
  45.   Width   := 1000;
  46.  
  47.   Panel1.Caption := '';
  48.   Panel1.Align   := alTop;
  49.   Panel1.Height  := Button1.Height * 2;
  50.   Panel1.Color   := clYellow;
  51.  
  52.   Button1.Caption  := 'Last clicked cell';
  53.   Button1.Top      := Panel1.Height div 4;
  54.   Button1.Left     := 10;
  55.   Button1.AutoSize := True;
  56.  
  57.   StringGrid1.Align := alClient;
  58.   StringGrid1.ColCount := 12;
  59.   StringGrid1.RowCount := 19;
  60.   StringGrid1.FixedCols := 0;
  61.   StringGrid1.FixedRows := 0;
  62.  
  63.  
  64.   // (Optional) Adjust automatic or fixed widths
  65.   StringGrid1.ColWidths[0] := 80;
  66.  
  67.   // Optional: Set headers in the first row (fixed)
  68.   for i := 0 to StringGrid1.ColCount - 1 do
  69.   begin
  70.     StringGrid1.Cells[i, 0] := 'Row ' + IntToStr(i + 1);
  71.  
  72.     // (Optional) Adjust automatic or fixed widths
  73.     if (i = 0) then
  74.       StringGrid1.ColWidths[0] := 75
  75.     else
  76.       StringGrid1.ColWidths[i] := 80;
  77.   end;
  78.  
  79.  
  80.   // Optional: Set headers in the first column (fixed)
  81.   for i := 0 to StringGrid1.RowCount - 1 do
  82.   begin
  83.     StringGrid1.Cells[0, i] := 'Column ' + IntToStr(i + 1);
  84.   end;
  85.  
  86.  
  87.   // Optional: Fill data cells
  88.   for i := 01 to StringGrid1.ColCount - 1 do
  89.   begin
  90.     for j := 1 to StringGrid1.RowCount - 1 do
  91.     begin
  92.       StringGrid1.Cells[i, j] := 'Cell[' + IntToStr(i + 1) + ', ' + IntToStr(j + 1) + ']';
  93.     end;
  94.   end;
  95. end;
  96.  
  97. procedure TForm1.Button1Click(Sender: TObject);
  98. begin
  99.   ShowMessage('Last clicked cell: Column ' + IntToStr(GridLastCol) +
  100.               ', Row ' + IntToStr(GridLastRow) + LineEnding +
  101.               StringGrid1.Cells[GridLastCol, GridLastRow]);
  102. end;
  103.  
  104. procedure TForm1.StringGrid1MouseDown(Sender: TObject; Button: TMouseButton;
  105.   Shift: TShiftState; X, Y: Integer);
  106. var
  107.   ACol, ARow: Integer;
  108. begin
  109.   if Button = mbRight then
  110.   begin
  111.     // Simulate a left-click to update Row/Col to the cell under the mouse
  112.     // Delphi StringGrid1.Perform(WM_LBUTTONDOWN, 0, MakeLParam(Word(X), Word(Y)));
  113.     StringGrid1.Perform(LM_LBUTTONDOWN, 0, LongInt(Word(X) or (Word(Y) shl 16)));
  114.   end;
  115.  
  116.   // Convert mouse coordinates to cell indices
  117.   StringGrid1.MouseToCell(X, Y, ACol, ARow);
  118.  
  119.   // Display the result
  120.   ShowMessage('Clicked Cell: Column ' + IntToStr(ACol) +
  121.                 ', Row ' + IntToStr(ARow) + LineEnding +
  122.                 StringGrid1.Cells[ACol, ARow]);
  123.  
  124.   // Store information about last clicked cell
  125.   GridLastCol := ACol;
  126.   GridLastRow := ARow;
  127.  
  128.   if Button = mbRight then
  129.   begin
  130.     // Cancel last simulated left-click to update Row/Col to the cell under the mouse
  131.     StringGrid1.Perform(LM_LBUTTONUP, 0, LongInt(Word(X) or (Word(Y) shl 16)));
  132.   end;
  133. end;
  134.  
  135. end.

jamie

  • Hero Member
  • *****
  • Posts: 7765
Re: StringGrid: which is "current" row/col when PopupMenu pops up?
« Reply #6 on: May 16, 2026, 04:10:34 pm »
here's my version, which I use something like this in code land..

For you this example seems to work like a charm!

Code: Pascal  [Select][+][-]
  1. procedure TForm1.PopupMenu1Popup(Sender: TObject);
  2. Var
  3.   P:TPoint;
  4. begin
  5.   If Assigned(StringGrid1.Editor)and(StringGrid1.Editor.Visible) Then exit;
  6.   P:= Mouse.CursorPos;
  7.   P := StringGrid1.ScreenToClient(P);
  8.   P:= StringGrid1.MouseToCell(P);
  9.   StringGrid1.ColRow := P;
  10.   StringGrid1.Repaint;
  11. end;                                  
  12.  

 I am sure the common (no names stated here) aristocrats will have their opinion like usual. :D

Jamie
The only true wisdom is knowing you know nothing

Bart

  • Hero Member
  • *****
  • Posts: 5727
    • Bart en Mariska's Webstek
Re: StringGrid: which is "current" row/col when PopupMenu pops up?
« Reply #7 on: May 17, 2026, 08:21:06 pm »
I have tried to simulate your problem to provide a viable solution.

Have I succeeded?

OK, nice solution.
Now right-clicking sets a focusrect on that cell (it does not show the editor if goAlwaysSHowEditor is not set).

One problem though: click on Cell[0,0] (assuming FixedRows and FixedCols = 1).
If no cell has a focusrect, then Cell[1,1] will be get a focusrect, but MouseToGrid gives Cell[0,0].

With goAlwaysShowEditor set (which I prefer anyway), it will select the cell and open the editor.
When clicking on a fixedcol or fixedrow it will not alter the selected cell (and if no cell is selected, it will select the topmost first editable cell and open the editor.

If the editor is open, I can simply rely on  SGrid.Row/SGrid.Col to tell me where I am.

Still need to test if this works on Linux as well.

Bart

Bart

  • Hero Member
  • *****
  • Posts: 5727
    • Bart en Mariska's Webstek
Re: StringGrid: which is "current" row/col when PopupMenu pops up?
« Reply #8 on: May 17, 2026, 10:41:30 pm »
B.t.w. We do have MakeLParam in LCLIntf unit.

Bart

Bart

  • Hero Member
  • *****
  • Posts: 5727
    • Bart en Mariska's Webstek
Re: StringGrid: which is "current" row/col when PopupMenu pops up?
« Reply #9 on: May 18, 2026, 08:18:51 pm »
Since my gridhelper has access to MouseDown() I ended up using that to simulate Left-Mouse click.
Principle is the same though, so still thanks.

Bart

 

TinyPortal © 2005-2018