[Okta Webinar] Learn how to a build a cloud-first strategyRegister Now

x
?
Solved

Memo1, Memo2 & Edit1 Add Additional Lines

Posted on 2006-06-06
37
Medium Priority
?
568 Views
Last Modified: 2010-04-04
Hello All;

Lets see if I can explain this so that everyone can understand it.

Edit1.Text := 'New';
Memo1.Lines -----
Memo2.Lines -----

OK.
  I need to have it to where when you type in a "Line" into [Memo1]
It will Trigger [Edit1] to insert a new line into [Memo2]

So it will do something like this:
Edit1.Text := 'New',

Memo1.Text                    Memo2
Something                       New
Everything                        New
Possibly                           New

===============
So, as you can see.
The information from the TEdit is being Repeatedly inserted into the Memo2.
Every time that Memo1 creates a [Break] (Press Enter Key to Tab Down)

Any idea's on this one?

Thanks All;
Carrzkiss
0
Comment
Question by:Wayne Barron
  • 15
  • 12
  • 10
37 Comments
 
LVL 28

Expert Comment

by:2266180
ID: 16849593
pretty simple actually :)

put 2 memos, 1 editbox on the form of a new application. assign an event to the onkeypress of memo1 and then replace all code from unti 1 with:
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    Memo1: TMemo;
    Memo2: TMemo;
    Edit1: TEdit;
    procedure Memo1KeyPress(Sender: TObject; var Key: Char);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Memo1KeyPress(Sender: TObject; var Key: Char);
begin
  if key=#13 then
    memo2.lines.add(edit1.text);
end;

end.

and you're done. you can of course customize the
    memo2.lines.add(edit1.text);
with something like:
    memo2.text:=memo2.text+' '+edit1.text+#13#10;

0
 
LVL 31

Author Comment

by:Wayne Barron
ID: 16849647
It works good except but when you "Paste" something into the Memo1.
Once you paste into Memo1, the Event is only fired "1-Time" Thus only putting
The Text for the Edit1, in at the very first line.
So if you [Paste] 50-Lines. Only the 1st Line gets information from Edit1.Text

Any idea's on how to make it work on [Memo1.PasteFromClipboard] ?

Thanks a bunch
0
 
LVL 31

Author Comment

by:Wayne Barron
ID: 16849739
Ciuly

If this is going to be a Difficult one, then once you have a working code.
Or if anyone has a working code that will work with the [Memo1.PasteFromClipboard]
I will gladly pop the Points up to: 500

Thanks All;
0
Independent Software Vendors: We Want Your Opinion

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
LVL 10

Expert Comment

by:atul_parmar
ID: 16849861
The good solution is to manage the paste yourself. Here is how you can do it.
derive a class from TMemo and add a message handler for WM_PASTE

  TMyMemo = class(TMemo)
    procedure OnPaste(var Msg : TMessage); message WM_PASTE;
  end;

implement it as

procedure TMyMemo.OnPaste(var Msg: TMessage);
var
  s : TStringList;
  i : integer;
begin
  if Clipboard.HasFormat(CF_TEXT) then
  begin
    s := TStringList.Create;
    s.Text := Clipboard.AsText;
    for i:= 0 to s.Count - 1 do
      Form1.Memo2.Lines.Add(Form1.Edit1.Text);
    s.Free;
  end;
  inherited;
end;

I hope this will help you to overcome the paste problem. :)

Atul
0
 
LVL 28

Expert Comment

by:2266180
ID: 16849896
here is the uupdated code:

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TMemo = class(StdCtrls.TMemo)
  private
    FOnNewLine:TNotifyEvent;
    procedure mypaste(var msg:TMessage); message WM_Paste;
  public
    property OnNewLine:TNotifyEvent read FOnNewLine write FOnNewLine;
  end;

  TForm1 = class(TForm)
    Memo1: TMemo;
    Memo2: TMemo;
    Edit1: TEdit;
    procedure Memo1KeyPress(Sender: TObject; var Key: Char);
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
    procedure FormOnNewLine(Sender: TObject);
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Memo1KeyPress(Sender: TObject; var Key: Char);
begin
  if key=#13 then
    memo2.lines.add(edit1.text);
end;

procedure TMemo.mypaste(var msg: TMessage);
var
  Handle: THandle;
  CText: string;
  LText: string;
  AText: string;
  i:integer;
