Project

General

Profile

Feature #2162

add support for CURRENT-ITERATION attribute (for widget objects)

Added by Eric Faulhaber over 10 years ago. Updated over 2 years ago.

Status:
New
Priority:
Normal
Assignee:
Vadim Gindin
Target version:
-
Start date:
12/29/2013
Due date:
01/08/2014
% Done:

0%

Estimated time:
0.00 h
billable:
No
vendor_id:
GCD

test_results.png (20.5 KB) Vadim Gindin, 12/31/2013 09:51 AM

widget_attrs.png (28 KB) Vadim Gindin, 01/03/2014 08:13 AM

widget_attrs-1.png (7.43 KB) Vadim Gindin, 01/09/2014 06:06 PM

widget_attrs_new.png (20.7 KB) Vadim Gindin, 01/09/2014 06:11 PM

2162_results.txt Magnifier (2.92 KB) Vadim Gindin, 01/18/2014 04:03 PM

vig_upd20140121a.zip (94.9 KB) Vadim Gindin, 01/20/2014 04:45 PM

2162_no_loop_ihandle.txt Magnifier (1.53 KB) Vadim Gindin, 01/22/2014 06:14 PM

2162_external-scoped-frame-ihandle.txt Magnifier (3.24 KB) Vadim Gindin, 01/22/2014 06:27 PM

2162_clear_frame.txt Magnifier (2.84 KB) Vadim Gindin, 01/25/2014 04:57 PM

2162_clear_frame_i3.txt Magnifier (2.92 KB) Vadim Gindin, 01/25/2014 04:57 PM

2162_ext_frame_stream.txt Magnifier (2.02 KB) Vadim Gindin, 01/27/2014 03:58 PM

2162_up_algo.txt Magnifier (1015 Bytes) Vadim Gindin, 01/29/2014 11:33 AM

up-algo-diff-steps1.txt Magnifier (2.56 KB) Vadim Gindin, 02/01/2014 06:09 AM

up_seq.txt Magnifier (2.23 KB) Vadim Gindin, 02/03/2014 08:49 PM

vig_upd20140227a.zip (131 KB) Vadim Gindin, 02/26/2014 06:35 PM

vig_upd20140306a.zip (144 KB) Vadim Gindin, 03/06/2014 06:23 AM

vig_upd20140314a.zip (149 KB) Vadim Gindin, 03/13/2014 06:12 PM

vig_upd20140322a.zip (150 KB) Vadim Gindin, 03/22/2014 05:53 PM

vig_upd20140324a.zip (147 KB) Vadim Gindin, 03/24/2014 05:53 PM

vig_upd20140330a.zip (148 KB) Vadim Gindin, 03/30/2014 03:46 PM

vig_upd20140401a.zip (148 KB) Vadim Gindin, 04/01/2014 03:42 PM

vig_upd20140403a.zip (148 KB) Vadim Gindin, 04/03/2014 05:47 PM

vig_upd20140505a.zip (148 KB) Vadim Gindin, 05/06/2014 03:32 AM

vig_upd20140507a.zip (149 KB) Vadim Gindin, 05/07/2014 08:28 AM

vig_upd20140507b.zip (150 KB) Constantin Asofiei, 05/07/2014 10:23 AM


Related issues

Related to User Interface - Feature #2490: fix movement of widgets part of a down frame's body New

History

#1 Updated by Eric Faulhaber over 10 years ago

The widget variant of the CURRENT-ITERATION attribute is used by ADM2 (specifically smart.p, procedure applyEntry). At present, we incorrectly convert this form to the BUFFER object handle variant of the attribute.

#2 Updated by Greg Shah over 10 years ago

From the Progress Language Reference for v11.0:

CURRENT-ITERATION attribute (widget objects)

   A handle for the current iteration of the frame or dialog box.

   Data type: HANDLE
   Access: Readable/Writeable

Applies to: DIALOG-BOX widget, FRAME widget

This attribute is a read-only attribute for dialog boxes.

The example code that references this attribute (this code is licensed as open source using the Possenet license, so it is OK to post here):

PROCEDURE applyEntry :
/*------------------------------------------------------------------------------
  Purpose:     Applies "ENTRY" to the first enabled and visible object 
               in the default frame (unless pcField is specified), 
               or in the first child which is a Frame.
  Parameters:  INPUT pcField AS CHARACTER -- optional fieldname; if specified,
               (if this parameter is not blank or unknown), then
               the frame field of that name will be positioned to. 
------------------------------------------------------------------------------*/

  DEFINE INPUT PARAMETER pcField  AS CHARACTER NO-UNDO.

  DEFINE VARIABLE hContainer      AS HANDLE NO-UNDO.
  DEFINE VARIABLE hWidget         AS HANDLE NO-UNDO.

    IF pcField = ? THEN pcField = "":U.

    /* {get Prop var [handle]} */   ASSIGN ghProp = WIDGET-HANDLE(ENTRY(1, TARGET-PROCEDURE:ADM-DATA, CHR(1))) ghProp = ghProp:BUFFER-FIELD('ContainerHandle':U) hContainer = ghProp:BUFFER-VALUE  
 .
    IF VALID-HANDLE(hContainer) THEN
    DO:     
       DO WHILE hContainer:TYPE = "WINDOW":U: 
           hContainer = hContainer:FIRST-CHILD.    
       END.
       ASSIGN hWidget = hContainer:CURRENT-ITERATION
              hWidget = hWidget:FIRST-TAB-ITEM.

          DO WHILE VALID-HANDLE(hWidget) :          
            IF hWidget:SENSITIVE AND hWidget:VISIBLE AND
              (pcField = "":U OR hWidget:NAME = pcField) THEN
            DO:
               /* In case the new widget with focus is not in the
                  CURRENT-WINDOW, change CURRENT-WINDOW to be the
                  window of the new widget; otherwise it won't actually
                  get focus. */
               DO WHILE (VALID-HANDLE(hContainer) AND 
                 hContainer:TYPE NE "WINDOW":U):
                   hContainer = hContainer:PARENT.
               END.
               IF hContainer:TYPE = "WINDOW":U THEN
                 CURRENT-WINDOW = hContainer.

               APPLY "ENTRY":U TO hWidget.                
               RETURN.
            END.
            ELSE ASSIGN hWidget = hWidget:NEXT-TAB-ITEM.            
          END.          
    END.  

    RETURN.
END PROCEDURE.

#3 Updated by Greg Shah over 10 years ago

  • Assignee set to Vadim Gindin
  • Start date set to 12/29/2013
  • Due date set to 01/08/2014

The objective for this task is to implement the CURRENT-ITERATION attribute for widgets. The documentation is quite weak on this attribute, but based on the code and the attribute's name, I guess it is useful when processing a DOWN frame.

A DOWN frame is one that displays a series of "rows", one of which is displayed with the current data being referenced. Each row is a set of widgets (possibly spread over multiple lines on the display). I guess that a row is the same thing as an "iteration". And the current-iteration attribute references the widget instances that are displaying the current row. The most common case is to use an iterating block that automatically implements a DOWN statement when the loop iterates (and when it exits).

def temp-table whatever 
   field num as int
   field descr as char.

/* create some rows in the table here */

FOR EACH whatever WHERE num > 0:
   display whatever.num whatever.descr with DOWN 5 frame my-down-frame.
END.

Each pass through the FOR loop will have a different "iteration" or "row" in the down frame.

Please review our documentation in the P2J Conversion Reference in regard to down frames (see the Frames chapter). Please review the ...ui.client.Frame class where we implement much of our DOWN processing from the frame's perspective. You will also want to review the GenericFrame.down() and ThinClient.down() where we handle the DOWN statement and all implicit/explicit down "events".

Once you have an idea of what DOWN frames do and some of the complexity of our implementation, you should explore the behavior of the CURRENT-ITERATION attribute and determine what exactly it seems to do.

I suspect the attribute also works for non-DOWN frames too, but in that case it probably always refers to the same set of widgets every time since there is only ever one "iteration" or "row" in that case.

Once you have a set of testcases that show the behavior of this attribute, document it here and offer a suggestion for implementation.

#4 Updated by Vadim Gindin over 10 years ago

I've prepared first test

def var i as int.
def var j as int init 1.

def temp-table whatever
  field num as int
  field descr as char.

do i=-3 to 15:
  create whatever.
  assign  num = i.
          descr = "descr" + string(i).
end.

for each whatever where num > 0: 
  display num descr with 5 down frame myFrame.
  put screen row j col 30 string(frame myFrame:current-iteration). 
  j = j + 1. 
  if j modulo 5 = 0 then 
  do:
    put screen row j col 30 "--".
    j = j + 1.
  end.
end.

See it's results in attached file test_results.png

#5 Updated by Greg Shah over 10 years ago

This is somewhat consistent with my theory. On a 5-down frame, you would expect 5 sets of widgets, but it looks like there are 6 sets in this case. I suspect there may be a set used for the column headers (e.g. -----). The other thing that is a bit strange is the numbering, which starts at 1000 and runs straight through to 1005. I would have expected that there would be gaps. This suggests that what may be returned is the field-group "widget" instead of the "num" widget.

From the v11.0 4GL docs:

FIELD-GROUP widget

A field group is the hidden parent of field-level widgets and child frames owned by a parent frame or dialog box. Thus, field groups are the actual children of frames and dialog boxes. A frame contains the following field groups:

• A background field group (which includes the frame header)
• For a one-down frame or dialog box: a single data field group containing field-level widgets and child frames
• For a multiple-down frame: one data field group for each data iteration in the frame

A field group has no visible representation. You cannot explicitly define or create field groups. They are generated automatically when frames are defined or created.

Please make some changes to try to use the returned handle to find out more information about the returned widget. For example, the NAME, TYPE, ROW, COLUMN, VISIBLE, PARENT... attributes should be informative.

#6 Updated by Constantin Asofiei over 10 years ago

Well, the current-iteration is not linked with an iterating block; lets take this case:

def var i as int.

form i with frame f1 5 down.

i = 0.
display i with frame f1.
put screen row 1 col 30 string(frame f1:current-iteration) + " " + string(frame f1:current-iteration:first-child:screen-value).

down 1 with frame f1.
i = 1.
display i with frame f1.
put screen row 2 col 30 string(frame f1:current-iteration) + " " + string(frame f1:current-iteration:first-child:screen-value).

up 1 with frame f1.
i = 2.
display i with frame f1.
put screen row 3 col 30 string(frame f1:current-iteration) + " " + string(frame f1:current-iteration:first-child:screen-value).

pause.

which provides this output:
┌──────────┐                 1000 0
│         i│                 1002 1
│──────────│                 1000 2
│         2│
│         1│
│          │
│          │
│          │
└──────────┘

Thus, I think the current-iteration is just a pointer to the current row in a down frame. The interesting fact is that SCROLL does not affect the current-iteration:
def var i as int.

form i with frame f1 5 down.

do i = 1 to 5:
  display i with frame f1.
  if i <> 5 then down 1 with frame f1.
end.

up 2 with frame f1.
put screen row 1 col 30 string(frame f1:current-iteration) + " " + string(frame f1:current-iteration:first-child:screen-value).
pause.

scroll up with frame f1.
put screen row 2 col 30 string(frame f1:current-iteration) + " " + string(frame f1:current-iteration:first-child:screen-value).
pause.

scroll up with frame f1.
put screen row 3 col 30 string(frame f1:current-iteration) + " " + string(frame f1:current-iteration:first-child:screen-value).
pause.

produces this output:
┌──────────┐                 1000 3
│         i│                 1002 4
│──────────│                 1004 5
│         3│
│         4│
│         5│
│          │
│          │
└──────────┘

The first UP statement will move the current-iteration to the widget with i = 3; the SCROLL statement will move the widgets upwards, but the position in the frame remains the same; thus, current-iteration points to i = 4 after the first SCROLL and to i = 5 after the second SCROLL.

PS: Greg, you are correct, CURRENT-ITERATION points to a FIELD-GROUP widget.

#7 Updated by Vadim Gindin over 10 years ago

Here are widget attrs:

In this result we can see values of following attrs (in order of appearing):

current-iteration visible type row column parent:type first-child:type

It seems that it really is: current-iteration is a handle of FIELD-GROUP of each row. and SCROLL really doesn't affect the current-iteration handle. Constantin thank you for your findings!

FIELD-GROUP is a hidden container for widgets of row in down frame as you wrote.

Are here some other aspect that needs to be tested?

P.S. About numbering. What problem do you see in it? I viewed same values for newly created widgets in earlier task about CREATE WINDOW: 1000, 1001 and so on.

#8 Updated by Constantin Asofiei over 10 years ago

Vadim Gindin wrote:

P.S. About numbering. What problem do you see in it? I viewed same values for newly created widgets in earlier task about CREATE WINDOW: 1000, 1001 and so on.

I think this is because a handle for the static widget is not created until it is accessed...

Also, there is one more thing for you to find: why is there an extra row reported in your test? You could walk each field-group and check what widgets it contains (and their screen-value).

#9 Updated by Greg Shah over 10 years ago

Well, the current-iteration is not linked with an iterating block

Yes, this is exactly how we have implemented down frames in P2J too. The link is simply that some iterating blocks will trigger an implicit DOWN statement if a down frame is scoped to that block. That is really the only linkage to block iteration.

Thus, I think the current-iteration is just a pointer to the current row in a down frame.

Exactly.

But I think that a non-down frame will probably also have a current iteration which is always the same field-group widget. Vadim, make sure you test that case.

#10 Updated by Vadim Gindin over 10 years ago

Sorry, I made a simple logical error in my test and this led us to confusion. My error was in a rows number in the output. DOWN frame has size 5 but the output has size 6 because I add additional line "--" to distinguish different "DOWNs". Here is the corrected test with screen values (last column).

def var i as int.
def var j as int init 1.
def var h as widget-handle.

def temp-table whatever
  field num as int
  field descr as char.

do i=-3 to 15:
  create whatever.
  assign  num = i.
          descr = "descr" + string(i).
end.

for each whatever where num > 0: 
  display num descr with 5 down frame myFrame.
  h = frame myFrame:current-iteration.

  if j modulo 6 = 0 then 
  do:
    put screen row j col 30 "--".
    j = j + 1.
  end.

  put screen row j col 30 string(h) + " " + string(h:visible) + " " + string(h:type) + " " + string(h:row) + " " + string(h:column) + " " + string(h:parent:type) + " " + string(h:first-child:type) + " " + string(h:first-child:screen-value).
  j = j + 1. 

end.

It outputs expected result:

And I wrote simple test for 1-DOWN frame. Greg, I thought that you meant 1-DOWN by the phrase "non-down frame". If I'm not right. Please, specify what did you mean. Here is the test.

def var i as int.
def var j as int init 1.
def var h as widget-handle.

def temp-table whatever
  field num as int
  field descr as char.

do i=-3 to 15:
  create whatever.
  assign  num = i.
          descr = "descr" + string(i).
end.

for each whatever where num > 0: 
  display num descr with 1 down frame myFrame.
  h = frame myFrame:current-iteration.

  put screen row j col 30 string(h) + " " + string(h:visible) + " " + string(h:type) + " " + string(h:row) + " " + string(h:column) + " " + string(h:parent:type) + " " + string(h:first-child:type) + " " + string(h:first-child:screen-value).
  j = j + 1. 
end.

You were right. There is only on current-iteration used for such frames:

#11 Updated by Greg Shah over 10 years ago

I thought that you meant 1-DOWN by the phrase "non-down frame".

Yes, you have it right.

#12 Updated by Vadim Gindin over 10 years ago

Lost new results picture.

#13 Updated by Constantin Asofiei over 10 years ago

About the tests at note 10: your conclusions are wrong. If you look at the repeating IDs, you will see that there are 6 repeating IDs with type field-group, not 5; and the frame is a 5-down frame. More, you are complicating the tests; there is no need for using temp-tables, a simple repeat is enough; if you think temp-tables are of interest and can affect this feature, you can go ahead and use them to confirm behaviour after the initial analysis with simple tests. But analysis should move from simple to complex, not viceversa.

