Subscribe for automatic updates: RSS icon RSS

Login icon Sign in for full access | Help icon Help
Advanced search

Pages: [1] 2
  Reply  |  Print  
Author Topic: Would like to pull part of an existing form and add it to current form  (Read 15661 times)
Candy M.
Posts: 139


« on: January 23, 2009, 09:16:20 pm »

I'll see if I can make sense with this question.

I would like to be able to take a Page of an existing
form and add it to the current form (a different form) in
the AUI tree and make use of it.

For example, I may have a customer master form
called customer and have a folder tab on
that form and the first page of the folder tab is
what I'm wanting to re-use.  Also, I have
a screen record that is associated with the fields
of that page.

Then, I may have several reports that have a CONSTRUCT
statement that uses that first page of the customer master maintenance.
Currently we are creating another .per, but duplicating
the section from the customer master.  Well, if you ever add
a field to customer master, then you would make changes
to the other report forms that use that customer as well which is time-consuming.

What I want to do is to extract that Page section from
that customer XML screen and add it to the report form
maybe as a second page of the report form.  Theoretically
I think I could do that, but what I would not have and
which I do not know how to add to the AUI tree is the
screen record for that page of the customer master.  I don't know
if that is possible. 

Does this make sense?  Has anyone else wanted to do that?

Thanks for any insight.
Reuben B.
Four Js
Posts: 990


« Reply #1 on: January 25, 2009, 09:50:42 am »

Candy,

You are not the first person to have a similar issue.

You can use the pre-processor to achieve what you want.  Create a serier of .inc files corresponding to the layout/attributes/instruction section of the customer bit of the .per

Code
  1. -- customer_layout.inc
  2. GRID
  3. {
  4. Customer Code    [cu01      ] Customer Name [cu02                       ]
  5. Customer Address [cu03                      ]
  6.                  [cu04                      ]
  7.                  [cu05                      ]
  8.                  [cu06                      ]
  9. }
  10. END

Code
  1. -- customer_attributes.inc
  2. cu01 = formonly.customer_code;
  3. cu02 = formonly.customer_name;
  4. cu03 = formonly.customer_address1;
  5. cu04 = formonly.customer_address2;
  6. cu05 = formonly.customer_address3;
  7. cu06 = formonly.customer_address4;

Code
  1. -- customer_maintenance.per
  2. LAYOUT
  3. &include "customer_layout.inc"
  4. END
  5. ATTRIBUTES
  6. &include "customer_attributes.inc"


Code
  1. -- customer_product_sales_report.per
  2. LAYOUT (TEXT="Customer Sales by Product Report")
  3. FOLDER
  4. PAGE (TEXT="Customer")
  5. &include "customer_layout.inc"
  6. END
  7. PAGE (TEXT="Product")
  8. GRID
  9. {
  10. Product   [sa01      ]
  11. }
  12. END
  13. END
  14. END
  15. END
  16. ATTRIBUTES
  17. &include "customer_attributes.inc"
  18. sa01 = formonly.product_code;


and to re-use the same CONSTRUCT for the customer in a number of 4gls

Code
  1. --customer_construct.inc
  2. CONSTRUCT BY NAME customer_sql ON customer_code, customer_name, customer_address1,  customer_address2, customer_address3,  customer_address4
  3.   # add zooms here
  4. END CONSTRUCT    



Code
  1. --customer_product_sales_report.4gl
  2.  DIALOG
  3.      &include "customer_construct.inc"
  4.      CONSTRUCT BY NAME product_sql ON product_code
  5.      END CONSTRUCT
  6.      ON ACTION accept
  7.         EXIT DIALOG
  8.      ON ACTION cancel
  9.         EXIT DIALOG
  10.   END DIALOG

Hope that helps,

Reuben

Product Consultant (Asia Pacific)
Developer Relations Manager (Worldwide)
Author of https://4js.com/ask-reuben
Contributor to https://github.com/FourjsGenero
Candy M.
Posts: 139


« Reply #2 on: January 30, 2009, 02:02:56 am »

