Recent

Author Topic: [Solved]Is it possible to intercept Copy2Clipboard? AND MVP-app  (Read 895 times)

cdbc

  • Hero Member
  • *****
  • Posts: 2466
    • http://www.cdbc.dk
[Solved]Is it possible to intercept Copy2Clipboard? AND MVP-app
« on: October 06, 2025, 04:27:38 pm »
Hi
In an effort to control what gets inserted into the clipboard, when I push Ctrl + C in a TStringGrid, by default it inserts the whole row of cells separated by #9, whereas I want to e.g.: just insert the value of 1 of the cells...
Is it possible to intercept the default behaviour?!?
...and how?  :D
Regards Benny
« Last Edit: October 07, 2025, 03:39:38 pm by cdbc »
If it ain't broke, don't fix it ;)
PCLinuxOS(rolling release) 64bit -> KDE6 -> FPC 3.2.2 -> Lazarus 4.0 up until Jan 2025 from then on it's both above &: KDE6/QT6 -> FPC 3.3.1 -> Lazarus 4.99

Aruna

  • Hero Member
  • *****
  • Posts: 747
Re: Is it possible to intercept Copy2Clipboard?
« Reply #1 on: October 06, 2025, 04:38:45 pm »
Hi
In an effort to control what gets inserted into the clipboard, when I push Ctrl + C in a TStringGrid, by default it inserts the whole row of cells separated by #9, whereas I want to e.g.: just insert the value of 1 of the cells...
Is it possible to intercept the default behaviour?!?
...and how?  :D
Regards Benny
Hi Benny, tested and woiks!
Code: Pascal  [Select][+][-]
  1. procedure TForm1.StringGrid1KeyDown(Sender: TObject; var Key: Word;
  2.   Shift: TShiftState);
  3. begin
  4.   // Detect Ctrl+C
  5.   if (Key = Ord('C')) and (ssCtrl in Shift) then
  6.   begin
  7.     // Prevent default TStringGrid copy behavior
  8.     Key := 0;
  9.  
  10.     // Now put your custom clipboard handling here
  11.     if StringGrid1.Col >= 0 then
  12.     begin
  13.       Clipboard.AsText := StringGrid1.Cells[StringGrid1.Col, StringGrid1.Row];
  14.       ShowMessage('Copied cell value: ' + Clipboard.AsText);
  15.     end;
  16.   end;
  17. end;                      

Hartmut

  • Hero Member
  • *****
  • Posts: 1007
Re: Is it possible to intercept Copy2Clipboard?
« Reply #2 on: October 06, 2025, 04:46:55 pm »
For the purpose to copy selectable parts of a TStringGrid into the clipboard I have created a common PopupMenu which allows to select:
 - copy whole StringGrid
 - copy complete row of the cursor
 - copy complete column of the cursor
 - copy only the selected field
 - copy the selected part of the StringGrid

Maybe this is an alternative for you.

cdbc

  • Hero Member
  • *****
  • Posts: 2466
    • http://www.cdbc.dk
Re: Is it possible to intercept Copy2Clipboard?
« Reply #3 on: October 06, 2025, 05:08:41 pm »
Hi Aruna
Cool mate, thanks.
I'll give it a try and report back  8)
@Hartmut: if that doesn't work I'll try yours...
Regards Benny
If it ain't broke, don't fix it ;)
PCLinuxOS(rolling release) 64bit -> KDE6 -> FPC 3.2.2 -> Lazarus 4.0 up until Jan 2025 from then on it's both above &: KDE6/QT6 -> FPC 3.3.1 -> Lazarus 4.99

Aruna

  • Hero Member
  • *****
  • Posts: 747
Re: Is it possible to intercept Copy2Clipboard?
« Reply #4 on: October 06, 2025, 05:27:57 pm »
For the purpose to copy selectable parts of a TStringGrid into the clipboard I have created a common PopupMenu which allows to select:
 - copy whole StringGrid
 - copy complete row of the cursor
 - copy complete column of the cursor
 - copy only the selected field
 - copy the selected part of the StringGrid

Maybe this is an alternative for you.
Hi Hartmut, where do I find this PopupMenu you have created please?

Aruna

  • Hero Member
  • *****
  • Posts: 747
Re: Is it possible to intercept Copy2Clipboard?
« Reply #5 on: October 06, 2025, 05:29:03 pm »
Hi Aruna
Cool mate, thanks.
I'll give it a try and report back  8)
@Hartmut: if that doesn't work I'll try yours...
Regards Benny
Benny I have attached a zip for you to play with.  Have fun!