Look at this test:
def var i as int.
def var j as int.
def var h as handle.

j = 1.
repeat i = 1 to 6:
  display i j with frame f1 2 down.
  h = frame f1:current-iteration.
  put screen row j col 30 string(h) + " " + string(h:type) + " " + h:first-child:screen-value.
  j = j + 1.
end.

/* output frame's direct children */
h = frame f1:first-child.
j = 1.
do while h <> ?:
  put screen row j col 50 string(h) + " " +  h:first-child:type + " " + h:first-child:screen-value + " " +
                          string(if h:first-child:type = "literal" then "" else  h:first-child:label) .

  h = h:next-sibling.
  j = j + 1.
end.

which outputs:
┌─────────────────────┐      1000 FIELD-GROUP 1  1000 FILL-IN  i
│         i          j│      1002 FIELD-GROUP 2  1002 FILL-IN 5 i
│────────── ──────────│      1004 FIELD-GROUP 3  1004 FILL-IN 6 i
│         5          5│      1000 FIELD-GROUP 4  1007 LITERAL ---------- ----------
│         6          6│      1002 FIELD-GROUP 5
└─────────────────────┘      1004 FIELD-GROUP 6

The test has:
  • a 2 down frame and there are 3 repeating field-groups: 1000, 1002 and 1004
  • checking the direct children of frame f1, the 1000, 1002 and 1004 field-groups are reported.
  • if we change the loop to output 7 rows of data (i = 1 to 7), then is output:
    ┌─────────────────────┐      1000 FIELD-GROUP 1  1000 FILL-IN 7 i
    │         i          j│      1002 FIELD-GROUP 2  1002 FILL-IN  i
    │────────── ──────────│      1004 FIELD-GROUP 3  1004 FILL-IN  i
    │         7          7│      1000 FIELD-GROUP 4  1007 LITERAL ---------- ----------
    │                     │      1002 FIELD-GROUP 5
    └─────────────────────┘      1004 FIELD-GROUP 6
                                 1000 FIELD-GROUP 7
    

Please extract rules describing the output you encounter, and document them in detail (not just "this is the output"). Use a structure like:

test program: some-test.p (the program must be in repository, not always here)
output file: some-test.4gl.txt (you can save the terminal screen by selecting it all with the mouse and and then pressing middle-click in a text document; @screen@ can be used for screen capturing too)
summary: testing 2-down frames and displaying 6 rows of data
details:
- there are reported 3 field-groups, with only two of them containing data after the loop ends.
- all 3 field-groups are reported as direct children of the frame.
- the separator between frame's header and body is reported as a frame child, but is not reported by current-iteration (which is expected).

If you have trouble interpreting the output, ask for help. But, the details should be in your own words, because if you can explain the output in your own words, then you are on a good path to allow others to understand it too. If it is just some output, anyone reading your comment will need to decipher it; and his/hers understanding may not be what others understood.

Something else you need to check (from my experiments):
  • if the loop is i = 1 to 2, then only 2 field groups exist; this suggests that the 3rd field-group is just an "auxiliary" widget group to output data and is created after all initial field-groups are already used and the frame is "full".
  • if we pause on i = 3 and output the frame's direct children, I think the data will be in the 3rd field-group; this 3rd field group will not be displayed until the existing iteration ends (i.e. down is silently called)
  • check how the VISIBLE flag is reported at various iterations of the REPEAT loop. I doubt that for a 2-down frame, all 3 field-groups will be visible at once; I suspect that they will be made visible when they are brought on the screen (via implicit down or display).
  • check what happens with frames scoped to the external program (for which an explicit down is required in the loop):
    form i with frame f1 2 down.
    repeat i = 1 to 6:
    display i with frame f1.
    down 1 with frame f1. /* an explicit down is needed in this case. */
    end.
    

Greg: AFAIK, P2J uses n widget rows for displaying the body of a n-down frame; assuming that 4GL indeed uses n+1 widgets for a n-down frame (with n != 1), then adding the additional set of widgets to the down body will not be trivial. My suggestion for a first phase is to add support for CURRENT-ITERATION without changing the client-side frame drawing.

#14 Updated by Greg Shah over 10 years ago

AFAIK, P2J uses n widget rows for displaying the body of a n-down frame;

Correct.

assuming that 4GL indeed uses n+1 widgets for a n-down frame (with n != 1), then adding the additional set of widgets to the down body will not be trivial.

Also correct. There are several issues:

1. Fully understanding when exactly the extra field group is created and when it is actually used.
2. Force fitting that behavior onto our more simple n-based approach. I can imagine that there are many loops in our code that may be very sensitive to this new behavior.

My suggestion for a first phase is to add support for CURRENT-ITERATION without changing the client-side frame drawing.

I completely agree. Vadim: this means that the output of our test programs would be expected to be different and that is OK so long as the basic CURRENT-ITERATION behavior is correct.

#15 Updated by Vadim Gindin over 10 years ago

I extended your test to output the content of each field group on every iteration and other useful information. Here is the test

def var i as int.
def var j as int.
def var h as handle.
def var p as handle. /* previous current-iteration value */
def var ch as char.

j = 1.
repeat i = 1 to 9:
  display i with frame f1 2 down.

  h = frame f1:current-iteration.
  ch = string(i) + ") " + string(h) + " " + string(h:type) + " " + h:first-child:screen-value.
  if p <> ? then ch = ch + "  " + string(p) + ":visible:"+ string(p:visible).

  put screen row j col 20 ch.
  p = h.

  run iteration_widgets.

  /* add empty string between DOWNs */
  if frame-line(f1) = frame-down(f1) then do:
    j = j + 1.
  end.
end.

/* output frame's direct children */
procedure iteration_widgets:
  h = frame f1:first-child.
  do while h <> ?:
    put screen row j col 70 string(h) + " " +  h:first-child:type + " " + h:first-child:screen-value + " " + string(if h:first-child:type = "literal" then  "" else  h:first-child:label).

    h = h:next-sibling.
    j = j + 1.
  end.
end procedure.

Output: 2162_results.txt

It's not easy to interpret it. I'll try.

Summary: Testing 2-down frames and displaying 9 lines. For each field-group its fields are displayed with screen-values. For each field-group there are also previous field-group visibility displayed in the format "handle:visible:<val>". When DOWN page is incremented - empty line is printed.
Details.
1) You was right about auxiliary rows. Look at the contents of first two field-groups. There are no empty fill-in's.
2) For every field-group there are 3 fields.
3) As you asked when i=3 a data are only in the third field-in. Commonly there is not obvious algorithm of filling the values of fill-ins with a cycle period of 2.
4) As we can see there are 3 field-groups in spite of only 2 lines in a DOWN. 1000, 1004, 1006. At the iteration 3 first DOWN page switch was done and 1006 field-group becomes visible, but previous field-group from previous DOWN page is also stayed visible.

So here are these strange results. I can't say I'm understanding the logic of this output. Could you help me?

#16 Updated by Constantin Asofiei over 10 years ago

The work to determine the rules when the auxiliary field-group is created and how the field-group rows are made visible will not be trivial. I understand it might be a little frustrating to ignore some behaviour while implementing other, but for now we need CURRENT-ITERATION to work only in terms of reporting the field-group for the active row (where the data will be displayed). We will add (and investigate) the auxiliary field-group part later (this auxiliary field-group doesn't even have to do with current-iteration, is related to how a down frame implements its body; current-iteration is what helps us understand how the frame's body "evolves" for a down frame).

So, what we need to do:
  • fix conversion of the CURRENT-ITERATION attribute, as currently a handle.unwrapBuffer is emitted (which will not work for widgets). You will need to find the interface which shares some other common attributes/methods between Buffer and Widgets. If this does not exist, create a new one and adjust unwrap and P2J buffer/widget resource definitions accordingly.
  • check if CURRENT-ITERATION can be used with widgets
  • implement the runtime for CURRENT-ITERATION, in Frame's case (without the auxiliary widget support). The implementation will be in FrameWidget (as this is the P2J class which is associated with the legacy frame resource).
  • search if there is some tracking of the current frame row on the server-side. If there isn't, determine a way to send this info from the P2J client to the server-side. It can be via a new ClientExports API or via StateSynchronizer. If possible, I would prefer StateSynchronizer which will cache the row index on the server-side, avoiding trips down to the client each time CURRENT-ITERATION is called.

Let me know if you have any questions. Also, post notes about your implementation decisions, so we can discuss them, before making the code changes.

#17 Updated by Vadim Gindin over 10 years ago

I tried to convert a simple procedure and it emitted correct (at first saw) java code myFrameFrame.currentIteration(). The class Buffer also contains the method Buffer.currentIteration() with its own implementation. I suppose that there is no conversion rules changes will be needed. What do you think?

At this moment there is a simple rule for CURRENT-ITERATION (method_attributes.rules):

1322                <rule>ftype == prog.kw_cur_iter
1323                   <!-- TODO: this can be used with frame/dialogs too -->
1324                   <action>hwrap = "Buffer"</action>
1325                   <action>methodText = "currentIteration"</action>
1326                </rule>

Yes hwrap = "Buffer" seems as not suitable in the case of frame, but do we really need conversion changes here? If so, how to distinguish different usages of CURRENT-ITERATION - frame and buffer?

#18 Updated by Constantin Asofiei over 10 years ago

Vadim Gindin wrote:

I tried to convert a simple procedure and it emitted correct (at first saw) java code myFrameFrame.currentIteration().

I guess you've tried a case like frame myframe:current-iteration, right? This will work, but a:

h = frame myframe:handle.
h = h:current-iteration@.

will be converted to a h.assign(handle.unwrapBuffer(h).currentIteration)) - which will fail, as the resource refered by h is a frame, not a buffer. So, conversion changes are needed.

Yes hwrap = "Buffer" seems as not suitable in the case of frame, but do we really need conversion changes here? If so, how to distinguish different usages of CURRENT-ITERATION - frame and buffer?

You need to:
  • add a new interface, IterableResource (to not collide with java.lang.Iterable), which will define APIs to access the CURRENT-ITERATION. This interface will be inherited by the Buffer and CommonFrame interfaces. Also, remove the currentIteration() from Buffer.
  • replace hwrap = "Buffer" with hwrap = "IterableResource" and add the appropriate handle.unwrapIterableResource in handle class (see how handle.unwrapBuffer is defined); note that handle.unwrap APIs are sorted by name.

This will allow proper access to this attribute, regardless of how is accessed (via handle var or frame).

#19 Updated by Vadim Gindin over 10 years ago

Here it is. An archive with recommended by you changes. The questions.
1) Do I correctly implenented handle.unwrapIterableResource()?
2) I suppose that FrameWidget will contain not an algorithm but some result as a private field. I think all logic will be in GenericFrame.
What do you think?

#20 Updated by Constantin Asofiei over 10 years ago

Review for 0121a.zip (btw, updates should be named using the day of work's date, not calendaristic):
  1. you are missing the methods_attributes.rules file with the hwrap = "IterableResource" change.
  2. don't forget about history entries and copyright year update
  3. Buffer - remove the Buffer.currentIteration definition, as this will be picked up from the IterableResource
  4. CommonFrame - this one should extend IterableResource, not FrameWidget. The interface annotated with a LegacyAnnotation is what defines the legacy resource in P2J.
  5. FrameWidget.currentIteration - this can call directly into GenericFrame.currentIteration, via the protected GenericWidget.frame field (which is accessible from FrameWidget). So you are correct, the real implementation will be in GenericFrame.
  6. there is no need to add LegacyAttribute annotations at the currentIteration implementation in FrameWidget and GenericFrame; remove those.
  7. handle.iterableResource - the implementation is OK

#21 Updated by Vadim Gindin over 10 years ago

The first obstacle is the CURRENT-ITERATION for Buffer is an integer but for Frame/DialogBox the CURRENT-ITERATION is a handle. By the way I didn't find Buffer:CURRENT-ITERATION in docs.. To overcome this obstacle I think we should refuse of the interface IterableResource (as we see that it's not common) and define separate rules for Buffer and Frame if it possible and assign hwrap="Buffer" or hwrap="Frame". The alternative way is to change Buffer.currentIteration() signature to make it returning handle in spite of integer. What do you think?

I can suppose following implementation skeleton.
1) There is Progress function FRAME-LINE that returns number of current logical row in frame iteration. This function is implemented. See GenericFrame.frameLine(). We need to get a handle of FieldGroup of this current logical row and we can use frameLine() for it.
2) If we look at GenericFrame.getFirstChild() we'll see that FieldGroup is created on the server-side and I couldn't find any FieldGroup processing on the client-side. FieldGroup is created with widgets list.
So if we want to implement currentIteration() in the same way we need the widgets list of the current logical row of the frame.

Questions.
1) What do you think about this variant of implementation?
2) If it is OK, how to get the widgets list of the current logical row?

#22 Updated by Constantin Asofiei over 10 years ago

Vadim Gindin wrote:

The first obstacle is the CURRENT-ITERATION for Buffer is an integer but for Frame/DialogBox the CURRENT-ITERATION is a handle.

Good finding, but I think we can postpone this. The documentation is at page 1389 of dvref.pdf (beginning of page): for buffers CURRENT-ITERATION is linked with a prodata-set, so we can ignore it for now. What we need to do:
  • move back one step and remove the IterableResource interface. For now we will assume that the CURRENT-ITERATION is supported only by frames, and we will ignore the BUFFER case.
  • in methods_attributes.rules, change the hwrap assignment to hwrap = "Widget". Add a comment knowing that we force it this way knowingly.
  • in the future, when we will need to add support for BUFFER:CURRENT-ITERATION, we will probably use some conversion-time hints to distinguish between these two cases. Add a comment at the hwrap = "Widget" assignment like: "we will need to use conversion-time hints to distinguish between BUFFER and FRAME usage". This will be needed because Java does not allow overloading by the return type (you can't have two methods with the same name and parameters, but different return types).

I can suppose following implementation skeleton.
1) There is Progress function FRAME-LINE that returns number of current logical row in frame iteration. This function is implemented. See GenericFrame.frameLine(). We need to get a handle of FieldGroup of this current logical row and we can use frameLine() for it.

Before we can use FRAME-LINE, we need to find how CURRENT-ITERATION behaves in cases when the frame is redirected to:
  • a named stream (via DISPLAY STREAM)
  • the unnamed stream (redirect default output with a OUTPUT TO statement)
  • the terminal, via unnamed stream (an OUTPUT TO TERMINAL statement).

If CURRENT-ITERATION behaves the same way as FRAME-LINE, then we can use FRAME-LINE as the index.

2) If we look at GenericFrame.getFirstChild() we'll see that FieldGroup is created on the server-side and I couldn't find any FieldGroup processing on the client-side. FieldGroup is created with widgets list.
2) If it is OK, how to get the widgets list of the current logical row?

After a quick look at GenericFrame, I think on server-side we keep only one field-group for any kind of frame. We should be keeping n field-groups, for a n-down frame... I think this was because in P2J, when a i.e. DISPLAY is used, this always targets the current row, thus we don't need to distinguish between rows on server-side; this is the client-side's job.

I'm thinking about an approach for field-groups on server-side, I'll get back to you. In the mean time, check the frame-line and current-iteration in the scenarios above.

#23 Updated by Greg Shah over 10 years ago

Constantin notes:

Considering that CURRENT-ITERATION is used in the server project only in the adm2/smart.p and the usage is also dependent on the FIRST-/NEXT-TAB-ITEM attributes:

    IF VALID-HANDLE(hContainer) THEN
    DO:
       DO WHILE hContainer:TYPE = "WINDOW":U:
           hContainer = hContainer:FIRST-CHILD.
       END.
       ASSIGN hWidget = hContainer:CURRENT-ITERATION
              hWidget = hWidget:FIRST-TAB-ITEM.

          DO WHILE VALID-HANDLE(hWidget) :
            IF hWidget:SENSITIVE AND hWidget:VISIBLE AND
              (pcField = "":U OR hWidget:NAME = pcField) THEN
            DO:
               /* In case the new widget with focus is not in the
                  CURRENT-WINDOW, change CURRENT-WINDOW to be the
                  window of the new widget; otherwise it won't actually
                  get focus. */
               DO WHILE (VALID-HANDLE(hContainer) AND
                 hContainer:TYPE NE "WINDOW":U):
                   hContainer = hContainer:PARENT.
               END.
               IF hContainer:TYPE = "WINDOW":U THEN
                 CURRENT-WINDOW = hContainer.

               APPLY "ENTRY":U TO hWidget.
               RETURN.
            END.
            ELSE ASSIGN hWidget = hWidget:NEXT-TAB-ITEM.
          END.
    END.

