Template for generating column equates for ABC browse

Tags: #<Tag:0x00007f6ddb94a818> #<Tag:0x00007f6ddb94a610>

Instead of using hard-coded numeric constants for column numbers in code for manipulating your list control columns, it is better to use an equate for the column number.
Hard-coded column numbers can become incorrect when new columns are added or columns deleted or reordered in the list format.
I have uploaded a small example template that automatically generates equates for the columns in your list control. Using the use equates means your code won’t need to be updated when the order of your list control columns change.
So instead of coding something like this:

IF BRW1.ILC.GetControl(){Prop:Column} = 3
  !! do something
END

instead use

IF BRW1.ILC.GetControl(){Prop:Column} = BRW1:FieldNo:CUS:LastName
  !! do something
END

You can get the template code on GitHub:

4 Likes

A Clarion limitation of PROP and PROPLIST is you cannot get to the #FIELD() or FROM(Q) at runtime. A template like yours could generate User Defined properties (added 10/27/2015 in C10.11975) with that information. E.g. given this LIST:

LIST,AT(9,7,668,179),USE(?BrowseEmpTypes),FROM(Queue:Browse)
#FIELDS(TYP:Code,TYP:Desc,TYP:NormalHrsPerDay,TYP:CertifiedPersonnel,TeacherSub,TYP:TrsJobCategory)

Template can generate these properties

?BrowseEmpTypes{'#Field',1}='TYP:Code'
?BrowseEmpTypes{'#Field',2}='TYP:Desc'
?BrowseEmpTypes{'#Field',3}='TYP:NormalHrsPerDay'
?BrowseEmpTypes{'#Field',4}='TYP:CertifiedPersonnel'
?BrowseEmpTypes{'#Field',5}='TeacherSub'
?BrowseEmpTypes{'#Field',6}='TYP:TrsJobCategory'

?BrowseEmpTypes{'#FldUse',1}=Address(TYP:Code)
?BrowseEmpTypes{'#FldUse',2}=Address(TYP:Desc)
?BrowseEmpTypes{'#FldUse',3}=Address(TYP:NormalHrsPerDay)
?BrowseEmpTypes{'#FldUse',4}=Address(TYP:CertifiedPersonnel)
?BrowseEmpTypes{'#FldUse',5}=Address(TeacherSub)
?BrowseEmpTypes{'#FldUse',6}=Address(TYP:TrsJobCategory)

?BrowseEmpTypes{'#From'}='Queue:Browse'
?BrowseEmpTypes{'#FromUse'}=Address(Queue:Browse)

Might need to know the Parents:

?BrowseEmpTypes{'#FieldParent',3}='TYP:Record'
?BrowseEmpTypes{'#FldParentUse',3}=ADDRESS(TYP:Record)

I tested the above code showing a message:

 Message(?BrowseEmpTypes{'#Field',4} &'<9>'&  ?BrowseEmpTypes{'#FldUse',4} & |
         '||'&?BrowseEmpTypes{'#From'} &'<9>'&  ?BrowseEmpTypes{'#FromUse'} ) 

The UDFs are nice because the scope is for any code that can see the Window so beyond the main any Routines and Classes. If we can agree on the {‘Names’} then any template or class code can use them. A name of {"#FIELD"} seems obvious and logical. I think we need the USE Address to be able to assign an ANY and operate on the variable.

So just an idea…

2 Likes

Similar to Bruce’s NAME() attribute thread.

I remember back in the 90s, there was talk on the CI$ forum about creating an EVENT repository, but managing 3rd Party folks is like herding kittens. :slight_smile:

FWIW,on a semi-related note,
I use WHERE() when writing hand coded PROP:Formats

For example (scroll to the right in the source below)

SELF.ListFEQ{PROP:Format} =                                                                                                     |         
                '11L'     &  'FIY' &                                 '@[email protected]' & '#' & WHERE(SELF.Q, SELF.Q.Tag          ) & '#' |
        &       '20C'     & '|Y'  & '~Area~'              & 'L'    & '@[email protected]?'  & '#' & WHERE(SELF.Q, SELF.Q.Wall_Area    ) & '#' |
        &       '37C'     & '|IY' & '~Shape~'             &          '@[email protected]'   & '#' & WHERE(SELF.Q, SELF.Q.Shape        ) & '#' |
        &       '145L(1)' & '|FY' & '~Wall Description~'  & 'C(0)' & '@[email protected]'  & '#' & WHERE(SELF.Q, SELF.Q.Description  ) & '#' |
        & '[' &  '5C'     &  '*Y'                                  & '@[email protected]'   & '#' & WHERE(SELF.Q, SELF.Q.Out_BoxTrans ) & '#' |
        &       '47L(1)'  & '|Y'  & '~Outside~'           & 'L(0)' & '@[email protected]'  & '#' & WHERE(SELF.Q, SELF.Q.Outside      ) & '#' |
        & ']'             & '|'                                                                                           &     |
        & '[' &  '5C'     &  '*Y'                                  & '@[email protected]'   & '#' & WHERE(SELF.Q, SELF.Q.In_BoxTrans  ) & '#' |
        &       '47L(1)'  & '|Y'  & '~Inside~'            & 'L(0)' & '@[email protected]'  & '#' & WHERE(SELF.Q, SELF.Q.Inside       ) & '#'