Thanks Reuben,
       That will help.
       But I still would like flexibility of doing in DOM tree.

Candy
Candy M.
Posts: 139


« Reply #3 on: January 30, 2009, 04:20:59 am »

Reuben,
     I tried that but there are some things I don't understand with the
preprocessor.  Say my file is "customer.per" and I have an error in
"customer_layout.inc".  When I issue the command fglform customer.per
it doesn't place errors in customer.err.  They are placed in a file with
the text on next line and .err.  Say if next line is GROUP, it creates
an  error file group.err with no errors in it.  I finally issued the
command fglform -M customer so I could see the errors.  If someone
could explain what is going on, I'd appreciate it.

Candy
Reuben B.
Four Js
Posts: 990


« Reply #4 on: February 02, 2009, 03:29:11 am »

Thanks Reuben,
       That will help.
       But I still would like flexibility of doing in DOM tree.

Candy

There is nothing stopping you using the DOM/XML classes in Genero to create a .42f at run-time and then loading it with OPEN WINDOW WITH FORM.  I did something very similar with the .4tb, .4tm, .4ad so that I had a consistent File/Edit/Help pattern in topmenu and a similarly consistent toolbar. 

The problem is, as alluded to in the CONSTRUCT thread, is that you can't modify the matching 4GL dialog statement at run-time.  So whilst at run-time you can create your report form files (.42f) to include what is in the customer maintenance form as one folder page, what you can't do at run-time is create/modify the input/construct to match.

Product Consultant (Asia Pacific)
Developer Relations Manager (Worldwide)
Author of https://4js.com/ask-reuben
Contributor to https://github.com/FourjsGenero
Reuben B.
Four Js
Posts: 990


« Reply #5 on: February 02, 2009, 03:46:26 am »

Reuben,
     I tried that but there are some things I don't understand with the
preprocessor.  Say my file is "customer.per" and I have an error in
"customer_layout.inc".  When I issue the command fglform customer.per
it doesn't place errors in customer.err.  They are placed in a file with
the text on next line and .err.  Say if next line is GROUP, it creates
an  error file group.err with no errors in it.  I finally issued the
command fglform -M customer so I could see the errors.  If someone
could explain what is going on, I'd appreciate it.

Candy

That doesn't look right, mind you it has been a while since I used an error file,  I tend to use Studio these days and when I do get to a command line I tend to use -M .  A similar thing happens with fglcomp.  I'll raise a support call.
 


Product Consultant (Asia Pacific)
Developer Relations Manager (Worldwide)
Author of https://4js.com/ask-reuben
Contributor to https://github.com/FourjsGenero
Reuben B.
Four Js
Posts: 990


« Reply #6 on: February 02, 2009, 04:12:13 am »

Reuben,
     I tried that but there are some things I don't understand with the
preprocessor.  Say my file is "customer.per" and I have an error in
"customer_layout.inc".  When I issue the command fglform customer.per
it doesn't place errors in customer.err.  They are placed in a file with
the text on next line and .err.  Say if next line is GROUP, it creates
an  error file group.err with no errors in it.  I finally issued the
command fglform -M customer so I could see the errors.  If someone
could explain what is going on, I'd appreciate it.

Candy

That doesn't look right, mind you it has been a while since I used an error file,  I tend to use Studio these days and when I do get to a command line I tend to use -M .  A similar thing happens with fglcomp.  I'll raise a support call.
 

Hang on, I don't get the same behaviour you are experiencing.  Perhaps create a simple example and send in a support call stating version numbers etc.  I would expect the behaviour to be such that tools such as vim -quickfix can edit the appropriate file.

Product Consultant (Asia Pacific)
Developer Relations Manager (Worldwide)
Author of https://4js.com/ask-reuben
Contributor to https://github.com/FourjsGenero
Candy M.
Posts: 139


« Reply #7 on: February 02, 2009, 06:06:31 am »

Reuben,
    I e-mailed 3 different test cases to Support on Friday.  One of the cases was a segmentation fault.
Candy
Candy M.
Posts: 139


« Reply #8 on: February 02, 2009, 06:14:55 am »