Can 2126 be re-assigned for M11 (to which NEXT/PREV-TAB-ITEM are assigned)? My thought is, even if we manage to bring CURRENT-ITERATION to a working state (i.e. for 1-down frames should work properly), this code will not work without the other attributes.

#24 Updated by Greg Shah over 10 years ago

  • Target version changed from Milestone 7 to Milestone 11

It does seem that this code can only be used from an interactive UI. That is not something that the server project requires for M7, so it is acceptable to move this to M11.

#25 Updated by Constantin Asofiei over 10 years ago

Vadim, using the test at note 15, check how the body of a down frame evolves in the cases I've mentioned at note 22. We need to determine the rules describing when a widget row is created for a down frame.

#26 Updated by Vadim Gindin over 10 years ago

Constantin Asofiei wrote:

Vadim, using the test at note 15, check how the body of a down frame evolves in the cases I've mentioned at note 22. We need to determine the rules describing when a widget row is created for a down frame.

I only tried to move frame widgets printing before DISPLAY statement. The difference is not big. Only screen values was changed, but widgets row are stayed as earlier.

There is 2 variants of when they could be created: during iteration processing or during DISPLAY processing. When I placed DISPLAY statement at the end of iteration I found that widget row is not changed. It means, that dynamic widgets creation is going during iteration processing. Moreover, this variant is seemed more straightforward.

What else can I do to find when a widget row is created?

#27 Updated by Constantin Asofiei over 10 years ago

Vadim Gindin wrote:

What else can I do to find when a widget row is created?

Check your findings with cases when the frame is not used in a iterating loop, i.e. DISPLAY and DOWN are explicitly called, like in the test from note 6.

In the test on note 15, as the frame is scoped to the repeat block, an implicit down is executed on each iteration; test what happens if the frame is scoped to the external procedure and a down is needed in the repeat loop, like this:

form i with frame f1 5 down.
repeat i = 1 to 10:
   display i with frame f1.

   down with frame f1.
end.

More, enhance your tests checking which resource is saved in h after a h = i:handle in frame f1. statement is executed (both with iterating and non-iterating block).

#28 Updated by Vadim Gindin over 10 years ago

Here are tests and those results.

1. no_loop_ihandle.p. Try to check CURRENT-ITERATION behavior in the case when frame is not scoped to repeat loop. Check the handle i attributes.
Results: 2162_no_loop_ihandle.txt
Summary of the test.
2-DOWN frame is created and filled by sequental DOWN/UP calls and values i from 0 to 4
Details:
Filling goes in the same way as in previous tests. Additional Fill-in is created only when necessary: current row is in the bottom of the frame and DOWN is called. Take a look at the step i=2 (UP). Current row became the first of two in the frame and additional Fill-in field is created only after 2 sequental DOWN calls.
About handles. We are checking CURRENT-ITERATION handle and i handle. CURRENT-ITERATION points to FIELD-GROUP and i handle points to Fill-In. Lets look at one row in result:
0) 1001 FIELD-GROUP 1000 0 | 1000 FILL-IN  0 FIELD-GROUP 1001    1001 FILL-IN 0 i
                                                                 1002 LITERAL ---------- 

Here we have current-iteration attrs from the left side of '|':
current-iteration:id=1001 (FIELD-GROUP) and its first child has id=1000
From the right side we have i handle that is:
i:id=1000 (FILL-IN) with parent=1001 (FIELD-GROUP) - notified above.
It means that current-iteration:first-child is i:handle in frame 1 and vice versa i:handle:parent is current-iteration.
At the right side of output is a children of frame1. We can see that the frame:first-child is fill-in missing field-group. Moreover, this field-in and field-group mentioned earlier have equal ids = 1001. Interesting peculiarity!
Conclusions:
  • current-iteration and i:handle are parent and child.
  • current-iteration and frame:first-child have equal ID's 1001 but they are different fields: FIELD-GROUP and FILL-IN correspondently
  • additional Fill-in is added only when it is necessary: current row is in the bottom of the frame and DOWN is called.
  • when I added i:handle processing all frame children takes shifted ids by 1.

#29 Updated by Vadim Gindin over 10 years ago

2. external_scoped_frame_ihandle.p This test checks behavior of the CURRENT-ITERATION in repeat loop in case when the frame has external scope (relative to repeat loop). Check the handle i attributes.
Results: external_scoped_frame_ihandle.txt
Summary of the test.
There is 2-DOWN frame created. After that a repeat loop i=0 to 9 is executed with explicit DOWN call. For each iteration it prints attributes of CURRENT-ITERATION, i:handle, and frame children.
Details.
Behavior is identical to the case when frame is scoped to repeat loop itself.
i:handle behavior is identical to behavior in the previous test.
Another interesting peculiarity:
FIELD-GROUP has Id equal to the last Fill-In in the frame, not the first one! I made a mistake in previous note when I wrote that FIELD=GROUP has Id equal to the first child of the frame. No - the last one.

#30 Updated by Vadim Gindin over 10 years ago

I'm repeating that i:handle peculiarities are the same in iterating and non-iterating tests. All tests I have are committed to bzr in the folder current-iteration

#31 Updated by Constantin Asofiei over 10 years ago

Vadim, good findings, but you are misinterpreting something. When you are printing the frame's children in no-loop-ihandle.p:

  h = frame f1:first-child.
  do while h <> ?:
    put screen row j col 80 string(h) + " " +  h:first-child:type + " " + h:first-child:screen-value + " " + string(if h:first-child:type = "literal" then  "" else  h:first-child:label).

    h = h:next-sibling.
    j = j + 1.
  end.

put screen is printing the ID of the field-group's first child, not the ID of the field-group. The h = frame f1:first-child will point to the field-group, but when you do h:first-child in the put screen, you are accessing the first child of the field-group (as this is what h is pointing to when put screen is executed). Thus the current-iteration and frame:first-child have equal ID's 1001 but they are different fields: FIELD-GROUP and FILL-IN correspondently assumption is incorrect.

To summarize, we can assume (for down frames):

  1. accessing a frame's widget via its name (i.e. i in frame f1) will always point to the corresponding widget, in the field-group returned by current-iteration.
  2. the frame starts with only one field-group (for the first row). When advancing to the next row, if a new field-group is not already created for the row, it will be created.
  3. there is a field-group associated with the separator between the label and the frame body, but no field-group for the actual labels:
    - please test the NO-LABELS and SIDE-LABELS clauses for the frame phrase, and see how this field group is reported.
    - I assume there is only one LITERAL with the separator text for all widgets, not one LITERAL for each widget - test how this is reported when the frame has more than one widget.

What else we need to determine:

  1. is FRAME-LINE always in sync with current-iteration?
  2. what is the behavior when the frame is redirected to a stream (named, unnamed, or redirected to terminal).
  3. when the frame is not scoped to a repeating block, what happens when i.e. DOWN 2 is called, and the down body is not yet fully initialized (i.e the next two rows are not yet created)? Does this create 2 rows for frame-line + 1 and frame-line + 2 or just one, for frame-line + 2?

About an implementation approach:

The problem in P2J is that on both server-side and client-side we have only one set of widgets, used to display the data on each row. Even if on client-side the frame's down body contains n sets of widgets (each widget on a row being a distinct instance), the same set of widget IDs is used for each row.

I've been thinking how to leverage existing implementation (to minimize code changes both on server-side and client-side), but I don't think we can get away without some major changes. Some findings/notes/ideas:
  • on client-side, although the down body for a frame (saved in Frame.downBody) contains n rows, the widget IDs are the same for each row (as noted in Frame.fillDownBodyParts, only the first row is real and the rest are "artificial"). This will not make it possible to properly implement widget-level statements and attributes for a down frame, like HIDE i IN FRAME f1 or the VISIBLE attribute, as we can't distinguish between the widget on row 1 and the widget on row 2 by their ID.
  • as a side note, the widget ID on client-side is different from the resource ID on server-side, so keep this in mind.
  • on server-side, we don't maintain the down body; all we have is a single set of widgets. Conversion-wise is OK (as in 4GL, at a certain time only one widget can be referenced via its name in a widget phrase like i IN FRAME f1), so we don't need to change how the frame is converted; the widget getters in the frame definition interface are OK as they are. What we need to change is which widget is returned by the widget getter (i.e. a frame.widgetI() call), depending on the row on which the frame is currently standing.
  • we need to link the down body on the server side with the widgets on the client side. But the client side currently requires that the Frame.downBody contain a number of widgets equal to the frame's down value; if possible, I don't want to change this. Instead, I think we should try to implement this "fixed down body" behavior on server-side too, with a tweak: we maintain some artificial widgets for rows which are "pending to be created". Same on client-side: it starts with only one real row, the others being artificial widgets. As new rows are added, they are replaced with real widgets (which have server-side counterparts). But we need to be careful to still make these "real" widgets linked to only one widget, so that i.e. the label is processed only once (there might be more to take care beside the label).

Please read carefully these notes and let me know what you think. Also, continue testing to find the rules describing when the extra field-group is created for the down frame, as is best to implement this too now than later. Keep in mind we will need to test various attributes for the widgets in a row (like VISIBLE, HIDDEN, SCREEN-VALUE, etc), so create tests for these too. Once the rules describing all of this start to make sense, please try to move into writing testcases which encode the output you've attached to the previous notes (via i.e. IF i in frame f1:screen-value <> "1" then message "something is wrong"), instead of using PUT SCREEN. I understand this will make the tests more complex and we will still have to do some side-by-side comparison (for checking how the frame is drawn), but it will make it easy for us to find problems and regressions.

#32 Updated by Vadim Gindin over 10 years ago

As I can understand this, it sounds reasonable. I just have several questions about details. See below.

Constantin Asofiei wrote:
..

I've been thinking how to leverage existing implementation (to minimize code changes both on server-side and client-side), but I don't think we can get away without some major changes. Some findings/notes/ideas:
  • on client-side, although the down body for a frame (saved in Frame.downBody) contains n rows, the widget IDs are the same for each row (as noted in Frame.fillDownBodyParts, only the first row is real and the rest are "artificial"). This will not make it possible to properly implement widget-level statements and attributes for a down frame, like HIDE i IN FRAME f1 or the VISIBLE attribute, as we can't distinguish between the widget on row 1 and the widget on row 2 by their ID.

Why it is needed to have downBody array with only the first row real? Do I correctly understand: two widgets in different rows will have the same ID value? Is that the reason why it is not possible to get correct widget by ID? But at the same time we know the current-line so first of all we can get correct row and then we can get the widget. Will it work?

  • as a side note, the widget ID on client-side is different from the resource ID on server-side, so keep this in mind.
  • on server-side, we don't maintain the down body; all we have is a single set of widgets. Conversion-wise is OK (as in 4GL, at a certain time only one widget can be referenced via its name in a widget phrase like i IN FRAME f1), so we don't need to change how the frame is converted; the widget getters in the frame definition interface are OK as they are. What we need to change is which widget is returned by the widget getter (i.e. a frame.widgetI() call), depending on the row on which the frame is currently standing.
  • we need to link the down body on the server side with the widgets on the client side. But the client side currently requires that the Frame.downBody contain a number of widgets equal to the frame's down value; if possible, I don't want to change this. Instead, I think we should try to implement this "fixed down body" behavior on server-side too, with a tweak: we maintain some artificial widgets for rows which are "pending to be created". Same on client-side: it starts with only one real row, the others being artificial widgets. As new rows are added, they are replaced with real widgets (which have server-side counterparts). But we need to be careful to still make these "real" widgets linked to only one widget, so that i.e. the label is processed only once (there might be more to take care beside the label).

Do you mean that we should implement something like downBody on server side and make it synchronized with it's "brother" on the client side? If it is so we should includes all array of widget to light object ClientState. It will make this object not so light ) as it is now. Isn't it?

#33 Updated by Vadim Gindin over 10 years ago

What do you mean when you are saying "the frame starts with only one field-group (for the first row). When advancing to the next row, if a new field-group is not already created for the row, it will be created."

Here is the first screen.

┌──────────┐ 1) 1000 FIELD-GROUP 1                        1000 FILL-IN 1 i
│         i│                                              1002 LITERAL ----------
│──────────│ 2) 1004 FIELD-GROUP 2  1000:visible:yes      1000 FILL-IN 1 i
│         1│                                              1004 FILL-IN 2 i
│         2│                                              1002 LITERAL ----------

Yes, the frame starts from only one field-group - for the first row (1000), but when it goes to the next row it creates another field-group for it (1004) and so on until the frame became full. The next frame starts also with new field-group (1006) and common amount of field-groups is becoming DOWN-size + 1. Then created field-group set is used in cyclic order i.e. next field-group for the next row will be the first created field-group (1000) and so on (1004, 1006...).

Field-group content is changes. It starts from the only one Fill-in field and LITERAL. But further this field-group is being extended by one Fill-in field every iteration until those count will become DOWN-size + 1. At this step extending of field-group stops and it's size stay unchanged for next iterations.

#34 Updated by Constantin Asofiei over 10 years ago

Vadim Gindin wrote:

Why it is needed to have downBody array with only the first row real?

The problems are in the code on P2J client-side, which draws the frame. This currently assumes the down body is fixed, so we need to have a fixed number of widgets at any time. I want to avoid rewriting the frame drawing code too much, because that part is hugely complicated. The idea was that initially only the first row is real; as real widgets are added for the other rows, the fake ones will be replaced with real ones.

Do I correctly understand: two widgets in different rows will have the same ID value?

No, you understood wrong. This is what we are trying to move away from: currently, on P2J client side, the same widget ID is used by all rows. This is not correct in 4GL terms, as a frame body has unique widgets for each row and doesn't reuse widgets.

Is that the reason why it is not possible to get correct widget by ID? But at the same time we know the current-line so first of all we can get correct row and then we can get the widget. Will it work?

When performing frame-level operations (drawing, hiding, etc), the protocol between the server and client side uses widget IDs (which, as I noted before, are not resource IDs). Thus, when i.e. performing HIDE i IN FRAME f1 in a down frame, we need to determine the widget ID for i in current-iteration and send that to the client. And if we reuse the ID, we will not be able to distinguish between i on current-iteration and i on another row.

Do you mean that we should implement something like downBody on server side and make it synchronized with it's "brother" on the client side?

Yes, you got it. We need to sync the downBody on server and client side.

If it is so we should includes all array of widget to light object ClientState. It will make this object not so light ) as it is now. Isn't it?

We will not need to send entire frame def back to the client. We will only need to send something like "for row i, create real widgets instead of fake ones and send back the IDs".

#35 Updated by Constantin Asofiei over 10 years ago

Vadim Gindin wrote:

What do you mean when you are saying "the frame starts with only one field-group (for the first row). When advancing to the next row, if a new field-group is not already created for the row, it will be created."

You got it right, your explanation is what I meant - the frame body is created dynamically. The extra row is tricky, but I think we are getting closer to understanding: it looks like some cyclic behaviour, and I think it looks like (for n = 2 there will be 3 field-group, each bullet represents a full displayed frame):
  • 1, 2 - first iteration
  • 3, 1 - second iteration (2 is the "extra")
  • 2, 3 - third iteration (1 is the "extra")

This looks like 4GL keeps a pointer in a circular list, to the field-group displayed on the first row. Please confirm/infirm this with testing; let me know if you have other ideas about this.

Something else I would like for you to check: what happens if CLEAR FRAME is executed after the frame is on its second "fill" (the extra field-group was created and now we are displayed again from row 1?) Is the field-group order affected?

#36 Updated by Vadim Gindin over 10 years ago

