Home ProtocolsProtocols AV protocolAV protocol Drag&Drop protocolDrag&Drop protocol

15.2 BubbleGEM

Modern GEM applications often include a bewildering array of options and icons which take time to learn. Even after learning a program interface a short break away from the keyboard often triggers amnesia!

The solution?

BubbleGEM! Programmers supporting this cute little program can add speech bubble help to their user interface making it much easier for users to find their way around.


15.2.1 How to call BubbleGEM?

There are two fundamental ways to call BubbleGEM. The following example code snippet describes the simplest way to call BubbleGEM from non-modal dialogs and window-dialogs. Since BubbleGEM Release 05 it's now also possible to call BubbleGEM from modal dialogs.

Before every Bubble-call, supporting applications search via appl_find("BUBBLE ") for the BubbleGEM AES application identification number (bubble_id). Once the AES ID has been received, a message BUBBLEGEM_SHOW (hexadecimal: 0xBABB) can be sent to the ap_id and should receive a BUBBLEGEM_ACK (0xBABC) message in reply.

It is important the character string passed is NULL-terminated, 255 characters maximum and global legible - otherwise this leads to memory corruption in systems with memory protection. Always request global memory.

The message BUBBLEGEM_SHOW should be sent out as soon as the the right mouse button is pressed - don't wait until the button is released!

The mouse coordinates are passed using x and y and these are used to determine where the speech bubble will be drawn. Calls for BubbleGEM may only be instigated from non-modal dialogs.

Changes to BubbleGEM bubbles are updated automatically, however, you can intervene manually using the vertical line "|" character. Preceding or following space characters are not necessary.

C example:

#include <portab.h>

/* BUBBLEGEM_SHOW - Message:
 * msg[0]   0xBABB
 * msg[1]   ap_id
 * msg[2]   0
 * msg[3]   mouse X
 * msg[4]   mouse Y
 * msg[5/6] to NULL-terminated character string in global memory
 * msg[7]   0
 */

...

#define MGLOBAL         0x20

#define BUBBLEGEM_REQUEST  0xBABA
#define BUBBLEGEM_SHOW     0xBABB
#define BUBBLEGEM_ACK      0xBABC
#define BUBBLEGEM_ASKFONT  0xBABD
#define BUBBLEGEM_FONT     0xBABE
#define BUBBLEGEM_HIDE     0xBABF

#define MagX_COOKIE     0x4D616758L
#define MiNT_COOKIE     0x4D694E54L
...

WORD msg[8];
WORD bubble_id;
BYTE *bubble_text;

/* Establish if Mxalloc() is available, if yes,
 * then memory protection mode is set to "Global"
 */
if ((get_cookie (MagX_COOKIE, &val) == TRUE) ||
     (get_cookie (MiNT_COOKIE, &val) == TRUE))
{
        bubble_text = (BYTE *) Mxalloc (256, 0 | MGLOBAL);
}
else
        bubble_text = (BYTE *) Malloc (256);

if (!bubble_text)               /* Pointer invalid, no memory there */
        return;

if (right_mouse key_button_pressed)
{
    /* fill buffer */
    strcpy(bubble_text, "My first help speech bubble.");

    bubble_id = appl_find("BUBBLE  ");

    if (bubble_id >= 0)
    {
        msg[0] = BUBBLEGEM_SHOW;
        msg[1] = ap_id;
        msg[2] = 0;
        msg[3] = x;
        msg[4] = y;
        msg[5] = (WORD)(((LONG) bubble_text >> 16) & 0x0000ffff);
        msg[6] = (WORD)((LONG) bubble_text & 0x0000ffff);
        msg[7] = 0;
        if (appl_write(bubble_id, 16, msg) == 0)
        {
            /* Error */
        }
    }
}

A reply message BUBBLEGEM_ACK (0xBABC) is received with the pointer to the character string, whose memory can now be released. The array elements 2, 3, 4 and 7 are set to null (zeroed). On non-modal calls the BUBBLEGEM_ACK comes only after closing the bubble!