begin
  if IsClipboardFormatAvailable(CF_TEXT) then
  begin
    try
      OpenClipBoard(Self.Handle);
      Handle := GetClipboardData(CF_TEXT);
      if Handle = 0 then
        Exit;
      CText := StrPas(GlobalLock(Handle));
      if assigned(FOnNewLine) then
      begin
        i:=1;
        while i<=length(ctext) do
        begin
          if (ctext[i]=#10) and (ctext[i-1]=#13) then// crlf
            FOnNewLine(self);
          inc(i);
        end;
      end;
      GlobalUnlock(Handle);
      LText := '';
      if SelStart > 0 then
        LText := Copy(Text, 1, SelStart);
      LText := LText + CText;
      AText := '';
      if (SelStart + 1) < Length(Text) then
        AText := Copy(Text, SelStart + SelLength + 1, Length(Text) - SelStart + SelLength + 1);
      Text := LText + AText;
    finally
      CloseClipBoard;
    end;
  end;{}
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  memo1.OnNewLine:=FormOnNewLine;
end;

procedure TForm1.FormOnNewLine(Sender: TObject);
begin
  memo2.lines.add(edit1.text);
end;

end.

the reason for defining the TMemo like that is that it will be used instead of the one from stdctrl (you are adding some stuff to it/extending it). this has it's advantages: you don't need to modify any code. just put that TMemo I defined in a unit if you want it to be used in more projects or more units and MAKE SURE that that unit with my TMemo is AFTER the StdCtrls in the uses clause.
thats it :)
0
 
LVL 10

Expert Comment

by:atul_parmar
ID: 16850028
ciuly, I think if you want it to work without modifying any line of code then just changing the TMyMemo to TMemo is enough. As it is short and simple. :)

However, using the same class name is BAD practice. Instead creating a custom memo component (with different name) is ideal solution.
0
 
LVL 28

Expert Comment

by:2266180
ID: 16850163
I will have to disagree with you atul:
1) if you want to access a protected property/function, you will need my method (there ere a few cases here on EE and in my own experience) It's not the same name, it's overwriting the old one. a sensitive difference in terms  :) Plus that you will have to modify the code to use tmymemo instead of tmemo: the designer might not cope with that (see dfm). too much manual stuff to do ;)
2) your code hardcoded the memo2 into it. what if I want to have 3 or 4 memo's getting the same or different result? I gave a general solution and it's up to the programmer on how to use it.
what if you want that one memo receives an add edit1.text on all new lines, and another receives them only on odd ones? the list can continue ;)
0
 
LVL 10

Assisted Solution

by:atul_parmar
atul_parmar earned 800 total points
ID: 16850187
Perhaps you didn't get me. Here is what I mean. As continuing with my previous code comment.

TMemo = class(StdCtrls.TMemo)
    procedure OnPaste(var Msg : TMessage); message WM_PASTE;
  end;

implement it as

procedure TMemo.OnPaste(var Msg: TMessage);
var
  s : TStringList;
  i : integer;
begin
  if Clipboard.HasFormat(CF_TEXT) then
  begin
    s := TStringList.Create;
    s.Text := Clipboard.AsText;
    for i:= 0 to s.Count - 1 do
      Form1.Memo2.Lines.Add(Form1.Edit1.Text);
    s.Free;
  end;
  inherited;
end;

1. As long as the access protected property/function is concerned, in Delphi scope of protected member is upto unit level NOT class level.

2. I tested it and it works fine on D7. and it does not require to modify any existing line of code :)

Atul.
0
 
LVL 28

Expert Comment

by:2266180
ID: 16850212
2) true. that indeed I didn not understand correrctly. but you still have the hardcoded stuff in it :)
1) true, but in case of TMemo and otehr components that come with delphi, you don't write your code in those units and recompile the bpl's and everything, do you? the method is to be used in cases where extra functionality needs to be added to existing components to which you cannot modify the unit files or recompile the component in an easy manner (I'm not saying it's impossible to recompile delphi's "own" compnents, it's just not trivial)
0
 
LVL 10

Expert Comment

by:atul_parmar
ID: 16850340
Anyway, this is just for knowledge sharing. :)
0
 
LVL 28

Expert Comment

by:2266180
ID: 16850369
true. I myself learned a lot here, even though my main "activity" is helping others :D
and my job is not even delphi related.
0
 
LVL 31

Author Comment

by:Wayne Barron
ID: 16851522
[ciuly];

Good Morning.
I tried your solution and it does not work?
I paste to the Memo1 and the Memo2 does nothing.
I hit the [Enter] key, and it add's in [1] entry into the Memo2
And I need it to add in the Same amount of lines that Memo1 has.

Going off to try [atul_parmar] code now.
0
 
LVL 28

Expert Comment

by:2266180
ID: 16851548
weird. it works fine for me. are you sure you have copy/pasted the entire thing without changing anything?
0
 
