Four Js Development Tools Forum

Discussions by product => Genero BDL => Topic started by: Stephen T. on March 04, 2014, 02:07:28 pm



Title: Subdialogs in general
Post by: Stephen T. on March 04, 2014, 02:07:28 pm
I am a bit late coming to release 2.5, so the subdialog feature is new to me.

From what I can see, I can now define 'external' modules that act as part of a main dialog and can then be used to get common elements.

Before I go to far down the wrong road, am I right in thinking that all the fields input in theory then need to be defined as Globals if I use external functions, as I can't see any way of passing and returning anything from the subdialogs.

IE in my test case, I have a claim that is made up of 'subdialogs' that cover:
claim header
insurer's/broker's references
client's name/address
client's contact methods
claim 'scope'

So I can now do:
GLOBALS
DEFINE g_claimH                        RECORD LIKE claimHeader.*,
            g_claimD                        RECORD LIKE claimDetail.*,
            g_claimrefs                     RECORD LIKE claimRefs.*,
            g_address                       RECORD LIKE address.*,
 DEFINE g_cMethArray                DYNAMIC ARRAY OF RECORD
             cMethLookUpCodesRecNo       LIKE lookUpCodes.recNo,
             cMethText                   VARCHAR(255)
             END RECORD
END GLOBALS
.....
             
DIALOG
    ATTRIBUTE(UNBUFFERED)
   
    SUBDIALOG ext_claimheader
    SUBDIALOG ext_references
    SUBDIALOG ext_nameAddress
    SUBDIALOG ext_contactmethods
    SUBDIALOG ext_claimScope

    ON ACTION .....

END DIALOG

......

So I can now re-use my address input code across all the structures that require a standard address layout - ditto for contact methods etc? We had partially gone down this route anyway, as we had the address/ contact input already as 'external' functions - but that then stopped them being bolted into an overall dialog.


Title: Re: Subdialogs in general
Post by: Sebastien F. on March 04, 2014, 02:42:56 pm
Stephen,

Consider using module variables defined as PUBLIC, instead of GLOBALS:
PUBLIC module variables can be shared between modules.

What was the previous version of Genero BDL?

Take the time to read all new features that were added since the version you used:

https://4js.com/online_documentation/fjs-fgl-manual-html/?path=fjs-fgl-manual#c_fgl_nf.html

If you have any question do not hesitate!

Seb


Title: Re: Subdialogs in general
Post by: Stephen T. on March 04, 2014, 04:14:21 pm
Cheers Seb.
I have been using up to 2.4, but really only using the 'changes' that were called for - if that makes sense - ie only using the new featues when ther was a call to use it. Now for 2.5, I'm trying to build some test modules that use ALL the new functionality - so I have a point of reference.

Am I then also right in thinking that I can't mix 'Globals' with IMPORT FGL? As when I tried that, fields in GLOBALS 'someGlobalsFile.4gl' didn't appear to be picked up and then caused undefined reference errors.

Now, I have quickly changed the 'globals' files to be 'PUBLIC' define - and then IMPORTED those files and it seems to then work fine.

Is that the way forward now - Globals is replaced with IMPORTED 'PUBLIC' definitions?

Finally, if I go down the route of IMPORT FGL, I can get a list of the modules to be imported via the --print-imports, but that seems to have two slight draw backs:
The first is that it seems to give me the names of the missing modules (obviously), NOT the encompassing 4gl they may be in.
Secondly, using libs was easy, as the modules got picked up automatically - all that was needed was to build all the library modules into a lib and the linker did the rest. If something changed, then there was nothing to be done (ie function x is changed to call function y in a new .4gl)  - as again all that was required was to keep the lib up to date. Am I right in thinking now, that if the function x is changed, I have to identify each module that uses it and changed that 4gl to now IMPORT FGL the .4gl that includes function Y?


Title: Re: Subdialogs in general
Post by: Sebastien F. on March 04, 2014, 05:13:54 pm
Stephen,

Ideally you should get rid of GLOBALS and 42x libs, using IMPORT FGL and PUBLIC/PRIVATE qualifiers. Each module should define its elements (types, constants, variables, functions). For better code reusability and readability, hide local module elements with PRIVATE qualifiers and expose what can be used by other modules with PUBLIC qualifiers. In fact IMPORT FGL is similar to the Java "import" instruction, to give you the big picture. With IMPORT FGL, you define a dependency between modules, and fglcomp can automatically compile all imported modules if needed.

I am not sure to get your problem with the x and y functions, but as long as you define properly the dependency tree (starting from the module containing MAIN), everything should be fine. Note also that we support mixing link and imports, to ease migrations.

Note that circular references are not supported yet and this can be an issue, you may need to re-orgnanize functions in different modules. And in some cases there is no solution.

For more details:

https://4js.com/online_documentation/fjs-fgl-manual-html/?path=fjs-fgl-manual#c_fgl_programs_IMPORT_FGL.html

Seb


