#include "portab.h" /* portable coding conv */ #include "machine.h" /* machine depndnt conv */ #include "obdefs.h" /* object definitions */ #include "gembind.h" /* gem binding structs */ #include "taddr.h" #define M1_ENTER 0x0000 #define M1_EXIT 0x0001 #define BS 0x0008 #define TAB 0x0009 #define CR 0x000D #define ESC 0x001B #define BTAB 0x0f00 #define UP 0x4800 #define DOWN 0x5000 #define DEL 0x5300 /* Global variables used by */ /* 'mapped' functions */ MLOCAL GRECT br_rect; /* Current break rectangle */ MLOCAL WORD br_mx, br_my, br_togl; /* Break mouse posn & flag */ MLOCAL WORD fn_obj; /* Found tabable object */ MLOCAL WORD fn_last; /* Object tabbing from */ MLOCAL WORD fn_prev; /* Last EDITABLE obj seen */ MLOCAL WORD fn_dir; /* 1 = TAB, 0 = BACKTAB */ /************* Utility routines for new forms manager ***************/ VOID objc_toggle(tree, obj) /* Reverse the SELECT state */ LONG tree; /* of an object, and redraw */ WORD obj; /* it immediately. */ { WORD state, newstate; GRECT root, ob_rect; objc_xywh(tree, ROOT, &root); state = LWGET(OB_STATE(obj)); newstate = state ^ SELECTED; objc_change(tree, obj, 0, root.g_x, root.g_y, root.g_w, root.g_h, newstate, 1); } VOID /* If the object is not already */ objc_sel(tree, obj) /* SELECTED, make it so. */ LONG tree; WORD obj; { if ( !(LWGET(OB_STATE(obj)) & SELECTED) ) objc_toggle(tree, obj); } VOID /* If the object is SELECTED, */ objc_dsel(tree, obj) /* deselect it. */ LONG tree; WORD obj; { if (LWGET(OB_STATE(obj)) & SELECTED) objc_toggle(tree, obj); } VOID /* Return the object's GRECT */ objc_xywh(tree, obj, p) /* through 'p' */ LONG tree; WORD obj; GRECT *p; { objc_offset(tree, obj, &p->g_x, &p->g_y); p->g_w = LWGET(OB_WIDTH(obj)); p->g_h = LWGET(OB_HEIGHT(obj)); } VOID /* Non-cursive traverse of an */ map_tree(tree, this, last, routine) /* object tree. This routine */ LONG tree; /* is described in PRO GEM #5. */ WORD this, last; WORD (*routine)(); { WORD tmp1; tmp1 = this; /* Initialize to impossible value: */ /* TAIL won't point to self! */ /* Look until final node, or off */ /* the end of tree */ while (this != last && this != NIL) /* Did we 'pop' into this node */ /* for the second time? */ if (LWGET(OB_TAIL(this)) != tmp1) { tmp1 = this; /* This is a new node */ this = NIL; /* Apply operation, testing */ /* for rejection of sub-tree */ if ((*routine)(tree, tmp1)) this = LWGET(OB_HEAD(tmp1)); /* Subtree path not taken, */ /* so traverse right */ if (this == NIL) this = LWGET(OB_NEXT(tmp1)); } else /* Revisiting parent: */ /* No operation, move right */ { tmp1 = this; this = LWGET(OB_NEXT(tmp1)); } } WORD /* Find the parent object of */ get_parent(tree, obj) /* by traversing right until */ LONG tree; /* we find nodes whose NEXT */ WORD obj; /* and TAIL links point to */ { /* each other. */ WORD pobj; if (obj == NIL) return (NIL); pobj = LWGET(OB_NEXT(obj)); if (pobj != NIL) { while( LWGET(OB_TAIL(pobj)) != obj ) { obj = pobj; pobj = LWGET(OB_NEXT(obj)); } } return(pobj); } WORD inside(x, y, pt) /* determine if x,y is in rectangle */ WORD x, y; GRECT *pt; { if ( (x >= pt->g_x) && (y >= pt->g_y) && (x < pt->g_x + pt->g_w) && (y < pt->g_y + pt->g_h) ) return(TRUE); else return(FALSE); } WORD rc_intersect(p1, p2) /* compute intersection of two GRECTs */ GRECT *p1, *p2; { WORD tx, ty, tw, th; tw = min(p2->g_x + p2->g_w, p1->g_x + p1->g_w); th = min(p2->g_y + p2->g_h, p1->g_y + p1->g_h); tx = max(p2->g_x, p1->g_x); ty = max(p2->g_y, p1->g_y); p2->g_x = tx; p2->g_y = ty; p2->g_w = tw - tx; p2->g_h = th - ty; return( (tw > tx) && (th > ty) ); } VOID rc_copy(psbox, pdbox) /* copy source to destination rectangle */ GRECT *psbox; GRECT *pdbox; { pdbox->g_x = psbox->g_x; pdbox->g_y = psbox->g_y; pdbox->g_w = psbox->g_w; pdbox->g_h = psbox->g_h; } /************* "Hot-spot" manager and subroutines ***************/ WORD break_x(pxy) WORD *pxy; { /* Breaking object is right of */ if (br_mx < pxy[0]) /* mouse. Reduce width of */ { /* bounding rectangle. */ br_rect.g_w = pxy[0] - br_rect.g_x; return (TRUE); } if (br_mx > pxy[2]) /* Object to left. Reduce width*/ { /* and move rect. to right */ br_rect.g_w += br_rect.g_x - pxy[2] - 1; br_rect.g_x = pxy[2] + 1; return (TRUE); } return (FALSE); /* Mouse within object segment. */ } /* Break attempt fails. */ WORD break_y(pxy) WORD *pxy; { if (br_my < pxy[1]) /* Object below mouse. Reduce */ { /* height of bounding rect. */ br_rect.g_h = pxy[1] - br_rect.g_y; return (TRUE); } if (br_my > pxy[3]) /* Object above mouse. Reduce */ { /* height and shift downward. */ br_rect.g_h += br_rect.g_y - pxy[3] - 1; br_rect.g_y = pxy[3] + 1; return (TRUE); } /* Emergency escape test! Protection vs. turkeys who nest */ /* non-selectable objects inside of selectables. */ if (br_mx >= pxy[0] && br_mx <= pxy[1]) { /* Will X break fail? */ br_rect.g_x = br_mx; /* If so, punt! */ br_rect.g_y = br_my; br_rect.g_w = br_rect.g_h = 1; return (TRUE); } return (FALSE); } WORD break_obj(tree, obj) /* Called once per object to */ LONG tree; /* check if the bounding rect. */ WORD obj; /* needs to be modified. */ { GRECT s; WORD flags, broken, pxy[4]; objc_xywh(tree, obj, &s); grect_to_array(&s, pxy); if (!rc_intersect(&br_rect, &s)) return (FALSE); /* Trivial rejection case */ flags = LWGET(OB_FLAGS(obj)); /* Is this object a potential */ if (flags & HIDETREE) /* hot-spot? */ return (FALSE); if ( !(flags & SELECTABLE) ) return (TRUE); if (LWGET(OB_STATE(obj)) & DISABLED) return (TRUE); for (broken = FALSE; !broken; ) /* This could take two passes */ { /* if the first break fails. */ if (br_togl) broken = break_x(pxy); else broken = break_y(pxy); br_togl = !br_togl; } return (TRUE); } WORD /* Manages mouse rectangle events */ form_hot(tree, hot_obj, mx, my, rect, mode) LONG tree; WORD hot_obj, mx, my, *mode; GRECT *rect; { GRECT root; WORD state; objc_xywh(tree, ROOT, &root); /* If there is already a hot-spot */ if (hot_obj != NIL) /* turn it off. */ objc_toggle(tree, hot_obj); if (!(inside(mx, my, &root)) ) /* Mouse has moved outside of */ { /* the dialog. Wait for return. */ *mode = M1_ENTER; rc_copy(&root, rect); return (NIL); } /* What object is mouse over? */ /* (Hit is guaranteed.) */ hot_obj = objc_find(tree, ROOT, MAX_DEPTH, mx, my); /* Is this object a hot-spot? */ state = LWGET(OB_STATE(hot_obj)); if (LWGET(OB_FLAGS(hot_obj)) & SELECTABLE) if ( !(state & DISABLED) ) { /* Yes! Set up wait state. */ *mode = M1_EXIT; objc_xywh(tree, hot_obj, rect); if (state & SELECTED) /* But only toggle if it's not */ return (NIL); /* already SELECTED! */ else { objc_toggle(tree, hot_obj); return (hot_obj); } } rc_copy(&root, &br_rect); /* No hot object, so compute */ br_mx = mx; /* mouse bounding rectangle. */ br_my = my; br_togl = 0; map_tree(tree, ROOT, NIL, break_obj); rc_copy(&br_rect, rect); /* Then return to wait state. */ *mode = M1_EXIT; return (NIL); } /************* Keyboard manager and subroutines ***************/ WORD find_def(tree, obj) /* Check if the object is DEFAULT */ LONG tree; WORD obj; { /* Is sub-tree hidden? */ if (HIDETREE & LWGET(OB_FLAGS(obj))) return (FALSE); /* Must be DEFAULT and not DISABLED */ if (DEFAULT & LWGET(OB_FLAGS(obj))) if ( !(DISABLED & LWGET(OB_STATE(obj))) ) fn_obj = obj; /* Record object number */ return (TRUE); } WORD find_tab(tree, obj) /* Look for target of TAB operation. */ LONG tree; WORD obj; { /* Check for hiddens subtree. */ if (HIDETREE & LWGET(OB_FLAGS(obj))) return (FALSE); /* If not EDITABLE, who cares? */ if ( !(EDITABLE & LWGET(OB_FLAGS(obj))) ) return (TRUE); /* Check for forward tab match */ if (fn_dir && fn_prev == fn_last) fn_obj = obj; /* Check for backward tab match */ if (!fn_dir && obj == fn_last) fn_obj = fn_prev; fn_prev = obj; /* Record object for next call. */ return (TRUE); } WORD form_keybd(tree, edit_obj, next_obj, kr, out_obj, okr) LONG tree; WORD edit_obj, next_obj, kr, *out_obj, *okr; { if (LLOBT(kr)) /* If lower byte valid, mask out */ kr &= 0xff; /* extended code byte. */ fn_dir = 0; /* Default tab direction if backward. */ switch (kr) { case CR: /* Zap character. */ *okr = 0; /* Look for a DEFAULT object. */ fn_obj = NIL; map_tree(tree, ROOT, NIL, find_def); /* If found, SELECT and force exit. */ if (fn_obj != NIL) { objc_sel(tree, fn_obj); *out_obj = fn_obj; return (FALSE); } /* Falls through to */ case TAB: /* tab if no default */ case DOWN: fn_dir = 1; /* Set fwd direction */ case BTAB: case UP: *okr = 0; /* Zap character */ fn_last = edit_obj; fn_prev = fn_obj = NIL; /* Look for TAB object */ map_tree(tree, ROOT, NIL, find_tab); if (fn_obj == NIL) /* try to wrap around */ map_tree(tree, ROOT, NIL, find_tab); if (fn_obj != NIL) *out_obj = fn_obj; break; default: /* Pass other chars */ return (TRUE); } return (TRUE); } /************* Mouse button manager and subroutines ***************/ WORD do_radio(tree, obj) LONG tree; WORD obj; { GRECT root; WORD pobj, sobj, state; objc_xywh(tree, ROOT, &root); pobj = get_parent(tree, obj); /* Get the object's parent */ for (sobj = LWGET(OB_HEAD(pobj)); sobj != pobj; sobj = LWGET(OB_NEXT(sobj)) ) { /* Deselect all but... */ if (sobj != obj) objc_dsel(tree, sobj); } objc_sel(tree, obj); /* the one being SELECTED */ } WORD /* Mouse button handler */ form_button(tree, obj, clicks, next_obj, hot_obj) LONG tree; WORD obj, clicks, *next_obj, *hot_obj; { WORD flags, state, hibit, texit, sble, dsbld, edit; WORD in_out, in_state; flags = LWGET(OB_FLAGS(obj)); /* Get flags and states */ state = LWGET(OB_STATE(obj)); texit = flags & TOUCHEXIT; sble = flags & SELECTABLE; dsbld = state & DISABLED; edit = flags & EDITABLE; if (!texit && (!sble || dsbld) && !edit) /* This is not an */ { /* interesting object */ *next_obj = 0; return (TRUE); } if (texit && clicks == 2) /* Preset special flag */ hibit = 0x8000; else hibit = 0x0; if (sble && !dsbld) /* Hot stuff! */ { if (flags & RBUTTON) /* Process radio buttons*/ do_radio(tree, obj); /* immediately! */ else if (!texit) { in_state = (obj == *hot_obj)? /* Already toggled ? */ state: state ^ SELECTED; if (!graf_watchbox(tree, obj, in_state, in_state ^ SELECTED)) { /* He gave up... */ *next_obj = 0; *hot_obj = NIL; return (TRUE); } } else /* if (texit) */ if (obj != *hot_obj) /* Force SELECTED */ objc_toggle(tree, obj); } if (obj == *hot_obj) /* We're gonna do it! So don't */ *hot_obj = NIL; /* turn it off later. */ if (texit || (flags & EXIT) ) /* Exit conditions. */ { *next_obj = obj | hibit; return (FALSE); /* Time to leave! */ } else if (!edit) /* Clear object unless tabbing */ *next_obj = 0; return (TRUE); } /************* New forms manager: Entry point and main loop *************/ WORD form_do(tree, start_fld) REG LONG tree; WORD *start_fld; { REG WORD edit_obj; WORD next_obj, hot_obj, hot_mode; WORD which, cont; WORD idx; WORD mx, my, mb, ks, kr, br; GRECT hot_rect; WORD (*valid)(); /* Init. editing */ next_obj = *start_fld; edit_obj = 0; /* Initial hotspot cndx */ hot_obj = NIL; hot_mode = M1_ENTER; objc_xywh(tree, ROOT, &hot_rect); /* Main event loop */ cont = TRUE; while (cont) { /* position cursor on */ /* the selected */ /* editting field */ if (edit_obj != next_obj) if (next_obj != 0) { edit_obj = next_obj; next_obj = 0; objc_edit(tree, edit_obj, 0, &idx, EDINIT); } /* wait for button or */ /* key or rectangle */ which = evnt_multi(MU_KEYBD | MU_BUTTON | MU_M1, 0x02, 0x01, 0x01, hot_mode, hot_rect.g_x, hot_rect.g_y, hot_rect.g_w, hot_rect.g_h, 0, 0, 0, 0, 0, 0x0L, 0, 0, &mx, &my, &mb, &ks, &kr, &br); if (which & MU_M1) /* handle rect. event */ hot_obj = form_hot(tree, hot_obj, mx, my, &hot_rect, &hot_mode); /* handle keyboard event*/ if (which & MU_KEYBD) { /* Control char filter */ cont = form_keybd(tree, edit_obj, next_obj, kr, &next_obj, &kr); if (kr && edit_obj) /* Add others to object */ objc_edit(tree, edit_obj, kr, &idx, EDCHAR); } /* handle button event */ if (which & MU_BUTTON) { /* Which object hit? */ next_obj = objc_find(tree, ROOT, MAX_DEPTH, mx, my); if (next_obj == NIL) next_obj = 0; else /* Process a click */ cont = form_button(tree, next_obj, br, &next_obj, &hot_obj); } /* handle end of field */ /* clean up */ if (!cont || (next_obj != edit_obj && next_obj != 0)) if (edit_obj != 0) objc_edit(tree, edit_obj, 0, &idx, EDEND); } /* If defaulted, may */ /* need to clear hotspot*/ if (hot_obj != (next_obj & 0x7fff)) if (hot_obj != NIL) objc_toggle(tree, hot_obj); /* return exit object */ /* hi bit may be set */ /* if exit obj. was */ /* double-clicked */ *start_fld = edit_obj; return(next_obj); }