LVL 31

Author Comment

by:Wayne Barron
ID: 16851620
Sorry.
Forgot to Trigger the [OnCreate]
I had Triggered the [OnKeyPress] for the Memo1
But not the OnCreate, when I pasted the code.

So far so good! :)

I tested the code from [atul_parmar]
And cannot get it to compile.

Something about
Undeclaired Identifier on: Clipboard
 if Clipboard.HasFormat(CF_TEXT) then   // <--

I am going to run the code from you [ciuly]
And make sure that it can withstand what I am about to do with it.
And make sure that I can get it to work in my actual project.
And I will come in within the hour to inform.

Thank you
0
 
LVL 28

Expert Comment

by:2266180
ID: 16851652
you must
uses clipbrd;
for atul's code to compile ;)
0
 
LVL 10

Expert Comment

by:atul_parmar
ID: 16851660
Yop will have to put the Clipbrd unit in your uses list.
0
 
LVL 31

Author Comment

by:Wayne Barron
ID: 16851707
Yep, just realized that. (Just woke up a few minutes ago, it is 8:55am here)

In your code Ciuly.
Need one more thing to make it work.

The Very last line is not getting the TEdit to the TMemo2 from the TMemo1.
Basically like this:

Edit1.Text := 'One';

Memo1                    Memo2
Are                          One
Are                          One
Are                          

In the above example, you see that "Memo1" has "3" Lines during the [Paste]
And the "Memo2" only has 2 lines during the paste.
This would be because there is not an extra "LineBreak" after the Last Line pasted in.

A #13 (or) #10
Would break it, but were to put it at?
0
 
LVL 31

Author Comment

by:Wayne Barron
ID: 16851733
atul_parmar
The same thing applies to your code as well.
(Read the last information sent to: Ciuly)
0
 
LVL 10

Expert Comment

by:atul_parmar
ID: 16851765
It's not big thing just remove the -1 e.g.

procedure TMemo.OnPaste(var Msg: TMessage);
var
  s : TStringList;
  i : integer;
begin
  if Clipboard.HasFormat(CF_TEXT) then
  begin
    s := TStringList.Create;
    s.Text := Clipboard.AsText;
    for i:= 0 to s.Count do
      Form1.Memo2.Lines.Add(Form1.Edit1.Text);
    s.Free;
  end;
  inherited;
end;
0
 
LVL 28

Accepted Solution

by:
2266180 earned 1200 total points
ID: 16851768
giid practice sais not to make ugly hacks, so I'm not going to. But instead, I will add another event to it that gets fired on paste:

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TPasteEvent = procedure(Sender:TObject; pastedString:string) of object;
  TMemo = class(StdCtrls.TMemo)
  private
    FOnNewLine:TNotifyEvent;
    FOnPaste:TPasteEvent;
    procedure mypaste(var msg:TMessage); message WM_Paste;
  public
    property OnNewLine:TNotifyEvent read FOnNewLine write FOnNewLine;
    property OnPaste:TPasteEvent read FOnPaste write FOnPaste;
  end;

  TForm1 = class(TForm)
    Memo1: TMemo;
    Memo2: TMemo;
    Edit1: TEdit;
    procedure Memo1KeyPress(Sender: TObject; var Key: Char);
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
    procedure FormOnNewLine(Sender: TObject);
    procedure FormOnPaste(Sender: TObject; pasteString:String);
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Memo1KeyPress(Sender: TObject; var Key: Char);
begin
  if key=#13 then
    memo2.lines.add(edit1.text);
end;

procedure TMemo.mypaste(var msg: TMessage);
var
  Handle: THandle;
  CText: string;
  LText: string;
  AText: string;
  i:integer;
