View Issue Details

IDProjectCategoryView StatusLast Update
0014615LazarusLCLpublic2009-11-08 11:45
ReporterBart Broersma Assigned ToPaul Ishenin  
PrioritynormalSeveritymajorReproducibilityalways
Status closedResolutionfixed 
Platformi386OSSuse Linux 
Product Version0.9.29 (SVN) 
Target Version0.9.28Fixed in Version0.9.29 (SVN) 
Summary0014615: MaskEdit is broken on GTK2
DescriptionMaskEdit is broken on GTK2.

If you try to insert a char in the control an endless loop starts.
Steps To ReproducePlace MaskEdit on a form
Set EditMask at runtime
Try to type in the control --> program freezes
Additional InformationAll setting of text is by design done in SetInheritedText
This sets a flag (FChangeAllowed) to True, indicating it is OK to change the text in the control
Then it sets Inherited text
This triggers TextChanged to be called
In TextChanged the flag is inspected, and if changes are not allowed the text is reset (this is really necessary or the control might be screwed up by past.drag etc).
Then we return to SetInheritedText and the flag is set to False again

This code/feature is unchanged since r18192 (and was tested at the time on GTK1, GTK2 and Win32 and functioned as expected)

However now the order in which things happen have changed:

SetInheritedText is called
The flag is set to True
Inherited text is set
At this point TextChanged should be called, but it is not
The flag is set back to False
SetInheritedText is finished
And only now TextChanged is called, which will miserably fail, since the flag has been reset to False, and it will therefore try to reset the text to CurrentText
To achieve this it will call SetInheritedText(CurrentText) and an endless loop will follow.

So the order/timing in which events are called has changed, and TMaskEdit relies on that order.
TagsNo tags attached.
Fixed in Revision21880
LazTarget0.9.28
WidgetsetGTK 2
Attached Files

Activities

Vincent Snijders

2009-09-18 15:55

manager   ~0030810

Do you know when this got broken (what revision)?

Paul Ishenin

2009-09-18 16:15

manager   ~0030813

Sorry, I can't reproduce a hang. Can you attach your example?

Bart Broersma

2009-09-18 21:33

developer   ~0030824

@Vincent: the patch I wrote for 0013074 was tested on GTK2 by me and others and worked fine (r18557), after that one I never tested again on GTK2, since I never altered the procs that set the cursor, sets text, or insert/deletes chars.

theo

2009-09-18 22:36

reporter   ~0030825

Bart, this changelog might be interesting (starting at rev 20178)
http://svn.freepascal.org/cgi-bin/viewvc.cgi/trunk/lcl/interfaces/gtk2/gtk2wscustommemo.inc?root=lazarus&sortby=date&view=log

2009-09-18 22:50

 

maskedit.gtk2_bug.tgz (99,738 bytes)

Bart Broersma

2009-09-18 23:06

developer   ~0030826

Last edited: 2009-09-18 23:09

I attached a sample program to demonstrate the problem.

I placed debugln statements in maskedit.pp like this:

procedure TCustomMaskEdit.SetInheritedText(const Value: String);
begin
  debugln('TMaskEdit.SetInheritedText:');
  debugln(' [SIT] Value = ',Value);
  if Value = Inherited Text then debugln(' [SIT] Value = inherited Text --> nothing to do');

  if Value <> Inherited Text then
  begin
 debugln(' [SIT] Setting FChangeAllowed := True');
    FChangeAllowed := True;
 debugln(' [SIT] do: inherited Text := ',Value);
    Inherited Text := Value;
 debugln(' [SIT] After: inherited Text := ',Value);
    FChangeAllowed := False;
 debugln(' [SIT] Setting FChangeAllowed := False');
  end;
  debugln('TMaskEdit.SetInheritedText End');
end;

procedure TCustomMaskEdit.TextChanged;
{ Purpose: to avoid messing up the control by
  - cut/paste/clear via OS context menu
    (we try to catch these messages and hadle them,
    but this is not garantueed to work)
  - dragging selected text in the control with the mous
  If one of these happens, then the internal logic of cursorpositioning,
  inserting characters is messed up.
  So, we simply restore the text from our backup: CurrenText
}
begin
  if not IsMasked then
  begin
    Inherited TextChanged;
    Exit;
  end;
  debugln('TMaskEdit.TextChanged (control is masked):');
  debugln(' [TC] FChangeAllowed = ',DbgS(FChangeAllowed),', CurrentText = ',CurrentText,' inherited Text = ',inherited Text);

  if FChangeAllowed then
  begin
    DebugLn(' [TC] FChangeAllowed = True --> do: Inherited TextChanged');
    Inherited TextChanged ;
    DebugLn(' [TC] After inherited TextChanged');
  end
  else
  begin
    DebugLn(' [TC] FChangeAllowed = False [',DbgS(FChangeAllowed),']--> reset Text to ',CurrentText);
    DebugLn(' [TC] so do: SetInheritedText(',CurrentText,')');
    SetInheritedText(CurrentText);
    //Reset cursor to last known position
    SetCursorPos;
  end;
  if (inherited Text = '') then Clear;
  debugln('TMaskEdit.TextChanged End ');
end;


I ran the program, clicked on "Apply Mask", entered the maskEdit control and type an A

On GTK it works as expected (and as before), the output is (from the moment I pressed the A)

TMaskEdit.SetInheritedText:
  [SIT] Value = ______
  [SIT] Value = inherited Text --> nothing to do
TMaskEdit.SetInheritedText End
TMaskEdit.SetInheritedText:
  [SIT] Value = A_____
  [SIT] Setting FChangeAllowed := True
  [SIT] do: inherited Text := A_____
TMaskEdit.TextChanged (control is masked):
  [TC] FChangeAllowed = True, CurrentText = A_____ inherited Text = A_____
  [TC] FChangeAllowed = True --> do: Inherited TextChanged
  [TC] After inherited TextChanged
TMaskEdit.TextChanged End
  [SIT] After: inherited Text := A_____
  [SIT] Setting FChangeAllowed := False
TMaskEdit.SetInheritedText End

(note: the first call to SetInheritedText is caused by deleting the selection under the cursor)

Under GTK2 when I type an A (or any char, or set text by code) I get an infinite loop.
The output:

TMaskEdit.SetInheritedText:
  [SIT] Value = ______
  [SIT] Value = inherited Text --> nothing to do
TMaskEdit.SetInheritedText End
TMaskEdit.SetInheritedText:
  [SIT] Value = A_____
  [SIT] Setting FChangeAllowed := True
  [SIT] do: inherited Text := A_____
TMaskEdit.TextChanged (control is masked):
  [TC] FChangeAllowed = True, CurrentText = A_____ inherited Text = (*)
  [TC] FChangeAllowed = True --> do: Inherited TextChanged
  [TC] After inherited TextChanged
TMaskEdit.SetInheritedText: (*****!!)
  [SIT] Value = ______
  [SIT] Setting FChangeAllowed := True
  [SIT] do: inherited Text := ______
TMaskEdit.TextChanged (control is masked):
  [TC] FChangeAllowed = True, CurrentText = ______ inherited Text = ______
  [TC] FChangeAllowed = True --> do: Inherited TextChanged
  [TC] After inherited TextChanged
TMaskEdit.TextChanged End
  [SIT] After: inherited Text := ______
  [SIT] Setting FChangeAllowed := False
TMaskEdit.SetInheritedText End
TMaskEdit.TextChanged End
TMaskEdit.TextChanged (control is masked):
  [TC] FChangeAllowed = False, CurrentText = ______ inherited Text = A___________
  [TC] FChangeAllowed = False [False]--> reset Text to ______
  [TC] so do: SetInheritedText(______)
TMaskEdit.SetInheritedText:
  [SIT] Value = ______
  [SIT] Setting FChangeAllowed := True
  [SIT] do: inherited Text := ______
TMaskEdit.TextChanged (control is masked):
  [TC] FChangeAllowed = True, CurrentText = ______ inherited Text =
  [TC] FChangeAllowed = True --> do: Inherited TextChanged
  [TC] After inherited TextChanged
TMaskEdit.SetInheritedText:
  [SIT] Value = ______
  [SIT] Setting FChangeAllowed := True
  [SIT] do: inherited Text := ______
TMaskEdit.TextChanged (control is masked):
  [TC] FChangeAllowed = True, CurrentText = ______ inherited Text = ______
  [TC] FChangeAllowed = True --> do: Inherited TextChanged
  [TC] After inherited TextChanged
TMaskEdit.TextChanged End
  [SIT] After: inherited Text := ______
  [SIT] Setting FChangeAllowed := False
TMaskEdit.SetInheritedText End
TMaskEdit.TextChanged End
TMaskEdit.TextChanged (control is masked):
........
... and it goes on forever

