Poxology

First Published in QL Today V10/I6/p??

One of the limitations of the PE is that any window opened by a program has to fit inside the main application window (or the Outline, in PE parlance). This can be a bloody nuisance, as you may be aware.

Pox sets out to demonstrate a workaround to this problem. But Pox is more than that. It brings together a number og useful techniques, many of them only recently available, to bring them to the attention of programmers and tinkerers who may not yet be aware of them. To name a few: Pox is a workable Hinting routine, it also demonstrates the use of larger-than-Outline windows and dialogs in programs. It demonstrates some rudimentary inter-job communication. It uses the Home Directory Thing, and shows some simple ways of using the System Palettes. Hopefully both beginners and more advanced programmers may find something of use. Technical criticism and debate is welcomed.

illustration of Pox

The examples here use the latest version of EasyPtr and QLiberator to achieve this, but the principles should be relatively easy to apply to other systems by those who are familiar with them. Though the programs are written for SMSQ/E V3.11+, it shouldnt be too difficult to alter them to work with Qdos/Minerva running PE2.

Pox - short for POpup eXtended - displays a string, that may be wider than the Outline, in a hint window allowing the full string to be shown. (See illustration below.)

Pox consists of a number of parts. Most of those parts are re-usable as templates for your own programs. You could either download the complete package from the official QLT site or create your own from scratch following the detailed instructions below.

Either way it is useful to keep all the bits together in a single project folder or directory. For simplicity, I'll refer to this folder as win1_prg_pox_. Wherever you see this location referred to, replace it with your chosen location.

Hint Menu

The first thing you need is the Hint menu, like the one used in a previous demonstration of hinting published here in V10/I3/p21. If you dont have it, then briefly, here are the details once again. Alternatively skip down to Demo Menu:

Create a null sprite, eg a sprite of size 0, 0 (PE2) or 2,0 the rest. Now fire up Easy Menu and load the saved null sprite into the program. Set that sprite to be the pointer sprite for the present menu definition.

Next choose the outline Attributes and set the border to Hint Border. No need to fiddle with any other attributes. Finally DO the Change menu to bring up the outline window metrics and enter the following data into the table:

        No.      X       Y       X0      Y0     Object
        ---  - ----- - ----- - ----- - -----  -----------
         1   4    24 4    12 0   250 0    92  -> spr

The significant columns here are columns 2 to 5. The rest can be left alone. The '4' in columns 2 and 4 signify that the subsequent dimension can be varied freely. Finally call your menu 'hint' and save it in the project folder.

Demo Menu

For the Demo menu you could re-use the Hintdemo menu described in V10/I3 as for the Hint menu above, or you could create a minimalist demonstration menu in EasyPtr with the following characteristics: A main window with a Move and an Exit button and room enough to take a no less than 124 x 116 pixel Application window, as per the illustration above.

Demo Program

The Demo program contains all the functional code you will need to get extended line hinting in your own programs. My comments normally describe the code block immediately above, unless otherwise stated:

1 rem                            Pox Demo V0.01
2 rem                            ©pjwitte 2oo6
3 :
4 timep%     = 100: rem Timeout before pop-up
5 timed%     = 300: rem Pop-up display time
6 pal        = 0  : rem User-configurable palette 0..3 [or addr]
7 :

timep% and timed% affect the behaviour of the popup window and could be user-configurable. pal defines the palette number. It can have the values 0 to 3. It could also be user-configurable.

8 randomise date
9 :
10 dim dat$(16, 30), ddat$(16, 18)
11 for i% = 0 to dimn(dat$)
12  for j% = 1 to rnd(2 to dimn(dat$(0)))
13   dat$(i%) = dat$(i%) & chr$(rnd(32 to 191))
14  endfor j%
15  ddat$(i%) = dat$(i%)
16 endfor i%
17 :

This is where your application would read or generate some data. Here it is replaced by some dummy code. This produces some random length random strings and puts them into two arrays. The first array, dat$, contains the full length string, the second, ddat$, only as much of the string as can be displayed in the Application window.

Note: The last dimension of ddat$ may need to be altered if you are using an Application window of a smaller size than my demo.

18 sp_jobpal -1, pal: rem Set system palette to use for this and sub-jobs
19 :

Sets the System palette for the program to use. See also the Pox program below.