begin
  if IsClipboardFormatAvailable(CF_TEXT) then
  begin
    try
      OpenClipBoard(Self.Handle);
      Handle := GetClipboardData(CF_TEXT);
      if Handle = 0 then
        Exit;
      CText := StrPas(GlobalLock(Handle));
      GlobalUnlock(Handle);
      if assigned(FOnNewLine) then
      begin
        i:=1;
        while i<=length(ctext) do
        begin
          if (ctext[i]=#10) and (ctext[i-1]=#13) then// crlf
            FOnNewLine(self);
          inc(i);
        end;
      end;
      if assigned(FOnPaste) then
        FOnPaste(self, CText);
      LText := '';
      if SelStart > 0 then
        LText := Copy(Text, 1, SelStart);
      LText := LText + CText;
      AText := '';
      if (SelStart + 1) < Length(Text) then
        AText := Copy(Text, SelStart + SelLength + 1, Length(Text) - SelStart + SelLength + 1);
      Text := LText + AText;
    finally
      CloseClipBoard;
    end;
  end;{}
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  memo1.OnNewLine:=FormOnNewLine;
  memo1.OnPaste:=FormOnPaste;
end;

procedure TForm1.FormOnNewLine(Sender: TObject);
begin
  memo2.lines.add(edit1.text);
end;

procedure TForm1.FormOnPaste(Sender: TObject; pasteString: String);
begin
  if copy(pasteString,length(pasteString)-1,2)<>#13#10 then// only do this if no line break at the end
    memo2.lines.add(edit1.text);
end;

end.
0
 
LVL 10

Expert Comment

by:atul_parmar
ID: 16851786
one more thing I would like to suggest you (if you use ciuly's code) is to replace the
     CText := StrPas(GlobalLock(Handle));
      if assigned(FOnNewLine) then
      begin
        i:=1;
        while i<=length(ctext) do
        begin
          if (ctext[i]=#10) and (ctext[i-1]=#13) then// crlf
            FOnNewLine(self);
          inc(i);
        end;
      end;
      GlobalUnlock(Handle);
part with using TStringlist. Because going through each character and checking for CRLF will take much time when you paste BIG sized code.
0
 
LVL 28

Expert Comment

by:2266180
ID: 16851853
atul, I think you should look in the classes.pas file from delphi and you will see that the algorithm used for setting the Text property is much more complicated and takes a little more time, not mentioning the time that takes for creating the stringlist class and freeing it. that searching algorithm is pretty optimized for it's puurpose ;) (maybe the length(ctext) can be replaced with a variable, but otehr than that I don't see any speed/memory improvements :)
0
 
LVL 31

Author Comment

by:Wayne Barron
ID: 16851963
Sorry [atul_parmar] Your code did produce the correct # of lines.
The ScrollBars were not equal, so I do appologize.


Ciuly.
Code works great.

I am going to Up the points to 500.

Ciuly - Accepted
Atul_parmar - Assisted.

I think you both for the information, and the time.
And the knowledge and know-how, from reading your comments.
That is what makes EE such a cool place to be a part of.
Everyone how their own opinion for how they like to do things.
Sometimes the opinion's of others can be a little too far fetched, while
At other times, the Opinion from a fellow EE can really make a world of difference.

Both of you come across with some great code, tips and what "you" as a programmer
Think should be done, and should not be done.
Both are equally correct in your own right, as I will not judge on who is correct or not.
You both gave 110% to this Post, and did not stop with it.
Both equally as fast on response time and comments posted.

Well, that is not on the Rambling from me.

Ciuly & Atul;
I wish you both the best, and I think ou both very much for the information that we all received here today.

Wayne
0
 
LVL 10

Expert Comment

by:atul_parmar
ID: 16851967
cuily, your argument seems right.
what abt holding the count property before paste and then use it after paste.

e.g.
  cnt := self.count; // count of lines before paste
  // here your paste operation
  for i:= cnt to self.count do
     FOnNewLine(self);
0
 
LVL 31

Author Comment

by:Wayne Barron
ID: 16851989
I will let you both decide on what to do, and what not to do.
As I am still learning the code provided.
Once a solid sollution/understanding between the both of you is accomplished
I will add in (or) remove code from my Project.

Thanks Guys.
Wayne
0
 
LVL 28

Expert Comment

by:2266180
ID: 16852039
that is indeed a neat idea:

something like this should work (not tested)

procedure TMemo.OnPaste(var Msg: TMessage);
var
  i,cnt : integer;
begin
  cnt := count; // count of lines before paste
  inherited;
  for i:= cnt to count do
    if assigned(FOnNewLine)
      FOnNewLine(self);
end;

@carrzkiss: thank you for your appreciation :)
0
 
LVL 28

Expert Comment

by:2266180
ID: 16852109
this is tested and works:

procedure TMemo.OnPaste(var Msg: TMessage);
var
  i,cnt : integer;
begin
  cnt := lines.count; // count of lines before paste
  inherited;
  for i:= cnt to lines.count do
    if assigned(FOnNewLine) then
      FOnNewLine(self);
end;
0
 
LVL 10

Expert Comment

by:atul_parmar
ID: 16852136
Great! now it looks complete. :)
0
 
LVL 31

Author Comment

by:Wayne Barron
ID: 16852155
Ciuly
Is the:           TMemo.OnPaste
To Replace    TMemo.MyPaste
(or)
Is this it's own Procedure to be added in?
0
 
LVL 28

Expert Comment