cdbc

  • Hero Member
  • *****
  • Posts: 2466
    • http://www.cdbc.dk
Re: Is it possible to intercept Copy2Clipboard?
« Reply #6 on: October 06, 2025, 07:05:34 pm »
Hi Aruna
Thanks mate =^, works like a charm  8-)
Code: Pascal  [Select][+][-]
  1. unit view.fra_showitem;
  2. {$mode ObjFPC}{$H+}
  3. interface
  4. uses
  5.   Classes, SysUtils, Forms, Graphics, Controls, ComCtrls, Grids, ExtCtrls, model.intf;
  6.  
  7. type
  8.   { TfraShowItem }
  9.   TfraShowItem = class(TFrame)
  10.     grdNvn: TStringGrid;
  11.     btnClose: TPanel;
  12.     lblTitleVal: TPanel;
  13.     lblExtraVal: TPanel;
  14.     pnlTiRight: TPanel;
  15.     pnlExtraId: TPanel;
  16.     pnlTitleId: TPanel;
  17.     pnlTiLeft: TPanel;
  18.     pnlTiEx: TPanel;
  19.     pnlBottom: TPanel;
  20.     pnlHead: TPanel;
  21.     procedure btnChooseClick(Sender: TObject);
  22.     procedure btnChooseMouseEnter(Sender: TObject);
  23.     procedure btnChooseMouseLeave(Sender: TObject);
  24.     procedure btnCloseClick(Sender: TObject);
  25.     procedure btnCloseMouseEnter(Sender: TObject);
  26.     procedure btnCloseMouseLeave(Sender: TObject);
  27.     procedure grdNvnDblClick(Sender: TObject);
  28.     procedure grdNvnKeyDown(Sender: TObject; var Key: Word; {%H-}Shift: TShiftState);
  29.     procedure grdNvnResize(Sender: TObject);
  30.     procedure grdNvnSelection(Sender: TObject; {%H-}aCol, {%H-}aRow: Integer);
  31.   private
  32.     fActiveTrx: ImemTransaction; { to keep our current item }
  33.     fOwner: TWinControl;
  34.     fParentForm: TForm;
  35.     fViMo: ImemViewModelMain;
  36.     procedure FrameShow(Sender: TObject);
  37.     procedure SetupFrame;
  38.   protected
  39.     procedure CopyValueToClip(aGridRow: cardinal);
  40.   public
  41.     procedure BeforeDestruction; override; { should get called just before we're out of business }
  42.     procedure Init(aViMo: ImemViewModelMain;anItem: ImemItem); { constructor / oncreate }
  43.   end;
  44.  
  45. { CreateShowFrame instantiates a show-frame in the anOwner(TTabSheet) provided
  46.   and works on the anItem provided, result is the new frame;
  47.   Caution: DO NOT pass nil in any parameter!!! }
  48. function CreateShowFrame(anOwner: TWinControl; aViMo: ImemViewModelMain; anItem: ImemItem): TfraShowItem;
  49.  
  50. implementation { bc_messages provides post/send-message + LM_CLOSETABSHEET etc... }
  51. uses bc.advstring, bc.pcthelp, bc.grdhelp, bc.messages, model.decl, clipbrd;
  52.  
  53. function CreateShowFrame(anOwner: TWinControl; aViMo: ImemViewModelMain; anItem: ImemItem): TfraShowItem;
  54. begin
  55.   Result:= TfraShowItem.Create(anOwner); { anOwner is the tab/page and will free us }
  56.   Result.Parent:= anOwner; { anOwner is also the tab/page that will draw us }
  57.   Result.fOwner:= anOwner;
  58.   TTabSheet(Result.fOwner).OnShow:= @Result.FrameShow;
  59.   Result.fParentForm:= bcGetParentForm(anOwner);       //GetParentForm(Self); from forms.pp
  60.   Result.Name:= 'ShowItem';
  61.   Result.Align:= alClient;
  62.   Result.Init(aViMo,anItem);
  63. end;
  64.  
  65. {$R *.lfm}
  66. { TfraShowItem }
  67. procedure TfraShowItem.btnCloseClick(Sender: TObject);
  68. begin // post msg with pg in wparam & -1 in lparam
  69.   try
  70.     if fOwner is TTabSheet then begin
  71.       if fParentForm <> nil then
  72.         bcPostMessage(fParentForm.Handle,LM_CLOSETABSHEET,ptrint(fOwner),-1); { trickery }
  73.     end;
  74.   except end;
  75. end;
  76.  
  77. procedure TfraShowItem.btnCloseMouseEnter(Sender: TObject);
  78. begin
  79.   with (Sender as TPanel) do begin
  80.     Font.Color:= clYellow;
  81.     Font.Style:= [fsBold];
  82.   end;
  83. end;
  84.  
  85. procedure TfraShowItem.btnCloseMouseLeave(Sender: TObject);
  86. begin
  87.   with (Sender as TPanel) do begin
  88.     Font.Color:= clAqua;
  89.     Font.Style:= [];
  90.   end;
  91. end;
  92.  
  93. procedure TfraShowItem.grdNvnDblClick(Sender: TObject);
  94. begin
  95.   btnCloseClick(Sender);
  96. end;
  97.  
  98. procedure TfraShowItem.grdNvnKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
  99. var ct: boolean;
  100. begin
  101.   ct:= (ssCtrl in Shift);
  102.   case Key of                    
  103.     $08: btnCloseClick(Sender); // backspace
  104.     $0D: btnCloseClick(Sender); // enter
  105.     $20: btnCloseClick(Sender); // space
  106.     $1B: btnCloseClick(Sender); // escape
  107.     $43: if ct then begin
  108.            CopyValueToClip(grdNvn.Row);// ctrl + C
  109.            Key:= 0;
  110.          end;
  111.   end;
  112.   if grdNvn.CanSetFocus then grdNvn.SetFocus; {=^}
  113. end;
  114.  
  115. procedure TfraShowItem.grdNvnResize(Sender: TObject);
  116. var sixth: word = 0;
  117. begin
  118.   sixth:= ((grdNvn.Width-25) div 6); // 25 for scrollbar, 1part, 2part & 3part
  119.   if grdNvn.ColCount >= 3 then begin
  120.     grdNvn.Columns[0].Width:= sixth+50;
  121.     grdNvn.Columns[1].Width:= sixth * 2;
  122.     grdNvn.Columns[2].Width:= (sixth * 3)-50;
  123.   end;      
  124.   if grdNvn.CanSetFocus then grdNvn.SetFocus; {=^}
  125. end;
  126.  
  127. procedure TfraShowItem.grdNvnSelection(Sender: TObject; aCol, aRow: Integer);
  128. begin
  129.   if grdNvn.CanSetFocus then grdNvn.SetFocus; {=^}
  130. end;
  131.  
  132. procedure TfraShowItem.btnChooseClick(Sender: TObject); /// not in use, but kept for reference
  133. begin // post msg with pg in wparam & id i lparam
  134.   try
  135.     if fOwner is TTabSheet then begin
  136.       if fParentForm <> nil then
  137.         bcPostMessage(fParentForm.Handle,LM_CLOSETABSHEET,ptrint(fOwner),fActiveTrx.ID); { trickery }
  138.     end;
  139.   except end;
  140. end;
  141.  
  142. procedure TfraShowItem.btnChooseMouseEnter(Sender: TObject); /// not in use, but kept for reference
  143. begin
  144.   with (Sender as TPanel) do begin
  145.     Font.Color:= clYellow;
  146.     Font.Style:= [fsBold];
  147.   end;
  148. end;
  149.  
  150. procedure TfraShowItem.btnChooseMouseLeave(Sender: TObject); /// not in use, but kept for reference
  151. begin
  152.   with (Sender as TPanel) do begin
  153.     Font.Color:= clAqua;
  154.     Font.Style:= [];
  155.   end;
  156. end;
  157.  
  158. procedure TfraShowItem.FrameShow(Sender: TObject);
  159. begin
  160.   if grdNvn.CanSetFocus then grdNvn.SetFocus; {=^}
  161. end;
  162.  
  163. procedure TfraShowItem.SetupFrame;
  164. var sixth: word = 0; i: integer; s: string;
  165. begin
  166.   lblTitleVal.Caption:= fActiveTrx.Title;
  167.   lblExtraVal.Caption:= fActiveTrx.Extra;
  168.   bcGridClear(grdNvn,false,false);
  169.   sixth:= ((grdNvn.Width-25) div 6); // 25 for scrollbar
  170.   bcGridCreateHeaders(grdNvn,['Name','Value','Note'],[sixth+50,sixth*2,(sixth*3)-50]);
  171.   grdNvn.TitleFont.Color:= clAqua;
  172.   grdNvn.TitleFont.Style:= [fsBold]; //???
  173.   for i:= 0 to fActiveTrx.Items.Count-1 do begin
  174.     s:= fActiveTrx.Items[i];
  175.     bcGridSetRowText(grdNvn,-1,[bcGetStrField(0,s,'|'),
  176.                                 bcGetStrField(1,s,'|'),
  177.                                 bcGetStrField(2,s,'|')]);
  178.   end;
  179.   if grdNvn.CanSetFocus then grdNvn.SetFocus; {=^}
  180. end;
  181.  
  182. procedure TfraShowItem.CopyValueToClip(aGridRow: cardinal);
  183. var ls: string;
  184. begin
  185.   ls:= fActiveTrx.Items[aGridRow-1]; { grids count /real/ values from 1 }
  186.   ls:= bcGetStrField(1,ls,'|'); { pick 'Value' from the entire row's text }
  187.   Clipboard.AsText:= ls;        { now insert the text into global clipboard }
  188.   fViMo.Provider.NotifyConsumers(13,nil,bcStrNew('(i) "'+ls+'" copied to clipboard')); // 13 = prStatus
  189. end;
  190.  
  191. procedure TfraShowItem.BeforeDestruction;
  192. begin
  193.   if Assigned(fActiveTrx) then fActiveTrx.Obj.Free;
  194.   inherited BeforeDestruction;
  195. end;
  196.  
  197. procedure TfraShowItem.Init(aViMo: ImemViewModelMain;anItem: ImemItem);
  198. begin
  199.   if anItem <> nil then begin
  200.     fViMo:= aViMo;
  201.     fActiveTrx:= TmemTransaction.Create;
  202.     fActiveTrx.AssignFrom(anItem);
  203.     pnlHead.Caption:= 'Memento #'+fActiveTrx.ID.ToString+', last edited: '+fActiveTrx.DateTime;
  204.     pnlBottom.Caption:= memShowItemBottomCapt; { was:  Press [ Space,  Escape,  Enter,  BackSpace ] to return }
  205.     SetupFrame;
  206.   end else btnCloseClick(btnClose);
  207. end;
  208.  
  209. end.
  210.  
Regards Benny
If it ain't broke, don't fix it ;)
PCLinuxOS(rolling release) 64bit -> KDE6 -> FPC 3.2.2 -> Lazarus 4.0 up until Jan 2025 from then on it's both above &: KDE6/QT6 -> FPC 3.3.1 -> Lazarus 4.99

Hartmut

  • Hero Member
  • *****
  • Posts: 1007
Re: Is it possible to intercept Copy2Clipboard?
« Reply #7 on: October 06, 2025, 07:25:03 pm »
Hi Hartmut, where do I find this PopupMenu you have created please?

Hello Aruna, I did not publish this PopupMenu. It consists only of
 - 1 procedure to populate a TPopupMenu with 5 TMenuItem's, which all are assigned the same given OnClick-Event (but with different 'Tag'-values)
 - 1 procedure, which is called by obove OnClick-Event and copies (depending of the 'Tag'-value) the wanted part of the StringGrid into the clipboard.

I hesitate to publish the code, because it has German comments and uses a couple of personal functions and types, which I all had to declare or replace. I think it should be not to difficult, to create those 2 procedures.

jamie

  • Hero Member
  • *****
  • Posts: 7308
Re: Is it possible to intercept Copy2Clipboard?
« Reply #8 on: October 07, 2025, 12:04:18 am »
Did I miss something?

Code: Pascal  [Select][+][-]
  1. procedure TForm1.StringGrid1CellProcess(Sender: TObject; aCol, aRow: Integer;
  2.   processType: TCellProcessType; var aValue: string);
  3. begin
  4.   If ProcessType = cpCopy Then
  5.     Begin
  6.       Caption := AValue;
  7.     end;
  8. end;                                                    
  9.  
  10.  
That only gets the current cell, not the whole row.

Of course, you can modify the string while you are there, too!

Jamie

The only true wisdom is knowing you know nothing

cdbc

  • Hero Member
  • *****
  • Posts: 2466
    • http://www.cdbc.dk
Re: Is it possible to intercept Copy2Clipboard?
« Reply #9 on: October 07, 2025, 09:15:44 am »
Hi
Thanks jamie, I learnt something new there...
Regards Benny
If it ain't broke, don't fix it ;)
PCLinuxOS(rolling release) 64bit -> KDE6 -> FPC 3.2.2 -> Lazarus 4.0 up until Jan 2025 from then on it's both above &: KDE6/QT6 -> FPC 3.3.1 -> Lazarus 4.99