(*) inherited Text should be A_____ not an empty string here, not sure if it's related
(*****!!) Why does it call SetInheritedText again? I cannot figure this out.

My system:
Intel celeron 700 Mhx (year 2000)
Suse Linux 10.0 (kernel 2.3.13-15 i686)
GTK: gtk-1.210-888 and gtk2-2.8.3-4
Lazarus r21739 with fpc 2.2.4

Bart Broersma

2009-09-18 23:34

developer   ~0030828

Here's a quote from Theo

I'm now on a different machine (opensuse 10.0, yesterday 11.1).
I'm using today's SVN rev:
Lazarus 0.9.29 r21742M FPC 2.2.4 i386-linux-gtk 2 (beta)

Here, it crashes the IDE when trying to set EditMask in the Obj. Insp.
No matter what I type in.
See backtrace.

#0 0x081a8584 in TWINCONTROL__HANDLEALLOCATED (this=0xb63f0f50)
    at wincontrol.inc:6802
0000001 0x081a9504 in TWINCONTROL__HANDLENEEDED (this=0xb63f0f50)
    at wincontrol.inc:7249
0000002 0x081a7681 in TWINCONTROL__GETHANDLE (this=0xb63f0f50)
    at wincontrol.inc:6120
0000003 0x082749a0 in TGTKWSWINCONTROL__GETTEXT (AWINCONTROL=0xb63f0f50,
    ATEXT=0x0, pvmt=0x8a16e88) at gtkwscontrols.pp:264
0000004 0x082c5fff in TGTK2WSWINCONTROL__GETTEXT (AWINCONTROL=0xb63f0f50,
    ATEXT=0x0, pvmt=0xb73d7b40) at gtk2wscontrols.pp:307
0000005 0x081aa1c9 in TWINCONTROL__REALGETTEXT (this=0xb63f0f50)
    at wincontrol.inc:7607
0000006 0x081afeaf in TCONTROL__GETTEXT (this=0xb63f0f50) at control.inc:2738
0000007 0x081b311b in TCONTROL__SETTEXT (VALUE=0xb6913958, this=0xb63f0f50)
    at control.inc:4127
0000008 0x082a837d in TCUSTOMMASKEDIT__SETINHERITEDTEXT (VALUE=0xb6913958,
    this=0xb63f0f50) at maskedit.pp:791
0000009 0x082aa724 in TCUSTOMMASKEDIT__CLEAR (this=0xb63f0f50) at maskedit.pp:1562
0000010 0x082a9752 in TCUSTOMMASKEDIT__TEXTCHANGED (this=0xb63f0f50)
    at maskedit.pp:1134

Maybe the backtrace is helpfull?

Paul Ishenin

2009-09-19 10:26

manager   ~0030831

Regards your demo. It works here:
[DBGTGT] TMaskEdit.SetInheritedText:
[DBGTGT] [SIT] Value =
[DBGTGT] [SIT] Value = inherited Text --> nothing to do
[DBGTGT] TMaskEdit.SetInheritedText End
[DBGTGT] TMaskEdit.SetInheritedText:
[DBGTGT] [SIT] Value = ______
[DBGTGT] [SIT] Setting FChangeAllowed := True
[DBGTGT] [SIT] do: inherited Text := ______
[DBGTGT] TMaskEdit.TextChanged (control is masked):
[DBGTGT] [TC] FChangeAllowed = True, CurrentText = ______ inherited Text = ______
[DBGTGT] [TC] FChangeAllowed = True --> do: Inherited TextChanged
[DBGTGT] [TC] After inherited TextChanged
[DBGTGT] TMaskEdit.TextChanged End
[DBGTGT] [SIT] After: inherited Text := ______
[DBGTGT] [SIT] Setting FChangeAllowed := False
[DBGTGT] TMaskEdit.SetInheritedText End
[DBGTGT] TMaskEdit.SetInheritedText:
[DBGTGT] [SIT] Value = ______
[DBGTGT] [SIT] Value = inherited Text --> nothing to do
[DBGTGT] TMaskEdit.SetInheritedText End
[DBGTGT] TMaskEdit.SetInheritedText:
[DBGTGT] [SIT] Value = ______
[DBGTGT] [SIT] Value = inherited Text --> nothing to do
[DBGTGT] TMaskEdit.SetInheritedText End
[DBGTGT] TMaskEdit.SetInheritedText:
[DBGTGT] [SIT] Value = A_____
[DBGTGT] [SIT] Setting FChangeAllowed := True
[DBGTGT] [SIT] do: inherited Text := A_____
[DBGTGT] TMaskEdit.TextChanged (control is masked):
[DBGTGT] [TC] FChangeAllowed = True, CurrentText = A_____ inherited Text = A_____
[DBGTGT] [TC] FChangeAllowed = True --> do: Inherited TextChanged
[DBGTGT] [TC] After inherited TextChanged
[DBGTGT] TMaskEdit.TextChanged End
[DBGTGT] [SIT] After: inherited Text := A_____
[DBGTGT] [SIT] Setting FChangeAllowed := False
[DBGTGT] TMaskEdit.SetInheritedText End
[DBGTGT] TMaskEdit.SetInheritedText:
[DBGTGT] [SIT] Value = A_____
[DBGTGT] [SIT] Value = inherited Text --> nothing to do
[DBGTGT] TMaskEdit.SetInheritedText End
[DBGTGT] TMaskEdit.SetInheritedText:
[DBGTGT] [SIT] Value = AB____
[DBGTGT] [SIT] Setting FChangeAllowed := True
[DBGTGT] [SIT] do: inherited Text := AB____
[DBGTGT] TMaskEdit.TextChanged (control is masked):
[DBGTGT] [TC] FChangeAllowed = True, CurrentText = AB____ inherited Text = AB____
[DBGTGT] [TC] FChangeAllowed = True --> do: Inherited TextChanged
[DBGTGT] [TC] After inherited TextChanged
[DBGTGT] TMaskEdit.TextChanged End
[DBGTGT] [SIT] After: inherited Text := AB____
[DBGTGT] [SIT] Setting FChangeAllowed := False
[DBGTGT] TMaskEdit.SetInheritedText End
[DBGTGT] TMaskEdit.SetInheritedText:
[DBGTGT] [SIT] Value = AB____
[DBGTGT] [SIT] Value = inherited Text --> nothing to do
[DBGTGT] TMaskEdit.SetInheritedText End
[DBGTGT] TMaskEdit.SetInheritedText:
[DBGTGT] [SIT] Value = ABV___
[DBGTGT] [SIT] Setting FChangeAllowed := True
[DBGTGT] [SIT] do: inherited Text := ABV___
[DBGTGT] TMaskEdit.TextChanged (control is masked):
[DBGTGT] [TC] FChangeAllowed = True, CurrentText = ABV___ inherited Text = ABV___
[DBGTGT] [TC] FChangeAllowed = True --> do: Inherited TextChanged
[DBGTGT] [TC] After inherited TextChanged
[DBGTGT] TMaskEdit.TextChanged End
[DBGTGT] [SIT] After: inherited Text := ABV___
[DBGTGT] [SIT] Setting FChangeAllowed := False
[DBGTGT] TMaskEdit.SetInheritedText End
[DBGTGT] TMaskEdit.SetInheritedText:
[DBGTGT] [SIT] Value = AB____
[DBGTGT] [SIT] Setting FChangeAllowed := True
[DBGTGT] [SIT] do: inherited Text := AB____
[DBGTGT] TMaskEdit.TextChanged (control is masked):
[DBGTGT] [TC] FChangeAllowed = True, CurrentText = AB____ inherited Text = AB____
[DBGTGT] [TC] FChangeAllowed = True --> do: Inherited TextChanged
[DBGTGT] [TC] After inherited TextChanged
[DBGTGT] TMaskEdit.TextChanged End
[DBGTGT] [SIT] After: inherited Text := AB____
[DBGTGT] [SIT] Setting FChangeAllowed := False
[DBGTGT] TMaskEdit.SetInheritedText End
[DBGTGT] TMaskEdit.SetInheritedText:
[DBGTGT] [SIT] Value = AB____
[DBGTGT] [SIT] Value = inherited Text --> nothing to do
[DBGTGT] TMaskEdit.SetInheritedText End
[DBGTGT] TMaskEdit.SetInheritedText:
[DBGTGT] [SIT] Value = ABC___
[DBGTGT] [SIT] Setting FChangeAllowed := True
[DBGTGT] [SIT] do: inherited Text := ABC___
[DBGTGT] TMaskEdit.TextChanged (control is masked):
[DBGTGT] [TC] FChangeAllowed = True, CurrentText = ABC___ inherited Text = ABC___
[DBGTGT] [TC] FChangeAllowed = True --> do: Inherited TextChanged
[DBGTGT] [TC] After inherited TextChanged
[DBGTGT] TMaskEdit.TextChanged End
[DBGTGT] [SIT] After: inherited Text := ABC___
[DBGTGT] [SIT] Setting FChangeAllowed := False
[DBGTGT] TMaskEdit.SetInheritedText End
[DBGTGT] TMaskEdit.SetInheritedText:
[DBGTGT] [SIT] Value = ABC___
[DBGTGT] [SIT] Value = inherited Text --> nothing to do
[DBGTGT] TMaskEdit.SetInheritedText End
[DBGTGT] TMaskEdit.SetInheritedText:
[DBGTGT] [SIT] Value = ABCD__
[DBGTGT] [SIT] Setting FChangeAllowed := True
[DBGTGT] [SIT] do: inherited Text := ABCD__
[DBGTGT] TMaskEdit.TextChanged (control is masked):
[DBGTGT] [TC] FChangeAllowed = True, CurrentText = ABCD__ inherited Text = ABCD__
[DBGTGT] [TC] FChangeAllowed = True --> do: Inherited TextChanged
[DBGTGT] [TC] After inherited TextChanged
[DBGTGT] TMaskEdit.TextChanged End
[DBGTGT] [SIT] After: inherited Text := ABCD__
[DBGTGT] [SIT] Setting FChangeAllowed := False
[DBGTGT] TMaskEdit.SetInheritedText End
[DBGTGT] TMaskEdit.SetInheritedText:
[DBGTGT] [SIT] Value = ABCD__
[DBGTGT] [SIT] Value = inherited Text --> nothing to do
[DBGTGT] TMaskEdit.SetInheritedText End
[DBGTGT] TMaskEdit.SetInheritedText:
[DBGTGT] [SIT] Value = ABCDE_
[DBGTGT] [SIT] Setting FChangeAllowed := True
[DBGTGT] [SIT] do: inherited Text := ABCDE_
[DBGTGT] TMaskEdit.TextChanged (control is masked):
[DBGTGT] [TC] FChangeAllowed = True, CurrentText = ABCDE_ inherited Text = ABCDE_
[DBGTGT] [TC] FChangeAllowed = True --> do: Inherited TextChanged
[DBGTGT] [TC] After inherited TextChanged
[DBGTGT] TMaskEdit.TextChanged End
[DBGTGT] [SIT] After: inherited Text := ABCDE_
[DBGTGT] [SIT] Setting FChangeAllowed := False
[DBGTGT] TMaskEdit.SetInheritedText End
[DBGTGT] TMaskEdit.SetInheritedText:
[DBGTGT] [SIT] Value = ABCDE_
[DBGTGT] [SIT] Value = inherited Text --> nothing to do
[DBGTGT] TMaskEdit.SetInheritedText End
[DBGTGT] TMaskEdit.SetInheritedText:
[DBGTGT] [SIT] Value = ABCDEF
[DBGTGT] [SIT] Setting FChangeAllowed := True
[DBGTGT] [SIT] do: inherited Text := ABCDEF
[DBGTGT] TMaskEdit.TextChanged (control is masked):
[DBGTGT] [TC] FChangeAllowed = True, CurrentText = ABCDEF inherited Text = ABCDEF
[DBGTGT] [TC] FChangeAllowed = True --> do: Inherited TextChanged
[DBGTGT] [TC] After inherited TextChanged