Here are next tests.

1. Test: frame_line.p
Description: The goal is to find out is the FRAME-LINE function and CURRENT-ITERATION attribute are synchronous. We are just running repeat cycle and output both of them.
Result:

┌──────────┐       1) 1 1000
│         i│       2) 2 1000
│──────────│       3) 1 1001
│         9│       4) 2 1002
│          │       5) 1 1000
└──────────┘       6) 2 1001
                   7) 1 1002
                   8) 2 1000
                   9) 1 1001

Conclusion: FRAME-LINE - is a logical line number. Its values can vary from 1 to DOWN-size. At the same time CURRENT-ITERATION is a handle to FIELD-GROUP corresponding to each row. But we know, that count of field-groups are 1 higher then DOWN-size. They are linked only indirectly.

2. named, unnamed streams and redirecting to terminal
I only say that it works in the same way (several field-groups created for every frame) only for redirecting to terminal variant. For other variants - there only one field-group is created during execution.
I will commit procedure and output results later. At this moment is not simple for me to compose good all information with DISPLAY statement in spite of PUT SCREEN and to format overall output

3. DOWN-2
Test: down2.p
Goal: Find out is the Progress4GL creates or process all rows in the case where DOWN is explicitly called without repeat cycle and it's size more than one, so it skips some rows count. Find out how such DOWN statement affects the frame output.
Details: Executing the following sequence of statements DOWN1, UP1, DOWN2, DOWN1.
Results:

┌──────────┐  0) 1000 FIELD-GROUP no_parent
│         i│  1) 1001 FIELD-GROUP 1000
│──────────│  2) 1000 FIELD-GROUP no_parent
│         2│  3) 1002 FIELD-GROUP 1001
│         1│  4) 1003 FIELD-GROUP 1002
│         3│
│         4│
└──────────┘

Summary:
After UP 1 statement the current row points to the first row of the frame. After that DOWN 2 statement was called. And as we could expect the current row is now points to the third row. This is founded behavior.
Lets see at the right part of the output. There are following format:
i)   current-iteration:handle   current-iteration:handle:type    current-iteration:prev-sibling:handle

Lets see at line that starts with "3)". We can see that for the current row points to the line 3 (after DOWN 2 statement was executed). And the previous field-group for the current is group with id=1001.
Here is more simple table:
Normal order (all @DOWN 1@)                  Order with @DOWN 2@ 
1001                                         1001
1005                                         1005   
1001                                         1001 
1005                                         1007  
1007                                         1001

Conclusion: For every row a FIELD-GROUP is created. It is just skipped (or did not become visible).

#37 Updated by Vadim Gindin over 10 years ago

About cyclic order of field groups I confirm it. All tests show it. About Fill-in fields - they have equal IDs at all iterations: 1000, 1004, 1006. May be they are reused?

More tests.

1. CLEAR FRAME
Test: clear_frame.p
Description. We have repeat loop from 1 to 9 with down-size=2 and we're trying to execute CLEAR FRAME for different iterations. After that we are looking at field-groups order and Fill-in order.
Results: 2162_clear_frame.txt (clear when i=2) and 2162_clear_frame_i3.txt
Summary:
For the even iterations (i=2,4,6) field-group is reused after CLEAR FRAME. i.e. at the next iteration after CLEAR FRAME the field-group from previous iteration is used.
For the odd iterations (i=3,5,7) field-group order is not affected

2. NO-LABELS
We are looking at affecting of output. No difference except of the fields label is always unknown.

3. additional LITERAL.
I tried to add additional LITERAL field to a frame. No effect. There was only additional string "----" was added to existing LITERAL field in FIELD-GROUP.

About residing of values in the Fill-in fields in one field-group. As far as I can see there are also some cyclic order, but it more complex than in the case of field-groups

#38 Updated by Constantin Asofiei about 10 years ago

Please commmit the tests for note 36/37 to bzr.

About cyclic order of field groups I confirm it. All tests show it.

OK, this means things are starting clear up.

About your DOWN 2 findings: this means that if we do a DOWN n for a frame (where n is the number of rows), field-groups are created for all rows, right? And if DOWN n+1 is executed, the extra field-group should be created too.

About Fill-in fields - they have equal IDs at all iterations: 1000, 1004, 1006. May be they are reused?

No, they are not reused in terms that a new resource is created with the same ID. The behaviour should be this: for each row, its field-group is created when that line is reached via down. When the row's field-group is created, its "columns" (i.e. widgets for each frame element) are created too. When frame reaches a cetain row i.e. a second time, it will determine which field-group will be displayed and use that field-group for the row. The widgets inside a field-group are not moved around, they remain associated with that field-group.

1. CLEAR FRAME

I need to take a look at your testcase first. But I think you should test using other values for the frame's down, beside 2-down.

2. NO-LABELS:

Check what happens if the field's label is changed, using the fill-in for a certain row; do all the fill-ins report the same label? Does the UI change?

additional LITERAL

What I was referring to was to use a frame with two widgets (i.e. FORM i j) and check how the separator is reported: in a single LITERAL (with its value something like ---- ----) or something else.

#39 Updated by Vadim Gindin about 10 years ago

I've committed all test procedures I had.

The result of stream test (streams.p) is in file 2162_ext_frame_stream.txt.
This file contains the procedure output from written file. It shows, that DOWN statement is not working in the case of stream because there is non-interactive UI. It shows, that all field-groups have the same ID, and field-groups contains only one LITERAL field and no other fields.

Constantin Asofiei wrote:

OK, this means things are starting clear up.

About your DOWN 2 findings: this means that if we do a DOWN n for a frame (where n is the number of rows), field-groups are created for all rows, right? And if DOWN n+1 is executed, the extra field-group should be created too.

Not exactly. There are possible 3 cases:
1. DOWN size > value, specified in the DOWN statement.
The Progress creates a number of rows that can fit in a frame with the step, equals to the value, specified in the DOWN statement. The Progress creates Corresponding number of field-groups each iteration.
2. DOWN size < value, specified in the DOWN statement.
The Progress creates rows one by one and outputs every row as first in the frame.
3. DOWN size = value, specified in the DOWN statement.
The Progress outputs exactly "size" rows and creates "size" field-group

When the Progress tries to create next field-groups it looks at groups created in a previous step and using current ordinal field-group number the Progress uses the next field-group in cycle or creates the new one if it does not exist. I.e. it creates additional field-group at the moment of processing the next row.

About Fill-in fields - they have equal IDs at all iterations: 1000, 1004, 1006. May be they are reused?

No, they are not reused in terms that a new resource is created with the same ID. The behaviour should be this: for each row, its field-group is created when that line is reached via down. When the row's field-group is created, its "columns" (i.e. widgets for each frame element) are created too. When frame reaches a cetain row i.e. a second time, it will determine which field-group will be displayed and use that field-group for the row. The widgets inside a field-group are not moved around, they remain associated with that field-group.

Now I understand. Thank you.

1. CLEAR FRAME

I need to take a look at your testcase first. But I think you should test using other values for the frame's down, beside 2-down.

See clear_frame.p procedure

2. NO-LABELS:

Check what happens if the field's label is changed, using the fill-in for a certain row; do all the fill-ins report the same label? Does the UI change?

If NO-LABEL argument is used there will be not LABELS in output at all and we'll not see any label changes.
If NO-LABEL is not used - all labels are changed. UI is changed.

additional LITERAL

What I was referring to was to use a frame with two widgets (i.e. FORM i j) and check how the separator is reported: in a single LITERAL (with its value something like ---- ----) or something else.

I already tested it and as a result the Progress always use single LITERAL and it's screen value just as you wrote).

Conclusion.
We already tested almost all cases. The only unclear moment is the algorithm of Fill-in filling with values for me. What do you think?

#40 Updated by Constantin Asofiei about 10 years ago

The only unclear moment is the algorithm of Fill-in filling with values for me.

I'm not sure what you are refering to. Do you refer to implementing this behavior in P2J or to interpreting 4GL runtime (when fill-in's get "populated" with data)?

#41 Updated by Vadim Gindin about 10 years ago

Constantin Asofiei wrote:

The only unclear moment is the algorithm of Fill-in filling with values for me.

I'm not sure what you are refering to. Do you refer to implementing this behavior in P2J or to interpreting 4GL runtime (when fill-in's get "populated" with data)?

I'm referring to interpreting 4GL runtime.

#42 Updated by Vadim Gindin about 10 years ago

I suppose I can formulate some rule, interpreting the rules, that Progress uses to fill fields with values.
We have 2 DOWN frame and the repeat cycle. At every iteration frame contains 1 or 2 numbers in following order (each column - is iteration):

1st iteration    2nd iteration    3rd iteration   4th iteration      5th iteration     6th iteration
1                 1                 3                3                5                5
                  2                                  4                                 6

For all field-groups starting from 3rd there are 3 Fill-in fields (IDs: 1000, 1004, 1006). They take values in a cyclic order from one iteration to other independent of DOWN calls. Here how it goes (test output):
1st iteration    2nd iteration    DOWN call    3rd iteration   4th iteration     DOWN call    5th iteration
1000  1          1000  1                       1000            1000  4                        1000
1004             1004  2                       1004            1004                           1004  5
1006             1006                          1006  3         1006  3                        1006

I.e. independent of DOWN call next field taking a value is 1006 in a 3rd iteration, then 1000, 1004 and again 1006. Each DOWN call just clears the frame and the next value will become first an a frame. I'm just changing the order of fields ids in a way of when fields are created:
1st iteration    2nd iteration    DOWN call    3rd iteration   4th iteration     DOWN call    5th iteration
1000  1          1000  1                       1006  3         1006  3                        1004  5
1004             1004  2                       1000            1004  4                        1006  
1006             1006                          1004            1000                           1000

The last table looks like the first one but with IDs.
What do you think?

#43 Updated by Greg Shah about 10 years ago

As we are getting close to the time for implementation, I want to mention something.

I would like to consider an implementation where the only changes are on the server side. I like most of the concepts that Constantin previously laid out for implementation ideas. The only difference is that I would like to explore the idea of having a completely fake set of widgets on the server side that duplicate this multiple field-group/sets of widgets behavior. But I don't see a strong reason to actually map all of these to different widgets on the client side. I prefer an approach where multiuple virtual widgets on the server resolve to the correct widget on the client. The client side logic for down processing is really complex. I am worried that if we try to change it to tightly couple that with the server side, that we will break things badly.

#44 Updated by Constantin Asofiei about 10 years ago

  • Estimated time set to 3.00

We need be careful when using conditional down (DOWN WITH FRAME f1, without an expression) and explicit down (i.e. DOWN 1 WITH FRAME f1). This is because frame-line can land "outside of frame" (i.e. on the "extra" row) if a conditional down is executed when frame-line is the same as the down size. This does not advance the row reported by current-iteration: it will advance it only when the next DISPLAY will be performed (as the conditional down is a kind of "delayed" down). So I don't think we can't use the frame-line, we need to use the real row in the frame to which current-iteration points.

In note 42 I think you have a typo, 4th iteration should look like:

4th iteration
1006  3
1000  4
1004

Each time down is called and we are advancing to the next row, we need to compute two pieces of data:
  1. the row to which current-iteration refers
  2. the index into the frame children for this row (i.e. map the row to an actual field-group child for the frame).

Lets assume the frame's field-group children are all kept in an n+1 size array (ignoring the LITERAL widget related to the separator for now); look at how these evolve, for each iteration, in a 2-down frame scoped to a repeat block, using the "hidden" implicit down called on each iteration, as in:

repeat i = 1 to 5:
   display i with frame f1 2 down.
end.

Let's make all indexes 0-based, because will be easier to compute:
                 1st iter     2nd iter     3rd iter     4th iter     5th iter
row:             0            1            0            1            0
field-group idx: 0            1            2            0            1

For a 3-down frame and 10 iterations:
                 1st iter     2nd iter     3rd iter     4th iter     5th iter     6th iter     7th iter     8th iter     9th iter     10th iter
row:             0            1            2            0            1            2            0            1            2            0
field-group idx: 0            1            2            3            0            1            2            3            0            1