Aruna

  • Hero Member
  • *****
  • Posts: 747
Re: Is it possible to intercept Copy2Clipboard?
« Reply #10 on: October 07, 2025, 01:25:27 pm »
Hi Aruna
Thanks mate =^, works like a charm  8-)
Hi Benny, I am happy you got things working but... I’m yet to start using TFrames, so this code is way above my current level of familiarity and understanding with Lazarus and Free Pascal. And in uses you have a 'model.int' ftw is that? If this isn’t a closed-source project, I’d love to see the full application, or even just a couple of screenshots if that’s possible. Or put it up on GitHub if that is allowed and ok? Or tell me how do I test your code? Because right now I have no clue where to start doing so..  :)

Aruna

  • Hero Member
  • *****
  • Posts: 747
Re: Is it possible to intercept Copy2Clipboard?
« Reply #11 on: October 07, 2025, 01:27:44 pm »
Hi Hartmut, where do I find this PopupMenu you have created please?

Hello Aruna, I did not publish this PopupMenu. It consists only of
 - 1 procedure to populate a TPopupMenu with 5 TMenuItem's, which all are assigned the same given OnClick-Event (but with different 'Tag'-values)
 - 1 procedure, which is called by obove OnClick-Event and copies (depending of the 'Tag'-value) the wanted part of the StringGrid into the clipboard.