Paul Ishenin

2009-09-19 10:28

manager   ~0030832

Moreover my IDE does not crash when I enter a new EditMask for the MaskEdit in the design-time.

My system is ubuntu 9.04 x64, gtk 2.16.1

Bart Broersma

2009-09-20 12:44

developer   ~0030857

Well, what can I say.
Something must be wrong, otherwise there would be no problem here.
If at all usefull, I can mail or attach my executables?
I have maskedit.pp from r18192 in my 0.9.26 (the only alteration I made to 0.9.26) and this one still runs fine.
I would guess that the problem is in the code for GTK2 widgets, probably after r18557.
Might be GTK2 version dependant?

@Theo:
- what version of GTK2 do you have on your Suse 10.0?
- can you run my demo, with the alterations made to maskedit.pp as in note 0030826 (the debugstatements)?

Bart Broersma

2009-09-20 13:34

developer   ~0030859

I have now tested the latest maskedit.pp (r21683) with Lazarus 0.9.26/fpc 2.2.2 on GTK2 and it behaves as expected.

Bart Broersma

2009-09-20 16:20

developer   ~0030860

Following the instructions from http://wiki.lazarus.freepascal.org/How_To_Help_Developing_Lazarus#Dealing_with_regressions I nailed it down to revision 19176.

(Starting from 19151 and 20106 as possible suspects, this took me 11 updates and builds, appr. 3,5 hours.
I did do a make clean all after each update, but I wonder (for the future) if I could not do with updating and building LCL only, if the bug is in LCL?)

theo

2009-09-20 17:45

reporter   ~0030862

Bart my GTK2 Version on Opensuse 10.0 is 2.10.6-24.2
Your prog goes in an infinite loop when I type sth. like 99.99.99 in "Sample Edit Mask", press "Apply Mask" then "Set Text"
After a while, the prog closes. That's is prob. the effect when I said the IDE crashes (meant closes).

AndrewH

2009-09-21 02:50

developer   ~0030866

the program works correctly for me. I am using gtk 2.12. no crashes

Bart Broersma

2009-09-21 13:28

developer   ~0030874

Last edited: 2009-09-21 13:28

Well, at least 2 persons have the infinite loop.
This is a regression introduced in r19176 and IMHO it should be fixed.

Mattias Gaertner

2009-09-21 14:54

manager   ~0030875

I can not reproduce it here.
But I find some unnecessary handle creations and fixed them. That could create anything wrong. So maybe it fixed something.

Mattias Gaertner

2009-09-21 14:57

manager   ~0030877

About the backtrace of (0030828) Bart Broersma:
It shows handle creation. But the handle should already be created. Maybe there was already an exception before?
Please start the IDE in gdb and set "break FPC_RAISEEXCEPTION". Then try to reproduce the crash and create the backtrace.

Bart Broersma

2009-09-21 18:59

developer   ~0030880

@Mattias: I tried that with r19176 (the one that breaks things), but Lazarus then freezes after the splash is shown (when it runs perfectly fine outside gdb).




bart@simenon:~/svnroot/lazarus> gdb ./lazarus
GNU gdb 6.3
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "i586-suse-linux"...Using host libthread_db library "/lib/tls/libthread_db.so.1".

(gdb) break FPC_RAISEEXCEPTION
Breakpoint 1 at 0x8069dd6
(gdb) run
Starting program: /home/bart/svnroot/lazarus/lazarus
[Thread debugging using libthread_db enabled]
[New Thread 1083227456 (LWP 6632)]
TMainIDE.ParseCmdLineOptions:
  PrimaryConfigPath="/home/bart/.lazarus"
  SecondaryConfigPath="/etc/lazarus"

(lazarus:6632): Pango-WARNING **: Invalid UTF-8 string passed to pango_layout_set_text()
Opmerking: Configuratie bestand voor code tools niet gevonden - de standaard wordt gebruikt
NOTE: help options config file not found - using defaults

(lazarus:6632): GLib-GObject-WARNING **: g_object_get_valist: object class `GtkSettings' has no property named `gtk-cursor-blink-timeout'
[Switching to Thread 1083227456 (LWP 6632)]

Breakpoint 1, 0x08069dd6 in fpc_raiseexception ()
(gdb) bt
#0 0x08069dd6 in fpc_raiseexception ()
0000001 0x080eac3e in CLASSES_TFILESTREAM_$__CREATE$ANSISTRING$WORD$$TFILESTREAM ()
0000002 0x082da3f1 in DIRECTORYISWRITABLE (DIRECTORYNAME=0x41afa688)
    at fileprocs.pas:607
0000003 0x082e1f7b in TFILESTATECACHE__DIRECTORYISWRITABLECACHED (
    DIRECTORYNAME=0x41afa688, this=0x4094edd0) at fileprocs.pas:2873
0000004 0x082e1986 in DIRECTORYISWRITABLECACHED (DIRECTORYNAME=0x41afa688)
    at fileprocs.pas:2714
0000005 0x0830e421 in DIRECTORYISWRITABLECACHED (DIRECTORYNAME=0x41afa688)
    at ideprocs.pp:1064
0000006 0x084395f6 in TPKGMANAGER__GETWRITABLEPKGOUTPUTDIRECTORY (
    APACKAGE=0x40b4b9b0, ANOUTDIRECTORY=0x41afa688, this=0x419cc690)
    at pkgmanager.pas:585
