|
1
|
/*------------------------------------------------------------------------------
|
|
2
|
tc11180d_false_steal.p
|
|
3
|
|
|
4
|
Recreate for review point #1 on the #11180b fix
|
|
5
|
(ThinClient.processProgressEvent, "stolen" predicate).
|
|
6
|
|
|
7
|
boolean stolen = oldFocusGone
|
|
8
|
&& redirected != null
|
|
9
|
&& redirected != src // <-- WRONG operand
|
|
10
|
&& redirected.isDisplayed()
|
|
11
|
&& UiUtils.locateFrame(redirected) != frame;
|
|
12
|
|
|
13
|
The predicate is meant to detect a trigger-REDIRECTED focus (a genuine
|
|
14
|
steal). The correct test - the one FocusManager.handleFocusChange uses
|
|
15
|
(curFocus != oldFocus) and the one both methods' comments describe - is
|
|
16
|
"redirected != inFocus" (inFocus is the analog of oldFocus; src is the
|
|
17
|
analog of newFocus). Using "redirected != src" instead produces a FALSE
|
|
18
|
POSITIVE in the legitimate frame-deletion-WITHOUT-steal case.
|
|
19
|
|
|
20
|
Topology (no steal at all):
|
|
21
|
- focus is on browse W in DYNAMIC frame A (inFocus, oldFrame)
|
|
22
|
- APPLY 'ENTRY' targets fill-in fB in a DIFFERENT, LIVE frame B (src/frame)
|
|
23
|
- frame A's LEAVE trigger DELETEs frame A (oldFocusGone becomes true) but
|
|
24
|
does NOT redirect focus anywhere - it does no APPLY.
|
|
25
|
|
|
26
|
What happens in ThinClient.processProgressEvent:
|
|
27
|
- tryFocusChange(src=fB, .., doEntry=false) returns before requestFocus
|
|
28
|
(doEntry is false) -> focus is NOT moved, still on W.
|
|
29
|
- sendLeave(oldFrame=A, frame=B) fires A's frame LEAVE -> A is deleted;
|
|
30
|
sendLeave never calls requestFocus, so focus stays on W and
|
|
31
|
destroyFrame() defers the hide -> getCurrentFocus() == W, W.isDisplayed().
|
|
32
|
- redirected = getCurrentFocus() == W == inFocus.
|
|
33
|
oldFocusGone = true (A deleted)
|
|
34
|
redirected != src = W != fB = true <-- FALSE POSITIVE
|
|
35
|
(with the correct "redirected != inFocus": W != W = false -> not stolen)
|
|
36
|
redirected.isDisplayed() = true
|
|
37
|
locateFrame(W)=A != frame=B = true
|
|
38
|
=> stolen == true (wrongly) => evt.consume(); return;
|
|
39
|
|
|
40
|
Result: the LEGITIMATE ENTRY into fB is silently dropped - fB's ENTRY
|
|
41
|
trigger never runs - and FOCUS is left parked on the DELETED browse W
|
|
42
|
(the invalid-focus outcome the fix was meant to prevent).
|
|
43
|
|
|
44
|
W is a BROWSE only so that getCurrentFocus() reliably survives A's deletion
|
|
45
|
(a read-only browse remains the focus widget after its frame is destroyed).
|
|
46
|
fB is a plain fill-in, so the both-Browse gate on the spurious-LEAVE emit is
|
|
47
|
NOT satisfied - this isolates the defect to the consume/return itself
|
|
48
|
(point #1), independent of the spurious-LEAVE emit (point #2).
|
|
49
|
|
|
50
|
Expected (OpenEdge / correctly-predicated FWD):
|
|
51
|
fB ENTRY fires, FOCUS ends on fB.
|
|
52
|
Bug (FWD 11180b as reviewed):
|
|
53
|
fB ENTRY never fires; FOCUS parked on the deleted browse.
|
|
54
|
|
|
55
|
FWD drives the whole path synchronously from APPLY "ENTRY", so this runs
|
|
56
|
head-less: log to tc11180d.log, then QUIT.
|
|
57
|
------------------------------------------------------------------------------*/
|
|
58
|
|
|
59
|
DEFINE TEMP-TABLE tt NO-UNDO FIELD nm AS CHARACTER.
|
|
60
|
DEFINE QUERY qA FOR tt SCROLLING.
|
|
61
|
|
|
62
|
DEFINE VARIABLE fB AS CHARACTER NO-UNDO FORMAT "x(8)" INITIAL "bbb".
|
|
63
|
DEFINE VARIABLE seq AS INTEGER NO-UNDO.
|
|
64
|
|
|
65
|
DEFINE VARIABLE hFrmA AS HANDLE NO-UNDO.
|
|
66
|
DEFINE VARIABLE hBrw AS HANDLE NO-UNDO.
|
|
67
|
|
|
68
|
DEFINE FRAME frmB fB WITH TITLE "FRAME-B (legitimate destination)" AT ROW 6 COLUMN 2 SIZE 40 BY 4.
|
|
69
|
|
|
70
|
PROCEDURE logit:
|
|
71
|
DEFINE INPUT PARAMETER m AS CHARACTER NO-UNDO.
|
|
72
|
seq = seq + 1.
|
|
73
|
OUTPUT TO "tc11180d.log" APPEND.
|
|
74
|
PUT UNFORMATTED STRING(seq, "99") " " m SKIP.
|
|
75
|
OUTPUT CLOSE.
|
|
76
|
END PROCEDURE.
|
|
77
|
|
|
78
|
/* Frame A's LEAVE: destroy A synchronously while its own LEAVE runs.
|
|
79
|
CRUCIALLY there is NO APPLY here - focus is NOT redirected/stolen.
|
|
80
|
This is the legitimate frame-deletion-without-steal case. */
|
|
81
|
PROCEDURE aLeave:
|
|
82
|
RUN logit("frmA LEAVE (begin - delete frame A, NO focus steal)").
|
|
83
|
IF VALID-HANDLE(hFrmA) THEN DELETE WIDGET hFrmA.
|
|
84
|
RUN logit("frmA LEAVE (end - no APPLY performed)").
|
|
85
|
END PROCEDURE.
|
|
86
|
|
|
87
|
/* ---- destination frame B (the legitimate ENTRY target that MUST fire) ---- */
|
|
88
|
ON ENTRY OF FRAME frmB
|
|
89
|
RUN logit("frmB ENTRY <== destination FRAME ENTRY (MUST fire)").
|
|
90
|
ON LEAVE OF FRAME frmB
|
|
91
|
RUN logit("frmB LEAVE").
|
|
92
|
ON ENTRY OF fB IN FRAME frmB
|
|
93
|
RUN logit("fB ENTRY <== LEGITIMATE destination ENTRY (MUST fire; dropped by bug)").
|
|
94
|
ON LEAVE OF fB IN FRAME frmB
|
|
95
|
RUN logit("fB LEAVE").
|
|
96
|
|
|
97
|
/* ---- main ---- */
|
|
98
|
OUTPUT TO "tc11180d.log".
|
|
99
|
PUT UNFORMATTED "=== tc11180d_false_steal start ===" SKIP.
|
|
100
|
OUTPUT CLOSE.
|
|
101
|
|
|
102
|
CREATE tt. tt.nm = "row1".
|
|
103
|
CREATE tt. tt.nm = "row2".
|
|
104
|
CREATE tt. tt.nm = "row3".
|
|
105
|
|
|
106
|
ENABLE fB WITH FRAME frmB.
|
|
107
|
VIEW FRAME frmB.
|
|
108
|
|
|
109
|
/* dynamic frame A holding a read-only browse (the source W) */
|
|
110
|
CREATE FRAME hFrmA
|
|
111
|
ASSIGN TITLE = "FRAME-A (browse source)"
|
|
112
|
ROW = 2 COLUMN = 2 WIDTH = 34 HEIGHT = 5
|
|
113
|
BOX = TRUE VISIBLE = FALSE.
|
|
114
|
|
|
115
|
OPEN QUERY qA FOR EACH tt.
|
|
116
|
|
|
117
|
CREATE BROWSE hBrw
|
|
118
|
ASSIGN FRAME = hFrmA
|
|
119
|
QUERY = QUERY qA:HANDLE
|
|
120
|
X = 4 Y = 4 WIDTH = 30 DOWN = 2
|
|
121
|
SEPARATORS = TRUE ROW-MARKERS = FALSE
|
|
122
|
SENSITIVE = TRUE VISIBLE = TRUE.
|
|
123
|
hBrw:ADD-COLUMNS-FROM(BUFFER tt:HANDLE).
|
|
124
|
hBrw:READ-ONLY = TRUE.
|
|
125
|
|
|
126
|
hFrmA:VISIBLE = TRUE.
|
|
127
|
|
|
128
|
/* Register the source-FRAME LEAVE trigger that deletes A (no steal).
|
|
129
|
Hooked on the FRAME (not the browse) so it fires from the fix's
|
|
130
|
sendLeave(oldFrame) at ThinClient ~line 23244, AFTER tryFocusChange -
|
|
131
|
this is what reaches the "stolen" branch. */
|
|
132
|
ON "LEAVE":U OF hFrmA PERSISTENT RUN aLeave.
|
|
133
|
|
|
134
|
RUN logit("--- APPLY ENTRY to browse (land focus in browse source W) ---").
|
|
135
|
APPLY "ENTRY" TO hBrw.
|
|
136
|
|
|
137
|
IF VALID-HANDLE(FOCUS) THEN
|
|
138
|
RUN logit("--- FOCUS before move: TYPE=" + STRING(FOCUS:TYPE)
|
|
139
|
+ " is-browse=" + STRING(FOCUS:TYPE = "BROWSE") + " ---").
|
|
140
|
ELSE
|
|
141
|
RUN logit("--- FOCUS before move: <none> ---").
|
|
142
|
|
|
143
|
RUN logit("--- APPLY ENTRY to fB (legitimate move browse-A -> destination B) ---").
|
|
144
|
APPLY "ENTRY" TO fB IN FRAME frmB.
|
|
145
|
|
|
146
|
RUN logit("--- done; FOCUS is now: "
|
|
147
|
+ (IF VALID-HANDLE(FOCUS) THEN STRING(FOCUS:TYPE) + "/" + FOCUS:NAME
|
|
148
|
ELSE "<none/invalid>") + " ---").
|
|
149
|
RUN logit("--- EXPECT (OE/fixed): fB ENTRY fired, FOCUS=FILL-IN/fB ---").
|
|
150
|
RUN logit("--- BUG (11180b): fB ENTRY absent, FOCUS on deleted browse ---").
|
|
151
|
QUIT.
|