/* BUBBLEGEM_ACK (0xBABC)
 *
 * msg[0]   0xBABC
 * msg[1]   ap_id
 * msg[2]   0
 * msg[3]   0
 * msg[4]   0
 * msg[5/6] Pointer from BUBBLEGEM_SHOW
 * msg[7]   Same value as msg[7] on sending BUBBLEGEM_SHOW;
 *          presently 0.
 */

/* example code snippet */

pointer = *(BYTE **) &msg[5];

if (pointer)
  Mfree(pointer)

To make translation into other languages easier, please consider storing the character strings externally, for example in the resource file (RSC) or a separate ASCII file.

If on BUBBLEGEM_SHOW the bit BGS7_MOUSE (0x0004) in msg[7] is set, the passed coordinates are only used for drawing; BubbleGEM ascertains the mouse coordinates, which can be used to recognize mouse movement (leading to bursting the bubble) shortly before automatically re- displaying the bubble.

The procedure to call BubbleGEM from modal dialogs (see also the call routine) proceeds similarly to calls from non-modal dialogs, assuming the BGEM cookie is available, except BGS7_USRHIDE (0x0001) is set in msg[7] instead.

Additionally, after evaluating the mouse coordinates through the calling application, BUBBLEGEM_HIDE (0xbabf) must be sent to BubbleGEM. The evaluation of the mouse and keys on calling modal dialogs is the responsibility of the calling application.

On systems without wind_update, testmode is possible. Double help texts can be displayed (directly above one another). Currently this is not possible otherwise.

15.2.2 Call routine

Here's a general routine in pseudocode.

bubblegem:=appl_find('BUBBLE  ');
if bubblegem<0 then
  begin
    path:=GetEnv('BUBBLEGEM');
    if length(path)>0 then
      begin
        { start BubbleGEM in parallel, notice ID in bubblegem }
        if bubblegem>=0 then evnt_timer(500,0)
      end
  end;
if bubblegem<0 then exit; { BubbleGEM not found -> Cancel}
StrPCopy(bubblebuf,helptext);
msg[0]:=BUBBLEGEM_SHOW;
msg[1]:=apID;
msg[2]:=0;
msg[3]:=mx;
msg[4]:=my;
msg[5]:=integer(HiWord(bubblebuf));
msg[6]:=integer(LoWord(bubblebuf));
msg[7]:=0;
if non-modal_call then
  begin
    appl_write(bubblegem,16,@msg);
    evnt_timer(100,0)
  end
else
  begin
    { call from a system modal dialog: }
    if not(GetCookie('BGEM',bgemcookie)) then Bing { Error }
      { modal call only possible if BGEM-Cookie available }
    else
      begin
        msg[7]:=msg[7] or BGS7_USRHIDE;
        appl_write(bubblegem,16,@msg);
        evnt_timer(10,0);
        graf_mkstate(dummy,dummy,ms,dummy);
        if (ms and 3)=0 then { no mouse key pressed? }
          begin
            bclicks:=258;
            bmask:=3;
            bstate:=0
          end
        else
          begin
            bclicks:=0;
            bmask:=3;
            bstate:=0
          end;
        if not(GetCookie('BHLP',delay)) then delay:=200
        else
          delay:=(delay shr 16) and 0x0000ffff;
        graf_mouse(USER_DEF,bgemcookie->mhelp);
        evnt_timer(delay,0);
        evnt_multi(MU_KEYBD or MU_BUTTON or MU_M1, bclicks, bmask,
          bstate, 1, mX-6, mY-6, 13, 13, 0, 0, 0, 0, 0, msg,
          0, 0, dummy, dummy, ms, dummy, dummy, dummy);
        msg[0]:=BUBBLEGEM_HIDE;
        msg[1]:=apID;
        msg[2]:=0;
        msg[3]:=0;
        msg[4]:=0;
        msg[5]:=0;
        msg[6]:=0;
        msg[7]:=0;
        appl_write(bubblegem,16,@msg);
        graf_mouse(ARROW,NULL)
      end;
    repeat
      graf_mkstate(dummy,dummy,ms,dummy)
    until (ms and 3)=0
  end;