0000007 0x083e8c7d in TLAZPACKAGE__GETWRITABLEOUTPUTDIRECTORY (
    ANOUTDIR=0x41afa688, this=0x40b4b9b0) at packagedefs.pas:2202
0000008 0x08373323 in TPARSEDCOMPILEROPTIONS__DOPARSEOPTION (
    OPTIONTEXT=0x419d5fd8, OPTION=PCOSOUTPUTDIR,
    USEGETWRITABLEOUTPUTDIRECTORY=true, PLATFORMINDEPENDENT=false,
    this=0x40d7c0b0) at compileroptions.pp:3138
0000009 0x08372af8 in TPARSEDCOMPILEROPTIONS__GETPARSEDVALUE (
    OPTION=PCOSOUTPUTDIR, this=0x40d7c0b0) at compileroptions.pp:3024
0000010 0x083ed209 in TLAZPACKAGE__GETOUTPUTDIRECTORY (this=0x40b4b9b0)
    at packagedefs.pas:3372
---Type <return> to continue, or q <return> to quit---
0000011 0x083efabc in TLAZPACKAGEDEFINETEMPLATES__UPDATEOUTPUTDIRECTORY (
    this=0x41af9c30) at packagedefs.pas:4092
0000012 0x083ef546 in TLAZPACKAGEDEFINETEMPLATES__PACKAGEIDCHANGED (
    this=0x41af9c30) at packagedefs.pas:3968
0000013 0x083ef661 in TLAZPACKAGEDEFINETEMPLATES__ALLCHANGED (this=0x41af9c30)
    at packagedefs.pas:4008
0000014 0x083efa22 in TLAZPACKAGEDEFINETEMPLATES__SETACTIVE (AVALUE=true,
    this=0x41af9c30) at packagedefs.pas:4082
0000015 0x08431a71 in TLAZPACKAGEGRAPH__ADDPACKAGE (APACKAGE=0x40b4b9b0,
    this=0x40aa7330) at packagesystem.pas:1661
0000016 0x0842cd9a in TLAZPACKAGEGRAPH__OPENDEPENDENCYWITHPACKAGELINK (
    DEPENDENCY=0x41af9af0, PKGLINK=0x419e5710, this=0x40aa7330)
    at packagesystem.pas:479
0000017 0x08437cb1 in TLAZPACKAGEGRAPH__OPENDEPENDENCY (DEPENDENCY=0x41af9af0,
    this=0x40aa7330) at packagesystem.pas:3747
0000018 0x08431f73 in TLAZPACKAGEGRAPH__LOADAUTOINSTALLPACKAGES (
    PKGLIST=0x40a82150, this=0x40aa7330) at packagesystem.pas:1782
0000019 0x0843e13e in TPKGMANAGER__LOADAUTOINSTALLPACKAGES (this=0x419cc690)
    at pkgmanager.pas:1695
0000020 0x0843ee59 in TPKGMANAGER__LOADINSTALLEDPACKAGES (this=0x419cc690)
    at pkgmanager.pas:1894
0000021 0x080940d3 in TMAINIDE__CREATE (THEOWNER=0x40aaf010, vmt=0x86d5fd8,
    this=0x40d6b510) at main.pp:1190
---Type <return> to continue, or q <return> to quit---
0000022 0x0805c7aa in main () at lazarus.pp:103

r19176 runs fine outside gdb, but when I try to set an EditMask in the IDE it goes into an endless loop (no crash).

Anyhow: the backtrace is from Theo, so maybe he can give you a backtrace in the way you described.

Vincent Snijders

2009-09-21 19:09

manager   ~0030881

After this exception, you can continue in gdb until you hit the next exception.

Bart Broersma

2009-09-21 19:15

developer   ~0030883

Last edited: 2009-09-21 19:46

@Mattias:
> I can not reproduce it here.
> But I find some unnecessary handle creations and fixed them. That
> could create anything wrong.

r19176 is about "another trial to fix double raising of OnChange event for TEdit (fixes bug 0013102, bug 0013432)" and concerns changes to TControl.RealSetText, TCustomEdit.CMTextChanged and TCustomEdit.TextChanged.

The problem I described (endless loop) also has to do with TextChanged, which is overridden for TMaskEdit, so it seems not unlikely that these changes are involved in the problem?

Bart Broersma

2009-09-21 20:37

developer   ~0030887

@Mattias:
> So maybe it fixed something.
r21800 now totally breaks things.

If I set EditMask to cccccc then set text to ABCDEF then the text in the control now is ABCDEF______
Note this text is longer then the (internal) mask, and cannot be reset anymore, so any DoExit will trigger an Exception (EDBEditError) now.

This is what happens now if in the example program you set the mask, then press the "Set Text" button:

TMaskEdit.SetInheritedText:
 [SIT] Value = ABCDEF
 [SIT] Setting OldChangeAllowed := True
 [SIT] Setting FChangeAllowed := True
 [SIT] do: inherited Text := ABCDEF
TMaskEdit.TextChanged (control is masked):
 [TC] FChangeAllowed = True, CurrentText = ABCDEF inherited Text =
 [TC] FChangeAllowed = True --> do: Inherited TextChanged
 [TC] After inherited TextChanged
TMaskEdit.SetInheritedText: <<<<<<<<--------- *********
 [SIT] Value = ______
 [SIT] Setting OldChangeAllowed := True
 [SIT] Setting FChangeAllowed := True
 [SIT] do: inherited Text := ______
TMaskEdit.TextChanged (control is masked):
 [TC] FChangeAllowed = True, CurrentText = ______ inherited Text = ______
 [TC] FChangeAllowed = True --> do: Inherited TextChanged
 [TC] After inherited TextChanged
TMaskEdit.TextChanged End
 [SIT] After: inherited Text := ______
 [SIT] Setting FChangeAllowed := OldChange [True]
TMaskEdit.SetInheritedText End
TMaskEdit.TextChanged End
TMaskEdit.TextChanged (control is masked):
 [TC] FChangeAllowed = True, CurrentText = ______ inherited Text = ABCDEF______
 [TC] FChangeAllowed = True --> do: Inherited TextChanged
 [TC] After inherited TextChanged
TMaskEdit.TextChanged End
TMaskEdit.TextChanged (control is masked):
 [TC] FChangeAllowed = True, CurrentText = ______ inherited Text = ABCDEF______
 [TC] FChangeAllowed = True --> do: Inherited TextChanged
 [TC] After inherited TextChanged
TMaskEdit.TextChanged End
 [SIT] After: inherited Text := ABCDEF
 [SIT] Setting FChangeAllowed := OldChange [False]
TMaskEdit.SetInheritedText End



I think teh clue to solving the problem is ton know why SetInheritedText is called in the line marked with "<<<<<<<<--------- *********" above.

  if FChangeAllowed then
  begin
    DebugLn(' [TC] FChangeAllowed = True --> do: Inherited TextChanged');
    Inherited TextChanged;
    DebugLn(' [TC] After inherited TextChanged');

>>>>> after this we should end up

[skipped code that should not executed here, since FChangeAllowed = True]

>>>>>>>>>>> here!
  if (inherited Text = '') then Clear;
  debugln('TMaskEdit.TextChanged End ');
end;

There is nothing in the code AFAICS that explains why there is a second call to SetInheritedText.
I checked that it is NOT invoked by a second call to SetText in this case.
SetInheritedText is only called from within TMaskEdit and it cannot be invoked from any of it's ancesters.

The logic behind the SetInheritedText / TextChanged combination is as follows:

If user enters text (keyboard)
SetIneritedText is called
Change is allowed
Inherited Text is set
This triggers TextChanged
Change is allowed, so nothing else happens in TextChanged
Return to SetInheritedtText
Reset flag to false

If Text is changed by paste/clear/drag via contextmenu or mouse
(in effect this means any alteration to the text that does not occur via SetInheritedText method)

TextChanged is called
Change is not allowed (flag is only set to true in SetInheritedText)
SetInheritedText is called with CurrentText (our Backup) as parameter
This sets flag to allow Change
Sets Inherited Text
TextChanged is called again
This time with flag set to allow change
Nothing else happens in TextChanged

No need for OldChange := FChangeAllowed if this logic is OK

Maybe the logic is wrong??

Mattias Gaertner

2009-09-21 22:52

manager   ~0030894

I added some debugging. Please update to svn 21809 and compile the LCL with
-dVerboseTWinControlRealText
Then reproduce the bug.

Bart Broersma

2009-09-22 08:30

developer   ~0030897

> compile the LCL with -dVerboseTWinControlRealText
How do I do that exactly?

Paul Ishenin

2009-09-22 09:33

manager   ~0030898

In the configure build lazarus dialog add -dVerboseTWinControlRealText to the options field and select build lcl.

Bart Broersma

2009-09-22 10:22

developer   ~0030900