I'm using a screen record in the CONSTRUCT so maybe if
I don't use that, I could maybe accomplish what I want.

Candy
Candy M.
Posts: 139


« Reply #9 on: February 02, 2009, 06:34:35 am »

If I attempt to do this, I see a <RecordView > tag in the form and I'm thinking I would
need to add that to the DOM tree in order for it to communicate with the CONSTRUCT statement.

Candy
Bernard M.
Four Js
Posts: 44


« Reply #10 on: February 02, 2009, 12:35:21 pm »

Hi Candy,

I guess you need to include a file in the LAYOUT section to generate the wrong behavior.
This has been filled as new bug #Bz.11931.

Regards,
Bernard
Reuben B.
Four Js
Posts: 990


« Reply #11 on: February 03, 2009, 07:02:19 am »

Here is another way of achieving this.  This takes 2 .42f form files, reads them both, and inserts one of them as a folder page into the other form file.   I've hard-coded it a bit and there is a number of changes I'd make before putting it in production but hopefully gives you an idea of the potential.


Code
  1. -- customer2.4gl
  2. MAIN
  3.   DEFER INTERRUPT
  4.   OPTIONS INPUT WRAP
  5.   OPTIONS FIELD ORDER FORM
  6.  
  7.   MENU ""
  8.      COMMAND "Maintenance"
  9.         CALL do_maintenance()
  10.      COMMAND "Sales Report by Customer and Product"
  11.         CALL do_report1()
  12.      ON ACTION close
  13.         EXIT MENU
  14.   END MENU
  15. END MAIN
  16.  
  17. FUNCTION do_maintenance()
  18. DEFINE customer_code CHAR(10)
  19. DEFINE customer_name CHAR(30)
  20. DEFINE customer_address1 CHAR(30)
  21. DEFINE customer_address2 CHAR(30)
  22. DEFINE customer_address3 CHAR(30)
  23. DEFINE customer_address4 CHAR(30)
  24.  
  25.   OPEN WINDOW maint WITH FORM "customer2_maint"
  26.   INPUT BY NAME customer_code, customer_name, customer_address1,  customer_address2, customer_address3,  customer_address4
  27.      ON ACTION accept
  28.         EXIT INPUT
  29.      ON ACTION cancel
  30.         EXIT INPUT
  31.   END INPUT
  32.   CLOSE WINDOW maint
  33. END FUNCTION
  34.  
  35. FUNCTION do_report1()
  36. DEFINE customer_sql, product_sql STRING
  37.  
  38.   OPEN WINDOW report1 WITH FORM build_report_formfile("customer2_report1", "customer2_maint")
  39.   DIALOG
  40.      CONSTRUCT BY NAME customer_sql ON customer_code, customer_name, customer_address1,  customer_address2, customer_address3,  customer_address4
  41.      END CONSTRUCT  
  42.      CONSTRUCT BY NAME product_sql ON product_code
  43.      END CONSTRUCT
  44.      ON ACTION accept
  45.         EXIT DIALOG
  46.      ON ACTION cancel
  47.         EXIT DIALOG
  48.   END DIALOG
  49.   DISPLAY SFMT("%1 AND %2", customer_sql, product_sql)
  50.   CLOSE WINDOW report1
  51. END FUNCTION
  52.  
  53.  
  54.  
  55. # include file2 as a folderpage in file1
  56. FUNCTION build_report_formfile(file1, file2)
  57. DEFINE file1, file2 STRING
  58. DEFINE dom1, dom2 om.domdocument
  59. DEFINE root1, root2, folder1, page1, form2, grid1, recordview1, recordview2, link1, link2 om.DomNode
  60. DEFINE folder_list1, form_list2, recordview_list1, recordview_list2 om.nodelist
  61. DEFINE filename STRING
  62. DEFINE i INTEGER
  63.  
  64.   LET dom1 = om.DomDocument.createfromxmlfile(SFMT("%1.42f", file1))
  65.   LET dom2 = om.DomDocument.createfromxmlfile(SFMT("%1.42f", file2))
  66.   LET root1 = dom1.getdocumentelement()
  67.   LET root2 = dom2.getdocumentelement()
  68.  
  69.   LET folder_list1 = root1.selectbytagname("Folder")
  70.   IF folder_list1.getlength() > 0 THEN
  71.      LET folder1 = folder_list1.item(1)
  72.   ELSE
  73.      -- Handle errors better
  74.      DISPLAY "1"
  75.      EXIT PROGRAM 1
  76.   END IF
  77.   LET page1 = folder1.createchild("Page")
  78.   CALL page1.setAttribute("text", "Customer")
  79.   LET grid1 = page1.createChild("Grid")
  80.  
  81.   LET form_list2 = root2.selectbytagname("Form")
  82.   IF form_list2.getlength() > 0 THEN
  83.      LET form2 = form_list2.item(1)
  84.   ELSE
  85.      -- Handle errors better
  86.      DISPLAY "2"
  87.      EXIT PROGRAM 2
  88.   END IF
  89.  
  90.   LET recordview_list1 = root1.selectByTagName("RecordView")
  91.   IF recordview_list1.getlength() != 1 THEN
  92.      -- Handle errors better
  93.      DISPLAY "3"
  94.      EXIT PROGRAM 3
  95.   ELSE
  96.      LET recordview1 = recordview_list1.item(1)
  97.   END IF
  98.  
  99.   LET recordview_list2 = root2.selectByTagName("RecordView")
  100.   IF recordview_list2.getlength() != 1 THEN
  101.      -- Handle errors better
  102.      DISPLAY "4"
  103.      EXIT PROGRAM 4
  104.   ELSE
  105.      LET recordview2 = recordview_list2.item(1)
  106.   END IF
  107.  
  108.   -- Copy each link
  109.   FOR i = 1 TO recordview2.getchildcount()
  110.      LET link1 = recordview1.createchild("Link")
  111.      LET link2 = recordview2.getchildbyindex(i)
  112.      CALL copy_node(link2, link1,1)
  113.   END FOR
  114.  
  115.   -- Copy the form layout contents
  116.   CALL copy_node(form2.getChildByIndex(1), grid1,1)
  117.  
  118.   LET filename = SFMT("my_generated_formfile.42f",FGL_GETPID())  
  119.   CALL root1.writeXml(filename)
  120.  
  121.   RETURN filename
  122. END FUNCTION
  123.  
  124. -- copy attributes and children of node n1 to node n2
  125. -- displacement is to add them at the end of the existing fields defined in the form
  126. FUNCTION copy_node(n1, n2, displacement)
  127. DEFINE n1, n2, c1, c2 om.DomNode
  128. DEFINE displacement INTEGER
  129. DEFINE i INTEGER
  130.  
  131.   FOR i = 1 TO n1.getattributescount()
  132.      CASE n1.getattributename(i)
  133.         WHEN "fieldId"
  134.            CALL n2.setAttribute(n1.getattributename(i), (n1.getattributevalue(i)+displacement) USING "<<&")
  135.         WHEN "fieldIdRef"
  136.            CALL n2.setAttribute(n1.getattributename(i), (n1.getattributevalue(i)+displacement) USING "<<&")
  137.         WHEN "tabIndex"
  138.            CALL n2.setAttribute(n1.getattributename(i), (n1.getattributevalue(i)+displacement) USING "<<&")
  139.         OTHERWISE
  140.            CALL n2.setAttribute(n1.getattributename(i), n1.getattributevalue(i))
  141.      END CASE
  142.   END FOR
  143.   FOR i = 1 TO n1.getchildcount()
  144.      LET c1 = n1.getchildbyindex(i)
  145.      LET c2 = n2.createChild(c1.getTagName())
  146.      CALL copy_node(c1, c2, displacement)
  147.   END FOR
  148. END FUNCTION

