Scripts/SourceDestDir

From J Wiki
Jump to navigation Jump to search

A small sample program that brings up an input window requesting the user to supply a "source" and "destination" directory, e.g. for getting files and zipping them together into a (possibly) different directory.

This is of particular interest to those using the "wd" windowing facilities as it implements its own explicit event-loop. This may help understand this normally hidden process as well as allowing one to customize it. In this case, the event-loop is explicit because we want to call this code and wait until the input has been entered before proceeding, e.g. something like:

NB.  Some code...
   sd=. sddir_handler_srcdest_ ''   NB. Get source and destination directories
NB.  More code using the input from above...

However, since windowing is event-driven, using the default event-loop handler will immediately proceed with the lines following the invocation of the input window - it won't wait for the user input before continuing. Having our own event-loop allows us to use a global variable "keepWaiting" to control when we return control to the calling function.

The script can be found here: File:SDDir.ijs .

Brief Explanation

Following is a brief overview the whole script, broken down by the major sections in the order in which they appear.

First, document what this script is supposed to do and give an example, in the text variable "usuinit", of how to invoke it.

NB.* SDDir.ijs: Source/Destination directory specification.

usuinit=: 0 : 0
   load '~User/code/SDDir.ijs'               NB. Assumes my own User area.
   coinsert_srcdest_ 'base'
   DEBUGON_srcdest_=: 1                      NB. Display events as they happen.
   sd=. sddir_handler_srcdest_ ''
)

Next, set up these functions in their own namespace "srcdest" to keep them neatly out of the way of code that may invoke this as a sub-routine. Also, load a couple of scripts whose functions we'll want to use. One of these, "dirbrowse", is included with the standard J libraries. The other, "logger", is on the J Wiki.

coclass 'srcdest'
load 'dirbrowse logger'                      NB. See "Scripts/logger.ijs"

DEBUGON=: 0                                  NB. 0 no debug, 1 debug
NB. INITFL=: 'SDDir.ini',~getTempDir ''      NB. Keep window field values here