(copied from note 0030887)
TMaskEdit.SetInheritedText:
 [SIT] Value = ABCDEF
 [SIT] Setting OldChangeAllowed := True
 [SIT] Setting FChangeAllowed := True
 [SIT] do: inherited Text := ABCDEF
TMaskEdit.TextChanged (control is masked):
 [TC] FChangeAllowed = True, CurrentText = ABCDEF inherited Text =

Why is the value of inherited Text an empty string here?
This is _after_ we set Inherited Text to 'ABCDEF'
(And even if that change had not taken place yet for some reason, then the value of inherited Text should be '______')

In TCustomMaskEdit we introduced new getters and setters for the Text property.
These are called SetText and GetText, but they are not an overridden form of the ancester, since these are not declared as virtual and cannot be overridden.
Both the new GetText and SetText rely on the value of inherited Text at some point.
Is this construction valid, or can it introduce strange things like this?

Bart Broersma

2009-09-22 10:25

developer   ~0030901

@Vincent:
> After this exception, you can continue in gdb until you hit
> the next exception.
Sorry for asking, but what to type then in gdb to continue?

Bart Broersma

2009-09-22 17:39

developer   ~0030912

r21819 with -dVerboseTWinControlRealText:

After setting EditMask to cccccc
(Text in EditMask now is '______')
Then pressed Set Text button

(I removed OnChange from MaskEdit1 in mainform to avoid cluttering)
(with some comments added)

GTK2

TWinControl.RealGetText Edit4:TEdit HandleAllocated=True csLoading=False
TWinControl.RealGetText using (V)TGtk2WSCustomEdit GetText
TWinControl.RealGetText Result="ABCDEF"
^^ just reading the value of Edit4 on mainform


TMaskEdit.SetText
TMaskEdit.SetInheritedText:
 [SIT] Value = ABCDEF
TWinControl.RealGetText MaskEdit1:TMaskEdit HandleAllocated=True csLoading=False
TWinControl.RealGetText using (V)TGtk2WSCustomEdit GetText
TWinControl.RealGetText Result="______"
TWinControl.RealGetText MaskEdit1:TMaskEdit HandleAllocated=True csLoading=False
TWinControl.RealGetText using (V)TGtk2WSCustomEdit GetText
TWinControl.RealGetText Result="______"

^^ 2 calls to inherited Text in [SIT]

 [SIT] Setting OldChangeAllowed := True
 [SIT] Setting FChangeAllowed := True
 [SIT] do: inherited Text := ABCDEF
TWinControl.RealGetText MaskEdit1:TMaskEdit HandleAllocated=True csLoading=False
TWinControl.RealGetText using (V)TGtk2WSCustomEdit GetText
TWinControl.RealGetText Result="______"

^^ not sure why there is a call to TWinControl.RealGetText here

TWinControl.RealSetText MaskEdit1:TMaskEdit AValue="ABCDEF" HandleAllocated=True csLoading=False
TGtkWSCustomEdit.SetText START MaskEdit1:TMaskEdit AText="ABCDEF"


  now executing
  gtk_entry_set_text(PGtkEntry(AWinControl.Handle), PChar(AText));
  which triggers TextChanged ?

TMaskEdit.TextChanged (control is masked):
 [TC] FChangeAllowed = True, CurrentText = ABCDEF
TWinControl.RealGetText MaskEdit1:TMaskEdit HandleAllocated=True csLoading=False
TWinControl.RealGetText using (V)TGtk2WSCustomEdit GetText
TWinControl.RealGetText Result=""
TWinControl.RealGetText MaskEdit1:TMaskEdit HandleAllocated=True csLoading=False
TWinControl.RealGetText using (V)TGtk2WSCustomEdit GetText
TWinControl.RealGetText Result=""

^^ This is WRONG at this point it should be "ABCDEF"

 [TC] inherited Text = EditText =
 [TC] FChangeAllowed = True --> do: Inherited TextChanged
 [TC] After inherited TextChanged
TWinControl.RealGetText MaskEdit1:TMaskEdit HandleAllocated=True csLoading=False
TWinControl.RealGetText using (V)TGtk2WSCustomEdit GetText
TWinControl.RealGetText Result=""
TMaskEdit.SetInheritedText:
 [SIT] Value = ______
TWinControl.RealGetText MaskEdit1:TMaskEdit HandleAllocated=True csLoading=False
TWinControl.RealGetText using (V)TGtk2WSCustomEdit GetText
TWinControl.RealGetText Result=""
TWinControl.RealGetText MaskEdit1:TMaskEdit HandleAllocated=True csLoading=False
TWinControl.RealGetText using (V)TGtk2WSCustomEdit GetText
TWinControl.RealGetText Result=""
 [SIT] Setting OldChangeAllowed := True
 [SIT] Setting FChangeAllowed := True
 [SIT] do: inherited Text := ______
TWinControl.RealGetText MaskEdit1:TMaskEdit HandleAllocated=True csLoading=False
TWinControl.RealGetText using (V)TGtk2WSCustomEdit GetText
TWinControl.RealGetText Result=""
TWinControl.RealSetText MaskEdit1:TMaskEdit AValue="______" HandleAllocated=True csLoading=False
TGtkWSCustomEdit.SetText START MaskEdit1:TMaskEdit AText="______"
TMaskEdit.TextChanged (control is masked):
 [TC] FChangeAllowed = True, CurrentText = ______
TWinControl.RealGetText MaskEdit1:TMaskEdit HandleAllocated=True csLoading=False
TWinControl.RealGetText using (V)TGtk2WSCustomEdit GetText
TWinControl.RealGetText Result="______"
TWinControl.RealGetText MaskEdit1:TMaskEdit HandleAllocated=True csLoading=False
TWinControl.RealGetText using (V)TGtk2WSCustomEdit GetText
TWinControl.RealGetText Result="______"
 [TC] inherited Text = ______ EditText = ______
 [TC] FChangeAllowed = True --> do: Inherited TextChanged
 [TC] After inherited TextChanged
TWinControl.RealGetText MaskEdit1:TMaskEdit HandleAllocated=True csLoading=False
TWinControl.RealGetText using (V)TGtk2WSCustomEdit GetText
TWinControl.RealGetText Result="______"
TMaskEdit.TextChanged End
TGtkWSCustomEdit.SetText END MaskEdit1:TMaskEdit New="______"
TControl.RealSetText
TWinControl.RealSetText MaskEdit1:TMaskEdit END
 [SIT] After: inherited Text := ______
 [SIT] Setting FChangeAllowed := OldChange [True]
TMaskEdit.SetInheritedText End
TMaskEdit.TextChanged End
TMaskEdit.TextChanged (control is masked):
 [TC] FChangeAllowed = True, CurrentText = ______
TWinControl.RealGetText MaskEdit1:TMaskEdit HandleAllocated=True csLoading=False
TWinControl.RealGetText using (V)TGtk2WSCustomEdit GetText
TWinControl.RealGetText Result="ABCDEF______"
TWinControl.RealGetText MaskEdit1:TMaskEdit HandleAllocated=True csLoading=False
TWinControl.RealGetText using (V)TGtk2WSCustomEdit GetText
TWinControl.RealGetText Result="ABCDEF______"
 [TC] inherited Text = ABCDEF______ EditText = ABCDEF______
 [TC] FChangeAllowed = True --> do: Inherited TextChanged
 [TC] After inherited TextChanged
TWinControl.RealGetText MaskEdit1:TMaskEdit HandleAllocated=True csLoading=False
TWinControl.RealGetText using (V)TGtk2WSCustomEdit GetText
TWinControl.RealGetText Result="ABCDEF______"
TMaskEdit.TextChanged End
TGtkWSCustomEdit.SetText END MaskEdit1:TMaskEdit New="ABCDEF______"
TControl.RealSetText
TMaskEdit.TextChanged (control is masked):
 [TC] FChangeAllowed = True, CurrentText = ______
TWinControl.RealGetText MaskEdit1:TMaskEdit HandleAllocated=True csLoading=False
TWinControl.RealGetText using (V)TGtk2WSCustomEdit GetText
TWinControl.RealGetText Result="ABCDEF______"
TWinControl.RealGetText MaskEdit1:TMaskEdit HandleAllocated=True csLoading=False
TWinControl.RealGetText using (V)TGtk2WSCustomEdit GetText
TWinControl.RealGetText Result="ABCDEF______"
 [TC] inherited Text = ABCDEF______ EditText = ABCDEF______
 [TC] FChangeAllowed = True --> do: Inherited TextChanged
 [TC] After inherited TextChanged
TWinControl.RealGetText MaskEdit1:TMaskEdit HandleAllocated=True csLoading=False
TWinControl.RealGetText using (V)TGtk2WSCustomEdit GetText
TWinControl.RealGetText Result="ABCDEF______"
TMaskEdit.TextChanged End
TWinControl.RealSetText MaskEdit1:TMaskEdit END
 [SIT] After: inherited Text := ABCDEF
 [SIT] Setting FChangeAllowed := OldChange [False]