Some ways to extract a formula for these two (assume arrays are 0-based, not 1-based):
  1. first attempt: keep a counter c which is adjusted each time an up or down is executed, with the number of steps. The row in the frame to which current-iteration refers will be c mod n and the index in the field-group array will be c mod (n + 1). Lets test using the 3-down frame (for the simplest case):
                     1st iter     2nd iter     3rd iter     4th iter     5th iter     6th iter     7th iter     8th iter     9th iter     10th iter
    row:             0            1            2            0            1            2            0            1            2            0
    field-group idx: 0            1            2            3            0            1            2            3            0            1
    c:               0            1            2            3            4            5            6            7            8            9
    

    But this will not be so simple if there are mixed up/down calls for a frame. As doing an UP 3 while on the 1st row will not "unwind" the frame and start counting from the last row... UP will stop when the first row is executed. Something similar for DOWN when the expression exceeds the number of remaining rows in the frame. This solution would work if we determine the real number of "skipped" rows by the DOWN or UP statement and use that value to adjust our counter.
  2. second attempt: use two indexes for the field-group and real frame row. But, we still need a way to leverage row adjustments in both ways, via UP and DOWN statements. I think this will also require knowledge of the "the real number of "skipped" rows" for the previous case. The first index (for the row) will always vary between 0 ... n - 1 (so this can be computed mod n) while the second index, for the field-group, will vary between 0 ... n (so this will be computed mod (n + 1).

These cases (when the DOWN and UP land outside of frame) should be implemented in P2J. Do some tests, look at the logic in GenericFrame.down, GenericFrame.downWorker, ThinClient.down and ThinClient.advanceCursor and see how these cases are treated in P2J. If we can easily access the real number of steps for UP or DOWN, then is just a matter of doing (rowIdx + steps) mod n for the row index and (fldGrpIdx + steps) mod (n + 1) for the field group index.

#45 Updated by Constantin Asofiei about 10 years ago

  • Estimated time changed from 3.00 to 0.00

Greg Shah wrote:

I prefer an approach where multiuple virtual widgets on the server resolve to the correct widget on the client.

We need to keep in mind that the same widget ID (i.e. for i) is used for each row in a frame. If i.e. we use the widget on field-group for row 1 while the frame is positioned on row 4 (for a 5-down frame), and we wan to hide this widget, P2J will have troubles identifying it. For these cases, P2J will need to use some other info, beside widget ID (like "use widget with ID 1001 on row 1" for my example). Same for accessing other widget attributes, which are not shared. Currently, I think VISIBLE, HIDDEN, SCREEN-VALUE are not shared, but LABEL is shared. We should determine which are shared and which are not.

The client side logic for down processing is really complex. I am worried that if we try to change it to tightly couple that with the server side, that we will break things badly.

I agree. The less we change the client-side logic, the less we will need to chase regressions.

#46 Updated by Vadim Gindin about 10 years ago

I just don't understand the logic of field-groups. Why authors decided to use spare field-group?! I just don't see any necessity of it. Of course I'm not a guru in Progress..
Moreover I reread the doc "Progress Programming Handbook" Chapter "Field-group widgets". It is also helpful..

I wrote test to find out how UP statement behaves and how the field-groups are created.
File: up_algo.p
Description: the test executes the sequence of UP statements and tries to imitate an algorithm of field-groups IDs generation. The test tries to use the logic Constantin wrote about in the note #44 case 1. It tries to keep an current index in the array of field-group IDs.
Results: 2162_up_algo.txt (attached to this task)

I don't see a behavior of UP statement, Constantin, you wrote about. I don't see that it stops after the first row is processed. At the same time I don't understand how it behaves.

#47 Updated by Constantin Asofiei about 10 years ago

Vadim Gindin wrote:

I just don't understand the logic of field-groups. Why authors decided to use spare field-group?! I just don't see any necessity of it. Of course I'm not a guru in Progress..
Moreover I reread the doc "Progress Programming Handbook" Chapter "Field-group widgets". It is also helpful..

The field-groups are used to group widgets together (as their names say). It is a lot easier to i.e. hide/remove the field-group instead of going through each and every one. More, I think there might be cases in 4GL when the child is visible but the parent is not - thus the child is not displayed, because the parent is not visible. You can also look at them as similar to the panel in swing/awt.

I wrote test to find out how UP statement behaves and how the field-groups are created.

Your test is stuck in an edge case, that's why the results are confusing. Make the frame i.e. 10 down and for the first iterate call, pass -4 as the UP step. As the frame is on the top row, it will go on the last row after the UP, not on the 7th row. This is what I've meant: when the UP/DOWN lands outside of frame, going past the edge will stop on the last/first row (depending on which direction you go); it will ignore the fact that there are some reminder steps.

#48 Updated by Vadim Gindin about 10 years ago

Constantin Asofiei wrote:

The field-groups are used to group widgets together (as their names say). It is a lot easier to i.e. hide/remove the field-group instead of going through each and every one. More, I think there might be cases in 4GL when the child is visible but the parent is not - thus the child is not displayed, because the parent is not visible. You can also look at them as similar to the panel in swing/awt.

That is clear. I only meant the spare field-group here. Sorry I formulated it poorly.

I wrote test to find out how UP statement behaves and how the field-groups are created.

Your test is stuck in an edge case, that's why the results are confusing. Make the frame i.e. 10 down and for the first iterate call, pass -4 as the UP step. As the frame is on the top row, it will go on the last row after the UP, not on the 7th row. This is what I've meant: when the UP/DOWN lands outside of frame, going past the edge will stop on the last/first row (depending on which direction you go); it will ignore the fact that there are some reminder steps.

Now I understand.
I modified my test, stayed only one UP call and tried it with different values. Interesting thing, the frame always was staying unchanged. Only the second UP call moved the value to the last row as you said right. Where the first call wrote the value, to the spare field-group? Docs also mentioned a background field-group. It's completely unclear for me how to discover it and distinguish it from a spare field-group. OK, It was just a excursus.. The third sequential UP call displayed it's value using the step from previous UP call (from the second). The fourth UP call displayed it's value using the step from the third call and so forth. It seems that is the strange logic. Take a look at this sequence of calls and its results.

run iterate (input 0, input -1). 
/* no value is displayed in a frame. 
   Probably the value have got into spare field-group 
   and the next call makes this spare group visible.
   Moreover, the step will be 1 independently of the value, 
   specified in the call.
 */

/* each followed call uses the step from the previous call */
run iterate (input 1, input -4).
run iterate (input 2, input -2).
run iterate (input 3, input -3).
/* frame is full. See the first result below */

run iterate (input 4, input -6).
/* this call shows the next frame that is empty (as the first call makes) */

message "next up frame".
run iterate (input 5, input -1).
run iterate (input 6, input -3).
run iterate (input 7, input -1).
run iterate (input 8, input -1).
/* frame is full. See the second result below */

The first argument is a screen value i, that will be displayed in the first column (in the right part). The second argument is the step. Negative step means UP call (positive step - DOWN). It will be displayed in the fourth column.

Here first frame and the correspondent output. The fourth column shows current step. The first column shows the screen-value i.

┌──────────┐            0:       1   1000()        -1      1001 : 2    -1
│       val│            1:       5   1002()        -4      1003 : 4    -5
│──────────│            2:       7   1004()        -2      1001 : 2    -7
│         4│            3:       10   1006()        -3      1009 : 10    -10
│          │          
│         3│
│          │
│         2│
│          │
│          │
│          │
│         1│
└──────────┘

We can see here (1st and 4th columns) that for 1 the step is always 1 as it is transition to the next frame. For 2 the step is 4. For 3 the step is 2. For 4 the step is 3.

Here is the second frame and the correspondent output:

┌──────────┐            0:       1   1000()        -1      1001 : 2    -1
│       val│            1:       5   1002()        -4      1003 : 4    -5
│──────────│            2:       7   1004()        -2      1001 : 2    -7
│          │            3:       10   1006()        -3      1009 : 10    -10
│          │            4:       1   1008()        -6      1001 : 2    -16
│          │            5:       2   1000()        -1      1000 : 1    -17
│          │            6:       5   1010()        -3      1003 : 4    -20
│         8│            7:       6   1002()        -1      1002 : 3    -21
│         7│            8:       7   1012()        -1      1001 : 2    -22
│          │         
│         6│
│         5│
└──────────┘

And here we can see that for 5 step is -1 (transition). For 6 the step is 1. For 7 the step is -3 and for 8 the step is 1.

That's it. What do you think?

#49 Updated by Vadim Gindin about 10 years ago

Marasmus... there is no shift. It's my mistake. It because of wrong order of operations: displaying the value, executing DOWN call, printing info. I'm sorry for that.

#50 Updated by Vadim Gindin about 10 years ago

There are possible several principally different cases:
1. Simple case: All DOWN calls have equal steps size. As it goes in case of implicit DOWN calls (for example in cycles).
2. Different DOWN steps sizes. FIELD-GROUPS set have fixed size. DOWN calls are explicit and have different sizes.
I wrote the test procedure that emulates field-group number generation for this case.
File: up_algo_diff_steps.p
Details: Procedure execute consecutive DOWN calls with different steps and outputs generated field groups and other info. During the calls procedure iterate calculate emulated field-group ID and also outputs real field-group ID (from the current-iteration attribute). There are an array of predefined FIELD-GROUP ids and we are calculating actual index (fgidx) in this array.
Result: attached up_algo_diff_steps1.txt
Details.
The result contains the sequence of output screens. See columns 3 (real ID) and 5 (emulated) in the right part. They have equal values. This algorithm should work for the simple case also. I'll test it later.
3. The starter case when FIELD-GROUPS set have no fixed size yet.
4. UP calls case. fixed and not-fixed size of FIELD-GROUP set.

#51 Updated by Constantin Asofiei about 10 years ago

Vadim Gindin wrote:

There are an array of predefined FIELD-GROUP ids and we are calculating actual index (fgidx) in this array.

Do not do this. Resource IDs are not the same in 4GL and P2J; also, resource IDs are not "fixed" - depending on the order of accessing the field-group or other resources as handles, the IDs may be different. Think of another way to mark a certain field-group: if you need to identify it (without using screen-value), maybe the private-data can help; you can populate all field-groups for the frame (i.e. fill all rows with data, then clear the frame so it is repositioned back to row 1), then iterate through all field-groups and set private-data to some unique identifier (maybe their position in the frame's children).

#52 Updated by Greg Shah about 10 years ago

I assume it is possible to access the PRIVATE-DATA for a FIELD-GROUP widget in 4GL code, which means using that attribute for our own internal P2J purposes could cause problems down the road. Instead, I prefer to just add a data member for this purpose.

#53 Updated by Vadim Gindin about 10 years ago

I tried to find out what algorithm is used to generate new field-groups and to reuse existed. The case 3 in my previous note was about how the new field-groups are generated. If I will mark them after they are created I won't be able to find the way they were generated..

#54 Updated by Vadim Gindin about 10 years ago

I found that I can use the NAME attribute for that purpose (mark the field-group). This attribute has unknown value for FIELD-GROUP widgets. Further I can use the following algorithm:
1. I'm running any of my "current-iteration" procedures.
2. If I've met some FIELD-GROUP first time (NAME attribute is ?) I will assign the next generated name to it, for example "grp1", "grp2" and so on.
3. If the FIELD-GROUP already has the NAMaaE attribute with some value not equal to ?, it will mean that this FIELD-GROUP is now reused.

What do you think about this attribute?

#55 Updated by Greg Shah about 10 years ago

The NAME can still be interrogated from converted 4GL code, right? That means there could be code that relies upon the "trick" of checking for unknown value in a name to avoid some processing on those widgets.

I think it is best to just create a separate data member that is only used for our internal purposes.

#56 Updated by Constantin Asofiei about 10 years ago

Greg: Vadim I think is talking about a way to identify a field-group without using the handle ID in 4GL code, not in P2J runtime.

Vadim: what are your concerns about field-group creation? Did you find that there might be empty rows between two field-groups? Or something else? BTW, you can use an extent var of type handle in 4GL, if you want to save them (in the order of their creation).

#57 Updated by Vadim Gindin about 10 years ago

Constantin Asofiei wrote:

Greg: Vadim I think is talking about a way to identify a field-group without using the handle ID in 4GL code, not in P2J runtime.

Yes, it's correct. That was the primary reason.

Vadim: what are your concerns about field-group creation? Did you find that there might be empty rows between two field-groups? Or something else? BTW, you can use an extent var of type handle in 4GL, if you want to save them (in the order of their creation).

Field-group creation is seemed straightforward except UP direction. No empty rows are possible between two field-groups. My test with different steps sizes shows it. It emulates generation order using that hypothesis and it was succeed. The only "strange" behavior occurs in UP direction. When I ran my script with the continuous sequence of UP 1 calls (starting from the position in the first row). I found that the order of created field-groups are following:

1  2  1  3  4  5  2  1  3  4  5  2  1 

It's field-groups numbers. If the UP calls sequence will start from the position in the second row the order will be following:
1  2  2  1  2  3  4  1  2  3  4  1  2

If the UP calls sequence will start from the position in the second row the order will be following:
1  2  3  2  1  4  3  2  1  5  4  3  2  1

If the UP calls sequence will start from the position in the second row the order will be following:
1  2  3  4  3  2  1  5  4  3  2  1  5  4

Finally in the last calls sequence (DOWN 1, DOWN 1, DOWN 1, UP 1, UP 1, UP 1, UP 1, UP 1, ..) I changed first 3 DOWN 1 calls with one DOWN 3 call. The result:
1  2  3  4  1  5  2  3  4  1  5  2

As a result. I can conclude, that Progress not only use the formula to generate or reuse field-groups but it use an some simple algorithm. That algorithm during its execution holds generated field-groups in some set along with those row numbers. The permutation in UP calls sequence is explained by reusing existing field-groups. When it goes to the other frame it remembers the last used field-group as spare field-group and reuse it later:

To demonstrate all this UP behavior (reusing and row numbers remembering) I can show the screens sequence for the last test (on DOWN 3 call and all other calls are UP 1). See attached file up_seq.txt. Screen values started from 2 (don't be confused by it).

P.S. My emulating algorithm only works for DOWN calls at this moment.

#58 Updated by Constantin Asofiei about 10 years ago

Vadim Gindin wrote:

Field-group creation is seemed straightforward except UP direction.

OK, I understand now. Make sure you include cases like these in your tests too:
  • a 10 down frame
  • use UP 1 calls to reach row 7.
  • use DOWN 1 calls to reach row 3.
  • how are the rows reused?

#59 Updated by Vadim Gindin about 10 years ago

Well, I finished the test for the case of any sequence of DOWN and UP calls.
Test: mixedupdown1.p
Description: The source is the array of steps (inp1, inp2 or other). Concrete source array is specified below in the cycle which runs iterate procedure.
Result: The test outputs some information. See at the columns 2 and 4. Column 2 contains real generated field-groups and column 4 contains emulated field-group and the values of these columns should be equal.

Remained case is: initialization of field-group set. As you know at the start of frame filling field-groups are created when it really needs. It goes until generated field-groups count is equal to frame-size + 1. So mixedupdown1.p works for all cases when all frame-size + 1 field-groups are generated and remained case is the algorithm of field-group generation until there are frame-size + 1 field-groups. This case is strongly confusing.
Test: fgset_init.p
Description: I just tried to find out that algorithm of initial field-group generation and I tried to output all frame field-groups after each initial iteration.
Results:
1. Strange, but access sibling attributes (PREV-SIBLING, NEXT-SIBLING) of field-groups itself distorted generated groups. It also true for FIRST-CHILD attribute.
For example:

Just current-iteration. Field-groups are:
1000, 1001, 1002, 1003, ...
with FIRST-CHILD output:
1000, 1002, 1003, 1004

I don't at this moment how to systematize results of different start of this test (with different input steps). Working on that.

#60 Updated by Vadim Gindin about 10 years ago

I'm trying to identify when the Progress creates auxiliary field-group. There are possible 2 variants:
1. Progress creates auxiliary row right after it creates the current-row.
2. Progress creates auxiliary row when DOWN or UP statement (PREV-SIBLING, NEXT-SIBLING, FIRST-CHILD or some others) is called.
But I don't have possibility to really find out how the Progress does it. The statements that can be used to go through field-groups itself distort target field-group set.

Short example. Suppose I want to execute DOWN 2 statement. After it is executed I want to know what field-groups are contained in a frame. When I will try to traverse trough frame field-groups I will inevitably distort target set.

What should I do further?

#61 Updated by Constantin Asofiei about 10 years ago

1. Strange, but access sibling attributes (PREV-SIBLING, NEXT-SIBLING) of field-groups itself distorted generated groups. It also true for FIRST-CHILD attribute.

You are still using the handle's ID to identify them. This is not the best approach, as (you probably noticed) running the test directly from the editor and not from the command line yields different IDs. For static resources, think of them this way (not necessary how they really work, but to understand how fluid the IDs are):
  • there is a global counter for handle IDs, which starts on 1000
  • all static resources are maintained in a different data structure; when you want to access the handle for a static resource, 4GL will assign the next available handle ID to it and map this ID to the static resource. The key here is "access": a handle ID is not assigned until you actually access that resource's handle.
  • the idea here is that the assigned ID depends on the order on which you access the handles for static resources. Thus, accessing resource R1 then R3 will map i.e. R1->1000 and R3->1001, but if you access R1, R2, R3 you will have R1->1000, R2->1001, R3->1002.

I'm looking through your tests, maybe I get lucky and find a pattern about all the frame row creation stuff. I'll get back to you hopefully today.

#62 Updated by Constantin Asofiei about 10 years ago

The assumption that they were using a circular linked list is valid, but the rules to add the new field-group are obscure. I think they work like this:
  • each frame starts with only one field-group; frame initialy is positioned on row 1, which is associated with the (currently) single available field-group;
  • assume that field-group FG is the field for current-iteration. If we are moving UP, assume we are moving to the left (prev) sibling and if we are moving DOWN, assume we are moving right (next) sibling. The frame's children (i.e. field-groups) are kept in a linked-list. Left-sibling of the first element is assumed to be the last element and right-sibling of the last element is assumed to be the first element (to emulate a circular linked list).
  • all rows which have an associated field-group are marked so (there will be a mapping row-to-fieldGroup, in a R2FG map)
  • when frame is cleared (explicit or implicit), the R2FG map is cleared too.
  • when we need to move UP to target row TR:
    - if TR has an associated field-group, that one will be used
    - if TR has no associated field-group:
    1. if FG's left-side (circular) field-group has no associated row, then it will be used.
    2. if FG's left-side (circular) field-group has an associated row, then a new field-group will be created and added: = to the end of the frame's children, if FG (for current-iteration) is the first element in the list = at the left-side of FG, if FG has a left-side sibling.
  • when we need to move DOWN to target row TR:
    - if TR has an associated field-group, that one will be used
    - if TR has no associated field-group:
    1. if the right-side (circular) field-group has no associated row, then it will be used.
    2. if the right-side (circular) field-group has an associated row, then a new field-group will be created and added at the right-side of FG. But keep in mind that this will not become the first element, will remain the last element in the list.

Note that the field-group associated with the label separator (which has a single LITERAL child, with a -----------like screen-value) is always the last child of the frame.

About that pesky extra field-group 4GL keeps adding. I think this can be explained with the fact that the field-group is created BEFORE the implicit frame clear is executed. Thus, as we are in a state when all field-groups are referenced by rows and all rows have an associated field-group, we have no choice but apply the "add new field-group (depending on direction)" rule. This field-group will be associated with the next row, after the implicit frame clear. Once the frame is cleared, all fieldGroup-to-row associations are removed too, so is no longer needed to create a new field-group, next time we call UP or DOWN.

The above rules describe UP 1 and DOWN 1 calls. When the step is greater than 1, we have some different rules:
  • assume we are on frame-line FL
  • if FL + steps lands outside of frame, then:
    - a number of steps - 1 steps are executed, and field-groups are created for each intermediate row, as if UP 1 or DOWN 1 is executed steps - 1 times.
    - one more step is executed, to land on the last row (for UP) or first row (for DOWN). during this time, the implicit frame clear is done and a the extra field-group be created.
  • if FL + steps lands inside the frame, then field-groups are created for each intermediate row, as if UP 1 or DOWN 1 is executed steps times.

See the up_down_field_group_test.p test I've added to bzr: the frame's children (without the LITERAL one) are shown after each UP or DOWN statement. The field-group's name tells you when it was accessed first (i.e. if the frame-group's name is unknown, then it means it was created just now). Adjust the step calls in up_down_field_group_test.p as you need. See in the following output how the resource's handle ID (last column) follows the internal counter (i.e. if lines are ordered by the internal counter, the ID will be ordered too):

      FIELD-GROUP   0 1647
      FIELD-GROUP   1 1651
      FIELD-GROUP   2 1653
      FIELD-GROUP   3 1655
207   FIELD-GROUP  13 1675
206   FIELD-GROUP  12 1673
205   FIELD-GROUP  11 1671
204   FIELD-GROUP  10 1669
203   FIELD-GROUP   9 1667
202   FIELD-GROUP   8 1665
201   FIELD-GROUP   7 1663
200   FIELD-GROUP   6 1661
      FIELD-GROUP   5 1659
      FIELD-GROUP   4 1657

Let me know if you see something wrong with my analysis or you have questions.

#63 Updated by Vadim Gindin about 10 years ago

The case with UP 1 and down 1 looks good. The question is why have you used additional display statement before the loop in the function step? If steps = 1 the function will output 2 values to the frame..

I found some confusing results with placement of values in the rows of the frame. I changed the down-size to 5 and steps to 3 -7 11 and here what I got.

Here are 2 neighbor iterations.

┌──────────┐       2     FIELD-GROUP   0 1000
│         i│       1     FIELD-GROUP   1 1004
│──────────│       0     FIELD-GROUP   2 1006
│         2│
│         1│
│         0│
│          │
│          │
└──────────┘

┌──────────┐       2     FIELD-GROUP   0 1000
│         i│       106   FIELD-GROUP   1 1004
│──────────│       1     FIELD-GROUP   2 1006
│         2│
│       106│
│         1│
│          │
│          │
└──────────┘

It's a first run of step(1, -7) command. Take a look at some "shift" of the value "1" from the second row to the third. FIELD-GROUP are not affected. This effect appears not every time. Here is another example:

┌──────────┐       101   FIELD-GROUP   0 1000
│         i│       102   FIELD-GROUP   1 1004
│──────────│       103   FIELD-GROUP   2 1006
│       100│       104   FIELD-GROUP   3 1008
│       101│       100   FIELD-GROUP   4 1010
│       102│
│       103│
│       104│
└──────────┘

┌──────────┐       210   FIELD-GROUP   0 1000
│         i│       102   FIELD-GROUP   1 1004
│──────────│       103   FIELD-GROUP   2 1006
│         2│       104   FIELD-GROUP   3 1008
│       210│       2     FIELD-GROUP   4 1010
│       102│
│       103│
│       104│
└──────────┘

It's a first run of step(2, 11) command. Take a look at the value "2" in the first row. Why it is there? FIELD-GROUP are not affected.

Nevertheless your algorithm looks good for the UP/DOWN-1 and looks to what I wrote in mixedupdown1.p.

For the case of UP/DOWN <> 1 I would objected. In my understanding when the step crosses the border amount of real steps is calculated in that way.
1. If the step crosses the top border of the frame than real steps count will be equal to currentRowIndex where index is 1-based as it is in the Progress. I.e. after that step current row will be the last row in the previous frame.
2. If the step crosses the bottom border of the frame than real steps count will be equal to DOWN-size - currentRowIndex + 1 where index is 1-based as it is in the Progress. I.e. after that step the current row will be the first row in the next frame.

What do you think?

#64 Updated by Constantin Asofiei about 10 years ago

Vadim, please commit the exact testcase you used to capture the screens, I can't reproduce your screens easily.

#65 Updated by Vadim Gindin about 10 years ago

Sorry, I already committed it. The file is up_down_field_group_test1.p

#66 Updated by Constantin Asofiei about 10 years ago

Vadim Gindin wrote:

The question is why have you used additional display statement before the loop in the function step? If steps = 1 the function will output 2 values to the frame..

Because I wanted to know on which field-group I am BEFORE I start moving UP or DOWN.

It's a first run of step(1, -7) command. Take a look at some "shift" of the value "1" from the second row to the third.

OK, I've confused you because there is a display k before we start the UP or DOWN, which is the actual case number. So, the shift you see is not a real shift. Replace:

 display k @ i with frame f1.

with
 display k * 100 @ i with frame f1.

and you will see that the 1 on 3rd row becomes 100.

1. If the step crosses the top border of the frame than real steps count will be equal to currentRowIndex where index is 1-based as it is in the Progress. I.e. after that step current row will be the last row in the previous frame.

Correct.

2. If the step crosses the bottom border of the frame than real steps count will be equal to DOWN-size - currentRowIndex + 1 where index is 1-based as it is in the Progress. I.e. after that step the current row will be the first row in the next frame.

Correct.

I don't understand what are your concerns about down size other than 1. Change the program as I noted above, and also change the step: you will see that new field-groups will be added following the same rules: when moving UP, always add to the left-side; when moving DOWN, always to the right-side.

#67 Updated by Vadim Gindin about 10 years ago

Here is some update. Could you look at it. It is not working. Several questions.
1. We need separate widgets lists for every field-group. How to get the widget list for the next field-group? If I correctly understand current implementation (wo my changes) support only one field-group for the frame, that contains widgets for all rows. We need a field-group for every row.
2. Do we really need to present field-groups on the client and correspondingly interchange the widgets between client and server?

#68 Updated by Constantin Asofiei about 10 years ago

Vadim:
  1. there is no IterableResource file in the update
  2. CommonFrame: as is extending IterableResource, the currentIteration method in this interface needs to be removed.
  3. GenericFrame.downWorker: the field-group's prev/next-sibling needs to be updated only when we create the field-group (as this is the time when it is put in place). Later on, someone else may become its prev/next-sibling, but this is because someone was added on his left/right side, not that it was moved. Also, when linking them, although this acts like a circular list, keep in mind that the first and last field-group have no prev or next sibling, accordingly. So, you need a way of knowing which element is first in the list and which is last.
About the algorithm, lets do this in phases:
  1. for a first approach:
    - assume the field-groups contain the same widgets (i.e. what you are currently doing)
    - only UP 1, DOWN 1 or implicit down is called
    - ignore the LITERAL widget for the frame header separator
  2. once first phase seems to work, add full support for the UP n and DOWN n statements and the LITERAL widget.
  3. once the field-group creation is working, we can move to the hard part: maintaining virtual copies of the frame's widgets, per each "row field-group". The P2J Client side needs to remain unchanged (or as few changes as possible), we don't want to add a widget for each row, as this will bring major changes in the drawing code. What this means: GenericFrame.n2w will be used for actual drawing, so we don't want to save these field-groups in the rowsFieldGroups map; instead, we will need to use copies of them, as the field-group's children. Some careful consideration will be needed so that SCREEN-VALUE and other widget-level functions use the "row field-group"-level data and not the "frame-line"-level data (i.e. currently, they all target the frame-line, so we can't access the widget data from another line).

#69 Updated by Vadim Gindin about 10 years ago

Here is the next update that works some way.

#70 Updated by Vadim Gindin about 10 years ago

Next update works for almost all up/down cases including different step sizes (not only 1). Here are two remained unclear cases.

1. I don't understand the logic of the case where all steps are -1. Here are two intermediate screens.

┌──────────┐       210   FIELD-GROUP   0 1054
│         i│       209   FIELD-GROUP   1 1058
│──────────│       208   FIELD-GROUP   2 1060
│       200│       207   FIELD-GROUP   3 1062
│       210│       200   FIELD-GROUP   4 1064
│       209│
│       208│
│       207│
└──────────┘

┌──────────┐             FIELD-GROUP   0 1054
│         i│             FIELD-GROUP   1 1058
│──────────│             FIELD-GROUP   2 1060
│       206│             FIELD-GROUP   3 1062
│          │       206   FIELD-GROUP   5 1066
│          │             FIELD-GROUP   4 1064
│          │
│          │
└──────────┘

Why did the field-group 5 became not the last in the field-group list?
Test file is up_down_field_group_test2.p

2. I don't understand why in the following test the next used field-group is grp2. Take a look:

┌──────────┐  1:       grp1        -1      grp1 :        0[1]
│       val│  2:       grp2        -1      grp5 :        4[4]
│──────────│  3:       grp1        -1      grp4 :        3[3]
│          │  4:       grp3        -1      grp3 :        2[2]
│          │  5:       grp4        -1      grp2 :        1[1]
│          │  6:       grp5        -1      grp1 :        0[4]
│         6│
└──────────┘
┌──────────┐  1:       grp1        -1      grp1 :        0[1]
│       val│  2:       grp2        -1      grp5 :        4[4]
│──────────│  3:       grp1        -1      grp4 :        3[3]
│          │  4:       grp3        -1      grp3 :        2[2]
│          │  5:       grp4        -1      grp2 :        1[1]
│         7│  6:       grp5        -1      grp1 :        0[4]
│         6│  7:       grp2        -1      grp5 :        4[3]
└──────────┘

It is two intermediate screens of the test case bp_mixedupdow1.p with input steps array inp2 (you can specify it in lines 42 and 43). Look only at the second column.

In other cases the current implementation works fine. Currently I'm working on described cases.

#71 Updated by Constantin Asofiei about 10 years ago

Vadim, when posting test scenarios, please make sure the bzr test is setup exactly the same way you used to produce the intermediate screens.
From your cases:

1. I don't understand the logic of the case where all steps are -1. Here are two intermediate screens.
[...]
Why did the field-group 5 became not the last in the field-group list?

I think this is because the frame is cleared AFTER the next row is computed and populated; thus, when all rows are referencing a field-group and there are the sibling field-group is "occupied", a new field-group will be created and added to the list, depending on the location of the current field-group and the direction (up or down). See these rules from note 62:

2. if FG's left-side (circular) field-group has an associated row, then a new field-group will be created and added: = to the end of the frame's children, if FG (for current-iteration) is the first element in the list = at the left-side of FG, if FG has a left-side sibling.

2. if the right-side (circular) field-group has an associated row, then a new field-group will be created and added at the right-side of FG. But keep in mind that this will not become the first element, will remain the last element in the list.

This is the reason the maximum number of existing field-groups for a frame is not the same as the frame's body. And I think your problem is because in GenericFrame.downWorker you are computing the target field-group AFTER the down/up is performed, not before.

It is two intermediate screens of the test case bp_mixedupdow1.p with input steps array inp2 (you can specify it in lines 42 and 43). Look only at the second column.

I need the exact setup of the bp_mixedupdow1.p used to capture the intermediate screens, to comment on this. Is not obvious just looking at the screens.

#72 Updated by Vadim Gindin about 10 years ago

Constantin Asofiei wrote:

..
I need the exact setup of the bp_mixedupdow1.p used to capture the intermediate screens, to comment on this. Is not obvious just looking at the screens.

Sorry. I just wanted to say that bp_mixedupdown1.p is intended to be ran for one of predefined tests - input arrays of steps defined at the start of procedure. Concrete input array of steps is specified in the line 42 And I talked about input array inp2.

Anyway I made bp_mixedupdown2.p for the input array inp2. Please use that script.

#73 Updated by Constantin Asofiei about 10 years ago

OK, I guess this test tries to predict which field-group will be next, after a UP/DOWN call? I think your problem is the same as the up_down_field_group_test2.p problem; for this screen:

┌──────────┐  1:       grp1        -1      grp1 :        0[1]
│       val│  2:       grp2        -1      grp5 :        4[4]
│──────────│  3:       grp1        -1      grp4 :        3[3]
│          │  4:       grp3        -1      grp3 :        2[2]
│          │  5:       grp4        -1      grp2 :        1[1]
│         7│  6:       grp5        -1      grp1 :        0[4]
│         6│  7:       grp2        -1      grp5 :        4[3]
└──────────┘

The frame's field-groups are chained like this:
grp1
grp2 7
grp5 6
grp4
grp3

If you print the state of the field-groups after each iteration, as you do for up_down_field_group_test2.p, things will make sense: when positioned on grp5, an UP -1 is performed; this will look at the left-side of grp5, which is grp2. As grp2 is free, it will be used to output 7.

#74 Updated by Vadim Gindin about 10 years ago

You are right. I made needed corrections and now it works for case "-1". At this moment I'm fixing another border case: up/down step (not equal to 1) when field-group set is not yet formed. In this case intermediate field-groups must been created. I'm debugging this case. Hope, I will upload next update tomorrow with all up/down cases work.

#75 Updated by Vadim Gindin about 10 years ago

Well It happens! I finished the implementation. At this moment all steps are supported. This implementation passed all tests I have in bp_mixedupdown1.p procedure. I've added some more tests there. I stuck with my previous implementation and I had to rewrite it. Take a look please.

#76 Updated by Constantin Asofiei about 10 years ago

Vadim, good work getting the algorithms implemented. See bellow for the code review for 0322a.zip and the next steps:
  • method_attributes.rules is missing a header entry
  • BaseEntity
    - please explain the reason behind "firstAsLast". What I don't understand is the if it initially assumed to be inserted as first. phrase from the parameter's javadoc.
    - getNextSibling - BaseEntity is always a HandleChain instance, so if (this instanceof HandleChain) will always return true.
  • CommonFrame
    - place extends IterableResource on its own row
    - CommonFrame.currentIteration needs to be removed
  • FrameWidget
    - there are no real changes in the file, so you can discard all of them
  • GenericFrame
    - when multiple classes from the same package are imported, you need to import the entire package, via *
    - wrapWidgetsToFieldGroup - the javadoc is incorrect, there is no asLast parameter
    - there is no need to add constants for error messages, you can inline them. So please remove INCORRECT_NUMBER_OF_LINES and GROUP_HANDLE_IS_NULL.
    - currentIteration: you are throwing an IllegalStateException if a field-group is not found for the current line, with the "There are no correspondant field-group in a frame" message. Please change the message to String.format("There is no correspondent field-group for line %d in frame %s [%d].", line, frameName, frameId).
    - clear: I don't think the logic is correct. clear corresponds for the CLEAR FRAME statement and clearAll corresponds to CLEAR FRAME frameName ALL statement. Only CLEAR ALL touches the entire frame, and currentFieldGroup I think must become the field-group for the first row. You need some testcases to determine which field-group is associated to the first row, after a CLEAR ALL statement.
    - getName: in 4GL, the equivalent to java null is the unknown value. So, when working with BDT instances (which map legacy 4GL data types), you need to use toStringMessage, to get the string representation of a BDT instance and isUnknown to check if the value is unknown. So, for FIELD-GROUP:NAME, looks like it can't accept an unknown value, but the initial value for this attribute IS unknown. Thus, you need to:
    1. add FieldGroup.name(character), which shows the appropriate error if the passed argument is unknown
    2. in GenericFrame.getName, use widget.name().toStringMessage(), if the widget is a FieldGroup.
    - you need to test how shared frames work with current-iteration: is current-iteration inherited for shared frames, and back to the shared frame definition?
    - downWorker - there is this line int down = dynamicDown; // TODO =0 at first start at this moment. It's wrong. - what is the state of this TODO?
    - actualizeFieldGroups - please rename it to updateFieldGroups. Also, the javadoc mentions that only DOWN statement can create multiple field-groups (when step is not 1), and this applies to UP too, when step is not 1.
    - instead of using getResource() != null (in several methods), use the isUnknown() API.
    - isFGAssigned - you can avoid calling asWidgetHandle for each entry and thus creating multiple handle instances, by comparing the FieldGroup instances directly: extract the FieldGroup from the groupHandle parameter before the for loop, and compare the entry's FieldGroup with the received one.
    - I think there is a problem with conditional UP or DOWN statements. downWorker always readjusts the field-groups, regardless of the type of down. Takes this example, which uses a conditional down:
    def var i as int.
    form i with frame f1 4 down.
    do i = 1 to 4:
      display i with frame f1.
      down with frame f1.
    end.
    message "frame is full, not cleared, on row" frame-line(f1).
    display i with frame f1.
    

    Although the frame is reported as being on row 5, it does not get cleared until one more display or explicit down is performed. I think downWorker and actualizeFieldGroups need to keep track of the state of the cond flag too.
  • HandleChain
    - please format the code at lines 153-155 according to standard
    - this test at line 154 before.getPrevSibling().getResource() == null, sugests that the before argument can be unknown: this should never be allowed, as it will result in an inconsistent chain.
    - if the c'tor or method signature exceeds the max line length, then the parameters need to be placed one per line, as in:
       public HandleChain(boolean     dynamic,
                          boolean     saveFirstLast, 
                          HandleChain before,
                          boolean     firstAsLast)
    

    Also, column-aligning the parameter names will make the code more readable.
    This applies to some other methods in your code, too.
    - please order the private instance methods according to standard (they are last in the class, before inner classes). Check your other code changes, too.
Conclusions, for your next steps (in this order):
  1. clean up the code (formatting, javadoc, address review notes, etc) and post the update here.
  2. check the shared frame cases: I suspect the current-iteration will be inherited from the "master" frame to the shared frame and back, but we need to confirm this. You will need two programs: one program with a DEFINE NEW SHARED FRAME f1... and another program, ran from the first program, with a DEFINE SHARED FRAME f1.... Check the syntax for the DEFINE FRAME statement.
  3. fix the CLEAR ALL, CLEAR, the implicit down and the conditional up/down cases: the frame clear from a conditional down I think is triggered directly at the P2J client, so you will need to start digging into P2J client code.
  4. once these are fixed, you can go ahead and think about the widget virtual copies in each field-group, only on server-side.

#77 Updated by Vadim Gindin about 10 years ago

I've fixed almost all your remarks. Here are to moments:
  1. GenericFrame imports couldn't become simpler because of possible ambiguity of classes ErrorManager or LogHelper or BlockManager.Condition wich causes compile errors.
  2. downWorker down = dynamicDown comments. The problem is following. dynamicDown is calculated on the server-side to prevent client calling. At the first iteration dynamicDown is equal to 0 independently of real down-size, but this behavior doesn't affect field-groups updating algorithm. So we can leave it "as is". What do you think?
  3. I don't understand your remark:

..
- this test at line 154 before.getPrevSibling().getResource() == null, sugests that the before argument can >be unknown: this should never be allowed, as it will result in an inconsistent chain.
..

In that place I'm looking at the previous element of before object to find out if before is the first element in this chain. About before object - as a parameter of constructor I allow it to be null to specify the case when I want to add new object been created would added as the last element in chain.

#78 Updated by Vadim Gindin about 10 years ago

Constantin Asofiei wrote:

..
  1. check the shared frame cases: I suspect the current-iteration will be inherited from the "master" frame to the shared frame and back, but we need to confirm this. You will need two programs: one program with a DEFINE NEW SHARED FRAME f1... and another program, ran from the first program, with a DEFINE SHARED FRAME f1.... Check the syntax for the DEFINE FRAME statement.
    ..

You are right. I've added 2 tests down_shared.p and down_shared_ext.p The first procedure calls the second procedure. Both procedures have same print_frame procedure logic and outputs the same output. It means that the shared frame inherits the current-iteration form the procedure where that frame was defined.

#79 Updated by Constantin Asofiei about 10 years ago

Vadim, about 0324a.zip: GenericFrame and other files don't have the proper history number (is the same as the previous one) - please double check all files.

GenericFrame imports couldn't become simpler because of possible ambiguity of classes ErrorManager or LogHelper or BlockManager.Condition wich causes compile errors.

Actually, your changes require only the import java.util.Map.Entry; line, the other import lines can remain unchanged, as they are in the previous version of GenericFrame.

downWorker down = dynamicDown comments. The problem is following. dynamicDown is calculated on the server-side to prevent client calling. At the first iteration dynamicDown is equal to 0 independently of real down-size, but this behavior doesn't affect field-groups updating algorithm. So we can leave it "as is". What do you think?

OK, what you are saying is that dynamicDown is updated only after the real frame down is computed on the P2J client side (after initial frame display/view), and is 0 until so. But, the frame view can be triggered by a DOWN (or UP), too, so when downWorker is called in this case:

def var i as int.
form i with frame f1 with down.
down 5 with frame f1.
display i with frame f1.

will dynamicDown be 0? If yes, are the field-groups computed correct? What if we do a DOWN 100 (to land outside of the last line)?

About before object - as a parameter of constructor I allow it to be null to specify the case when I want to add new object been created would added as the last element in chain.

Is OK allowing before to be null. And is OK to test for

before.getPrevSibling() == null
. But what I don't like is the
before.getPrevSibling().getResource() == null
test, which is equivalent to before.getPrevSibling().isUnknown(): this suggests that it is possible for the prev sibling of the before parameter to be unknown, and we intentionally expect for cases when the sibling is unknown - this must never happen. This test is confusing, and I think is best to be removed.

#80 Updated by Vadim Gindin about 10 years ago

  1. Yes, Constantin. I really skipped this case. In your example dynamicDown=0 indeed. The problem appears when display is called after down statement or display is called before current-iteration. In the first case the place where the problem is born is GenericFrame.downWorker (where I placed TODO). In the second case the problem is born in GenericFrame.currentIteration. It calls frameLineWorker() that returns 0. Could you advice me something in this situation? The comments in the code tell that there is a try to avoid client call in this case, but we need to know real down-size at the start to make field-group algorithm work correctly.
  2. About before.getPrevSibling().isUnknown(). Take a look at the implemenation of methods HandleChain.getPrevSibling() and HandleChain.getNextSibling(). They create the new handle even if prevSibling/nextSibling is null. It forces me to check isUnknown() too.

#81 Updated by Vadim Gindin about 10 years ago

I forgot to add, that your short test case shows the problem dynamicDown=0 in another aspect. This wrong behavior leads to incorrect display of the current value:

Progress4GL   Java impl
┌──────────┐ ┌──────────┐
│         i│ │         i│
│──────────│ │──────────│
│          │ │         0│
│          │ │          │
│          │ │          │
│          │ │          │
│          │ │          │
│         0│ │          │
│          │ │          │
│          │ │          │
│          │ │          │
│          │ │          │
└──────────┘ └──────────┘

#82 Updated by Constantin Asofiei about 10 years ago

Vadim Gindin wrote:

I forgot to add, that your short test case shows the problem dynamicDown=0 in another aspect. This wrong behavior leads to incorrect display of the current value:

OK, this suggests that we may need to force a "view frame" before (or at the beginning) of downWorker. This forced view will be done only if this is the very first view (I think testing for "dynamicDown is zero" should be enough). The view will layout the frame and compute the dynamicDown value, on P2J client side; after the view, when the P2J client returns control back to the P2J server, it will sync the client-side state with the server-side state, via the StateSynchronized.applyChanges (which is implemented by LogicalTerminal on server-side).

#83 Updated by Constantin Asofiei about 10 years ago

Vadim Gindin wrote:

  1. Yes, Constantin. I really skipped this case. In your example dynamicDown=0 indeed. The problem appears when display is called after down statement or display is called before current-iteration. In the first case the place where the problem is born is GenericFrame.downWorker (where I placed TODO). In the second case the problem is born in GenericFrame.currentIteration. It calls frameLineWorker() that returns 0. Could you advice me something in this situation? The comments in the code tell that there is a try to avoid client call in this case, but we need to know real down-size at the start to make field-group algorithm work correctly.

See the previous reply: the forced view should solve this case too.

  1. About before.getPrevSibling().isUnknown(). Take a look at the implemenation of methods HandleChain.getPrevSibling() and HandleChain.getNextSibling(). They create the new handle even if prevSibling/nextSibling is null. It forces me to check isUnknown() too.

You are correct, forgot about that. So is the null equality still needed, if getPrevSibling and getNextSibling always returns something not-null?

#84 Updated by Vadim Gindin about 10 years ago

Here is the next update with the following changes.
  1. I added forced view at the start of downWorker method and it really helps. Could you look there - Did I used correct method?
  2. I changed and checked clear all case. It works well. I also added new test case clearall.p that tests this case. It creates down frame, makes couple of conditional down calls and clears the frame.
  3. I changed currentIteration implementation. Previous depended on frame-line. That was wrong for conditional calls. Such calls change frame-line but can stay current-iteration unchanged. Instead of calculating current-iteration using frame-line I simply used existing currentFieldGroup handle that was added during common algorithm implementation. It works. Checked also by test clearall.p where current-iteration is outputted as first number in a row. I also found that current-iteration is 0-based.
  4. I implemented field-groups updating for conditional up/down calls. It works, but during the work on that I found the displaying bug:
    Testcase conditional.p
    Makes down-frame and executes 10 iterations. Each iteration it makes 2 up() calls. Each 3rd iteration it displays the number in the frame.
    Progress4GL:
    ┌──────────┐       (0) 1000 FIELD-GROUP      FIELD-GROUP gr1 1000 !
    │         i│       (0) 1000 FIELD-GROUP
    │──────────│       (0) 1000 FIELD-GROUP
    │         3│
    │          │
    │          │
    │          │
    └──────────┘
    Java:
    ┌──────────┐       (0) 8924077583479155      FIELD-GROUP gr1 892407758347915515
    │         i│       (0) 892407758347915515 FIELD-GROUP                          
    │──────────│       (0) 892407758347915515 FIELD-GROUP                          
    │          │                                                                   
    │          │                                                                   
    │          │                                                                   
    │         3│                                                                   
    └──────────┘                                                                   
    

    Note that in spite of only up() calls. after the first display the value is in the first line in P4GL and I don't understand why. It is expected that after up() call it will be the last, as it really is in Java impl.

Finally, you asked about necessity of null-checking of prev-sibling in a HandleChain constructor. It really necessary because of the following reason. HandleChain has successor BaseEntity it overrides siblings methods in a way little different from obvious HandleChain implementation. It has group field and redirect sibling calls to groups child methods calls. Look at BaseEntity.getPrevSibling() for example. Child methods can return null in common case. That is why the source null-check is needed, If I understand situation correctly.

#85 Updated by Vadim Gindin about 10 years ago

I found and fixed a bug with start conditional step. The key was in the fact that conditional calls do not affect the first display call and for the up() method there wasn't such implementation as it is in down() method. I also checked implicit calls - It works. Here is the current update that covers all cases.

#86 Updated by Vadim Gindin about 10 years ago

I'm thinking about virtual copies of widgets on the server side. I need some advice. I'm planning to create new field GenericWidget[][] downBody in GenericFrame class that will be an "alter ego" of Frame.downBody. If I correctly understand I should fill this array with virtual widgets at the start. During the displaying of a next row Frame.drawUninitialized() (client-side) should initiate a call of server-side with some info "for row i create real widgets instead of fake ones and send back the IDs" (as you wrote earlier). So probably this info could consist of 2 fields: boolean createRealWidgets, int i added to ServerState. Server side will create real widgets and return those IDs to the client in ClientState. That is my view. Is it correct?
Questions:
  1. Server side can initiate a call to the client-side by LogicalTerminal methods. How can client-side initiate a call to the server-side?
  2. Virtual widgets. How they differ from real widgets? May be real widgets have IDs and virtual widgets don't? When ID is assigned to newly created widget?

#87 Updated by Constantin Asofiei about 10 years ago

Vadim Gindin wrote:

I'm thinking about virtual copies of widgets on the server side. I need some advice. I'm planning to create new field GenericWidget[][] downBody in GenericFrame class that will be an "alter ego" of Frame.downBody. If I correctly understand I should fill this array with virtual widgets at the start.

Yes, the server-side will need the "alter ego" downBody, but the widgets will not be populated by the client-side and the client-side will have no real knowledge about these widgets.

  1. Server side can initiate a call to the client-side by LogicalTerminal methods. How can client-side initiate a call to the server-side?

The client-side can use the ThinClient.server field, which is a network proxy to the server-side LogicalTerminal, which statically implements the ServerExports interface.

  1. Virtual widgets. How they differ from real widgets? May be real widgets have IDs and virtual widgets don't? When ID is assigned to newly created widget?

How I image the virtual widgets working: on server-side they need to be real resources, as their state can be changed/checked individually. Thus, each of them will have their own resource ID (as 4GL does). The tricky part is, when accessing these "virtual widgets" from server side: I think is best to intercept these calls via an anonymous proxy and redirect them to the client-side, so that the appropriate widget (from Frame.downBody) is accessed/changed. I guess this access will depend on the row to which the field-group is associated and also each individual widget's internal ID (the ID used by the server-side and client-side to identify a widget in a frame, set at the ComponentConfig.id field - this is not the resource ID, is an internal ID used by P2J).

Before moving into implementing the virtual widgets, I think is best to:
  1. merge 0401a.zip with the latest bzr revision
  2. explain and address the remaining TODOs added by you in GenericFrame
  3. put the revised 0401a.zip into runtime and conversion regression testing. This will help us isolate any problems with this update early on. And if it passes, I think is best to release it to bzr.
  4. experiment and create testcases to see what happens when a down frame is used and widget state is changed, for a widget which is not the one on the current frame-line:
    - what happens if the HIDDEN or VISIBLE state is changed?
    - same for SCREEN-VALUE?
    - what happens when changing the widget's LABEL attribute, when the FRAME is set with or without SIDE-LABELS? When side-labels are use, looks like the row's FIELD-GROUP is added some LITERAL widgets for each widget, with the widget's label.
    - what happens if the cases above are done from a trigger? When are the changes pushed to the screen - when the trigger ends, or is it needed an explicit view of the frame, to update the screen?
    - whatever else you can think of.

The test can look like, but not limited to:

def var h as handle.
def var i as int.
def var j as int.
form i j with 10 down frame f1.

do i = 1 to 9:
   display i j with frame f1.
   if i = 1 then h = frame f1:current-iteration.
   down 1 with frame f1.
end.

h:first-child:screen-value = "11".

up 1 with frame f1.
frame f1:current-iteration:first-child:screen-value = "22".

view frame f1.

up 1 with frame f1.
frame f1:current-iteration:first-child:visible = false. /* does this update the screen right away or on next view? if update is done right away, does this affect the entire frame (i.e. the screen buffer is pushed to the client for the entire frame), or just this widget? */

#88 Updated by Vadim Gindin about 10 years ago

1. I forgot about LITERAL field-group. Added this implementation.

2. I also found one bug during the work on TODOs in GenericFrame. The case is in the following thing: frameLineWorker() can return the value outside the frame. For example 4-down frame and following calls:

down(3). /* go to the last row */
down().  /* conditional call +1 */
down(1). /* wrong value in the first  frameLineWorker() call */

During processing of the last down(1) frameLineWorker() will return 5. By the way the next frameLineWorker() call will return correct value 1. I fixed this bug by correction of returned value.

Take a look at next update please. If it is OK I will run regression testing.

#89 Updated by Constantin Asofiei about 10 years ago

Vadim, about 0403a.zip:
  • after merging, the history entry numbers for changes which are in bzr can't be changed. This translates to: after merging, your history entry needs to be last. i.e. in BufferImpl instead of this:
    ** 035 VIG 20140314 Changed currentIteration() method signature. 
    ** 036 VMN 20140330 Added LockType errors processing.
    

    you must have this:
    ** 035 VMN 20140330 Added LockType errors processing.
    ** 036 VIG 20140314 Changed currentIteration() method signature. 
    
  • you have some code which doesn't follow the coding standards. See the conditions in GenericFrame lines 10073, 10152, 10160, 10175: the operator needs to be right-aligned and the operands need to be left-aligned.
  • GenericFrame.frameLineWorker - isn't the comment related to how actually FRAME-LINE function works? More, I think FRAME-LINE follows the CURRENT-ITERATION attribute, so this is why it can return the frame-down + 1 value.
  • GenericFrame.downWorker - this code I think reads better with Math.min and Math.max:
          // correct if it really needed
          savedFrameLine = savedFrameLine > dynamicDown ? dynamicDown : savedFrameLine;
          savedFrameLine = savedFrameLine < 0 ? 1 : savedFrameLine;
    

#90 Updated by Vadim Gindin almost 10 years ago

It happen! The fixed update passed regression testing. I also made corrections corresponding to previous note. Please take a look. Can I commit it?

#91 Updated by Constantin Asofiei almost 10 years ago

Vadim Gindin wrote:

It happen! The fixed update passed regression testing. I also made corrections corresponding to previous note. Please take a look. Can I commit it?

Great. Before committing, I need to know what is the state of the TODOs which are left behind the code, in GenericFrame:
  • conditionUpDown
    // TODO frameLine can be incorrect here (not in a frame)
             // int currentRow = frameLine;
    
  • printFieldGroups and updateFieldGroups - TODO delete it. - please post how the debug info looks like, as it might be OK to leave this API behind, and not just delete it.
  • getFGRow - TODO: use bidirectional map

#92 Updated by Vadim Gindin almost 10 years ago

Constantin Asofiei wrote:
..

Great. Before committing, I need to know what is the state of the TODOs which are left behind the code, in GenericFrame:
  • conditionUpDown
    [...]

The point is that updateFieldGroup methods takes currentRow that must be valid row number, but the frameLine returned from correspondent method can be out of the frame borders. So we can't use it as a parameter for updateFieldGroup. I'll delete TODO

  • printFieldGroups and updateFieldGroups - TODO delete it. - please post how the debug info looks like, as it might be OK to leave this API behind, and not just delete it.

Here is an example:

3
 gr1 [null, gr2]      gr2 [gr1, gr3]      gr3 [gr2, gr4]      gr4 [gr3, gr5]      gr5 [gr4, UN]      UN [gr5, null]     
1 -> gr5, 

3
 gr1 [null, gr2]      gr2 [gr1, gr3]      gr3 [gr2, gr4]      gr4 [gr3, gr5]      gr5 [gr4, UN]      UN [gr5, null]     
1 -> gr5, 2 -> gr1, 

3
 gr1 [null, gr2]      gr2 [gr1, gr3]      gr3 [gr2, gr4]      gr4 [gr3, gr5]      gr5 [gr4, UN]      UN [gr5, null]     
1 -> gr5, 2 -> gr1, 3 -> gr2, 

Here are groups of several triplets of strings.
The first line in a triplet is a number of lines to step. Can be < 0.
The second row displays groups each with it's neighbors in square brackets [left, right].
The third row is a mapping from the row number to field-group.

  • getFGRow - TODO: use bidirectional map

The field rows2fg is used in both directions: get the value by the key, and vice versa - the key by value. So bidirectional map (such as some implementation of org.apache.commons.collections.BidiMap) would be better here instead of usual HashMap, but this class is in an external jar file. I'm not sure if I can use it.

#93 Updated by Constantin Asofiei almost 10 years ago

Vadim Gindin wrote:

  • conditionUpDown
    [...]

The point is that updateFieldGroup methods takes currentRow that must be valid row number, but the frameLine returned from correspondent method can be out of the frame borders. So we can't use it as a parameter for updateFieldGroup. I'll delete TODO

OK, but leave behind a comment with what you noted here (that frameLine can't be used because we need valid row numbers).

  • printFieldGroups and updateFieldGroups - TODO delete it. - please post how the debug info looks like, as it might be OK to leave this API behind, and not just delete it.

Here are groups of several triplets of strings.

OK, add this to the printFieldGroups's javadoc, and remove both TODOs

  • getFGRow - TODO: use bidirectional map

The field rows2fg is used in both directions: get the value by the key, and vice versa - the key by value. So bidirectional map (such as some implementation of org.apache.commons.collections.BidiMap) would be better here instead of usual HashMap, but this class is in an external jar file. I'm not sure if I can use it.

OK, for this update leave the TODO behind. As the commons-collections jar is already in the project, it doesn't add any new dependencies if using BidiMap; but which implementation do you want to use?

#94 Updated by Vadim Gindin almost 10 years ago

About bidirectional map implementation. That is the moment I also wanted to ask you advice about. There are possible two choices:

1. Simple choice is TreeMap (no external jars dependencies) and use its appropriate containsKey and containsValue methods. The minus of this variant is doubled map storage (there are two maps are stored keyToVal and valToKey If I remember correctly).
2. TreeBidiMap implementation of BidiMap (from common-collections.jar). It holds the common map storage for both cases.

I suppose that the storage capacity is not critical here and we can just use the first variant. What do you think?

#95 Updated by Constantin Asofiei almost 10 years ago

Vadim Gindin wrote:

1. Simple choice is TreeMap (no external jars dependencies) and use its appropriate containsKey and containsValue methods. The minus of this variant is doubled map storage (there are two maps are stored keyToVal and valToKey If I remember correctly).

I'm not sure how you can use containsKey and containsValue to retrieve the key associated with a certain value.

2. TreeBidiMap implementation of BidiMap (from common-collections.jar). It holds the common map storage for both cases.

Does this require the values to be Comparable? Because when I hear Tree, I think of something sorted...

What do you think?

This is for a future update. In the end, it might be simpler to just add an additional map, fg2rows, which is maintained alongside rows2fg.

At this time: please make the changes in note 93 related to javadoc/comments, submit the update for review, and you can release it after I take a final look at it.

#96 Updated by Vadim Gindin almost 10 years ago

Constantin Asofiei wrote:

Vadim Gindin wrote:

1. Simple choice is TreeMap (no external jars dependencies) and use its appropriate containsKey and containsValue methods. The minus of this variant is doubled map storage (there are two maps are stored keyToVal and valToKey If I remember correctly).

I'm not sure how you can use containsKey and containsValue to retrieve the key associated with a certain value.

Oh, It was my brain fog ). Sure the TreeMap can't be used here.

2. TreeBidiMap implementation of BidiMap (from common-collections.jar). It holds the common map storage for both cases.

Does this require the values to be Comparable? Because when I hear Tree, I think of something sorted...

Yes, It does require that.

What do you think?

This is for a future update. In the end, it might be simpler to just add an additional map, fg2rows, which is maintained alongside rows2fg.

At this time: please make the changes in note 93 related to javadoc/comments, submit the update for review, and you can release it after I take a final look at it.

I corrected TODOs corresponding to your remarks. Take a look at this update please.

#97 Updated by Constantin Asofiei almost 10 years ago

Vadim, I've made some edits in the attached update: please commit and release this one.

#98 Updated by Vadim Gindin almost 10 years ago

Committed to bzr rev #10522.

#99 Updated by Vadim Gindin almost 10 years ago

Tests about virtual widgets (сommitted to current-iteration folder).

1. The frame without side labels. Changing widget attributes from different rows.
Test: virtual-widgets.p
Description: 10-down frame without side-labels, filled by values from 1 to 9. Several attributes from different widgets (from different rows) are changed: "screen-value", "hidden", "label", "visible".
Result: all changes was applied immediately and only for corresponding row. Only label attribute changed the column label.

┌─────────────────────┐
│         5lab       j│
│────────── ──────────│
│        11          0│
│         2          0│
│                    0│
│         4          0│
│         5          0│
│         6          0│
│         7          0│
│                    0│
│        22          0│
│                     │
└─────────────────────┘

2. The frame with side labels. Changing widget attributes from different rows.
Test: virtual-widgets-slabels.p
Description: 10-down frame with side-labels, filled by values from 1 to 9. Several attributes from different widgets (from different rows) are changed: "screen-value", "hidden", "label", "visible".
Result: all changes was applied immediately and only for corresponding row. When trying to change the label attribute the following error is happen:
**Unable to set SCREEN-VALUE. LITERAL widget does not fit in FRAME f1. (4054)
I don't understand why it happen.

┌───────────────────────────┐
│i: 11         j: 0         │
│i: 2          j: 0         │
│              j: 0         │
│i: 4          j: 0         │
│i: 5          j: 0         │
│i: 6          j: 0         │
│i: 7          j: 0         │
│              j: 0         │
│i: 22         j: 0         │
│                           │
└───────────────────────────┘

3. The frame without side labels. Changing widget attributes from different rows. The place: F4-trigger
Test: virtual-widgets-trigger.p
Description: same as issue №1. The difference is the changes are applied when user pressed F4.
Result after pressing F4 the same as in the issue №1

4. The frame with side labels. Changing widget attributes from different rows. The place: F4-trigger
Test: virtual-widgets-trigger-slabels.p
Description: same as issue №2. The difference is the changes are applied when user pressed F4.
Result after pressing F4 the same as in the issue №2. The same error is happen.

#100 Updated by Constantin Asofiei almost 10 years ago

Vadim Gindin wrote:

Tests about virtual widgets (сommitted to current-iteration folder).

These tests look good, and give a good indication of what is going on: the changes reflect the widget referenced by the handle var or by CURRENT-ITERATION. What we are primarily interested is the case when widget's handle is saved in a var (i.e. firsth, secondh, thirdh in your tests) - these need to be implemented in P2J.

Result: ... When trying to change the label attribute the following error is happen:
**Unable to set SCREEN-VALUE. LITERAL widget does not fit in FRAME f1. (4054)
I don't understand why it happen.

I suspect this is because your test changed the length of the label, as you used 11 (two letters) - try using a single-letter string. I suspect this is a limitation from 4GL, to not modify the layout of the frame (as changing the label width will cause the entire frame to relayout...).

Conclusion: investigate how widgets are kept on the client-side (in WidgetRegistry, Frame, and other client-side classes) and find a way of virtualizing access to this data, from the server-side.

#101 Updated by Constantin Asofiei almost 10 years ago

Vadim, some more thoughts about how current P2J works and what is missing (for down frames). Calling frame.<widget-name> ends up calling GenericFrame.getWidget - this will always reference the widget in the CURRENT-ITERATION field-group (which is correct). As all the row-level widgets can be accessed in 4GL, we need to expand our code so that each row-level widget has its own GenericWidget instance, on server-side. The main problem here is most of the data structures in GenericFrame which hold widget-level data work like "I need to know only about the widget on the CURRENT-ITERATION row":
  • ScreenBuffer and ScreenDefinition classes - data structures are kept only per-widget (usually the one on CURRENT-ITERATION field-grou), and not per-each-row-widget
  • GenericFrame.screenBuf - this is always in sync with CURRENT-ITERATION row
  • GenericFrame.screenBufferCache - this is a data structure which keeps data per each row (an exception to the rule)
  • GenericFrame.frameDef - this is always in sync with CURRENT-ITERATION row
The same applies to most of the GenericFrame APIs, i.e.:
  • set/getScreenValue - this works as if the widget on the CURRENT-ITERATION field-group is accessed
  • pushScreenDefinition (is called each time a widget's attribute is changed (i.e. VISIBLE/HIDDEN/LABEL/etc)) - same, this works as if the widget on the CURRENT-ITERATION field-group is accessed.
Our main problems are:
  • when a widget from CURRENT-ITERATION is accessed or <widget>:handle IN FRAME is called, then a handle with a GenericWidget unique to that row/field-group must be returned.
  • when communicating with the client-side via a widget outside of CURRENT-ITERATION field-group, the client-side needs to receive both the widget ID (returned by WidgetConfig.getId) and the ROW to which the widget is associated. My thought at this time is that ScreenBuffer, ScreenDefinition and the widgets all should keep an info about which frame row is targeted, and send this to the client-side. But there is a problem on the client-side too: in a down-frame, each widget has only ONE config, thus i.e. the VISIBLE flag can be set in only once, and not for each row-level widget.

At this time, I think we should look into how the CURRENT-ITERATION and widget-handles are used in the GUI source code and determine which widget attributes/methods are accessed via handles, in down frames. Because if only SCREEN-VALUE is checked and only the widgets on CURRENT-ITERATION are used, we can avoid the complex changes and limit to only what the GUI code actually uses.

Greg: I think we should wait for the delivery of the GUI sources before continuing, and run the reports on that; because, to add full-compatibility, then I don't think we can get away without major changes on the client-side, too.

#102 Updated by Greg Shah almost 10 years ago

Greg: I think we should wait for the delivery of the GUI sources before continuing, and run the reports on that; because, to add full-compatibility, then I don't think we can get away without major changes on the client-side, too.

Agreed.

#103 Updated by Greg Shah almost 10 years ago

  • Target version changed from Milestone 11 to Milestone 12

I'm moving this to the GUI milestone as we have done more than enough for the needs of M11.

Vadim: we will pick this back up later. For now, don't do anything further.

#104 Updated by Greg Shah about 8 years ago

In the customer's GUI application, this attribute it used 915 times in 888 files. However, all usage is of this same pattern:

      ASSIGN curr-widget = FRAME F-Main:CURRENT-ITERATION. /* Field group*/
      ASSIGN curr-widget = curr-widget:FIRST-CHILD. /* First field */
      DO WHILE VALID-HANDLE (curr-widget):
         ...
      END.

In other words, all usage is just to get the field-group out of the frame. I think we support this already.

Constantin: Please correct me if I am wrong.

#105 Updated by Greg Shah about 8 years ago

  • Target version deleted (Milestone 12)

#106 Updated by Roger Borrello almost 3 years ago

I was looking for a testcase to deal with a frame-col issue, and found this one uast/current_iteration/clear-frame.p is not working. It returns an error Lead attributes in a chained-attribute expression (a:b:c) must be type HANDLE or a user-defined type and valid (not UNKNOWN). (10068) when getting first-child. In debugger, the handle is 1001. In 4GL it should have found the literal with handle 1002.

Expected 4GL output:


┌──────────┐  1) 1000 FIELD-GROUP 1  <?>              1000 FILL-IN 1 i
│         i│                                          1002 LITERAL ----------
│──────────│  2) 1004 FIELD-GROUP 2  1000:visible:yes 1000 FILL-IN 1 i
│         1│                                          1004 FILL-IN 2 i
│         2│                                          1002 LITERAL ----------
└──────────┘

FWD output:

┌──────────┐  1) 1000 FIELD-GROUP 1  <?>              1000 FILL-IN 1 i
│         i│  2) 1002 FIELD-GROUP 2  1000:visible:no  1000 FILL-IN 2 i
│──────────│                                          1002 FILL-IN 2 i
│         1│
│         2│
└──────────┘