Note that this latter script is loaded using only a shortcut name "logger", not the fully-qualified name. This is made possible by an entry in the global variable PUBLIC_j_ which is a two-column table where each row is short name ; fully-qualified name. So, on my system,

   PUBLIC_j_{~(0{"1 PUBLIC_j_)i.<'logger'
+------+---------------------+
|logger|~User\code\logger.ijs|
+------+---------------------+

You'll have to add an entry like this to your own PUBLIC_j_, preferably in your startup, to load this the same way. Alternately, you can change the load command to use the fully-qualified name for the "logger" script, e.g. something like

   load '~user/logger.ijs'

if you saved the script in your "user" sub-directory under the main J directory.

Input Window

Now we define the text of the input window:

SDDIR=: 0 : 0
pc sddir;
pn "Source and Destination Directories";
xywh 240 8 34 12;cc ok button;cn "OK";
xywh 240 23 34 12;cc cancel button;cn "Cancel";

xywh 2 6 40 12;cc inpBoxSD static;cn "Source Directory";
xywh 60 6 115 12;cc SDsrcd edit;
xywh 190 6 34 12;cc brwsSrc button;cn "Browse";

xywh 2 23 60 12;cc inpBoxDD static;cn "Destination Directory";
xywh 60 23 115 12;cc SDdestd edit;
xywh 190 23 34 12;cc brwsDest button;cn "Browse";

xywh 12 40 150 12;cc inpBoxMsg static;cn "Enter directories";
pas 6 6;pcenter;
rem form end;
)

When this literal string SDDIR is given to "wd" as an argument, it will set up the intial window to be displayed when the command wd 'pshow' is subsequently invoked. This window will look like this:

SrcDestWindowInitialView.png

This window is displayed, and its events are explicitly handled, by the following code:

sddir_handler=: 3 : 0
   wd SDDIR
NB.   SDHWNDP=: wd 'qhwndp;'
NB. Initialize entry fields here...
   if. 0>4!:0 <'SRCDIR' do. SRCDIR=: '<source directory>' end.
   if. 0>4!:0 <'DESTDIR' do. DESTDIR=: '<destination or target directory>' end.
NB. Put current values or explanatory text in the entry fields.
   wd 'set SDsrcd "',SRCDIR,'"'
   wd 'set SDdestd "',DESTDIR,'"'
   wd 'setfocus SDsrcd'
   wd 'pshow'
NB. Our very own event loop.
   keepWaiting=: 1
   while. keepWaiting do.               NB. Wait until user exits...
NB. This is the key: assign the entire queue to "wdq" and
       wdq=: wd 'wait;q'
NB. assign every variable in 1st column to its value in the 2nd column.
       ({."1 wdq)=: {:"1 wdq
       if. DEBUGON do.
           smoutput 'syschild is "',syschild,'"; systype is "',systype,'".'
       end.
       cxl=. (<'cancel') e. systype;syschild
       button=. systype -: 'button'
       if. button do.
           select. syschild
               fcase. 'enter' do.                 NB. Don't think this works.
               case. 'ok' do. sddir_ok_button ''
NB. Treat enter in an input field just like pressing OK.
               case. 'SDdestd' do. sddir_ok_button ''
               case. 'SDsrcd' do. sddir_ok_button ''
               case. 'brwsSrc' do.
                   wd 'set inpBoxMsg "Enter Directory or press OK"'
                   sddir_brwsSrc_button ''
               case. 'brwsDest' do. sddir_brwsDest_button ''
               case. 'cancel' do. sddir_cancel_button ''
           end.
       end.
   end.
)

The code for the "OK" button will fill in the destination directory to be the same as the source directory if only the source is specified.

sddir_ok_button=: 3 : 0
NB.* sddir_ok_button: set source and destination directory values: SRCDIR & DESTDIR.
NB.   ({."1 wdq)=: {:"1 wdq
   SRCDIR=: SDsrcd
   DESTDIR=: SDdestd          NB. Destination same as source if not spec'd.
   if. '<'={.DESTDIR do. DESTDIR=: SRCDIR end.    NB. '<' is from original
   wd 'set SDdestd "',DESTDIR,'"'                 NB.  default text.
   if. DEBUGON do.
       smoutput 'ok_button: Source dir: ',SRCDIR,'; dest dir: ',DESTDIR,'.'
   end.
   wd 'reset' [ sddir_close ''
   smoutput ''           NB. Seems necessary to -> immediate execution.
NB. Ensure dirs end with path separator (i.e. '/').
   ps=. PATHSEP_j_
   'SRCDIR DESTDIR'=: (SRCDIR;DESTDIR),&.>(ps~:&.>{:&.>SRCDIR;DESTDIR)#&.><ps
   keepWaiting=: 0
   SRCDIR;DESTDIR
)

The following code is invoked to browse for the source and destination directories using the jdirbrowse function from the dirbrowse namespace.

sddir_brwsSrc_button=: 3 : 0
   sv=. SRCDIR
   if. DEBUGON do.
       smoutput 'brwsSrc_button: Source dir: ',SRCDIR,'; dest dir: ',DESTDIR,'.'
   end.
   SRCDIR=: dirbrowse_jdirbrowse_ ''
   if. 0=#SRCDIR do. SRCDIR=: sv end.
   if. DEBUGON do.
       smoutput 'brwsSrc_button: Source dir: ',SRCDIR,'; dest dir: ',DESTDIR,'.'
   end.
   wd 'set SDsrcd "',SRCDIR,'"'
)

sddir_brwsDest_button=: 3 : 0
   sv=. DESTDIR
   if. DEBUGON do.
       smoutput 'brwsDest_button: Source dir: ',SRCDIR,'; dest dir: ',DESTDIR,'.'
   end.
   DESTDIR=: dirbrowse_jdirbrowse_ ''
   if. 0=#DESTDIR do. DESTDIR=: sv end.
   if. DEBUGON do.
       smoutput 'brwsDest_button: Source dir: ',SRCDIR,'; dest dir: ',DESTDIR,'.'
   end.
   wd 'set SDdestd "',DESTDIR,'"'
)

Finally, we implement the code for the "Close" and "Cancel" buttons, then finish off by returning control to the base namespace.

sddir_close=: 3 : 0
   wd'pclose'
)

sddir_cancel_button=: 3 : 0
   4!:55&><&.>'SRCDIR';'DESTDIR'
   keepWaiting=: 0
   sddir_close''
   smoutput ''           NB. Seems necessary to -> immediate execution.
)

coclass 'base'