TMaskEdit.SetInheritedText End


And to compare: GTK1

TMaskEdit.SetInheritedText:
 [SIT] Value = ABCDEF
TWinControl.RealGetText MaskEdit1:TMaskEdit HandleAllocated=True csLoading=False
TWinControl.RealGetText using (V)TGtkWSCustomEdit GetText
TWinControl.RealGetText Result="______"
TWinControl.RealGetText MaskEdit1:TMaskEdit HandleAllocated=True csLoading=False
TWinControl.RealGetText using (V)TGtkWSCustomEdit GetText
TWinControl.RealGetText Result="______"
 [SIT] Setting OldChangeAllowed := True
 [SIT] Setting FChangeAllowed := True
 [SIT] do: inherited Text := ABCDEF
TWinControl.RealGetText MaskEdit1:TMaskEdit HandleAllocated=True csLoading=False
TWinControl.RealGetText using (V)TGtkWSCustomEdit GetText
TWinControl.RealGetText Result="______"
TWinControl.RealSetText MaskEdit1:TMaskEdit AValue="ABCDEF" HandleAllocated=True csLoading=False
TGtkWSCustomEdit.SetText START MaskEdit1:TMaskEdit AText="ABCDEF"
TMaskEdit.TextChanged (control is masked):
 [TC] FChangeAllowed = True, CurrentText = ABCDEF
TWinControl.RealGetText MaskEdit1:TMaskEdit HandleAllocated=True csLoading=False
TWinControl.RealGetText using (V)TGtkWSCustomEdit GetText
TWinControl.RealGetText Result="ABCDEF"
TWinControl.RealGetText MaskEdit1:TMaskEdit HandleAllocated=True csLoading=False
TWinControl.RealGetText using (V)TGtkWSCustomEdit GetText
TWinControl.RealGetText Result="ABCDEF"
 [TC] inherited Text = ABCDEF EditText = ABCDEF
 [TC] FChangeAllowed = True --> do: Inherited TextChanged
 [TC] After inherited TextChanged
TWinControl.RealGetText MaskEdit1:TMaskEdit HandleAllocated=True csLoading=False
TWinControl.RealGetText using (V)TGtkWSCustomEdit GetText
TWinControl.RealGetText Result="ABCDEF"
TMaskEdit.TextChanged End
TGtkWSCustomEdit.SetText END MaskEdit1:TMaskEdit New="ABCDEF"
TControl.RealSetText
TMaskEdit.TextChanged (control is masked):
 [TC] FChangeAllowed = True, CurrentText = ABCDEF
TWinControl.RealGetText MaskEdit1:TMaskEdit HandleAllocated=True csLoading=False
TWinControl.RealGetText using (V)TGtkWSCustomEdit GetText
TWinControl.RealGetText Result="ABCDEF"
TWinControl.RealGetText MaskEdit1:TMaskEdit HandleAllocated=True csLoading=False
TWinControl.RealGetText using (V)TGtkWSCustomEdit GetText
TWinControl.RealGetText Result="ABCDEF"
 [TC] inherited Text = ABCDEF EditText = ABCDEF
 [TC] FChangeAllowed = True --> do: Inherited TextChanged
 [TC] After inherited TextChanged
TWinControl.RealGetText MaskEdit1:TMaskEdit HandleAllocated=True csLoading=False
TWinControl.RealGetText using (V)TGtkWSCustomEdit GetText
TWinControl.RealGetText Result="ABCDEF"
TMaskEdit.TextChanged End
TWinControl.RealSetText MaskEdit1:TMaskEdit END
 [SIT] After: inherited Text := ABCDEF
 [SIT] Setting FChangeAllowed := OldChange [False]
TMaskEdit.SetInheritedText End


So inside TextChanged the return value of TWincontrol.RealGetText seems to be wrong?

Mattias Gaertner

2009-09-22 18:12

manager   ~0030914

Then it means that your gtk library triggers the event before it changes the text.
In 21823 I changed the gtk signal to after.
Please update svn, compile again with -dVerboseTWinControlRealText and test.

Bart Broersma

2009-09-22 20:24

developer   ~0030916

I think I solved it (?)

2 problems:

TGtkWSCustomEdit.SetText calls
  gtk_entry_set_text(PGtkEntry(AWinControl.Handle), PChar(AText));

This first seems to clear the control, triggering an TextChanged
then it sets the Text in the control, triggering an second TextChanged

So 2 instead of 1 expected TextChanged

When it clears the control Inherited text = "" (empty string)
which then caused
  if (inherited Text = '') then Clear;
to be executed, which eventually calls TGtkWSCustomEdit.SetText and this clears the control etc. etc.

This line is not necessary there, so i commented it out

After that maskedit seemed to work just fine.


I wrote a simple testcase, that shows that TGtkWSCustomEdit.SetText first clears the control, then sets the text and 2 consecutive TextChanges are fired.
This behaviour might well be GTK version dependent? which would explain why not many people could reproduce.

However it does not explain why this broke on r19176 !



I'll upload my testcase program (myedit.tgz) so you can see.

On my system if the text in the upper edit ( TMyEdit) = 'ABCDEF' and the text in the lower edit = '12346' then the output after prressing the "Set Text" button is:

Clicked on Button Set Text

TMyedit.SetText START
  [ST] Value = "123456"
  [ST] do SetInheritedText("123456")
TMyEdit.SetInheritedText
  [SIT] Value = "123456"
  [SIT] Set FChangeAllowed := True
  [SIT] before inherited Text := "123456"
TGtkWSCustomEdit.SetText START :TMyEdit AText="123456"
TMyedit.TextChanged START
  [TC] FCurrentText = 123456 inherited Text = ""

^^ it looks like the control is cleared first

  [TC] FChangeAllowed = True
  [TC] FChangeAllowed = True --> do: Inherited TextChanged
  [TC] After inherited TextChanged
TMyedit.TextChanged END
TMyedit.TextChanged START
  [TC] FCurrentText = 123456 inherited Text = "123456"

^^ and now it is set with the desired value

  [TC] FChangeAllowed = True
  [TC] FChangeAllowed = True --> do: Inherited TextChanged
  [TC] After inherited TextChanged
TMyedit.TextChanged END
TGtkWSCustomEdit.SetText END :TMyEdit New="123456"
  [SIT] after inherited Text := "123456"
  [SIT] Set FChangeAllowed := False
TMyEdit.SetInheritedText END
  [ST] after SetInheritedText("123456")
TMyedit.SetText END


I'll make some minor alterartions later to maskedit.pp and then upload the diff.

2009-09-22 20:26

 

myedit.tgz (3,471 bytes)

Vincent Snijders

2009-09-22 23:05

manager   ~0030920

@Bart Broersma, the gdb command is: c

Paul Ishenin

2009-09-23 01:21

manager   ~0030924

LCL code must be widgetset independent, so please try to avoid workarounds in the LCL. If error exists only in gtk interface then better to fix it only in gtk interface and leave mask edit code as is.

Bart Broersma

2009-09-23 13:21

developer   ~0030934

> LCL code must be widgetset independent, so please try to avoid
> workarounds in the LCL.

In principle I agree, although it seems unlikely that the "TextChanged is fired twice" is gonna be solved in a more definite form in the near future.
It seems to be quite in illusive one ;-)

So even if there was nothing wrong with the code in maskedit.pp I would then propose a workaround, clearly documented as being on in the code.


However the problem is twofold.

1.
TextChanged is called twice in TCustomEdit if property text is set.
On my system the first TextChanged has the Text property set to an empty string, but this seems not to be the case with others?

2.
The line
  if (inherited Text = '') then Clear;
should not be in TextChanged (it is a remnant from the past) dating from before my major rewrite.
At this point in code the text in the control should never be empty (but sadly is so because of problem 1.)



1 + 2 creates the infinite loop.

So while issue 1 should be solved, there is no need to not remove wrong code from 2.

Also the more recent changes made by Mattias (IIRC) with saving FChangeAllowed seems to be unneccesary and possibly harmful.
(if OldChange evaluates to True, then any next change will be allowed, which is wrong, which I discussed in note 0030887).

Also there seems to me no need for
  try
    inherited Text := Value
  finally
    FChangeAllowed := OldChange; //or False for that matter
  end;

(Mattias: feel free to comment)

So I propose to change that back to

  FChangeAllowed := True;
  inherited text := Value;
  FChangeAllowed := False;


Then there is one (unrelated) cleanup.
Setting FCurrentText can be moved to one location only: SetIheritedText.

