/* Copyright 1986 - 1999 by Abacus Research and * Development, Inc. All rights reserved. */ #if !defined (OMIT_RCSID_STRINGS) char ROMlib_rcsid_ctlArrows[] = "$Id: ctlArrows.c 63 2004-12-24 18:19:43Z ctm $"; #endif #include "rsys/common.h" #include "QuickDraw.h" #include "CQuickDraw.h" #include "WindowMgr.h" #include "ControlMgr.h" #include "MemoryMgr.h" #include "ToolboxUtil.h" #include "rsys/cquick.h" #include "rsys/ctl.h" #include "rsys/image.h" #include "rsys/host.h" #include "rsys/wind.h" #include "arrow_up_active.c" #include "arrow_up_inactive.c" #include "arrow_down_active.c" #include "arrow_down_inactive.c" #include "arrow_right_active.c" #include "arrow_right_inactive.c" #include "arrow_left_active.c" #include "arrow_left_inactive.c" #include "thumb_horiz.c" #include "thumb_vert.c" #define SB_VERT_P(h, w) ((h) > (w)) /* one if the control (scrollbar) currently being drawn is color. this is set by `validate_colors_for_ctl ()' called from `cdef16 ()', the single entry point to this function if drawing may ensue */ static int current_ctl_color_p; /* control colors */ enum { arrow_bk, first_arrow = arrow_bk, arrow_tl_shadow, arrow_br_shadow, /* also thumb_br_shadow */ arrow_outline, first_thumb = arrow_outline, arrow_inactive, /* arrow and thumb use the same frame color */ sb_frame, thumb_tl_edge, /* also light strip color */ thumb_tl_shadow, thumb_dk_strip, thumb_bk, page_fg, page_bk, n_ctl_colors, }; static RGBColor current_ctl_colors[n_ctl_colors]; /* indexed by [up_or_left_p][vert_p][active_p] */ pixel_image_t *sb_arrow_images[2][2][2]; /* indexed by [vert_p] */ pixel_image_t *sb_thumb_images[2]; #define ARROW_PART_P(part) ((part) == inUpButton \ || (part) == inDownButton) #define UP_OR_LEFT_P(part) ((part) == inUpButton) #define SB_IMAGE(part, vert_p, active_p) \ (ARROW_PART_P (part) \ ? sb_arrow_images[UP_OR_LEFT_P \ (part)][vert_p][active_p] \ : sb_thumb_images[vert_p] /* active_p unused */) void sb_ctl_init () { #define VERT 1 #define HORIZ 0 #define UP_OR_LEFT 1 #define DOWN_OR_RIGHT 0 /* wow, is it just me, or is this really... ugly. but hey, it works */ #define UP UP_OR_LEFT][VERT #define DOWN DOWN_OR_RIGHT][VERT #define LEFT UP_OR_LEFT][HORIZ #define RIGHT DOWN_OR_RIGHT][HORIZ #define CTL_ACTIVE 1 #define CTL_INACTIVE 0 sb_arrow_images[UP][CTL_ACTIVE] = arrow_up_active; sb_arrow_images[LEFT][CTL_ACTIVE] = arrow_left_active; sb_arrow_images[DOWN][CTL_ACTIVE] = arrow_down_active; sb_arrow_images[RIGHT][CTL_ACTIVE] = arrow_right_active; sb_arrow_images[UP][CTL_INACTIVE] = arrow_up_inactive; sb_arrow_images[LEFT][CTL_INACTIVE] = arrow_left_inactive; sb_arrow_images[DOWN][CTL_INACTIVE] = arrow_down_inactive; sb_arrow_images[RIGHT][CTL_INACTIVE] = arrow_right_inactive; #undef CTL_INACTIVE #undef CTL_ACTIVE #undef RIGHT #undef LEFT #undef DOWN #undef UP #undef DOWN_OR_RIGHT #undef UP_OR_LEFT sb_thumb_images[VERT] = thumb_vert; sb_thumb_images[HORIZ] = thumb_horiz; #undef HORIZ #undef VERT } static void validate_colors_for_control (ControlHandle ctl) { int hilited_p; RGBColor ctl_ctab_colors[15]; AuxCtlHandle t_aux_c; int i; /* FIXME: tmp hack */ current_ctl_color_p = (CGrafPort_p (thePort) != 0); hilited_p = (CTL_HILITE_X (ctl) != 255 && CTL_MIN (ctl) < CTL_MAX (ctl)); /* * NOTE: the use of default_ctl_colors to fill in gaps is incorrect. * To see this, try using a 'cctb' on a Mac that only fills in entries * 1, 2 and 3 (like LogDig does). */ for (i = 0; i <= 14; i ++) ctl_ctab_colors[i] = default_ctl_colors[i].rgb; t_aux_c = MR (*lookup_aux_ctl (ctl)); if (t_aux_c && HxZ (t_aux_c, acCTable)) { CTabHandle c_ctab; ColorSpec *c_ctab_table; int c_ctab_size; c_ctab = (CTabHandle) HxP (t_aux_c, acCTable); c_ctab_table = CTAB_TABLE (c_ctab); c_ctab_size = CTAB_SIZE (c_ctab); for (i = c_ctab_size; i >= 0; i --) { ColorSpec *c_ctab_entry; int index; c_ctab_entry = &c_ctab_table[i]; index = CW (c_ctab_entry->value); if (index >= 0 && index < NELEM (ctl_ctab_colors)) ctl_ctab_colors[index] = c_ctab_entry->rgb; } } #define FAIL goto failure #define DONE goto done #define DO_BLOCK_WITH_FAILURE(try_block, fail_block) \ { \ { try_block } \ goto done; \ failure: \ { fail_block } \ /* fall through */ \ done:; \ } DO_BLOCK_WITH_FAILURE ({ RGBColor temp1; RGBColor temp2; RGBColor temp3; RGBColor temp4; RGBColor temp5; RGBColor temp6; int vert_p; int up_or_left_p; int active_p; if (!current_ctl_color_p) FAIL; if (!AVERAGE_COLOR (&ctl_ctab_colors[cArrowsColorLight], &ctl_ctab_colors[cArrowsColorDark], 0xDDDD, ¤t_ctl_colors[arrow_bk])) FAIL; current_ctl_colors[arrow_outline] = ctl_ctab_colors[cTingeDark]; current_ctl_colors[sb_frame] = ctl_ctab_colors[cFrameColor]; /* page colors are the only two colors which are actually used outside of this function */ if (!AVERAGE_COLOR (&ctl_ctab_colors[cHiliteLight], &ctl_ctab_colors[cHiliteDark], 0x7777, ¤t_ctl_colors[page_bk])) FAIL; if (!AVERAGE_COLOR (&ctl_ctab_colors[cArrowsColorLight], &ctl_ctab_colors[cArrowsColorDark], 0x7777, &temp1)) FAIL; if (!AVERAGE_COLOR (&ctl_ctab_colors[cTingeLight], &ctl_ctab_colors[cTingeDark], 0xAAAA, &temp2)) FAIL; if (!AVERAGE_COLOR (&ctl_ctab_colors[cThumbLight], &ctl_ctab_colors[cThumbDark], 0x5555, &temp3)) FAIL; if (!AVERAGE_COLOR (&ctl_ctab_colors[cTingeLight], &ctl_ctab_colors[cThumbDark], 0x8888, &temp4)) FAIL; if (!AVERAGE_COLOR (&ctl_ctab_colors[cThumbLight], &ctl_ctab_colors[cThumbDark], 0xAAAA, &temp5)) FAIL; if (!AVERAGE_COLOR (&ctl_ctab_colors[cHiliteLight], &ctl_ctab_colors[cHiliteDark], 0xDDDD, &temp6)) FAIL; if (hilited_p) { current_ctl_colors[arrow_tl_shadow] = ctl_ctab_colors[cArrowsColorLight]; current_ctl_colors[arrow_br_shadow] = temp1; current_ctl_colors[arrow_inactive] = temp2; /* thumb colors are only used when hilited */ current_ctl_colors[thumb_tl_edge] = temp3; current_ctl_colors[thumb_tl_shadow] = ctl_ctab_colors[cTingeLight]; current_ctl_colors[thumb_dk_strip] = temp4; current_ctl_colors[thumb_bk] = temp5; current_ctl_colors[page_fg] = temp6; } else { current_ctl_colors[arrow_tl_shadow] = current_ctl_colors[arrow_bk]; current_ctl_colors[arrow_br_shadow] = current_ctl_colors[arrow_bk]; current_ctl_colors[arrow_inactive] = current_ctl_colors[arrow_bk]; current_ctl_colors[page_fg] = current_ctl_colors[page_bk] = current_ctl_colors[arrow_bk]; } for (vert_p = 0; vert_p < 2; vert_p ++) { for (up_or_left_p = 0; up_or_left_p < 2; up_or_left_p ++) for (active_p = 0; active_p < 2; active_p ++) image_update_ctab (sb_arrow_images[up_or_left_p][vert_p][active_p], ¤t_ctl_colors[first_arrow], 5); if (hilited_p) image_update_ctab (sb_thumb_images[vert_p], ¤t_ctl_colors[first_thumb], 6); } }, { current_ctl_color_p = FALSE; /* only the ones that matter */ current_ctl_colors[page_fg] = ROMlib_white_rgb_color; if (hilited_p) current_ctl_colors[page_bk] = ROMlib_black_rgb_color; else current_ctl_colors[page_bk] = ROMlib_white_rgb_color; }); } void draw_arrow (ControlHandle ctl, int part) { int height, width; int vert_p, active_p; Rect r; r = CTL_RECT (ctl); height = RECT_HEIGHT (&r); width = RECT_WIDTH (&r); vert_p = SB_VERT_P (height, width); if (vert_p) { if (part == inUpButton) r.bottom = CW (CW (r.top) + width); else r.top = CW (CW (r.bottom) - width); } else { if (part == inUpButton) r.right = CW (CW (r.left) + height); else r.left = CW (CW (r.right) - height); } active_p = (CTL_HILITE (ctl) == part); RGBForeColor (&ROMlib_black_rgb_color); RGBBackColor (&ROMlib_white_rgb_color); /* draw_image (SB_IMAGE (part, vert_p, active_p), &r); */ image_copy (SB_IMAGE (part, vert_p, active_p), current_ctl_color_p, &r, srcCopy); } void draw_page (ControlHandle ctl) { Rect r, *rp; int height, width; r = CTL_RECT (ctl); height = RECT_HEIGHT (&r); width = RECT_WIDTH (&r); if (SB_VERT_P (height, width)) { r.top = CW (CW (r.top) + width); r.bottom = CW (CW (r.bottom) - width); r.left = CW (CW (r.left) + 1); r.right = CW (CW (r.right) - 1); } else { r.left = CW (CW (r.left) + height); r.right = CW (CW (r.right) - height); r.top = CW (CW (r.top) + 1); r.bottom = CW (CW (r.bottom) - 1); } /* page_{fg, bk} colors are dependent on whether or not the sb is currently hilited */ RGBForeColor (¤t_ctl_colors[page_bk]); RGBBackColor (¤t_ctl_colors[page_fg]); FillRect (&r, ltGray); rp = &HxX (CTL_DATA (ctl), rgnBBox); rp->top = rp->bottom = 0; } void thumb_rect (ControlHandle ctl, Rect *thumb_rect_out) { int diff, height, width, val, max, min; Rect r; max = CTL_MAX (ctl); min = CTL_MIN (ctl); diff = max - min; if (diff > 0 && CTL_HILITE (ctl) != 255) { r = CTL_RECT (ctl); height = RECT_HEIGHT (&r); width = RECT_WIDTH (&r); val = CTL_VALUE (ctl); if (SB_VERT_P (height, width)) { thumb_rect_out->top = CW ((short) (CW (r.top) + width + ((val - min) * ((LONGINT) height - 3 * width) / diff))); thumb_rect_out->bottom = CW (CW (thumb_rect_out->top) + width); thumb_rect_out->left = CW (CW (r.left) + 1); thumb_rect_out->right = CW (CW (r.right) - 1); } else { thumb_rect_out->left = CW ((short) (CW (r.left) + height + ((val - min) * ((LONGINT) width - 3 * height) / diff))); thumb_rect_out->right = CW (CW (thumb_rect_out->left) + height); thumb_rect_out->top = CW (CW (r.top) + 1); thumb_rect_out->bottom = CW (CW (r.bottom) - 1); } } else { SetRect (thumb_rect_out, 0, 0, 0, 0); } } PRIVATE void GlobalToLocalRect (Rect *rp) { GlobalToLocal ((Point *) &rp->top); GlobalToLocal ((Point *) &rp->bottom); } PRIVATE void LocalToGlobalRect (Rect *rp) { LocalToGlobal ((Point *) &rp->top); LocalToGlobal ((Point *) &rp->bottom); } PRIVATE void GlobalToLocalRgn (RgnHandle rgn) { OffsetRgn (rgn, CW (PORT_BOUNDS (thePort).left), CW (PORT_BOUNDS (thePort).top)); } typedef struct { ControlHandle ctl; LONGINT param; } device_loop_param; void draw_thumb (ControlHandle ctl) { Rect old_thumb, new_thumb; Rect dst_rect, *ctl_rect; old_thumb = HxX (CTL_DATA (ctl), rgnBBox); GlobalToLocalRect (&old_thumb); thumb_rect (ctl, &new_thumb); if (old_thumb.bottom != new_thumb.bottom || old_thumb.right != new_thumb.right) { if (!EmptyRect (&new_thumb)) { int vert_p; ctl_rect = &CTL_RECT (ctl); vert_p = SB_VERT_P (RECT_HEIGHT (ctl_rect), RECT_WIDTH (ctl_rect)); if (vert_p) { dst_rect.top = new_thumb.top; dst_rect.bottom = new_thumb.bottom; dst_rect.left = CW (CW (new_thumb.left) - 1); dst_rect.right = CW (CW (new_thumb.right) + 1); } else { dst_rect.left = new_thumb.left; dst_rect.right = new_thumb.right; dst_rect.top = CW (CW (new_thumb.top) - 1); dst_rect.bottom = CW (CW (new_thumb.bottom) + 1); } /* if the old_thumb rect is empty, ie., the thumb was not previously drawn (the ctl was not active), then redraw the arrows as well */ if (current_ctl_color_p && EmptyRect (&old_thumb)) { draw_arrow (ctl, inUpButton); draw_arrow (ctl, inDownButton); } /* previously only the old_thumb rect was redrawn if it was not empty */ draw_page (ctl); RGBForeColor (&ROMlib_black_rgb_color); RGBBackColor (&ROMlib_white_rgb_color); image_copy (SB_IMAGE (inThumb, vert_p, /* dummy */ -1), current_ctl_color_p, &dst_rect, srcCopy); } else draw_page (ctl); LocalToGlobalRect (&new_thumb); RectRgn (CTL_DATA (ctl), &new_thumb); } } P4 (PUBLIC pascal, void, new_draw_scroll, INTEGER, depth, INTEGER, flags, GDHandle, target, LONGINT, l) { device_loop_param *dlp; ControlHandle ctl; INTEGER part; Rect r; dlp = (device_loop_param *)l; ctl = dlp->ctl; part = dlp->param; if (CTL_VIS_X (ctl) == CWC (0)) return; switch (part) { case ENTIRECONTROL: r = CTL_RECT (ctl); RGBForeColor (¤t_ctl_colors[sb_frame]); FrameRect (&r); draw_arrow (ctl, inUpButton); draw_page (ctl); draw_arrow (ctl, inDownButton); if (CTL_HILITE_X (ctl) != 255) draw_thumb(ctl); break; case inUpButton: case inDownButton: draw_arrow (ctl, part); break; case inThumb: /* that is all indicators ... values may have changed */ draw_thumb (ctl); break; case inPageUp: case inPageDown: break; default: /* someone is asking us to do weird things */ break; } } LONGINT where (ControlHandle ctl, Point p) { int height, width; Rect r; Rect thumbr; thumbr = HxX (CTL_DATA (ctl), rgnBBox); GlobalToLocalRect (&thumbr); if (PtInRect (p, &thumbr)) return inThumb; else { r = CTL_RECT (ctl); height = RECT_HEIGHT (&r); width = RECT_WIDTH (&r); if (SB_VERT_P (height, width)) { if (p.v <= CW (r.top) + width) return inUpButton; else if (p.v >= CW (r.bottom) - width) return inDownButton; else if (p.v < CW (thumbr.top)) return inPageUp; else return inPageDown; } else { if (p.h <= CW (r.left) + height) return inUpButton; else if (p.h >= CW (r.right) - height) return inDownButton; else if (p.h < CW (thumbr.left)) return inPageUp; else return inPageDown; } } } P4 (PUBLIC pascal, void, new_pos_ctl, INTEGER, depth, INTEGER, flags, GDHandle, target, LONGINT, l) { device_loop_param *dlp; ControlHandle ctl; LONGINT p; int height, width, thumb_top, thumb_left, a, min, max; INTEGER top, left, bottom, right; Rect thumbr; Rect r; dlp = (device_loop_param *)l; ctl = dlp->ctl; p = dlp->param; r = CTL_RECT (ctl); top = CW (r.top); left = CW (r.left); bottom = CW (r.bottom); right = CW (r.right); height = bottom - top; width = right - left; min = CTL_MIN (ctl); max = CTL_MAX (ctl); if (max < min) max = min; thumbr = HxX (CTL_DATA (ctl), rgnBBox); GlobalToLocalRect (&thumbr); if (SB_VERT_P (height, width)) { a = top + width; thumb_top = CW (thumbr.top) + HiWord (p) - a; CTL_VALUE_X (ctl) = CW (min + (thumb_top * ((LONGINT) max - min + 1) / (bottom - a - (2 * width) + 1))); if (CTL_VIS_X (ctl) == 255) draw_thumb (ctl); } else { a = left + height; thumb_left = CW (thumbr.left) + LoWord(p) - a; CTL_VALUE_X (ctl) = CW (min + (thumb_left * ((LONGINT) max - min + 1) / (right - a - (2 * height) + 1))); if (CTL_VIS_X (ctl) == 255) draw_thumb (ctl); } } typedef struct { GrafPtr port; CGrafPort cp; } save_t; PRIVATE Handle CopyMacHandle (Handle h) { Handle retval; Size s; h = MR (h); s = GetHandleSize (h); retval = NewHandle (s); if (retval) memcpy (STARH (retval), STARH (h), s); retval = RM (retval); return retval; } PRIVATE boolean_t save_and_switch_to_color_port_if_needed (save_t *sp) { boolean_t retval; if (CGrafPort_p (thePort)) retval = FALSE; else { CGrafPtr wp; sp->port = thePort; sp->cp = * (CGrafPtr) thePort; wp = (CGrafPtr) MR (wmgr_port); sp->cp.portPixMap = (PixMapHandle) CopyMacHandle ((Handle) wp->portPixMap); PIXMAP_BOUNDS (PPR (sp->cp.portPixMap)) = thePort->portBits.bounds; sp->cp.portVersion = wp->portVersion; sp->cp.grafVars = wp->grafVars; sp->cp.chExtra = wp->chExtra; sp->cp.pnLocHFrac = wp->pnLocHFrac; sp->cp.bkPixPat = wp->bkPixPat; sp->cp.rgbFgColor = wp->rgbFgColor; sp->cp.rgbBkColor = wp->rgbBkColor; sp->cp.pnPixPat = wp->pnPixPat; sp->cp.fillPixPat = wp->fillPixPat; sp->cp.grafProcs = wp->grafProcs; SetPort ((GrafPtr) &sp->cp); retval = TRUE; } return retval; } PRIVATE void restore (const save_t *sp) { SetPort (sp->port); DisposHandle ((Handle) PPR (sp->cp.portPixMap)); } P4 (PUBLIC pascal, LONGINT, cdef16, /* IMI-328 */ INTEGER, var, ControlHandle, c, INTEGER, mess, LONGINT, param) { Point p; PenState ps; Rect r, *rp, tempr; struct lsastr *pl; int height, width; Handle temph; draw_state_t draw_state; int draw_p; Rect thumbr; save_t save; boolean_t need_to_restore_p; switch (mess) { case calcCRgns: case calcCntlRgn: case calcThumbRgn: case thumbCntl: param = (LONGINT) SYN68K_TO_US (param); break; default: break; } /* if drawing can occur, validate the color state */ draw_p = (mess == drawCntl /* || mess == thumbCntl REALLY? */ || mess == posCntl); if (draw_p) { need_to_restore_p = save_and_switch_to_color_port_if_needed (&save); draw_state_save (&draw_state); } #if !defined (LETGCCWAIL) else need_to_restore_p = FALSE; #endif switch (mess) { case drawCntl: if (Hx (c, contrlVis) && SectRect (&HxX (PORT_VIS_REGION (thePort), rgnBBox), &HxX (c, contrlRect), &r)) { validate_colors_for_control (c); TRAPBEGIN (); GetPenState (&ps); PenNormal (); { RgnHandle rh; device_loop_param dlp; rh = NewRgn (); RectRgn (rh, &HxX (c, contrlRect)); dlp.ctl = c; dlp.param = param; DeviceLoop (rh, P_new_draw_scroll, (LONGINT) &dlp, 0); DisposeRgn (rh); } SetPenState (&ps); TRAPEND (); break; } case testCntl: p.v = HiWord (param); p.h = LoWord (param); if (U(Hx(c, contrlHilite)) != 255 && Hx(c, contrlMin) < Hx(c, contrlMax) && PtInRect(p, &(HxX(c, contrlRect)))) return where (c, p); else return 0; case calcCRgns: if (param & 0x80000000) /* ICK! */ { param &= 0x7FFFFFFF; /* IMI-331 */ case calcThumbRgn: CopyRgn((RgnHandle)(long)HxP(c, contrlData), (RgnHandle)(long)param); GlobalToLocalRgn ((RgnHandle)(long) param); break; } else { case calcCntlRgn: r = HxX(c, contrlRect); RectRgn((RgnHandle)(long)param, &r); break; } break; case initCntl: temph = RM((Handle) NewRgn()); HxX(c, contrlData) = temph; thumb_rect(c, &tempr); LocalToGlobalRect (&tempr); #if 1 /* MacBreadboard's behaviour suggests the following line is needed */ tempr.top = tempr.bottom = 0; #endif RectRgn((RgnHandle) HxP(c, contrlData), &tempr); break; case dispCntl: DisposHandle((Handle) HxP(c, contrlData)); break; case posCntl: validate_colors_for_control (c); { RgnHandle rh; device_loop_param dlp; rh = NewRgn (); RectRgn (rh, &HxX (c, contrlRect)); dlp.ctl = c; dlp.param = param; DeviceLoop (rh, P_new_pos_ctl, (LONGINT) &dlp, 0); DisposeRgn (rh); } break; case thumbCntl: pl = (struct lsastr *) param; p.v = CW (pl->limitRect.top); p.h = CW (pl->limitRect.left); pl->slopRect = pl->limitRect = CTL_RECT (c); thumbr = HxX (CTL_DATA (c), rgnBBox); GlobalToLocalRect (&thumbr); rp = &thumbr; height = CW (pl->slopRect.bottom) - CW (pl->slopRect.top); width = CW (pl->slopRect.right) - CW (pl->slopRect.left); if (SB_VERT_P (height, width)) { pl->axis = CWC (vAxisOnly); pl->limitRect.top = CW (CW (pl->limitRect.top) + (width - (CW (rp->top) - p.v))); pl->limitRect.bottom = CW (CW (pl->limitRect.bottom) - (width - (p.v - CW (rp->bottom)) - 1)); } else { pl->axis = CWC (hAxisOnly); pl->limitRect.left = CW (CW (pl->limitRect.left) + height - (CW (rp->left) - p.h)); pl->limitRect.right = CW (CW (pl->limitRect.right) - height - (p.h - CW (rp->right)) - 1); } InsetRect (&pl->slopRect, -20, -20); break; case dragCntl: /* NOT NEEDED */ case autoTrack: /* NOT NEEDED */ break; default: warning_unexpected ("unknown message `%d'", mess); break; } if (draw_p) { draw_state_restore (&draw_state); if (need_to_restore_p) restore (&save); } return 0; }