548 lines
13 KiB
C
548 lines
13 KiB
C
/* Copyright 1995 by Abacus Research and
|
|
* Development, Inc. All rights reserved.
|
|
*/
|
|
|
|
#if !defined (OMIT_RCSID_STRINGS)
|
|
char ROMlib_rcsid_ctlPopup[] =
|
|
"$Id: ctlPopup.c 86 2005-05-25 00:47:12Z ctm $";
|
|
#endif
|
|
|
|
|
|
#include "rsys/common.h"
|
|
|
|
#include "QuickDraw.h"
|
|
#include "CQuickDraw.h"
|
|
#include "ControlMgr.h"
|
|
#include "MenuMgr.h"
|
|
#include "ToolboxUtil.h"
|
|
#include "Iconutil.h"
|
|
#include "FontMgr.h"
|
|
#include "OSUtil.h"
|
|
|
|
#include "rsys/ctl.h"
|
|
#include "rsys/menu.h"
|
|
|
|
#if 0 /* It's not clear why Cotton had all these global variables,
|
|
but it is clear that in at least one case he was using one
|
|
before setting it. Use RCS to see what used to be done. */
|
|
|
|
static draw_state_t draw_state;
|
|
static ControlHandle ctl;
|
|
static WindowPtr ctl_owner;
|
|
static int16 window_font;
|
|
static int16 window_size;
|
|
static boolean_t window_font_p;
|
|
|
|
#endif
|
|
|
|
static void
|
|
init (ControlHandle ctl)
|
|
{
|
|
popup_data_handle data;
|
|
MenuHandle mh;
|
|
int16 mid;
|
|
int flags;
|
|
|
|
data = (popup_data_handle) NewHandle (sizeof (popup_data_t));
|
|
/* ### check for NULL return */
|
|
|
|
mid = CTL_MIN (ctl);
|
|
mh = GetMenu (mid);
|
|
InsertMenu (mh, -1);
|
|
{
|
|
HIDDEN_Handle hh;
|
|
|
|
hh.p = (Handle) mh;
|
|
HandToHand (&hh);
|
|
flags = CTL_VALUE (ctl);
|
|
|
|
POPUP_MENU_ID_X (data) = CW (mid);
|
|
POPUP_MENU_X (data) = RM ((MenuHandle) hh.p);
|
|
}
|
|
/* private fields */
|
|
POPUP_TITLE_WIDTH (data) = CTL_MAX (ctl);
|
|
POPUP_FLAGS (data) = flags;
|
|
|
|
CTL_DATA_X (ctl) = (Handle) RM (data);
|
|
|
|
CTL_ACTION_X (ctl) = (ProcPtr) CLC (-1);
|
|
CTL_VALUE_X (ctl) = CWC (1);
|
|
CTL_MIN_X (ctl) = CWC (1);
|
|
CTL_MAX_X (ctl) = CW (CountMItems (mh));
|
|
CheckItem (mh, 1, TRUE);
|
|
}
|
|
|
|
static void
|
|
calc_rgns (ControlHandle ctl, RgnHandle rgn)
|
|
{
|
|
SignedByte state;
|
|
|
|
state = HGetState ((Handle) rgn);
|
|
if (state & LOCKBIT)
|
|
HSetState ((Handle) rgn, state & ~LOCKBIT);
|
|
|
|
RectRgn (rgn, &CTL_RECT (ctl));
|
|
|
|
HSetState ((Handle) rgn, state);
|
|
}
|
|
|
|
static void
|
|
restore_bk_color (ControlHandle ctl, draw_state_t draw_state)
|
|
{
|
|
WindowPtr ctl_owner;
|
|
|
|
ctl_owner = CTL_OWNER (ctl);
|
|
if (CGrafPort_p (ctl_owner))
|
|
CPORT_RGB_BK_COLOR (ctl_owner) = draw_state.bk_color;
|
|
PORT_BK_COLOR_X (ctl_owner) = draw_state.bk;
|
|
}
|
|
|
|
static void
|
|
set_text_face (boolean_t item_p, int flags, mextp item_info)
|
|
{
|
|
if (item_p)
|
|
TextFace (item_info->mstyle);
|
|
else
|
|
{
|
|
if (flags & popupTitleNoStyle)
|
|
TextFace (0);
|
|
else
|
|
TextFace ((flags >> 8) & 0x7F);
|
|
}
|
|
TextMode (srcOr);
|
|
}
|
|
|
|
/* #### todo:
|
|
title should be hilited in menu item text color
|
|
title should be hilited height of menu item (see #### below)
|
|
menu item text should be drawn in menu item text color
|
|
menu item text should be disabled if appropraite */
|
|
|
|
static void
|
|
draw (ControlHandle ctl, draw_state_t draw_state,
|
|
int16 window_font, int16 window_size, boolean_t window_font_p,
|
|
boolean_t invert_title_p, boolean_t title_only_p,
|
|
int *return_popup_top, int *return_popup_left)
|
|
{
|
|
FontInfo font_info;
|
|
int ascent;
|
|
int height;
|
|
int title_width, actual_title_width;
|
|
StringPtr title;
|
|
|
|
int item_total_height;
|
|
int item_pad;
|
|
|
|
icon_info_t icon_info;
|
|
boolean_t icon_p;
|
|
|
|
mextp item_info;
|
|
StringPtr item_title;
|
|
|
|
Rect t_rect;
|
|
Rect *ctl_rect;
|
|
int top, left, bottom, right;
|
|
int draw_top, draw_bottom;
|
|
int item_top, item_left;
|
|
int center, baseline;
|
|
|
|
popup_data_handle data;
|
|
int flags;
|
|
|
|
/* true if we should draw the pulldown arrow; pulldown arrow is not
|
|
drawn if there are no menu items */
|
|
boolean_t draw_pulldown_arrow_p = TRUE;
|
|
|
|
data = (popup_data_handle) CTL_DATA (ctl);
|
|
flags = POPUP_FLAGS (data);
|
|
|
|
item_info = ROMlib_mitemtop (POPUP_MENU (data), CTL_VALUE (ctl), &item_title);
|
|
|
|
if (item_info == NULL)
|
|
{
|
|
item_title = (StringPtr) "";
|
|
|
|
item_info = alloca (sizeof *item_info);
|
|
memset (item_info, '\000', sizeof *item_info);
|
|
|
|
draw_pulldown_arrow_p = FALSE;
|
|
}
|
|
|
|
ctl_rect = &CTL_RECT (ctl);
|
|
top = CW (ctl_rect->top);
|
|
left = CW (ctl_rect->left);
|
|
bottom = CW (ctl_rect->bottom);
|
|
right = CW (ctl_rect->right);
|
|
|
|
title = CTL_TITLE (ctl);
|
|
|
|
if (window_font_p)
|
|
{
|
|
TextFont (window_font);
|
|
TextFont (window_size);
|
|
}
|
|
else
|
|
{
|
|
TextFont (0);
|
|
TextSize (0);
|
|
}
|
|
|
|
GetFontInfo (&font_info);
|
|
ascent = CW (font_info.ascent);
|
|
height = ( ascent
|
|
+ CW (font_info.descent)
|
|
+ CW (font_info.leading));
|
|
title_width = POPUP_TITLE_WIDTH (data);
|
|
|
|
icon_p = get_icon_info (item_info, &icon_info, TRUE);
|
|
|
|
item_total_height = ((icon_p ? MAX (icon_info.height, height)
|
|
: height)
|
|
+ 4);
|
|
|
|
item_pad = CharWidth (' ');
|
|
|
|
center = (top + bottom) / 2;
|
|
if (item_total_height > height)
|
|
{
|
|
draw_top = center - item_total_height / 2;
|
|
draw_bottom = draw_top + item_total_height;
|
|
if (draw_top < top)
|
|
{
|
|
center += (top - draw_top);
|
|
draw_bottom += (top - draw_top);
|
|
draw_top = top;
|
|
}
|
|
|
|
baseline = center - height / 2 + ascent;
|
|
}
|
|
else
|
|
{
|
|
draw_top = center - height / 2;
|
|
draw_bottom = draw_top + height;
|
|
if (draw_top < top)
|
|
{
|
|
center += (top - draw_top);
|
|
draw_bottom += (top - draw_top);
|
|
draw_top = top;
|
|
}
|
|
baseline = center - height / 2 + ascent;
|
|
}
|
|
|
|
{
|
|
Rect erase_rect;
|
|
|
|
/* clear out the entire control */
|
|
restore_bk_color (ctl, draw_state);
|
|
erase_rect.top = ctl_rect->top;
|
|
erase_rect.left = ctl_rect->left;
|
|
erase_rect.bottom = ctl_rect->bottom;
|
|
if (title_only_p)
|
|
erase_rect.right = CW (left + title_width);
|
|
else
|
|
erase_rect.right = ctl_rect->right;
|
|
EraseRect (&erase_rect);
|
|
}
|
|
|
|
/* draw the title */
|
|
set_text_face (FALSE, flags, item_info);
|
|
|
|
RGBForeColor (invert_title_p ? &ROMlib_white_rgb_color
|
|
: &ROMlib_black_rgb_color);
|
|
if (invert_title_p)
|
|
RGBBackColor (&ROMlib_black_rgb_color);
|
|
|
|
/* ### if icon_p && icon_info.height > height
|
|
then should probably hilite from
|
|
`center - icon_info.height / 2' to
|
|
`center + icon_info.height / 2' */
|
|
t_rect.top = CW (baseline - ascent);
|
|
t_rect.left = CW (left);
|
|
t_rect.bottom = CW (baseline - ascent + height);
|
|
t_rect.right = CW (left + title_width);
|
|
|
|
PenMode (patCopy);
|
|
EraseRect (&t_rect);
|
|
|
|
TextMode (srcCopy);
|
|
actual_title_width = StringWidth (title);
|
|
switch (flags & 255)
|
|
{
|
|
case popupTitleLeftJust:
|
|
MoveTo (left, baseline);
|
|
break;
|
|
case popupTitleRightJust:
|
|
MoveTo (left + title_width - actual_title_width - item_pad, baseline);
|
|
break;
|
|
default:
|
|
case popupTitleCenterJust:
|
|
MoveTo (left + (title_width - actual_title_width) / 2, baseline);
|
|
break;
|
|
}
|
|
DrawText ((Ptr) title, 1, *title);
|
|
|
|
|
|
item_top = center - item_total_height / 2;
|
|
item_left = left + title_width;
|
|
|
|
if (return_popup_top && return_popup_left)
|
|
{
|
|
if (icon_p)
|
|
*return_popup_top = center - icon_info.height / 2;
|
|
else
|
|
*return_popup_top = baseline - ascent;
|
|
*return_popup_left = item_left;
|
|
}
|
|
|
|
if (title_only_p)
|
|
return;
|
|
|
|
/* draw box */
|
|
RGBForeColor (&ROMlib_black_rgb_color);
|
|
RGBBackColor (&ROMlib_white_rgb_color);
|
|
|
|
t_rect.top = CW (draw_top);
|
|
t_rect.left = CW (item_left - 1);
|
|
t_rect.bottom = CW (draw_bottom - 1);
|
|
t_rect.right = CW (right - 1);
|
|
|
|
EraseRect (&t_rect);
|
|
|
|
PenSize (1, 1);
|
|
|
|
FrameRect (&t_rect);
|
|
|
|
/* line along bottom */
|
|
MoveTo (item_left + 2,
|
|
draw_bottom - 1);
|
|
LineTo (right - 2,
|
|
draw_bottom - 1);
|
|
|
|
MoveTo (right - 1,
|
|
draw_top + 3);
|
|
LineTo (right - 1,
|
|
draw_bottom - 1);
|
|
|
|
/* draw the icon */
|
|
item_left += item_pad;
|
|
|
|
if (icon_p)
|
|
{
|
|
t_rect.top = CW (center - icon_info.height / 2);
|
|
t_rect.left = CW (item_left);
|
|
t_rect.bottom = CW (CW (t_rect.top) + (icon_info.height - ICON_PAD));
|
|
t_rect.right = CW (CW (t_rect.left) + (icon_info.width - ICON_PAD));
|
|
|
|
if (icon_info.color_icon_p)
|
|
PlotCIcon (&t_rect, (CIconHandle) icon_info.icon);
|
|
else
|
|
PlotIcon (&t_rect, icon_info.icon);
|
|
}
|
|
|
|
/* draw the item name, clipping to max width, displaying `...' if
|
|
appropriate */
|
|
{
|
|
int title_left, title_right;
|
|
uint8 title_length;
|
|
|
|
set_text_face (TRUE, flags, item_info);
|
|
|
|
title_left = item_left + icon_info.width;
|
|
/* arrow is padded by the arrow width `11' on either side */
|
|
title_right = right - 33;
|
|
|
|
if (title_right - title_left < StringWidth (item_title))
|
|
{
|
|
int i, width;
|
|
char ellipsis[] = { 4, ' ', '.', '.', '.' };
|
|
|
|
title_right -= StringWidth ((StringPtr) ellipsis);
|
|
|
|
title_length = *(uint8 *) item_title;
|
|
for (i = 1, width = 0; i <= title_length; i ++)
|
|
{
|
|
width += CharWidth (item_title[i]);
|
|
|
|
if (title_right - title_left < width)
|
|
{
|
|
i --;
|
|
break;
|
|
}
|
|
}
|
|
|
|
MoveTo (title_right, baseline);
|
|
DrawString ((StringPtr) ellipsis);
|
|
|
|
*(uint8 *) item_title = i;
|
|
MoveTo (item_left + icon_info.width,
|
|
baseline);
|
|
DrawString (item_title);
|
|
*(uint8 *) item_title = title_length;
|
|
}
|
|
else
|
|
{
|
|
MoveTo (item_left + icon_info.width,
|
|
baseline);
|
|
DrawString (item_title);
|
|
}
|
|
}
|
|
|
|
/* draw pulldown arrow */
|
|
if (draw_pulldown_arrow_p)
|
|
{
|
|
BitMap arrow_bitmap;
|
|
Rect dst_rect;
|
|
char down_arrow_bits[] =
|
|
{
|
|
image_bits (11111111), image_bits (11100000),
|
|
image_bits (01111111), image_bits (11000000),
|
|
image_bits (00111111), image_bits (10000000),
|
|
image_bits (00011111), image_bits (00000000),
|
|
image_bits (00001110), image_bits (00000000),
|
|
image_bits (00000100), image_bits (00000000),
|
|
};
|
|
|
|
arrow_bitmap.baseAddr = RM ((Ptr) down_arrow_bits);
|
|
arrow_bitmap.rowBytes = CWC (2);
|
|
SetRect (&arrow_bitmap.bounds, 0, 0, /* right, bottom */ 11, 6);
|
|
|
|
dst_rect.top = CW (center - 3);
|
|
dst_rect.left = CW (right - 22);
|
|
dst_rect.bottom = CW (center - 3 + /* arrows are `6' tall */ 6);
|
|
dst_rect.right = CW (right - 22
|
|
+ /* arrows are `11' wide */ 11);
|
|
CopyBits (&arrow_bitmap, PORT_BITS_FOR_COPY (thePort),
|
|
&arrow_bitmap.bounds, &dst_rect, srcCopy, NULL);
|
|
}
|
|
}
|
|
|
|
P4 (PUBLIC pascal, int32, cdef1008,
|
|
int16, var, ControlHandle, ctl, int16, message, int32, param)
|
|
{
|
|
boolean_t draw_p;
|
|
int32 retval = /* dummy */ 0;
|
|
draw_state_t draw_state;
|
|
int16 window_font;
|
|
int16 window_size;
|
|
boolean_t window_font_p;
|
|
|
|
|
|
if (message == initCntl)
|
|
{
|
|
init (ctl);
|
|
return /* dummy */ 0;
|
|
}
|
|
|
|
draw_p = (message == drawCntl || message == autoTrack);
|
|
/* technically we only need to do the following block if draw_p is true,
|
|
but gcc will then warn us that window_font, window_size and window_font_p
|
|
may be used uninitialized */
|
|
{
|
|
WindowPtr ctl_owner;
|
|
|
|
ctl_owner = CTL_OWNER (ctl);
|
|
window_font_p = (var & popupUseWFont ? TRUE : FALSE);
|
|
window_font = PORT_TX_FONT (ctl_owner);
|
|
window_size = PORT_TX_SIZE (ctl_owner);
|
|
}
|
|
|
|
if (draw_p)
|
|
draw_state_save (&draw_state);
|
|
|
|
switch (message)
|
|
{
|
|
case drawCntl:
|
|
draw (ctl, draw_state, window_font, window_size, window_font_p,
|
|
FALSE, FALSE, NULL, NULL);
|
|
break;
|
|
|
|
case testCntl:
|
|
{
|
|
Point pt;
|
|
|
|
pt.v = HiWord (param);
|
|
pt.h = LoWord (param);
|
|
|
|
if (CTL_HILITE (ctl) != 255
|
|
&& PtInRect (pt, &CTL_RECT (ctl)))
|
|
retval = inButton;
|
|
else
|
|
retval = 0;
|
|
break;
|
|
}
|
|
case calcCRgns:
|
|
case calcCntlRgn:
|
|
calc_rgns (ctl, (RgnHandle) SYN68K_TO_US (param));
|
|
break;
|
|
|
|
case dispCntl:
|
|
{
|
|
popup_data_handle data;
|
|
|
|
data = (popup_data_handle) CTL_DATA (ctl);
|
|
|
|
DeleteMenu (POPUP_MENU_ID (data));
|
|
DisposHandle ((Handle) POPUP_MENU (data));
|
|
DisposHandle ((Handle) data);
|
|
break;
|
|
}
|
|
|
|
case autoTrack:
|
|
{
|
|
MenuHandle mh;
|
|
int top, left;
|
|
int16 orig_value, value;
|
|
Rect *port_bounds;
|
|
popup_data_handle data;
|
|
int i, count;
|
|
|
|
data = (popup_data_handle) CTL_DATA (ctl);
|
|
mh = POPUP_MENU (data);
|
|
count = CountMItems (mh);
|
|
|
|
if (! count)
|
|
break;
|
|
|
|
/* make sure `orig_value' is checked */
|
|
orig_value = CTL_VALUE (ctl);
|
|
|
|
for (i = 0; i <= count; i ++)
|
|
CheckItem (mh, i, FALSE);
|
|
CheckItem (mh, orig_value, TRUE);
|
|
|
|
draw (ctl, draw_state, window_font, window_size, window_font_p,
|
|
TRUE, TRUE, &top, &left);
|
|
|
|
port_bounds = &PORT_BOUNDS (CTL_OWNER (ctl));
|
|
top -= CW (port_bounds->top);
|
|
left -= CW (port_bounds->left);
|
|
CalcMenuSize (mh);
|
|
value = PopUpMenuSelect (mh, top, left, orig_value);
|
|
|
|
if (value)
|
|
CTL_VALUE_X (ctl) = CW (value);
|
|
draw (ctl, draw_state, window_font, window_size, window_font_p,
|
|
FALSE, FALSE, NULL, NULL);
|
|
|
|
break;
|
|
}
|
|
|
|
case posCntl:
|
|
case dragCntl:
|
|
case thumbCntl:
|
|
/* fall through */
|
|
|
|
/* case calcCntlRgn: */
|
|
/* case calcThumbRgn: */
|
|
|
|
default:
|
|
warning_unexpected ("surprising message %d", message);
|
|
break;
|
|
}
|
|
|
|
if (draw_p)
|
|
draw_state_restore (&draw_state);
|
|
|
|
return retval;
|
|
}
|