Ask Reuben – February 7, 2024
Set Current Row To First Sort Row
How can I set the current row to be the first sorted row?
When the front-end applies a sort to the rows in a table, a question that then gets asked is how can I change the current row so that it is the first sorted row.
Before answering the question I will provide some background. In the 4gl there will be an underlying array variable. When the front-end applies a sort (clicking in the table column header), this does not change the values in the array nor does it change the pointer to the current row. array_variable[i].* will still hold the same values, and arr_curr() will still return the same value.
With the example using the code at the end of this article, we can illustrate this. I have coded it so that if sorting on the Date column, rows 3 and 7 will be our first and last row, and row 1 will always be somewhere in the middle. So we start like this with the rows being displayed in the same order as they are in the 4gl array, I then click on the column header for the Date column and the table is now sorted by the Date column. Row 1, the current row is now in the middle somewhere, if we click the Row Details we see that arr_curr(), the Current Row still remains 1 …


… now a question that gets asked is how can we make the current row the top row.
First of all, if you press the HOME key, this will make the top row the current row.
On touch devices or with a mouse you can tap/click on the top row to make it the current row, this may require some scrolling as well in order to see the top row before you tap/click it.
Alternatively we could add some code like in the ON ACTION gototop in that sample code. The key is that it uses the visualToArrayIndex method to find what the row index for the top row is, and then calls setCurrentRow to set the current row to that row. If you do that and again click the Row Details button, we see that scr_line() returns 1 being the top row, and arr_curr() returns a value other than 1, being the position of the row in the 4gl array. Behind the scenes, the 4gl array remains unchanged.


Now exit the program and start it again. With stored settings, the table remembers to sort by the Date Column. The current row is still 1, and so the current row is in the middle of the array. If you uncomment the ON SORT block and repeat …
ON SORT
IF first THEN
CALL DIALOG.setCurrentRow("scr", DIALOG.visualToArrayIndex("scr", 1))
LET first = FALSE
END IF
… this is one technique you can use to position the current row at the top of the array when an array is sorted by stored settings. The use of the IF first means this code is only executed when the dialog starts. You could remove the IF first and have it always set the current row to the top row after a sort.
There is an fglprofile entry I should also mention at this point. The Dialog.currentRowVisibleAfterSort entry will change the offset (move the scrollbars) so that the current row is always visible after a sort. It does not make the current row the top row. You see the benefit of this with large arrays where a sort is likely to move the current row outside the currently visual area. It typically makes sense to move the scrollbars so that the current row remains visible rather than having a table display and no current row be visible and you having to scroll up/down to find it.
Consideration has been given to adding adding another fglprofile to change the current row to be the new top row but there are some complications. Should we process the AFTER ROW/BEFORE ROW that would occur if user moves to the top row themselves. An extra interchange would have to occur with the front-end to determine the sort column. (you can see this by putting the CALL DIALOG.setCurrentRow("scr", DIALOG.visualToArrayIndex("scr", 1)) in a BEFORE DISPLAY. At the time of the BEFORE DISPLAY, the sort has not been done and so visualToArrayIndex does not return the new values.
IMPORT util
MAIN
DEFINE arr DYNAMIC ARRAY OF RECORD
field1 INTEGER,
field2 STRING,
field3 DATE
END RECORD
DEFINE i INTEGER
DEFINE first BOOLEAN
WHENEVER ANY ERROR STOP
DEFER INTERRUPT
DEFER QUIT
OPTIONS FIELD ORDER FORM
OPTIONS INPUT WRAP
CALL ui.Interface.loadStyles( base.Application.getArgument(0))
CLOSE WINDOW SCREEN
FOR i = 1 TO 10
LET arr[i].field1 = i
LET arr[i].field2 = ASCII (64 + i), ASCII (96 + i), ASCII (96 + i)
LET arr[i].field3 = TODAY - (1 + util.Math.rand(100))
END FOR
-- Set row 10 and 20 ot be our end rows, and oprevent row 1 being top/bottom by chance
LET arr[3].field3 = TODAY
LET arr[7].field3 = TODAY - 102
OPEN WINDOW w WITH FORM base.Application.getArgument(0) ATTRIBUTES(TEXT = "Example")
DISPLAY ARRAY arr TO scr.* ATTRIBUTES(UNBUFFERED)
BEFORE DISPLAY
LET first = TRUE
ON ACTION gototop ATTRIBUTES(TEXT = "Home")
CALL DIALOG.setCurrentRow("scr", DIALOG.visualToArrayIndex("scr", 1))
ON ACTION display_current_row ATTRIBUTES(TEXT = "Row Details")
MESSAGE SFMT("Current row is %1, Screen Line is %2", arr_curr(), scr_line())
--ON SORT
-- IF first THEN
-- CALL DIALOG.setCurrentRow("scr", DIALOG.visualToArrayIndex("scr", 1))
-- LET first = FALSE
-- END IF
END DISPLAY
END MAINLAYOUT
TABLE
{
[f01 ][f02 ][f03 ]
}
END
END
ATTRIBUTES
EDIT f01 = formonly.field1, TITLE="Code";
EDIT f02 = formonly.field2, TITLE="Name";
DATEEDIT f03 = formonly.field3, TITLE="Date";
INSTRUCTIONS
SCREEN RECORD scr(field1, field2, field3)<?xml version="1.0" encoding="ANSI_X3.4-1968"?>
<StyleList>
<Style name="Window">
<StyleAttribute name="windowType" value="normal"/>
</Style>
<Style name="Table">
<StyleAttribute name="headerAlignment" value="auto"/>
</Style>
</StyleList>