I didn't want to pass through this testcase without adding this information.

#107 Updated by Constantin Asofiei over 2 years ago

I'm refactoring the field-group/current-iteration support in GenericFrame as it causes an infinite loop, and these are some problems:
  • field-groups are 'static' in a frame - once they are built, they are not deleted and built again. I have a fix for this, but it affects CURRENT-ITERATION and I need to test it. Plus some issues how CURRENT-ITERATION behaves when the frame is cleared. Also, I really don't recall why the initial implementation had to re-build the field-groups.
  • I have a deja-vu that we've discussed (or I've noticed) before about an additional FIELD-GROUP created in a DOWN frame, on which CURRENT-ITERATION 'stays' when 'going out of frame' (i.e. past last row to go back to first row). But I can't find notes about this. My problem about this additional FIELD-GROUP is that it affects CURRENT-ITERATION.

#108 Updated by Constantin Asofiei over 2 years ago

The refactoring is in 3821c/13120.

Known issues which are not yet fixed:
  • OE shows a 'hidden' field-group when going beyond the last row (or above the first row) - this field-group is moved as the first row in the list.
  • literal field-group: I've added some code to populate it with widgets, so some tests pass, but this is most likely incomplete
  • accessing a widget's SCREEN-VALUE or other attributes, for a widget not part of CURRENT-ITERATION (one can always save the handle of the widget while in CURRENT-ITERATION, and after that alter the frame). This is not working properly in FWD, the widget will always be the one in CURRENT-ITERATION
  • the tests in testcases/uast/current-iteration while they are interesting, they are not working properly in FWD. Mostly because of the hidden field-group, FIELD-GROUP attributes like ROW, SCREEN-VALUE for a widget not part of CURRENT-ITERATION, etc.

Also available in: Atom PDF