by:2266180
ID: 16852209
to be replaced. me and atul used different names for the same procedure(event) ;)
0
 
LVL 28

Expert Comment

by:2266180
ID: 16852220
sorry. incorrect. just to make sure I don't generate otehr confusion:

procedure TMemo.MyPaste(var Msg: TMessage);
var
  i,cnt : integer;
begin
  cnt := lines.count; // count of lines before paste
  inherited;
  for i:= cnt to lines.count do
    if assigned(FOnNewLine) then
      FOnNewLine(self);
end;

there :) (repalce only the body of the function ;) )
0
 
LVL 31

Author Comment

by:Wayne Barron
ID: 16852248
That is what I did. :)
Thanks "Ciuly"

You both have a great day.
Wayne
0
 
LVL 31

Author Comment

by:Wayne Barron
ID: 16854080
Update on the "Updated" Code.

procedure TMemo.MyPaste(var Msg: TMessage);
var
  i,cnt : integer;
begin
  cnt := lines.count; // count of lines before paste
  inherited;
  for i:= cnt to lines.count do
    if assigned(FOnNewLine) then
      FOnNewLine(self);
end;

The code is very buggy.
After a few times of using it, it stops responding.
Replacing it with the Original Code, work perfectly.

So, rather it is a good idea to have the above code, it is not ready yet.

Too recreate the issue.
========================================
Have the above code in place.
Work in Delphi, then [Run] the project.
Do this several times, and you will see that it stops working.
========================================
Wayne
0
 
LVL 28

Expert Comment

by:2266180
ID: 16854577
Hi Wayne,

I'm a little caught up in a project right now.
that is pretty weird. I'll look into it tomorrow if atul doesn't do it before me :)
0
 
LVL 31

Author Comment

by:Wayne Barron
ID: 16855317
OK.
It is not a big problem right now.
As the code that you originally posted is working great for now.
But will wait your outcome.

Take Care Now;
Wayne
0
 
LVL 10

Expert Comment

by:atul_parmar
ID: 16859090
That's ok Wayne.
0
 
LVL 28

Expert Comment

by:2266180
ID: 16859262
hm... I tried to reproduce but I couldn't. However, I found and fixed a bug :) (when you select and copy say x lines, then you again select x lines and paste, it add a new entry in memo2, even though no line has been added to memo1)
updated code:

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TPasteEvent = procedure(Sender:TObject; pastedString:string) of object;
  TMemo = class(StdCtrls.TMemo)
  private
    FOnNewLine:TNotifyEvent;
    procedure mypaste(var msg:TMessage); message WM_Paste;
  public
    property OnNewLine:TNotifyEvent read FOnNewLine write FOnNewLine;
  end;

  TForm1 = class(TForm)
    Memo1: TMemo;
    Memo2: TMemo;
    Edit1: TEdit;
    procedure Memo1KeyPress(Sender: TObject; var Key: Char);
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
    procedure FormOnNewLine(Sender: TObject);
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Memo1KeyPress(Sender: TObject; var Key: Char);
begin
  if key=#13 then
    memo2.lines.add(edit1.text);
end;

procedure TMemo.mypaste(var msg: TMessage);
var
  i,cnt : integer;
begin
  cnt := lines.count; // count of lines before paste
  inherited;
  if cnt<>lines.count then
    for i:= cnt to lines.count-1 do
      if assigned(FOnNewLine) then
        FOnNewLine(self);
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  memo1.OnNewLine:=FormOnNewLine;
end;

procedure TForm1.FormOnNewLine(Sender: TObject);
begin
  memo2.lines.add(edit1.text);
end;

end.

I would need a ste-by-step reproduction, that works every time. if you can provide that :)
0

Featured Post

Free Tool: SSL Checker

Scans your site and returns information about your SSL implementation and certificate. Helpful for debugging and validating your SSL configuration.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

A lot of questions regard threads in Delphi.   One of the more specific questions is how to show progress of the thread.   Updating a progressbar from inside a thread is a mistake. A solution to this would be to send a synchronized message to the…
Objective: - This article will help user in how to convert their numeric value become words. How to use 1. You can copy this code in your Unit as function 2. than you can perform your function by type this code The Code   (CODE) The Im…
This video shows how to quickly and easily deploy an email signature for all users in Office 365 and prevent it from being added to replies and forwards. (the resulting signature is applied on the server level in Exchange Online) The email signat…
Whether it be Exchange Server Crash Issues, Dirty Shutdown Errors or Failed to mount error, Stellar Phoenix Mailbox Exchange Recovery has always got your back. With the help of its easy to understand user interface and 3 simple steps recovery proced…
Suggested Courses

873 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question