Here's the solution that I developed for the ProcBrowser and WindowBrowser utilities that shipped on CRL3. It's easy to use and requires only minor changes to the window-handling code in your application. For efficiency, it retains & re-uses all the duplicates that it creates. It also automatically handles the deletion of obsolete duplicates when you update the master format.
Implementation
As an example, assume you have a window format, wCustomer, that you would like to convert to a multiple-instance window: First, you need to ensure that wCustomer stores all of its data in format variables, i.e. no globals. It _can_ use CRB fields, but only for temporary or scratch purposes, such as reading/writing data to disk. (This encapsulation is necessary, regardless of how you implement multiple instances). Then follow these steps:
1) Rename wCustomer as wCustomer01. (This is the master format, and it also serves as instance #1).
2) Choose any menu format in your library (e.g. STARTUP ) and add the following two format vars and three procedures:
Format variable fMaster (char 10000000) = 'wCustomer' Format variable fAllocTable (char 10000000)
Procedure Initialize
Local variable lWindows (List) Local variable lWind_Name (char 10000000) Local variable lWind_ModDate (Short date 1980-2079) Calculate lWindows as $clib.$windows.$makelist($ref.$name,$ref.$moddate) Set current list lWindows Redefine list {lWind_Name, lWind_ModDate} ;; Find and delete obsolete duplicates, if any, by comparing moddates Set search as calculation {mid(lWind_Name, 1, len(fMaster))=fMaster & lWind_ModDate<>$clib.$windows.[//fMaster]01.$moddate} Search list (From start, Select matches (OR), Deselect non-matches (AND)) For each line in list (Selected lines only) from 1 to #LN step 1 Delete format {[lst(lWind_Name)] End for Delete selected lines ;; Now delete them from the list too ;; Build a string of zeros in fAllocTable to serve as a bitmap Calculate %NumDupes as totc(mid(lWind_Name,1,len(fMaster)))=fMaster Calculate fAllocTable as jst('',con(%NumDupes,'P0'))
Procedure GetInstance
Local variable lBitPos (Long integer) Calculate lBitPos as pos('0',fAllocTable) ;; Look for unused format If not(lBitPos) ;; If they're all in use Calculate lBitPos as len(fAllocTable)+1 ;; Get next format number Duplicate format {[$clib().$name].[fMaster]01/[con(fMaster,jst(lBitPos,'-2P0'))]} ;; Clone the master format If flag false ;; If duplication failed OK message (Sound bell) {Unable to create new instance!} Set return value {''} ;; Return empty name to indicate failure Quit procedure End if End if ;; Set the appropriate bit to 1 to indicate that the format is now in use Calculate fAllocTable as con(mid(fAllocTable,1,lBitPos-1),1,mid(fAllocTable,lBitPos+1,100)) Set return value {con(fMaster,jst(lBitPos,'-2P0'))} ;; Return the name of the new 'instance'
Procedure FreeInstance
Parameter pInstanceName (Character 100) ;; Get the position of this format's bit in the allocation bitmap Calculate %BitPos as mid(pInstanceName, len(pInstanceName)-1,2) ;; Set it back to zero to indicate that it's available for use Calculate fAllocTable as con(mid(fAllocTable,1,%BitPos-1),'0',mid(fAllocTable,%BitPos+1,1000))
3) Ensure that the 'Initialize' procedure above is called when your library opens, i.e. add a "Call procedure Initialize" in your STARTUP/0.
4) This is how you create new instances: Search your library for all the places where you open the window wCustomer. These could be commands such as "Open window wCustomer" or "Call procedure wCustomer/Open", etc. Replace each such occurrence with:
Call procedure STARTUP/GetInstance with return value %%InstanceName If len(%%instanceName) Open [%%InstanceName] ;; or Call proc [%%InstanceName]/Open, etc End if
5) This is what you do when you're done with the instance: Modify your window control procedure in wCustomer01 as follows:
If #CLOSE Call procedure Close SNA Do not perform default action Else if #WCLICK; etc, etc.
Now add this procedure to wCustomer01:
Close ;; Do all the stuff you normally do upon closing your window, then
Call procedure STARTUP/FreeInstance ($cformat().$name) Close window $format
6) Make sure that there are no other "Close window wCustomer" commands anywhere in your library (This is generally a bad practice anyway). If you do have code that closes the top window or all open windows, have it call the window's Close routine instead. Whenever you need to make changes to your master format, just update and deploy wCustomer01. The next time the library is opened, it will automatically first delete any older duplicates of the format.