I hesitate to publish the code, because it has German comments and uses a couple of personal functions and types, which I all had to declare or replace. I think it should be not to difficult, to create those 2 procedures.
Hi Hartmut, Hello, and thank you for the explanation! That makes perfect sense. No worries at all about not publishing it. I completely understand if it’s using personal functions and German comments. Your description is already very helpful, and I think I can put something similar together based on that. I really appreciate you taking the time to outline how it works!

Aruna

  • Hero Member
  • *****
  • Posts: 747
Re: Is it possible to intercept Copy2Clipboard?
« Reply #12 on: October 07, 2025, 01:29:31 pm »
Hi
Thanks jamie, I learnt something new there...
Regards Benny
Yes me too I never knew we had : StringGrid1CellProcess  :o

Aruna

  • Hero Member
  • *****
  • Posts: 747
Re: [Solved]Is it possible to intercept Copy2Clipboard?
« Reply #13 on: October 07, 2025, 01:43:11 pm »
Benny, I ran your unit through chatGPT and it generated this: TFrame Breakdown so now I have a fair understandiing of what your doing but things are still very foggy to me :)

cdbc

  • Hero Member
  • *****
  • Posts: 2466
    • http://www.cdbc.dk
Re: [Solved]Is it possible to intercept Copy2Clipboard?
« Reply #14 on: October 07, 2025, 03:34:03 pm »
Hi Aruna
Hehehe, how COOL is that then... Please send my regards to ChatGPT, for a nice write-up  :D
Right, So this little project is/was actually my very first _real_ MVVM project after I read a book about these patterns, it's the preamble to my research into (M)odel (V)iew (P)resenter frameworks, for which I've later implemented a setup-utility 'MVP-Setup' that delivers you a ready-made connected and compilable (&runable) skeleton application bundle...
It has a sibling, coded for the command-line with a TUI user interface that employs the 'Viewmodel' & 'Models' etc. i.e.: the entire backend of 'Memento'
, with only the UI as difference  ;D
Both projects employs my 'Plugin' framework ((P)lug(I)n (S)ervices (S)olution) to further separate concerns into  j.i.t. loadable modules...
Furthermore, they boast 256 bit AES encryption of data and detailed logging...
I'll see if I can make it available to you, if you want that ofc?
Regards Benny
« Last Edit: October 07, 2025, 03:36:14 pm by cdbc »
If it ain't broke, don't fix it ;)
PCLinuxOS(rolling release) 64bit -> KDE6 -> FPC 3.2.2 -> Lazarus 4.0 up until Jan 2025 from then on it's both above &: KDE6/QT6 -> FPC 3.3.1 -> Lazarus 4.99

 

TinyPortal © 2005-2018