As a final observation:
The construct using FChangeAllowed in theory could be replaced by 1 check in TextChanged: if (FCurrentText <> inherited Text) then revert the text change.
For the time being however, this is not possible due to problem 1.

So if we can agree on this, I will make these three changes and upload the diff.

After that we can close this issue and re-open 0013102 instead?

Mattias Gaertner

2009-09-23 13:53

manager   ~0030936

FChangeAllowed is now set to false. revision 21834.

Paul Ishenin

2009-09-23 14:05

manager   ~0030937

Bart, maybe you will fix gtk problems better?

Mattias Gaertner

2009-09-23 14:08

manager   ~0030938

Last edited: 2009-09-23 14:09

About firing twice:
I locked the gtk event during set and the event is now send afterwards in revision 21836.
Please test.

Bart Broersma

2009-09-23 23:28

developer   ~0030951

@Paul: I'm afraid that's gonna require far better skills than I have.
Whenever things are at widgetset level, I almost always loose track...

Sorry if I offended any of you, that was never my intention.

Bart Broersma

2009-09-24 00:34

developer   ~0030952

Last edited: 2009-09-24 11:34

@Mattias: r21841 now fires one TextChanged, (GTK & GTK2)

I made some further modifications to maskedit, because it used to do 2 textchanges (and so 2 times TextChanged) if you typed in the control.
(No relation to the endless loop I experienced)

Will test my latest changes later on win32 and GTK1, then post my diff so (any of) you may comment.
I'm going to sleep now.

Bart Broersma

2009-09-24 22:19

developer   ~0030963

Mattias:

As I come to think (and sleep) about it some more,

  try
    inherited Text := Value
  finally
    FChangeAllowed := False;
  end;

is actually better. If in a users OnChange event un unhandled exception occurs, the control is in an "unsafe" state if FChangeAllowed is not set back to False.

2009-09-24 22:54

 

maskedit.pp.fix-endless-loop-and-cleanup.diff (7,698 bytes)   
Index: lcl/maskedit.pp
===================================================================
--- lcl/maskedit.pp	(revision 21841)
+++ lcl/maskedit.pp	(working copy)
@@ -118,12 +118,12 @@
  Since we want total control over anything that is done to the text in the control
  we have to take into consideration the fact that currently we cannot prevent
  cutting/pasting/clearing or dragging selected text in the control, these are handled by the OS
- and text is changed before can prevent it.
+ and text is changed before we can prevent it.
  Not all widgetsets currently handle the messages for cut/paste/clear. Actually we would
  like to have a LM_BEFORE_PASTE (etc.) message...
  If we allow the OS to cut/clear/paste etc. a situation can occur where mask-literals in the
  control are changed with random chars (and cannot be undone) or text is shorter or larger than
- the editmask calls for, whic again cannot be undone.
+ the editmask calls for, which again cannot be undone.
 
 
  So, as a horrible hack I decided  to only allow changing of the text if we coded
@@ -131,11 +131,13 @@
  write action (in SetInherited Text() ).
  We try to intercept the messages for cut/paste/copy/clear and perform the appropriate
  actions instead.
- If this fails, then in TextChanged we check will see that FChangeAllowed = False
+ If this fails, then in TextChanged we check and will see that FChangeAllowed = False
  and we will undo the changes made.
 
- To make this undo possible it is necessary to set CurrentText every time you set
+ To make this undo possible it is necessary to set FCurrentText every time you set
  the text in the control!
+ This is achieved in SetInheritedText() only, so please note:
+ !! It is unsafe to make changes to inherited Text unless done so via SetInheritedText() !!!
 
  (Bart Broersma, januari 2009)
 
@@ -184,7 +186,6 @@
     Function  CanInsertChar(Position : Integer; Var Ch : Char) : Boolean;
     procedure DeleteSelected;
     procedure DeleteChars(NextChar : Boolean);
-    //Function  SearchDeletedText : Boolean;
   protected
     Function  GetText : String;
     Procedure SetText(Value : String);
@@ -321,7 +322,7 @@
   FMaskSave      := True;
   FChangeAllowed := False;
   FTrimType      := metTrimRight;
-  FCurrentText    := Inherited Text;
+  FCurrentText   := Inherited Text;
   FTextOnEnter   := Inherited Text;
   FInitialText   := '';
   FInitialMask   := '';
@@ -617,12 +618,9 @@
   HasNextDot, HasCommaAndPeriod, CanJump: Boolean;
   P, P2: Integer;
 begin
-  //DebugLn('TCustomMaskEdit.JumpToNextDot A');
-  //DebugLn('  Dot = ',Dot);
   if not (Dot in [Period, Comma]) then Exit;
   P := PosEx(Dot, FMask, FCursorPos + 1);
   HasNextDot := P > 0;
-  //DebugLn('  HasNextDot = ',DbgS(HasNextDot));
   If (Dot = Period) then
   begin
     P2 := Pos(Comma, FMask);
@@ -633,24 +631,17 @@
     P2 := Pos(Period, FMask);
     HasCommaAndPeriod := HasNextDot and (P2 >0);
   end;
-  //DebugLn('  HasCommaAndPeriod = ',DbgS(HasCommaAndPeriod));
-  //DebugLn('  FCursorPos = ',DbgS(FCursorPos));
-  //DebugLn('  P  = ',DbgS(P));
-  //DebugLn('  P2 = ',DbgS(P));
   if HasCommaAndPeriod then
   begin
     //When mask has both period and comma only the first occurence is jumpable
     if P2 < P then HasNextDot := False;
-    //DebugLn('  Recalc: HasNextDot = ',DbgS(HasNextDot));
   end;
   CanJump := HasNextDot and (P < Length(FMask)) and (not IsLiteral(FMask[P+1]));
-  //DebugLn('  CanJump := ',DbgS(CanJump));
   if CanJump then
   begin
     FCursorPos := P;
     SetCursorPos;
   end;
-  //DebugLn('TCustomMaskEdit.JumpToNextDot B');
 end;
 
 function TCustomMaskEdit.HasSelection: Boolean;
@@ -782,14 +773,17 @@
 //Set text in the control with FChangeAllowed flag set appropriately
 procedure TCustomMaskEdit.SetInheritedText(const Value: String);
 begin
-  if Value <> Inherited Text then
+  if (Value <> Inherited Text) then
   begin
     FChangeAllowed := True;
+    FCurrentText := Value;
+    //protect resetting FChangeAllowed := False against unhandled exceptions in user's
+    //OnChange, otherwise risk leaving the control in an "unsafe" state regarding text changes
     try
       Inherited Text := Value;
     finally
       FChangeAllowed := False;
-    end;
+    end;//finally
   end;
 end;
 
@@ -833,18 +827,25 @@
 procedure TCustomMaskEdit.InsertChar(Ch : Char);
 Var
   S    : ShortString;
+  i, SelectionStart, SelectionStop: Integer;
 begin
   if CanInsertChar(FCursorPos + 1, Ch) then
   begin
-    DeleteChars(True);
-    S    := Inherited Text;
+    S := Inherited Text;
+    if HasSelection then
+    begin
+      //replace slection with blank chars
+      //don't do this via DeleteChars(True), since it will do un unneccesary
+      //update of the control and 2 TextChanged's are triggerd for every char we enter
+      GetSel(SelectionStart, SelectionStop);
+      for i := SelectionStart + 1 to SelectionStop do S[i] := ClearChar(i);
+    end;
     S[FCursorPos + 1] := Ch;
-    FCurrentText := S;
     SetInheritedText(S);
     SelectNextChar;
   end
   else
-  //If we have a selcetion (> 1) then Delete the selected text: Delphi compatibility
+  //If we have a selection > 1 (and cannot insert) then Delete the selected text: Delphi compatibility
   if HasExtSelection then DeleteSelected;
 end;
 
@@ -919,7 +920,6 @@
   GetSel(SelectionStart, SelectionStop);
   S := Inherited Text;
   for i := SelectionStart + 1 to SelectionStop do S[i] := ClearChar(i);
-  FCurrentText := S;
   SetInheritedText(S);
   SetCursorPos;
 end;
@@ -1040,7 +1040,6 @@
     begin
       if Length(Value) > Length(FMask) then Value := Copy(Value, 1, Length(FMask));
       while (Length(Value) < Length(FMask)) do Value := Value + FSpaceChar;
-      FCurrentText := Value;
       SetInheritedText(Value);
       Exit;
     end;
@@ -1067,8 +1066,6 @@
       Inc(i);
       Inc(j);
     end;
-
-    FCurrentText := S;
     SetInheritedText(S);
   end//Ismasked
   else
@@ -1098,7 +1095,6 @@
     //Make sure we don't copy more or less text into the control than FMask allows for
     S := Copy(AValue, 1, Length(FMask));
     while Length(S) < Length(FMask) do S := S + FSpaceChar;