15.2.3 Time-controlled call

The configuration from applications is simply:

15.2.4 BubbleGEM cookies

BHLP:

BubbleGEM evaluates the cookie "BHLP". The top WORD sets the minimum visible time a bubble is displayed; the default setting is 200 milliseconds. The lower WORD is a bitmap; if bit 0 is set (0x0001 = BGC_FONTCHANGED), this means the FONT_CHANGED message will be evaluated. If BGC_NOWINSTYLE (0x0002) (i.e. no-win-style, not now-in- style) set, the bubble help will be displayed as a speech balloon. Conversely a deleted bit corresponds to Windows-help style display. A bit set in BGC_SENDKEY (0x0004) means AV_SENDKEY is sent to the caller after closing the bubble via a keypress. BGC_DEMONACTIVE (0x0008) means the demon is switched on. BGC_TOPONLY (0x0010) decides whether the help demon is only active for the topped window.

BGEM:

In addition, the BGEM cookie is placed in the cookie jar. It is available only on launching BubbleGEM, therefore it is installed on starting BubbleGEM and removed with AP_TERM.

 typedef struct
 {
   long   magic;   /* 'BGEM'                                      */
   long   size;    /* Size of the structure, currently 18         */
   int    release; /* Currently 7, never smaller than 5           */
   int    active;  /* <>0, if immediately help is displayed;
                        0  otherwise                              */
   MFORM *mhelp;   /* Pointer to help-mouse form                  */
   int    dtimer;  /* Demon-timer; Default 200ms; from Release 06 */
 } BGEM;

The structure lies in the global memory. Important: dtimer is the only field released for read and write. All other fields are read only!

15.2.5 BubbleGEM environmental variable

If you want to start BubbleGEM manually and it doesn't run, try setting the 'BUBBLEGEM' environmental variable. This specifies the absolute path to BubbleGEM including the program name. For example with MagiC, something like:

#_ENV BUBBLEGEM=D:\TOOLS\BUBBLE\BUBBLE.APP

15.2.6 Font-selection

On starting BubbleGEM a BUBBLEGEM_ASKFONT message will be sent to the AV-Server or the application with the AES ID 0. This should be answered with a BUBBLEGEM_FONT message (Jinnee supports this).

msg[0] = BUBBLEGEM_ASKFONT;
msg[1] = apID;
msg[2] = 0;
msg[3] = 0;
msg[4] = 0;
msg[5] = 0;
msg[6] = 0;
msg[7] = 0;

msg[0] = BUBBLEGEM_FONT;
msg[1] = apID;
msg[2] = 0;
msg[3] = FontID;
msg[4] = FontPt;
msg[5] = 0;
msg[6] = 0;
msg[7] = 0;

If BubbleGEM receives this message, it immediately takes the font with the ID msg[3] and the point size from msg[4] to display help bubbles. The ID and point size is adopted without checking validity. Additionally BubbleGEM reacts to FONT_CHANGED if the corresponding checkbox is active in the CPX module.

15.2.7 AV_SENDCLICK, BubbleGEM

msg[0] = AV_SENDCLICK  /* 0x4709 */
msg[1] = ap_id;
msg[2] = 0;
msg[3] = ev_mmox;
msg[4] = ev_mmoy;
msg[5] = ev_mmobutton;
msg[6] = ev_mmokstate;
msg[7] = ev_mbreturn;

AV_SENDCLICK will be sent if the bubble was closed by clicking (as opposed bursting bubbles by releasing the mouse button!).

AV_SENDCLICK is the counterpart to AV_SENDKEY.


Home ProtocolsProtocols AV protocolAV protocol Drag&Drop protocolDrag&Drop protocol