Code
  1. -- customer2_maint.per
  2. LAYOUT
  3. GRID
  4. {
  5. Customer Code    [cu01      ] Customer Name [cu02                       ]
  6. Customer Address [cu03                      ]
  7.                  [cu04                      ]
  8.                  [cu05                      ]
  9.                  [cu06                      ]
  10. }
  11. END
  12. END
  13. ATTRIBUTES
  14. cu01 = formonly.customer_code;
  15. cu02 = formonly.customer_name;
  16. cu03 = formonly.customer_address1;
  17. cu04 = formonly.customer_address2;
  18. cu05 = formonly.customer_address3;
  19. cu06 = formonly.customer_address4;

Code
  1. -- customer2_report1.per
  2. LAYOUT (TEXT="Customer Sales by Product Report")
  3. FOLDER
  4. PAGE (TEXT="Product")
  5. GRID
  6. {
  7. Product   [sa01      ]
  8. }
  9. END
  10. END
  11. END
  12. END
  13. ATTRIBUTES
  14. sa01 = formonly.product_code;

Product Consultant (Asia Pacific)
Developer Relations Manager (Worldwide)
Author of https://4js.com/ask-reuben
Contributor to https://github.com/FourjsGenero
Candy M.
Posts: 139


« Reply #12 on: February 03, 2009, 07:48:20 am »