Title: Re: Subdialogs in general
Post by: Stephen T. on March 04, 2014, 06:02:11 pm
OK. I think you answered the function x - function y issue - as from what you've said, each module contains its own IMPORT clauses.

Thanks again Seb.


Title: Re: Subdialogs in general
Post by: Reuben B. on March 04, 2014, 11:16:56 pm
Hi Stephen,

Your original question was discussed here https://4js.com/fjs_forum/index.php?topic=778.new&key=7a6687458834#new

... taking that example and making the suggested modification may help anyone else who has the same question.

With regards IMPORT FGL it has many other benefits.

With your existing code, you were going down the path of running --print-imports to find out what IMPORT FGL you need to add.  In time you will find that you don't need to do that because you added IMPORT FGL when you start modifying a piece of code, because by adding IMPORT FGL, you then get the benefits of code completion.  Type module-name <dot> and you get a list of public variables and/or functions 

If you go 100% down the path of IMPORT FGL, then the .42r becomes redundant, and you can go fglrun module-name.42m on any 42m that contains a MAIN. It also means if you need to change a function signature (number of parameters in/out), you can compile and deploy just the module and the modules that call it, rather than having to relink all the affected .42r's

Reuben


Title: Re: Subdialogs in general
Post by: Stephen T. on March 05, 2014, 09:03:20 am
Thanks Reuben.

I can see the benefits if starting from fresh - and building the IMPORT list as you go, but I was attempting to look at moving an existing muti-input dialog that had proven a problem for dialog before as two or three parts of the dialog had been split into external functions to enable them to be re-used. So it was a case of seeing how I could sew those bits back together again in a true 'DIALOG'.

Luckily, we had a convention here where all external function calls  were prefixed ext_ - so I can hopefully easily convert all the modules/functions to IMPORT and then see what I get.


Title: Re: Subdialogs in general
Post by: Stephen T. on March 07, 2014, 11:32:11 am
So far so good - I have converted all the external functions to work on the IMPORT FGL model, and only had one hitch with a cyclical reference. At the same time, I simply converted the 'external' global files to be 'PUBLIC' references. And that seems to hang together ok so far.

I do have a couple of questions though:

In some 'inputs', the mode ('add', 'update') was passed to the 'function' to control the fields accessible etc. I can see that I can set up a PUBLIC variable to now pass that same info into the external subdialog - BUT - at the moment it seems that I can't define PUBLIC within the subdialog itself - so have to define PUBLIC variables within the .4gl module that encompasses the subdialog. Is that correct? Also then is it envisaged that an external subdialog will always be in a .4gl module by itself?
IE say I have a module called subDialogs.4gl and within that I have a set of subdialogs - subdialog1, subdialog2...etc. Each of those subdialogs can 'receive' a passed parameter (mode) and 'return' some form of result (inputResult). At the moment I can only define PUBLIC subDialogs.mode NOT PUBLIC subDialogs.subdialog1.mode - ditto for subDialogs.inputResult.

In the same vein, if the subdialog then uses a PUBLIC variable to 'communicate' back to the main calling dialog (as in inputResult), it doesn't appear that I can have any 'procedural' statements around the SUBDIALOG call - ie I cannot have:
DIALOG
    SUBDIALOG subDialogs.subdialog1
    IF subDialogs.inputResult = 3 THEN
       ...
    END IF
    SUBDIALOG subDialogs.subdialog2


I'm not sure if either is/will be  a problem per se 'in the real world' (they appear more to be issues because of the restrictions of previously having to link multiple disparate inputs) - I'm just trying to get a feel for what I can and can't do.

 


Title: Re: Subdialogs in general
Post by: Stephen T. on March 07, 2014, 12:50:30 pm
I'm not sure if this is related to this topic.

In the past, we had comboboxes linked to 'standard' initialisers. So the form would have INITIALIZER=ext_init_combos for each relevant combo.

That function was 'external' - ie it did not sit inside the .4gl module that opened the form.

That function (ie ext_init_combos) has now been converted to use IMPORT FGL . From what I can see, the INITIALIZER was then never 'bound' to the module opening the form(pre IMPORT 4gl conversion and now after). I can only assume that the form 'found' the ext_init_combos function via an environment variable 'path'.