-    FCurrentText := S;
     SetInheritedText(S);
   end;
 end;
@@ -1112,7 +1108,7 @@
   - dragging selected text in the control with the mouse
   If one of these happens, then the internal logic of cursorpositioning,
   inserting characters is messed up.
-  So, we simply restore the text from our backup: CurrenText
+  So, we simply restore the text from our backup: FCurrenText
 }
 begin
   if not IsMasked then
@@ -1122,16 +1118,16 @@
   end;
   if FChangeAllowed then
   begin
-    Inherited TextChanged
+    Inherited TextChanged;
   end
   else
-  //if not FChangeAllowed then
-  begin
+  begin//Undo changes: restore with value of FCurrentText
+    //we do not call inherited TextChanged here, because the following SetInheritedText
+    //will trigger TextChanged with FChangeAllowed = True and inherited TextChanged is called then
     SetInheritedText(FCurrentText);
     //Reset cursor to last known position
     SetCursorPos;
   end;
-  if (inherited Text = '') then Clear;
 end;
 
 procedure TCustomMaskEdit.Loaded;
@@ -1196,7 +1192,6 @@
       end;
     end;
     S := StringReplace(S, #32, FSpaceChar, [rfReplaceAll]);
-    FCurrentText := S;
     SetInheritedText(S);
   end//Ismasked
   else
@@ -1540,7 +1535,6 @@
        else
          Break;
      end;
-     FCurrentText := S;
      SetInheritedText(S);
      SetCursorPos;
    end;
@@ -1558,7 +1552,6 @@
   begin
     S  := '';
     for I := 1 To Length(FMask) do S := S + ClearChar(I);
-    FCurrentText := S;
     SetinheritedText(S);
     FCursorPos := 0;
     SetCursorPos;

Bart Broersma

2009-09-24 22:57

developer   ~0030964

Here's my diff for maskedit.pp.

It mainly does the following:

procedure TextChanged
- removed unneccessary code that caused endless loop (described in 0014165)
 
procedure InsertChar
- only do virtual clearing of selected text, thus avoiding unneccessary TextChanged when inserting characters in the control
 
Cleanups
- save FCurrentText in one central place (SetInheritedText) to make cleaner code
- updated comments and fixed some typo's
- removed several debugstatements

Mattias Gaertner

2009-09-24 23:20

manager   ~0030965

procedure TextChanged
- removed unneccessary code that caused endless loop (described in 0014165)

I don't understand what 0014165 has to do with that.

I thought the endless loop is caused by the double gtk2 event.
This was fixed. Why is there still an endless loop?

Bart Broersma

2009-09-25 08:36

developer   ~0030968

Mattias: sorry, 14165 should of course be 14615 (this bugreport), it's a simple typo from me.

Yes, the double gtk2 event is fixed, and after that there was no more endless loop, so the description should be
- removed unneccessary code

(Maybe I should consider not typing in the bugtracker anymore after 22:00 ??)

Vincent Snijders

2009-09-25 21:18

manager   ~0030980

This part seems new, not part of the cleanup, correct?
+ if HasSelection then
+ begin
+ //replace selection with blank chars
+ //don't do this via DeleteChars(True), since it will do un unneccesary
+ //update of the control and 2 TextChanged's are triggerd for every char we enter
+ GetSel(SelectionStart, SelectionStop);
+ for i := SelectionStart + 1 to SelectionStop do S[i] := ClearChar(i);
+ end;

Bart Broersma

2009-09-25 22:37

developer   ~0030982

Yes it's new.
After Mattias resolved the double TextChanged (GTK2) from TCustomEdit, I noticed TMaskEdit also had 2 TextChanged every time you typed in the control, so I fixed that.

Should I seperate that one from the rest and make a seperate diff, or even make a new issue in the bugtracker for it?
I'm not quite sure what the appropriate "etiquette" is.

Vincent Snijders

2009-09-26 14:39

manager   ~0030989

I applied the last patch.

Issue History

Date Modified Username Field Change
2009-09-17 19:26 Bart Broersma New Issue
2009-09-17 19:26 Bart Broersma LazTarget => -
2009-09-17 19:26 Bart Broersma Widgetset => GTK 2
2009-09-18 15:55 Vincent Snijders LazTarget - => 0.9.28
2009-09-18 15:55 Vincent Snijders Note Added: 0030810
2009-09-18 15:55 Vincent Snijders Status new => feedback
2009-09-18 15:55 Vincent Snijders Target Version => 0.9.28
2009-09-18 16:15 Paul Ishenin Note Added: 0030813
2009-09-18 21:33 Bart Broersma Note Added: 0030824
2009-09-18 22:36 theo Note Added: 0030825
2009-09-18 22:50 Bart Broersma File Added: maskedit.gtk2_bug.tgz
2009-09-18 23:06 Bart Broersma Note Added: 0030826
2009-09-18 23:08 Bart Broersma Note Edited: 0030826
2009-09-18 23:09 Bart Broersma Note Edited: 0030826
2009-09-18 23:27 Vincent Snijders Assigned To => Paul Ishenin
2009-09-18 23:27 Vincent Snijders Status feedback => assigned
2009-09-18 23:34 Bart Broersma Note Added: 0030828
2009-09-19 10:26 Paul Ishenin Note Added: 0030831
2009-09-19 10:28 Paul Ishenin Note Added: 0030832
2009-09-19 10:29 Paul Ishenin Status assigned => feedback
2009-09-20 12:44 Bart Broersma Note Added: 0030857
2009-09-20 13:34 Bart Broersma Note Added: 0030859
2009-09-20 16:20 Bart Broersma Note Added: 0030860
2009-09-20 17:45 theo Note Added: 0030862
2009-09-21 02:50 AndrewH Note Added: 0030866
2009-09-21 13:28 Bart Broersma Note Added: 0030874
2009-09-21 13:28 Bart Broersma Note Edited: 0030874
2009-09-21 14:54 Mattias Gaertner Note Added: 0030875
2009-09-21 14:57 Mattias Gaertner Note Added: 0030877
2009-09-21 18:59 Bart Broersma Note Added: 0030880
2009-09-21 19:09 Vincent Snijders Note Added: 0030881
2009-09-21 19:15 Bart Broersma Note Added: 0030883
2009-09-21 19:46 Bart Broersma Note Edited: 0030883
2009-09-21 20:37 Bart Broersma Note Added: 0030887
2009-09-21 22:52 Mattias Gaertner Note Added: 0030894
2009-09-22 08:30 Bart Broersma Note Added: 0030897
2009-09-22 09:33 Paul Ishenin Note Added: 0030898
2009-09-22 10:22 Bart Broersma Note Added: 0030900
2009-09-22 10:25 Bart Broersma Note Added: 0030901
2009-09-22 17:39 Bart Broersma Note Added: 0030912
2009-09-22 18:12 Mattias Gaertner Note Added: 0030914
2009-09-22 20:24 Bart Broersma Note Added: 0030916
2009-09-22 20:26 Bart Broersma File Added: myedit.tgz
2009-09-22 23:05 Vincent Snijders Note Added: 0030920
2009-09-23 01:21 Paul Ishenin Note Added: 0030924
2009-09-23 13:21 Bart Broersma Note Added: 0030934
2009-09-23 13:53 Mattias Gaertner Note Added: 0030936
2009-09-23 14:05 Paul Ishenin Note Added: 0030937
2009-09-23 14:08 Mattias Gaertner Note Added: 0030938
2009-09-23 14:09 Mattias Gaertner Note Edited: 0030938
2009-09-23 23:28 Bart Broersma Note Added: 0030951
2009-09-24 00:34 Bart Broersma Note Added: 0030952
2009-09-24 11:34 Bart Broersma Note Edited: 0030952
2009-09-24 22:19 Bart Broersma Note Added: 0030963
2009-09-24 22:54 Bart Broersma File Added: maskedit.pp.fix-endless-loop-and-cleanup.diff
2009-09-24 22:57 Bart Broersma Note Added: 0030964
2009-09-24 23:20 Mattias Gaertner Note Added: 0030965
2009-09-25 08:36 Bart Broersma Note Added: 0030968
2009-09-25 21:18 Vincent Snijders Note Added: 0030980
2009-09-25 22:37 Bart Broersma Note Added: 0030982
2009-09-26 14:39 Vincent Snijders Fixed in Revision => 21880
2009-09-26 14:39 Vincent Snijders Status feedback => resolved
2009-09-26 14:39 Vincent Snijders Fixed in Version => 0.9.29 (SVN)
2009-09-26 14:39 Vincent Snijders Resolution open => fixed
2009-09-26 14:39 Vincent Snijders Note Added: 0030989
2009-11-08 11:45 Bart Broersma Status resolved => closed