Thanks, Reuben.  I will look at that.

One of the things I did try today is that in the report form, on the second folder tab,
I had basically an empty page.

I opened the report form, then read in another form which contained the page
I wanted to extract.  I used the replaceChild() method and the program complained
that I was inserting a node where it didn't belong or something like that. So I didn't get
very far. 

Thanks for this idea.  I will look at it.

Candy
Rene S.
Four Js
Posts: 106


« Reply #13 on: February 10, 2009, 10:09:22 am »

Hi,
especially since the introduction of folders many real-live per files tends to be overcrowded. The problem with those overcrowded forms becomes more critical since having the DIALOG statement. That's why the request of modularizing forms sounds very important.

The proposed solutions - using the preprocessor, merging two forms at runtime via dom - have one important limitation: all form fields (and of course all other objects defined in the different sources) will appear at runtime within the same scope.

There is another suggestion. Imagine a new form-item in the per file is a placeholder for a 4gl-window. This would instruct the runtime to render the new window into this rectangle. No new 4gl instruction is required. The 4gl-programmer can use the CURRENT WINDOW  statement to select the "sub" window of his choice. This solution would help to split rich forms into logically units fitting the program logic.

This code fragment could define placeholders for two 4gl-windows:
Code
  1. GRID
  2. {
  3. <W window1            >
  4.  
  5. <W window2            >
  6.  
  7. }
  8. END

After displaying this form, a OPEN WINDOW window1 WITH FORM "foo" would merge at runtime the form "foo" into the "master" form. A TOOLBAR  or a TOPMENU defined in the "foo" form could replace temporarily  (or be merged into) the TOOLBAR or TOPMENU defined in the "master" form if the WINDOW window1 is the current window.

From my perspective this solution is easy to understand and very transparent. BTW, this would also help when migrating from the text-ui to the graphical-ui: If a programmer expects to open two or more windows at a given position, he can use this solution to reach the target with (hopefully) less amount of work.

Rene

Bryce S.
Posts: 52


« Reply #14 on: February 10, 2009, 10:40:34 pm »

Hi,

It's good to see this topic raised again - when I started using Genero it was one of the first things I wanted to be able to do (Search for thread: "Is it possible to open a group from one form inside another form?" Reply #6 on: February 28, 2008, 08:53:12 PM ) so as I could reuse the form elements from common modules in the main program window when those modules got called, instead of popping up a new window.  I ended up cloning the module's form sections (at the group level under the layout section with matching attributes  section) into the main program form to have it look good for a couple of programs, but this is not very good for maintenance so of limited use.

Reuben indicates it can be done with the pre-processor, but I can't really get my head around this and it seems complicated. So maybe Fourjs can come up with a friendly method to achieve this in the code? It seems like such an obvious thing to want to do once you get into the way Genero can display forms and hide/unhide groups, etc...

Regards,
  Bryce Stenberg.
Pages: [1] 2
  Reply  |  Print  
 
Jump to:  

Powered by SMF 1.1.21 | SMF © 2015, Simple Machines