20 ch% = fopen(#0; 'con_')
21 mdraw#ch%; home_dir$ & 'hintdemo_men'
22 mawdraw#ch%; 1, ddat$

Opens and draws the main program menu. Note the use of home_dir$: This applies only to SMSQ/E versions 3.11+. The hintdemo_men file must then be located in the directory from which the demo is executed. Non-SMSQ/E users will have to hard-wire the location of the menu and accompanying Pox_obj program.

23 :
24 wsa = mwdef(#ch%): cia = peek_l(wsa)+ 48: rem Pointer to current item
25 :

cia points to the location in the Working Definition that is updated with the Current Item (if any) on return from the mcallt call.

26 ev% = 0:                rem Events
27 it% = -1: lit% = it%:   rem Current and last items
28 sw% = -1: lsw% = sw%:   rem Current sub window and last sub window
29 dim pv%(15):            rem Init pointer record
30 :

These variables must be initialised before the loop runs. ev% could be used for other things, such as timers. Lines 27 and 28 initialise some program switches.

31 rep main
32  item = mcallt(#ch%, ev%, timep%)
33  pval#ch%; pv%

If an item is hit, or it timed out, a snapshot of the pointer record is taken in pv%.

34  if item = -1280 then
35   rem timed out

mcallt returns -1280 on timeout, so this is where the hinting action goes:

36   lit% = it%: lsw% = sw%
37   it% = peek_w(cia): sw% = pv%(2)
38   if it% = lit% and sw% = lsw%: next main
39 :

Switches are set to ensure that the same item doesnt get called up repeatedly. The significance of the Current item (it%) is different in the main window from the application window, so therefore we need two values to judge what is required; one for each (sub-)window. The window the pointer is in is found in pv%(2) and now in sw%.

40   sel on sw%
41   = -1: rem Main window
42    sel on it%
43    = 0: Pox 'Move Window'
44    = 1: Pox 'Quit Program'
45    = remainder: Pox 'Furniture ' & it%
46    endsel

Only the standard buttons are found in the main window, such as Move and Exit. Anything else is trapped, for the sake of this demo.

47   = 0: rem Sub window 0
48    sel on it%
49    = -250: Pox 'Blank'
50    = 28000 to 32767: Pox 'Dont know ' & it%
51    = remainder: Pox dat$(it%)
52    endsel
53   endsel

This handles the Application window, trapping the furniture and any object or action it doesnt know. The relevant line is pointed to by it%, derived directly from the Current Item item number. The line can now be sent to Pox to be displayed in its full glory.

54  else
55   if item < 0 then
56    select on item
57    = -1: rem Wmove (but never gets here)
58    = -2: quit
59    endsel
60   endif
61   sw% = -1111: rem Reset
62  endif
63 endrep main

Above are the normal action routines of the program. Not much in the case of this demo.

64 :
65 def proc Pox(txt$)
66 loc id
67 id = few(home_dir$ & 'Pox_obj'; hex$(pal, 32) & txt$)
68 enddef Pox
69 :

The Pox function merely calls up the program that does all the hard work. For the meaning of the parameters, see the Pox program, below. Note the use of home_dir$ again. id could return a message from the called job: a negative intger representing an error, or any other return value. This would be returned by the calling job with QUIT (with being some return code).

The pal parameter is not used to good effect in this demo. In a real program yould allow your user to specify any of the four built-in system palettes. If you had a house livery, for example, you could specify your own palette, or choice of palettes. If you were feeling particularly generous, you might even allow the user to devise his own palette specifically for use with your program. Either way, Pox will handle it. For Pox pal can have a value from 0 to 3 or the address of a valid palette definition in memory.

Beware: Any value outside 0..3 is taken to be an address, so carelessness could crash the machine.

Pox Program

Youve seen how the Demo program used the home_dir$ to find its menu definition. I could well have used this technique here, but thought Id move on to something more advanced. The program is to be compiled. The following steps should be familiar to those who frequently use EasyPtr:

You need to use EasyPtr's Appman utility to prepare the hint menu for appending to the compiled code. Simply start up Appman and specify that you want to load a menu. Load the hint_men menu prepared earlier and save the file as Pox_app. Then load it into S*Basic with LRESPR. Modify line 1 below, according to where you put it. Type in and compile the lines below and place the object file in the pox project folder.

1 remark $$asmb=win1_prg_pox_Pox_app,0,10
2 remark $$chan=2
3 remark $$stak=128
4 remark $$heap=512
5 :

Show QLib where the binary is, and set the memory overheads. These values are rough estimates.

6 rem POX - POpup eXternal - External Hint
7 :
8 rem ©pjwitte 2oo5
9 rem V0.01 November 17th 2005
10 :
11 sp_hintbd%      = 552: rem Hint border
12 sp_hintbg%      = 553: rem Hint background
13 sp_hintfg%      = 554: rem Hint foreground
14 :
15 if len(CMD$) < 9: quit
16 c$ = CMD$

If the command line is wrong, whatever executed this program didnt mean to and the program terminates without a squeak.

17 mn_pox = appa0("hint"): rem Menu address
18 :

Get the address of the menu definition

19 pal = hex(c$(1 to 8))
20 if pal > 3 or pal < 0 then
21  sp_jobownpal -1, pal: rem Use calling job's private palette
22 else
23  sp_jobpal -1, pal: rem Use system palette
24 endif
25 :

If pal is an address (presumably somewhere in the calling job's heap) use that, otherwise use system palette 0..3 (The standard system palettes all use a yellow hint window background, so you wont see much difference between them. The technique is more useful with other external objects you could implement.)

26 c$ = c$(9 to)

The rest of the parameter is the text to display.

27 dim pv%(15)
28 :
29 ch% = fopen(#0; "con_")
30 rdpt#ch%; 48: pval#ch%; pv%: close#ch%
31 x% = pv%(14): y% = pv%(15) + 2

Find out pointer location, but discard window again as it interferes with operations..

32 l% = len(c$) * 6 + 4
33 xs% = x% - (l% div 2): if xs% < 1: xs% = 1
34 :

Calculate size of window required. (There is no contingency for text that exceeds the width of the screen!)

35 ch% = fopen(#0; "con_")
36 mdraw#ch%; mn_pox, xs%, y%, l%, 14
37 rdpt#ch%; 48, x%, y%
38 wm_paper#ch%; sp_hintbg%: wm_ink#ch%; sp_hintfg%
39 cursor#ch%; 2, 2: print#ch%; c$;
40 rdpt#ch%; 9
41 :

Open and display hint window and show the text. Die on a direct click or the slightest movement of the pointer - even if the window is buried.

DMiner

The concept of using external dialogs or utilities can of course be widened. QMenu's file selector and other dialogs lend themselves particularly well to such use (as illustrated above). No excuse anymore taking up half the screen with two buttons and a line of text because you need the window for File Select later!

The program above and associated files can be downloaded here.

An updated version (V0.04) of the program, without the text, is here.

Have fun!

©pjwitte August 26th 2oo6