Now it seems that the ext_init_combos function does not get picked up at all (and I don't seem to see any error thrown), UNLESS I copy the contents of that function inside the module opening the form.

Is that a change based on the IMPORT FGL changes (stupidly I didn't check the system pre converting to IMPORT FGL) - or is it that simply 2.50 enforces different rules for locating the INITIALIZER function?


Title: Re: Subdialogs in general
Post by: Stephen T. on March 08, 2014, 10:40:39 am

A couple or so more questions:

The 'globals' were converted to PUBLIC defs - that was relatively straight forward as the vast majority of the usage was via 'global files' - so they were converted and IMPORTED. However, we do have a few locations where the full global definition files weren't used and individual globals were defined. That worked ok before (ie pre IMPORT and PUBLIC def). But that (obviously?) then didn't convert as intended. The 'globals' setter was ext_setGlobalEnv, that itself used base_globals.4gl - so a database environment variable was defined 'PUBLIC' in base_globals - ie base_globals.g_DBNAME. If then g_DBNAME was defined as a standalone 'PUBLIC' in another function - ext_open_database - then presumably that is in effect ext_open_database.g_DBNAME - and as such is a completely different variable. I can see a way around the issue (ie to split the 'base_globals' file into smaller chunks - so that all references to globals are via the same imported file. But am I using PUBLIC definitions correctly here?

This second point is/was more of an issue. The function above - ext_open_database - used the global (now public) variable g_DBNAME to open the database. That worked fine. Also, ext_open_database had 'standalone' globals that were converted to PUBLIC defs. g_DBNAME was then defined as a PUBLIC CHAR(40) and was actually set in ext_open_database by FGL_GETENV. Fine. That worked as expected. However, when the issue above was noted, the standalone PUBLIC def of g_DBNAME was replaced with the full IMPORT of base_globals. The database open then started to fail. Looking at the FGLSQLDEBUG output, the problem appears to be that g_DBNAME, if IMPORTed in an FGL, is treated as the name of the database, rather than a variable containing the name. If a local variable was set to the the PUBLIC variable and that used in the DATABASE statement, then it works fine. It just seems to object to using a PUBLIC IMPORTED def (see debug and ext_open_database extracts attached).

Thirdly, is there a way of getting DIALOG and SUBDIALOG names? I see that ui.dialog.nextField() can be used to move to fields (in conjunction with CONTINUE DIALOG). But I can't see where I can get the order of the DIALOGS/SUBDIALOGS and what fields they contain. The reason for the question is that if I want to 're-use' code, I will be using the same inputs for references, addresses, contact methods etc in multiple places. If the user leaves an 'array' by leaving a row blank say, then I want to move the user onto the first field (or last field) of the next/previous SUBDIALOG. Does that make sense? It seems that ui.dialog.nextField gives the way to code that, but I missed where I could find the fields and order of dialogs. Is that possible?
IE from the array, if the user is in the array and 'tabs' from the first field on a blank row, I want to move the user to the first input field of the next subdialog.
I think again, that I can work around this though by setting some public variables that contain the various 'next fields'.


Can I just say though that this is looking exceptionally good from my point of view. The migration of the code seems quite simple and the facilities offered by subdialogs (and the FORM re-use) look to be what's required. Good stuff. Thanks.
 


Title: Re: Subdialogs in general
Post by: Sebastien F. on March 09, 2014, 11:01:17 am
Trying to help you on all the topics of your last 3 posts (you better open a separate thread for each subject)

Regarding passing/returning information from SUBDIALOGs, yes you must use PUBLIC module variables. No, you cannot make local declaractive DIALOG variables public (these are local to the DIALOG block, like variables defined in FUNCTIONs). You can also define functions beside the declarative dialog, to encapsulate module variables, and add some code arround. No you cannot execute procedural code inside a procedural DIALOG block (the IF subDialogs.inputResult = 3 THEN example)

Modules defining combobox initializers must have been loaded to make the initializer work. You need to call another function from that module before the OPEN WINDOW / OPEN FORM + DISPLAY FORM instructions.

Regarding the "base_globals" case, to me this looks like a candidate for a configurable utility module, where you could define PRIVATE module variables, that can be set by PUBLIC module functions (to set the dbname for ex), and execution functions (like connecting to the database). Think encapsulation and modularisation:
PRIVATE DEFINE dbname VARCHAR(50)
PUBLIC FUNCTION set_dbname(n)
  DEFINE n VARCHAR(50)
  LET dbname = n
END FUNCTION
PUBLIC FUNCTION do_connect()
  DATABASE dbname
END FUNCTION

About the second point (g_DBNAME/FGL_GETENV), sorry but I don't get it. You better provide a little sample to reproduce the problem to let us identify if the behavior described is expected or is a bug...

Regarding the "next field" case:
Here again, a little sample would be appreciated to let us understand, here some remarks...
I think that you should design your dialogs so that you don't have to add particular code to jump to a specific field in a given condition.
The field navigation should be natural and implicit.
If the "always in edit mode" for record lists (INPUT ARRAY semantics) is not mandatory, consider using DISPLAY ARRAY with modification triggers (ON APPEND, ON INSERT, ON DELETE, ON UPDATE) - It's easier to deal with a DISPLAY ARRAY as with all special cases of INPUT ARRAY.

I think you should have a look at the latest version of the documentation that is under construction.
It has updated topics that will help you to better understand dialog programming.
https://intranet.4js.com/distrib/manuals/FOURJS/FJSONLY/trunk/fjs-fgl-latest-manual-html-draft/index.html#c_fgl_prog_dialogs.html
You need a EAP login to access this doc.

Seb


Title: Re: Subdialogs in general
Post by: Sebastien F. on March 09, 2014, 05:24:16 pm
The link to the new doc was wrong in previous post, here the correct link:
https://4js.com/techdocs/fjs-fgl-manual/#c_fgl_prog_dialogs.html
Seb


Title: Re: Subdialogs in general
Post by: Reuben B. on March 09, 2014, 10:28:19 pm
Stephen said ...

Quote
In the past, we had comboboxes linked to 'standard' initialisers. So the form would have INITIALIZER=ext_init_combos for each relevant combo.

That function was 'external' - ie it did not sit inside the .4gl module that opened the form.

That function (ie ext_init_combos) has now been converted to use IMPORT FGL . From what I can see, the INITIALIZER was then never 'bound' to the module opening the form(pre IMPORT 4gl conversion and now after). I can only assume that the form 'found' the ext_init_combos function via an environment variable 'path'.

Now it seems that the ext_init_combos function does not get picked up at all (and I don't seem to see any error thrown), UNLESS I copy the contents of that function inside the module opening the form.

... to understand what was happening in the past, have a look at a .42r using od -c, or strings.  The .42r simply consists of a list of function-names and modules.  So at run time, to find a function, the runner looks first in the current .42m, and if it can't find it then it reads the .42r, looking up the function-name to find the module-name, and then using FGLLDPATH to load the .42m.

Because of the way, the optimizer works, it was possible for the initializer functions or some of the functions they call not be included in the .42r, and hence the advice from me over the years would be if you had...

INITIALIZER=ext_init_combos

... was to add the following code to force your initializer function into the .42r ...

Code
  1. IF 1=0 THEN
  2.   CALL ext_init_combos(ui.ComboBox.forName("dummy"))
  3. END IF

(the optimizer function isn't clever enough to say that that line of code is never executed)


Looking at your issue, I'm surprised that at run-time, the runtime doesn't do a similar search through the IMPORT FGL references looking for the initializer function.  However if there were no other calls to that IMPORT FGL reference, it may have been optimized away, and a quick test suggests that Sebs advise to "call another function from that module" is what is required.  However that is going to be tough if all that is in that module is combobox initializer functions.  So rather than adding a call to another function from that module, I'd suggest try adding a dummy call to your initializer function.  So ...

Code
  1. IF 1=0 THEN
  2.   CALL ext_init_combos(ui.ComboBox.forName("dummy"))
  3. END IF

Reuben


Title: Re: Subdialogs in general
Post by: Reuben B. on March 09, 2014, 10:54:19 pm

Quote
Regarding the "next field" case:
Here again, a little sample would be appreciated to let us understand, here some remarks...
I think that you should design your dialogs so that you don't have to add particular code to jump to a specific field in a given condition.
The field navigation should be natural and implicit.

Seb:

... this was raised during the EAP for multi-dialogs.  Have a look at Bz14858.

We were discussing the requirement for NEXT FIELD FIRST / NEXT FIELD LAST, and whether there would be a similar requirement with multi-dialog.

Stephen:

The tabIndex value should be accessible via the AUI Tree.  Sounds like you want to find the fieldname with a higher tabIndex value that is not in the current table.

Reuben


Title: Re: Subdialogs in general - INITIALIZER
Post by: Stephen T. on March 10, 2014, 08:59:45 am
Reuben/Seb,
The INITIALIZER - I am just trying to get at the changes to migrate to 2.50.
In the past the INITIALIZER was three functions down -  ie:
Program Module (Main) - ext_hotkey_combos - ext_reload_combos - ext_init_combos

That then appeared to 'work' - as we never had any issues with the form combo INITIALIZER not working.

Now, after migrating to IMPORT FGL, the structure is still the same - ie the main program calls ext_hotkey_combos which call ext_reload_combos which then finally calls ext_init_combos. That then isn't getting picked up by the form.

Are you saying that is correct? And that to get the INITIALIZER to be recognised I need to add the dummy call to all program ('main') modules?


Title: Re: Subdialogs in general - Global PUBLIC
Post by: Stephen T. on March 10, 2014, 09:14:58 am
Quote from: steveT
This second point is/was more of an issue. The function above - ext_open_database - used the global (now public) variable g_DBNAME to open the database. That worked fine. Also, ext_open_database had 'standalone' globals that were converted to PUBLIC defs. g_DBNAME was then defined as a PUBLIC CHAR(40) and was actually set in ext_open_database by FGL_GETENV. Fine. That worked as expected. However, when the issue above was noted, the standalone PUBLIC def of g_DBNAME was replaced with the full IMPORT of base_globals. The database open then started to fail. Looking at the FGLSQLDEBUG output, the problem appears to be that g_DBNAME, if IMPORTed in an FGL, is treated as the name of the database, rather than a variable containing the name. If a local variable was set to the the PUBLIC variable and that used in the DATABASE statement, then it works fine. It just seems to object to using a PUBLIC IMPORTED def (see debug and ext_open_database extracts attached).

Quote from: seb
About the second point (g_DBNAME/FGL_GETENV), sorry but I don't get it. You better provide a little sample to reproduce the problem to let us identify if the behavior described is expected or is a bug...

Seb,
The code snippet attached to the post attempted to show the error (that was also shown in the attached SQLDEBUG output). The code is:
Quote from: snippet
IMPORT FGL base_globals   

 
################################################################################
#  OPEN THE DATABASE........................................
################################################################################
 FUNCTION ext_open_database(p_dbase,p_mode)   
 DEFINE p_dbase,
        p_mode                      CHAR(40)

 DEFINE l_string                    STRING,
        l_dbExists                  INTEGER
 
 define l_dbname                    char(40)

 CONSTANT lc_rcs                    STRING = '$Id: ext_open_database.4gl,v 1.1 2005/06/16 15:24:13 stevet Exp stevet $'

 WHENEVER ERROR CONTINUE

    -- Set the date and time.....
    LET g_today                     = TODAY
    LET g_current                   = CURRENT


    -- Get the user log in code
    SELECT USER
        INTO g_user
        FROM blankTable
        WHERE recNo     = 1

    -- Get the database Type
    LET g_DATABASETYPE  =   FGL_GETENV('DATABASETYPE')
    IF NOT LENGTH(g_DATABASETYPE) THEN
        LET g_DATABASETYPE = 'INFV9.3'
    END IF   

    IF LENGTH(p_dbase) THEN
        LET g_DBNAME    = p_dbase
    ELSE       
        LET g_DBNAME    = FGL_GETENV('DBNAME')
    END IF

    LET g_FGLRUN        = FGL_GETENV('FGLRUN')
    IF NOT LENGTH(g_FGLRUN) THEN
        LET g_FGLRUN    = 'fglrun'
    END IF   

    CLOSE DATABASE

--RE--    LET l_string = 'DATABASE ', g_DBNAME CLIPPED,' ',p_mode CLIPPED
--RE--    IF ext_execute_mysql(l_string,1) THEN
--RE--    END IF
    DATABASE g_DBNAME
    -- NB for some reason under FGL 2.50, g_DBNAME as a public def gets interpreted as the DBNAME
    --    itself rather than a variable containing the DBNAME - if the g_DBNAME is moved to a local
    --    variable, it works fine
    LET l_DBNAME    = g_DBNAME
    DATABASE l_DBNAME

Quote from: SQLDEBUG
SQL: CLOSE DATABASE
 | 4gl source      : ext_open_database.4gl line=101
 | sqlcode         : -1803
 |   sqlstate      : HY000
 |   sqlerrd2      : -1
 |   sql message   : Connection does not exist.
 |   sql msg param :
 | curr driver     : ident='dbmpgs91x'
 | curr connection : ident='none' (dbspec=[none])
 | Execution time  :   0 00:00:00.00004
SQL: DATABASE
 | 4gl source      : ext_open_database.4gl line=106
 | pgs.c:00453(3)  : Connection parameters for '?':
 | pgs.c:00454(3)  :  Database      = 'g_dbname'
 | pgs.c:00455(3)  :  Hostname      = ''
 | pgs.c:00456(3)  :  Port          = ''
 | pgs.c:00457(3)  :  User name     = ''
 | pgs.c:00458(3)  :  Password      = ''
 | pgs.c:00486(2)  : Could not connect to database.
 | pgs.c:00367(1)  : Warning: NULL PGresult handler.
 | sqlcode         : -6372
 |   sqlstate      : XX000
 |   sqlerrd2      : -1
 |  sql message   : FATAL:  database "g_dbname" does not exist
 |   sql msg param :
 | curr driver     : ident='dbmpgs91x'
 | curr connection : ident='none' (dbspec=[none])
 | Execution time  :   0 00:00:00.00248
SQL: DATABASE
 | 4gl source      : ext_open_database.4gl line=111
 | pgs.c:00453(3)  : Connection parameters for '?':
 | pgs.c:00454(3)  :  Database      = 'dev_rand'
 | pgs.c:00455(3)  :  Hostname      = ''
 | pgs.c:00456(3)  :  Port          = ''
 | pgs.c:00457(3)  :  User name     = ''
 | pgs.c:00458(3)  :  Password      = ''
 | pgs.c:00494(3)  :  PGS version   = 901
 | pgs.c:00192(2)  : Execdirect : set intervalstyle='iso_8601'
 | sqlcode         : 0



Title: Re: Subdialogs in general
Post by: Sebastien F. on March 10, 2014, 09:27:11 am
Sorry but I get confused:
The principle of the INITIALIZER combobox attribute is to have the runtime system call the initializer function automatically when a form is loaded/displayed.
Here you write that you call "by hand" your combobox initializers from the MAIN module (through different other functions)...
What are you doing exactly in your code?
Why are you calling the combobox initializers explicitely?
Maybe you have this "ext_reload_combos" function that must be called in some particular case?
It's quite difficult to help without seeing the code.
Anyway, yes, assuming that ext_init_combos() is the initialization function defined in the INITIALIZER attribute, if that function was called once before OPEN FORM/DISPLAY FORM or OPEN WINDOW, the module containing this function is loaded, and then the function is available for the automatic combobox initialization call. But maybe you are doing the job twice.
Seb


Title: Re: Subdialogs in general - Global PUBLIC
Post by: Sebastien F. on March 10, 2014, 09:35:31 am
Quote from: steveT
This second point is/was more of an issue. The function above - ext_open_database - used the global (now public) variable g_DBNAME to open the database. That worked fine. Also, ext_open_database had 'standalone' globals that were converted to PUBLIC defs. g_DBNAME was then defined as a PUBLIC CHAR(40) and was actually set in ext_open_database by FGL_GETENV. Fine. That worked as expected. However, when the issue above was noted, the standalone PUBLIC def of g_DBNAME was replaced with the full IMPORT of base_globals. The database open then started to fail. Looking at the FGLSQLDEBUG output, the problem appears to be that g_DBNAME, if IMPORTed in an FGL, is treated as the name of the database, rather than a variable containing the name. If a local variable was set to the the PUBLIC variable and that used in the DATABASE statement, then it works fine. It just seems to object to using a PUBLIC IMPORTED def (see debug and ext_open_database extracts attached).

Quote from: seb
About the second point (g_DBNAME/FGL_GETENV), sorry but I don't get it. You better provide a little sample to reproduce the problem to let us identify if the behavior described is expected or is a bug...

Seb,
The code snippet attached to the post attempted to show the error (that was also shown in the attached SQLDEBUG output). The code is:
Quote from: snippet
IMPORT FGL base_globals   

 
################################################################################
#  OPEN THE DATABASE........................................
################################################################################
 FUNCTION ext_open_database(p_dbase,p_mode)   
 DEFINE p_dbase,
        p_mode                      CHAR(40)

 DEFINE l_string                    STRING,
        l_dbExists                  INTEGER
 
 define l_dbname                    char(40)

 CONSTANT lc_rcs                    STRING = '$Id: ext_open_database.4gl,v 1.1 2005/06/16 15:24:13 stevet Exp stevet $'

 WHENEVER ERROR CONTINUE

    -- Set the date and time.....
    LET g_today                     = TODAY
    LET g_current                   = CURRENT


    -- Get the user log in code
    SELECT USER
        INTO g_user
        FROM blankTable
        WHERE recNo     = 1

    -- Get the database Type
    LET g_DATABASETYPE  =   FGL_GETENV('DATABASETYPE')
    IF NOT LENGTH(g_DATABASETYPE) THEN
        LET g_DATABASETYPE = 'INFV9.3'
    END IF   

    IF LENGTH(p_dbase) THEN
        LET g_DBNAME    = p_dbase
    ELSE       
        LET g_DBNAME    = FGL_GETENV('DBNAME')
    END IF

    LET g_FGLRUN        = FGL_GETENV('FGLRUN')
    IF NOT LENGTH(g_FGLRUN) THEN
        LET g_FGLRUN    = 'fglrun'
    END IF   

    CLOSE DATABASE

--RE--    LET l_string = 'DATABASE ', g_DBNAME CLIPPED,' ',p_mode CLIPPED
--RE--    IF ext_execute_mysql(l_string,1) THEN
--RE--    END IF
    DATABASE g_DBNAME
    -- NB for some reason under FGL 2.50, g_DBNAME as a public def gets interpreted as the DBNAME
    --    itself rather than a variable containing the DBNAME - if the g_DBNAME is moved to a local
    --    variable, it works fine
    LET l_DBNAME    = g_DBNAME
    DATABASE l_DBNAME

Quote from: SQLDEBUG
SQL: CLOSE DATABASE
 | 4gl source      : ext_open_database.4gl line=101
 | sqlcode         : -1803
 |   sqlstate      : HY000
 |   sqlerrd2      : -1
 |   sql message   : Connection does not exist.
 |   sql msg param :
 | curr driver     : ident='dbmpgs91x'
 | curr connection : ident='none' (dbspec=[none])
 | Execution time  :   0 00:00:00.00004
SQL: DATABASE
 | 4gl source      : ext_open_database.4gl line=106
 | pgs.c:00453(3)  : Connection parameters for '?':
 | pgs.c:00454(3)  :  Database      = 'g_dbname'
 | pgs.c:00455(3)  :  Hostname      = ''
 | pgs.c:00456(3)  :  Port          = ''
 | pgs.c:00457(3)  :  User name     = ''
 | pgs.c:00458(3)  :  Password      = ''
 | pgs.c:00486(2)  : Could not connect to database.
 | pgs.c:00367(1)  : Warning: NULL PGresult handler.
 | sqlcode         : -6372
 |   sqlstate      : XX000
 |   sqlerrd2      : -1
 |  sql message   : FATAL:  database "g_dbname" does not exist
 |   sql msg param :
 | curr driver     : ident='dbmpgs91x'
 | curr connection : ident='none' (dbspec=[none])
 | Execution time  :   0 00:00:00.00248
SQL: DATABASE
 | 4gl source      : ext_open_database.4gl line=111
 | pgs.c:00453(3)  : Connection parameters for '?':
 | pgs.c:00454(3)  :  Database      = 'dev_rand'
 | pgs.c:00455(3)  :  Hostname      = ''
 | pgs.c:00456(3)  :  Port          = ''
 | pgs.c:00457(3)  :  User name     = ''
 | pgs.c:00458(3)  :  Password      = ''
 | pgs.c:00494(3)  :  PGS version   = 901
 | pgs.c:00192(2)  : Execdirect : set intervalstyle='iso_8601'
 | sqlcode         : 0




Yes I understand now, and I confirm that variables imported from other modules cannot be used in Static SQL statements.
We are discussing this internally to decide if it's a bug or an expected behavior:
It's probably not a good practice to use imported module variables in local SQL statements, you may have the same problem as with combobox initializer functions (must discuss this with the team)
The compiler should however print and error or warning, that imported variables are used in SQL statements.

Seb


Title: Re: Subdialogs in general - NEXT FIELD
Post by: Stephen T. on March 10, 2014, 10:29:56 am
[qoute="Seb]

Regarding the "next field" case:
Here again, a little sample would be appreciated to let us understand, here some remarks...
I think that you should design your dialogs so that you don't have to add particular code to jump to a specific field in a given condition.
The field navigation should be natural and implicit.
If the "always in edit mode" for record lists (INPUT ARRAY semantics) is not mandatory, consider using DISPLAY ARRAY with modification triggers (ON APPEND, ON INSERT, ON DELETE, ON UPDATE) - It's easier to deal with a DISPLAY ARRAY as with all special cases of INPUT ARRAY.
[/quote]

Seb,
Exactly. So in our case, if the user is in an array and leaves a row blank (ie they TAB from the first field of the row, and that row is blank), then the code jumps out of that array and to the next dialog (whatever that is). I have emulated this by creating an array of next/previous fields by dialog name and populating the array before the main DIALOG and then using a wrapper around DIALOG.nextField to pick up the current dialog name and set the field focus based on the array.
The issue being (from my perspective) is that I can never know what the next/previous dialog is and hence what the next/previous field is. The code we repeat all over the place is address input with contact methods. The contact methods are then editable and an array. So in that case, after the user has finished entering contact methods, tabbing from a blank contact method type would typically take the user to the start of the next dialog.

How then is it intended to move between the dialogs as (ie as shown), without knowing what fields make up the 'active' dialogs and what dialogs are active? IE in this case, if postcode lookup is active, I may want to jump from the postcode field to the contact methods. But this is this case - elsewhere the field following the postcode may be a completely different field. Does that make sense?


Title: Re: Subdialogs in general
Post by: Stephen T. on March 10, 2014, 10:54:39 am
Quote from: seb
Sorry but I get confused:
The principle of the INITIALIZER combobox attribute is to have the runtime system call the initializer function automatically when a form is loaded/displayed.

Here you write that you call "by hand" your combobox initializers from the MAIN module (through different other functions)...
What are you doing exactly in your code?
Why are you calling the combobox initializers explicitely?
Seb,
The combo boxes are primarily those that are loaded from the DB - so lookup codes, small lists of names etc.
The issue for us is that we allow the users to 'hotkey' to maintenance modules - ie the user is in a main claim entry, and they look at the claim analysis combo and want to add another entry. They can 'hotkey' (ext_hotkey_combos) to the module that maintains the analysis codes and add/change the details. The ext_reload_combos then detects underlying table changes and reloads the combos using ext_init_combos. So ext_init_combos is used on form initialization and again when the underlying data tables change.

Quote from: seb
Maybe you have this "ext_reload_combos" function that must be called in some particular case?
It's quite difficult to help without seeing the code.
Anyway, yes, assuming that ext_init_combos() is the initialization function defined in the INITIALIZER attribute, if that function was called once before OPEN FORM/DISPLAY FORM or OPEN WINDOW, the module containing this function is loaded, and then the function is available for the automatic combobox initialization call. But maybe you are doing the job twice.

But the problem is Seb is that it's now not being done at all (ie the form based initialisation) - no error appears to be  thrown, just that the combos aren't being populated. If I bolt the ext_init_combos code directly into the 'main' module (not IMPORTED, but physically added), then the FORM combo initializers work fine.
ext_init_combos was included in the old 42r because, instead of creating a dummy link as per Reuben, our hotkey function automatically then meant it was included in the linked objects. Now, using IMPORT FGL, the IMPORT of ext_init_combos is not in the main module, but two modules down and then does not appear to be being picked up by the FORM. 


Title: Re: Subdialogs in general
Post by: Sebastien F. on March 10, 2014, 05:38:15 pm
Regarding combos initializers:
I repeat myself: A function from the same imported module of the initializer must be called before loading the form.
Having a call reference in the code is not sufficient, the program flow must go through a call, to load the module:
Imported modules are loaded dynamically on demand.
Seb


Title: Re: Subdialogs in general
Post by: Sebastien F. on March 10, 2014, 05:54:26 pm
Regarding your next field case:
I see no other way except doing a DIALOG.nextField("field_name") + CONTINUE DIALOG, by naming the field explicitely.
NEXT FIELD NEXT / PREVIOUS will not work because when called in the context of an INPUT ARRAY (even in DIALOG), you stay in the same row.
Consider trying DISPLAY ARRAY + modification triggers, it may change a bit the ergonomics (you are no longer in "edit mode" in the lists and you must hit an action to modify/append rows, but it drastically simplifies the dialog coding: You certailty know that INPUT ARRAY programming was always quite difficult.
Seb


Title: Re: Subdialogs in general - COMBO INITIALIZERS
Post by: Stephen T. on March 10, 2014, 06:43:55 pm
Quote from: Seb
Regarding combos initializers:
I repeat myself: A function from the same imported module of the initializer must be called before loading the form.
Having a call reference in the code is not sufficient, the program flow must go through a call, to load the module:
Imported modules are loaded dynamically on demand.

Seb,
I think I got that - but is that then a change in 2,50 in general or is it specifically tied to the use of the IMPORT FGL statements?


Title: Re: Subdialogs in general _ SUBDIALOG NEXT FIELD
Post by: Stephen T. on March 10, 2014, 06:52:41 pm
Quote from: Seb
Regarding your next field case:
I see no other way except doing a DIALOG.nextField("field_name") + CONTINUE DIALOG, by naming the field explicitely.
NEXT FIELD NEXT / PREVIOUS will not work because when called in the context of an INPUT ARRAY (even in DIALOG), you stay in the same row.
Consider trying DISPLAY ARRAY + modification triggers, it may change a bit the ergonomics (you are no longer in "edit mode" in the lists and you must hit an action to modify/append rows, but it drastically simplifies the dialog coding: You certailty know that INPUT ARRAY programming was always quite difficult.

OK - is there then anything planned to be able to interrogate the dialog structure and to be able to work out where in a complete dialog you are and which fields then follow or precede the current subdialog? If not I'll firm up the array based 'subdialog/field' mechanism that I've been playing with and implement it that way.


Title: Re: Subdialogs in general
Post by: Sebastien F. on March 11, 2014, 10:30:40 am
Regarding IMPORT FGL, nothing changed in 2.50 in the module loading principle: At runtime these are loaded on demand.
Regading the NEXT FIELD need, no dialog introspection is planed.
Seb


Title: Re: Subdialogs in general - FORM INITIALIZERs
Post by: Stephen T. on March 11, 2014, 11:31:59 am
Seb,
Sorry to be a pain - but I still don't follow.
We had, in 2.4 using 'traditional' linking (ie 42r), the same code. IE the forms called ext_init_combos as the combo initializer. That function was also called via ext_hotkey_combos - ext_reload_combos. It worked fine - the forms found the function ok and the combo boxes were populated.

Under 2.5, the linking has been changed and IMPORT FGL used. The forms have not been altered - so still call ext_init_combos as the combo initializer - and the programs still refer to ext_init_combos via ext_hotkey_combos - ext_reload_combos. But the combos aren't being populated (OK I take on board I can get around the issue by calling the module that contains ext_init_combos before opening the forms) - but what is the cause of the issue in the first place?


Title: Re: Subdialogs in general
Post by: Sebastien F. on March 11, 2014, 11:47:26 am
No prob Stephen...

Understand the different between a function that is referenced in the code and a function that is called at runtime (and obsiously referenced).

Code
  1. IF 1=0 THEN
  2.    CALL myfunction()  -- referenced, but will not be called at runtime
  3. END IF
  4. -- versus:
  5. CALL myfunction()  -- referenced, and will be called at runtime
  6.  

When using IMPORT FGL, modules are loaded on demand (i.e. when one of the module elements (function) is used/called.

According to your previous posts, the ext_reload_combos() is called only when the user wants to add items to the comboboxes.

When the form is loaded, ext_reload_combos() + ext_init_combos() are not yet called, hence the corresponding module is not yet loaded.

We are discussing the fact that combos initializers should load the module automatically, like an explicit CALL does.

Seb


Title: Re: Subdialogs in general
Post by: Stephen T. on March 11, 2014, 01:29:30 pm
Seb,
That is what I thought - but I must then have misunderstood your comment:
'...Regarding IMPORT FGL, nothing changed in 2.50 in the module loading principle: At runtime these are loaded on demand....'

As I took that to mean that it wasn't the IMPORT statements that were the difference.

Cheers Seb.


Title: Re: Subdialogs in general
Post by: Sebastien F. on March 11, 2014, 03:05:29 pm
We have an ref in the bug database for IMPORT FGL + Combos INITIALIZER: bz#1709
Seb