TMemo's empty last line lazarus / 31-LCL ---------------------------------------------------------------------- lagprogramming · 2015-06-19 09:07 Why was TMemo designed to append an additional empty line at the end? ---------------------------------------------------------------------- Leledumbo · 2015-06-19 13:03 Quote from: lagprogramming on June 19, 2015, 11:07:48 am   Why was TMemo designed to append an additional empty line at the end? I guess it's because its backend, which is a TStrings descendant, behaves that way. ---------------------------------------------------------------------- lagprogramming · 2015-07-08 13:14 What's Delphi's behaviour? Does it add an empty line at the end? ---------------------------------------------------------------------- typo · 2015-07-08 13:16 Yes. ---------------------------------------------------------------------- Graeme · 2015-07-11 17:10 Quote from: lagprogramming on June 19, 2015, 11:07:48 am   Why was TMemo designed to append an additional empty line at the end? Please supply a sample program so I can see what you mean. Then I'll be able to understand what you are saying, and I'll be able to correctly under the question about fpGUI. ---------------------------------------------------------------------- Windsurfer · 2015-07-11 18:03 When a single TMemo is dropped on a form, it starts with a line that says 'Memo1'. This is followed by a blank line. Every time a line is added, Code: [Select]Memo1.Lines.Add('New line')the blank line is pushed down. I always assumed this was so that there was a blank line for the user to write on. ---------------------------------------------------------------------- jc99 · 2015-07-11 21:06 If anybody likes, Here's a small test-program Code: [Select]unit frm_MemoEmptyLineMain; {$mode objfpc}{$H+} interface uses   Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls; type   { TForm1 }   TForm1 = class(TForm)     btnClearMemo: TButton;     btnMemoAdd: TButton;     btnSetText: TButton;     btnAddText: TButton;     btnInsertText: TButton;     lblShowText: TLabel;     Memo1: TMemo;     procedure btnClearMemoClick(Sender: TObject);     procedure btnMemoAddClick(Sender: TObject);     procedure btnSetTextClick(Sender: TObject);     procedure btnAddTextClick(Sender: TObject);     procedure btnInsertTextClick(Sender: TObject);   private     { private declarations }   public     { public declarations }   end; var   Form1: TForm1; implementation {$R *.lfm} { TForm1 } procedure TForm1.btnClearMemoClick(Sender: TObject); begin   Memo1.Clear;   lblShowText.Caption:=''''+memo1.Text+''''; end; procedure TForm1.btnMemoAddClick(Sender: TObject); begin   memo1.lines.add('Add Testline '+ inttostr(memo1.Lines.Count));   lblShowText.Caption:=''''+memo1.Text+''''; end; procedure TForm1.btnSetTextClick(Sender: TObject); begin   memo1.lines.Text:='This is a test'+LineEnding+   'if there is alway an empty line'+LineEnding+   'at the end.';   lblShowText.Caption:=''''+memo1.Text+''''; end; procedure TForm1.btnAddTextClick(Sender: TObject); begin   memo1.lines.AddText('AddText Testline '+ inttostr(memo1.Lines.Count));   lblShowText.Caption:=''''+memo1.Text+''''; end; procedure TForm1.btnInsertTextClick(Sender: TObject); begin   memo1.lines.Insert(0,'Insert Testline '+ inttostr(memo1.Lines.Count));   lblShowText.Caption:=''''+memo1.Text+''''; end; end. The form: Code: [Select]object Form1: TForm1   Left = 207   Height = 217   Top = 361   Width = 594   Caption = 'Form1'   ClientHeight = 217   ClientWidth = 594   LCLVersion = '1.4.0.4'   object Memo1: TMemo     Left = 0     Height = 176     Top = 0     Width = 320     Lines.Strings = (       'Memo1'     )     TabOrder = 0   end   object btnClearMemo: TButton     Left = 8     Height = 25     Top = 184     Width = 75     Caption = 'Clear'     OnClick = btnClearMemoClick     TabOrder = 1   end   object btnMemoAdd: TButton     Left = 92     Height = 25     Top = 184     Width = 75     Caption = 'Add'     OnClick = btnMemoAddClick     TabOrder = 2   end   object btnSetText: TButton     Left = 176     Height = 25     Top = 184     Width = 75     Caption = 'set Text'     OnClick = btnSetTextClick     TabOrder = 3   end   object btnAddText: TButton     Left = 256     Height = 25     Top = 184     Width = 75     Caption = 'AddText'     OnClick = btnAddTextClick     TabOrder = 4   end   object btnInsertText: TButton     Left = 336     Height = 25     Top = 184     Width = 75     Caption = 'Insert'     OnClick = btnInsertTextClick     TabOrder = 5   end   object lblShowText: TLabel     Left = 325     Height = 15     Top = 8     Width = 64     Caption = 'lblShowText'     ParentColor = False   end end The only way not to have an empty line, is to set the .text property directly. ---------------------------------------------------------------------- eny · 2015-07-11 23:41 Quote from: Windsurfer on July 11, 2015, 08:03:34 pmWhen a single TMemo is dropped on a form, it starts with a line that says 'Memo1'. This is followed by a blank line. Every time a line is added, Code: [Select]Memo1.Lines.Add('New line')the blank line is pushed down. It's a matter of definition. If you were to check Memo.Lines.Count it would return 1, which is correct. There is no 2nd ('blank') line. However by default each line is displayed ending with a line ending character/characters (platform dependant constant 'LineEnding'). So the cursor in the TMemo component can be positioned just after the text of the first line (including the LineEnding), which is on line 2. That second line is not part of the memo text. Quote from: jc99 on July 11, 2015, 11:06:58 pmThe only way not to have an empty line, is to set the .text property directly.True; this is the confusing part. If you do this: Code: [Select]Memo1.Lines.Text := 'One line'... then the cursor can not be placed beyond the end of the first line as there is no LineEnding (yet). If you then in turn do "Memo1.Lines.Add('New line')" this is restored and line 1 ánd line 2 will have a LineEnding at the end and you can position the cursor on line 3 (which in turn is not part of the actual Memo text...). ---------------------------------------------------------------------- lagprogramming · 2015-07-12 09:01 I've attached an example. As previously pointed by other users, I think the important thing is that Code: [Select]Memo.Clear;Memo.Lines.Add('Text line');will make the TMemo have two lines, the additional empty line influences the vertical scrollbar. The user would have to scroll each TMemo on the form because he would think that additional informations appear upon scrolling the list. Things start to become annoying when dealing with multiple TMemos vertically arranged. For this reason I'd like to know why people chose this design. I don't see the benefits of having that empty line. ---------------------------------------------------------------------- eny · 2015-07-12 09:25 Quote from: lagprogramming on July 12, 2015, 11:01:01 amThe user would have to scroll each TMemo on the form because he would think that additional informations appear upon scrolling the list.Which is absolutely no problem for anybody that is used to editors or word processors in general. If you only want to display information (no editing) simply disable the scroll bar. Or choose a different component (listbox, virtual string list etc.). ---------------------------------------------------------------------- jc99 · 2015-07-12 09:35 Quote from: lagprogramming on July 12, 2015, 11:01:01 amI've attached an example. As previously pointed by other users, I think the important thing is that Code: [Select]Memo.Clear;Memo.Lines.Add('Text line');will make the TMemo have two lines, the additional empty line influences the vertical scrollbar. The user would have to scroll each TMemo on the form because he would think that additional informations appear upon scrolling the list. Things start to become annoying when dealing with multiple TMemos vertically arranged. For this reason I'd like to know why people chose this design. I don't see the benefits of having that empty line. ... but that's up to you, because you can always do : Code: [Select]  Memo.Lines.Text := TrimRight(Memo.Lines.Text); // Get rid of the last line. And your annoying design issue only apply when you have exactly so much lines in your memo that it fits. But you are using a memo, meaning your user can change data, also when the form resizes your loose your design-advantage. And if you only want to display a static text, why not use a label ? if you want to get rid of the scrollbars use Code: [Select]  Memo.Scrollbars := ssNone; // Get rid of the scrollbars. [EDIT] @eny: +1 you've been a little faster. ---------------------------------------------------------------------- eny · 2015-07-12 10:40 Quote from: jc99 on July 12, 2015, 11:35:32 am@eny: +1 you've been a little faster. I had less text to type ---------------------------------------------------------------------- lagprogramming · 2015-07-12 11:22 Thank you for your recommendations but I'm still interested in why this design has been chosen. I still fail to understand the benefits of that empty line.    Are you absolutely sure that Delphi's TMemo has two lines after executing the code: Code: [Select]Memo.Clear;Memo.Lines.Add('Text line');    Reading http://bugs.freepascal.org/view.php?id=24973 it looks like I've remembered right. In the past TListBox acted the same as TMemo acts now. Why is that nowadays for a TListBox is not acceptable to have an empty line but it's acceptable for a TMemo?    Also, could that empty line at the end have an impact on copying to clipboard processes!?    By the way, "Memo1.Lines.Text := TrimRight(Memo1.Lines.Text);" not only slows down things but also removes all [#0..' '] characters. This means all empty lines at the end, space only lines, and who knows what else... I'm not sure this workaround is usable.    I'm still looking forward. If you have a new idea on the subject, share it here. I'm not much interested in workarounds as I'm interested in why is TMemo behaving this way. ---------------------------------------------------------------------- jc99 · 2015-07-12 14:09 Quote from: lagprogramming on July 12, 2015, 01:22:05 pm[...]   Are you absolutely sure that Delphi's TMemo has two lines after executing the code: Code: [Select]Memo.Clear;Memo.Lines.Add('Text line');positive !! Two lines (one line with CRLF at the end) With trim CRLF is gone -> one line (Testet on DXE) ---------------------------------------------------------------------- Graeme · 2015-07-13 08:30 Quote from: Windsurfer on July 11, 2015, 08:03:34 pmWhen a single TMemo is dropped on a form, it starts with a line that says 'Memo1'. This is followed by a blank line. Every time a line is added, Code: [Select]Memo1.Lines.Add('New line')the blank line is pushed down. I always assumed this was so that there was a blank line for the user to write on. No, fpGUI's TfpgMemo doesn't do that. The Lines property is completely empty, not even the silly 'Memo1' text. Here is code that the fpGUI UI Designer creates when you drop a memo on a form. Code: [Select]  Memo1 := TfpgMemo.Create(self);   with Memo1 do   begin     Name := 'Memo1';     SetPosition(20, 10, 225, 194);     FontDesc := '#Edit1';     Hint := '';     TabOrder := 2;   end; If you do the following on a newly created Memo (as shown above) and execute the following code... Code: [Select]procedure TMainForm.ButtonClicked(Sender: TObject); begin   ShowMessage(IntToStr(Memo1.Lines.Count));   Memo1.Lines.Add('new line');   ShowMessage(IntToStr(Memo1.Lines.Count)); end; The results would be 0 and then 1. ---------------------------------------------------------------------- Graeme · 2015-07-13 08:33 Quote from: Leledumbo on June 19, 2015, 03:03:18 pmI guess it's because its backend, which is a TStrings descendant, behaves that way. Rubbish! When you create a new TStrings or TStringList, the line count is 0 (no text at all).  It is VCL or LCL that injects text into the Memo.Lines property at creation of the Memo component. ---------------------------------------------------------------------- jc99 · 2015-07-13 16:08 Quote from: Graeme on July 13, 2015, 10:33:24 amRubbish! When you create a new TStrings or TStringList, the line count is 0 (no text at all).  It is VCL or LCL that injects text into the Memo.Lines property at creation of the Memo component. Hey keep the temper down. Have you even tried, what you caim ? I tried, they behave EXACTLY the same on adding text. The only difference is, that you can't get rid of the last CRLF with trimright. In TStringList the LineEnding seems to be hard-coded the "End-of-the- Line". Quote from: Graeme on July 13, 2015, 10:30:55 am[..] If you do the following on a newly created Memo (as shown above) and execute the following code... Code: [Select]procedure TMainForm.ButtonClicked(Sender: TObject); begin   Memo1.Clear;   ShowMessage(IntToStr(Memo1.Lines.Count));   Memo1.Lines.Add('new line');   ShowMessage(IntToStr(Memo1.Lines.Count)); end; The results would be 0 and then 1. The results are 0 and then 1. But ... Memo1.Lines.Text is 'new line'+LineEnding; !! You see 2 lines, but you actually have only one, with LineEnding at the end. By the way that stupid 'Memo1' as a start is not added by default of the wigetset, it is inserted because you didn't cleared Lines property in the Designer. YOU can put there whatever you like, even rubbish or nothing. ---------------------------------------------------------------------- Graeme · 2015-07-13 23:22 Quote from: jc99 on July 13, 2015, 06:08:13 pmHave you even tried, what you caim ? Yes I did. QuoteMemo1.Lines.Text is 'new line'+LineEnding; !! You see 2 lines, but you actually have only one, with LineEnding at the end. If you bothered to try it in fpGUI you would have noticed that what you said is NOT true with fpGUI. If you run the sample code I listed, you have exactly 1 line of text in the memo. To get to line 2, YOU the user need to press the Enter key. Simply clicking with the mouse, you will only be able to place the cursor on line 1, because there is only one line of text. QuoteBy the way that stupid 'Memo1' as a start is not added by default of the wigetset, It is inserted by the VCL or LCL forms designer - forcing the developer to manually go and clear something that the form designer inserted. Such a stupid idea - hence the fpGUI UI Designer does NOT insert such pointless lines of text. ---------------------------------------------------------------------- jc99 · 2015-07-14 00:12 If you had read my reply right I was not referring to fpGUI but to TStringList. You are right, I didn't use fpGui, but i never said, I did. I used delphi, win32, win64, in lazarus, all with the same result. maybe fpGui has a compatibility-issue here. And as I said before, please keep your tamper down. BTW. Inserting the Name of the Control as default text is also done in all of these,   maybe there is a compatibility issue here too. ---------------------------------------------------------------------- eny · 2015-07-14 06:47 Quote from: jc99 on July 14, 2015, 02:12:15 amAnd as I said before, please keep your tamper down.http://learning.colostate.edu/guides/guide.cfm?guideid=4 ---------------------------------------------------------------------- lagprogramming · 2015-07-14 08:00 As I understand, this behaviour is implemented in Lazarus in order to be compatible with Delphi. Unlike Lazarus, fpGui acts differently, probably mseIDE, too. Each project implements it's own philosophy and it's good to know that there is diversity. There is a single situation where Lazarus's behaviour doesn't fit very well the needs: form with multiple vertically arranged Memos that have vertical scrollbars and approximately the same number of lines as memo's rows. This is an unpleasant situation(situation that I've encountered), but it's a situation with few chances to be encountered by other developers.    Thank you very much for your replies, they cleared things up to me. ---------------------------------------------------------------------- Graeme · 2015-07-14 08:59 Quote from: jc99 on July 14, 2015, 02:12:15 amIf you had read my reply right I was not referring to fpGUI but to TStringList. Then please take a look at the implementation of TStringList in Free Pascal. The TStringList.Add(...) boils down to the following code: Code: [Select]Procedure TStringList.InsertItem(Index: Integer; const S: string; O: TObject); begin   Changing;   If FCount=Fcapacity then Grow;   If Index0 then       System.Move(Pointer(S)^,P^,L);     P:=P+L;     For L:=1 to NLS do       begin       P^:=NL[L];       inc(P);       end;     end; end; ---------------------------------------------------------------------- Graeme · 2015-07-14 09:44 Quote from: jc99 on July 14, 2015, 11:18:20 amNo, but it's in TStrings.GetTextStr file: stringl.inc (line 463 ff) That is only if you reference all the content via the Text property. How else is TStrings supposed to differentiated between individual lines of text, if you ask for all the data return at once. Alternatively, you can query the data line by line in a loop, which means there will be no LineEnding character appended for each line returned. The point is, by adding text it doesn't add a LineEnding character. On returning data, you as a developer have a choice - all in one go (which means it needs to somehow delimit each line), or line by line. Regards,   Graeme