1 Like

I like that a lot for flexibility in designing Q versus List.

Too bad the ## has to be numeric. An idea for you to use the LIST formatter would be to stick your #Where()# code into the Default Tip then in your final code change the Q’‘xxx’’ to #xxx#. But… the Q’’ currently breaks the window previewer, it stops generating Q fields. I have reported to SV.

 FORMAT('21C|M~Code~L(3)@[email protected]''WHERE(SELF.Q, SELF.Q.Tag)''' &|
        '140L(3)|~Employee Type~L(4)@[email protected]''WHERE(SELF.Q, SELF.Q.Wall_Area)'''     &|
        '27R(3)|*~Normal<0DH,0AH>Hours~C(0)@[email protected]''WHERE(SELF.Q, SELF.Q.Shape)''' &|

After S&R :

 FORMAT('21C|M~Code~L(3)@[email protected]#'& WHERE(SELF.Q, SELF.Q.Tag) &'#' &|
        '140L(3)|~Employee Type~L(4)@[email protected]#'& WHERE(SELF.Q, SELF.Q.Wall_Area) &'#'     &|
        '27R(3)|*~Normal<0DH,0AH>Hours~C(0)@[email protected]#'& WHERE(SELF.Q, SELF.Q.Shape) &'#' &|
        '29C|[email protected]@'                               &|

Seems more logical to use PROPList:FieldNo rather than mess around with the FORMAT string like that.

I store a Reference to the Queue in a user defined property ‘FromQ’ of the LIST (code below). I can then use TUFO Interface, WHO, WHAT, and PROPLIST to figure out the Q and List #Fields at runtime, so there is no need to store the individual #fields.

I would like to avoid this user property if anyone knows a way to get a reference to the FROM(Q)??

Maybe a RTL export or PROP I missed. Simply using ADDRESS(Q) does not work, maybe a reference to the “group” of fields.

   StoreListFromQProp(?Browse:1,BrowseQueue1,'BrowseQueue1')

    ...

FromQ &QUEUE
     CODE
      FromQ &= (?List{'FromQ'})  !Get List FromQ 
      Now use WHO(FromQ,#) ... WHAT(FromQ,#) ... TUFO


StoreListFromQProp     PROCEDURE(LONG FEQ,*QUEUE FrmQ,<STRING NameQ>)
Ref GROUP,AUTO
Q    &QUEUE
L    LONG,OVER(Q)
      END
      CODE
      Ref.Q &=FrmQ 
      FEQ{'FromQ'}=Ref.L
      FEQ{'FromWho'}=CHOOSE(~OMITTED(NameQ),NameQ,'Queue' & Ref.L)
      RETURN

Try this

qRef  &QUEUE
  threadno = THREAD()
  qRef &= INSTANCE(q, threadno)

Thanks! I forgot about that.

INSTANCE() does return a &Queue reference. The help makes it clear … sort of … “internal structure = reference” is clear from the example code:

INSTANCE can be used instead of the ADDRESS( ) statement when ADDRESS( ) is not valid or available (e.g. FILE and QUEUE structures). ADDRESS(QUEUE) is a legal call, but it returns the address of the queue’s internal buffer. On the other hand, INSTANCE(QUEUE,THREAD()) returns the address of the queue’s internal structure.

For example, given the following QUEUE declarations:

SomeQueue QUEUE
 ...
 
          END
 
 QueueRef &QUEUE

The following assignment is correct:

QueueRef &= INSTANCE (SomeQueue, Somethread)

while the assignment shown below is not correct:

QueueRef &= ADDRESS (SomeQueue)

and sets the QueueRef variable to the wrong value.

You could store the address of a named GROUP/OBJECT that has a &QUEUE as a member.

Would be nice to get at the &QUEUE in the PROP:FROM, though.

Hi Carl -

The stuff in abllist.int looks interesting. It doesn’t give you a queue reference, but you can do stuff with the listbox. It’s been around since at least C5.5, but it’s changed a bit since then.
I wonder if that could help with your window interrogation stuff. I’ve never used it.

PROP:FromPtr looks interesting too, but I’m not sure what it points to yet.

PROP:FromPtr looks interesting, but it looks to me like it returns an IMappedListContents INTERFACE which I concluded based on looking at ABLPROPR.CLW:

 IF (lFeq{PROP:fromptr} <> 0)
    SELF.From &= CreateListboxContents(lFeq)
 END

MAP; MODULE('ABLLIST')
CreateListboxContents     PROCEDURE(SIGNED Feq),*IMappedListContents
END; END

I don’t see anything useful in that interface, it looks to be for making web pages. There is also “IListboxContents INTERFACE” but nothing obvious jumps out. There is no use of that and a comment “!Internal interfaces to the list boxes - not supported”

I guess one would need to play with it to find out whether it’s useful.

It might have been created to create web pages, but oftentimes the original purpose of something doesn’t end up being the primary usage.