#include #include #include #include #include #include "walkmenu_impl.h" #include "image_impl.h" #undef min #undef max #undef fabs /* * Copyright 1987 by Mark Weiser. * Permission to reproduce and use in any manner whatsoever on Suns is granted * so long as this copyright and other identifying marks of authorship * in the code and the game remain intact and visible. Use of this code * in other products is reserved to me--I'm working on Mac and IBM versions. */ /* * Preliminary implementation of pie menus (Patent Applied for.) */ #ifndef M_PI #define M_PI 3.14159265358979323846 #endif static short pie_cursor_array[] = { #include "pie_generic_cursor.h" }; mpr_static(pie_generic_cursor, 16, 16, 1, pie_cursor_array); double fabs(); int dead_zone = 5; /* this is not used consistently. */ static Notify_value main_event_proc(); double angle, fabs(), fmin(), fmax(); struct menu_item *menu_track_helper(); static int done; static caddr_t done_val; static Frame mainframe; static Canvas maincanvas; static int (*return_proc)(); struct menu_item * pie_menu(m, event, x, y, w, f, s) struct menu *m; Event *event; Window w; int (*f)(); char *s; { struct menu_item *value; construct_pie_frame(w, x, y, m, s); window_set(mainframe, WIN_SHOW, TRUE, 0); return_proc = f; } construct_pie_frame(w, x, y, m, s) Window w; struct menu *m; char *s; { int width, height; int max_len; Pixwin *pw; if (m->default_image.font == NULL) m->default_image.font = pf_open("/usr/lib/fonts/fixedwidthfonts/screen.b.12"); if (m->default_image.font == NULL) m->default_image.font = pf_default(); max_len = pie_process_args(m); m->default_image.width = m->default_image.height = 3.5*(float)max_len+dead_zone + 1; mainframe = window_create(w, FRAME, FRAME_LABEL, s, FRAME_SHOW_LABEL, TRUE, FRAME_NO_CONFIRM, TRUE, 0); maincanvas = window_create(mainframe, CANVAS, WIN_WIDTH, m->default_image.width, WIN_HEIGHT, m->default_image.height, WIN_CLIENT_DATA, m, #ifndef SUNOS3_0 WIN_GRAB_ALL_INPUT, TRUE, #endif WIN_CONSUME_PICK_EVENT, WIN_IN_TRANSIT_EVENTS, 0, 0); window_set(maincanvas, WIN_EVENT_PROC, main_event_proc, 0); window_fit(mainframe); (Pixwin *)m->default_image.pr = pw = canvas_pixwin(maincanvas); (Canvas)m->dynamic_info = maincanvas; window_set(mainframe, WIN_X, x - m->default_image.width/2, WIN_Y, y - m->default_image.height/2, 0); init_pies(m, pw); new_cursor(0.0, -1); } #define DESELECT_ON_EXIT static Notify_value main_event_proc(win, event, arg) Window win; Event *event; caddr_t arg; { struct menu *m = (struct menu *)window_get(win, WIN_CLIENT_DATA); Pixwin *pw = (Pixwin *)m->default_image.pr; int id = event_id(event); int x = event_x(event); int y = event_y(event); int x_inc, y_inc; double theta; double inc = 2.0*M_PI/m->nitems; int i; int pie_x = m->default_image.width/2; int pie_y = m->default_image.height/2; static outside_window = 0; if (id == WIN_RESIZE) { window_set(win, WIN_MOUSE_XY, pie_x, pie_y, 0); } if ((!outside_window && (id == LOC_MOVE || event_is_button(event))) || id == LOC_WINENTER || id == LOC_RGNENTER) { outside_window = 0; x_inc = pie_x - x; y_inc = pie_y - y; if (abs(x_inc) > dead_zone || abs(y_inc) > dead_zone || (int)sqrt((double)(x_inc*x_inc + y_inc*y_inc)) > dead_zone) { /* Found an item */ theta = M_PI + atan2((double)x_inc, (double)y_inc); i = ((theta+inc)/inc) - 1; if (i >= m->nitems) i = m->nitems - 1; if (i != m->selected_position) { /* Remove old highlighting, if any */ if (m->selected_position != -1) draw_item(pw, m, PIX_SRC, m->selected_position ); /* Highlight new selection */ m->selected_position = i; draw_item(pw, m, PIX_NOT(PIX_SRC), m->selected_position); } } else if (m->selected_position != -1) { /* Remove old highlighting */ draw_item(pw, m, PIX_SRC, m->selected_position); m->selected_position = -1; } } else if (id == LOC_WINEXIT || id == LOC_RGNEXIT) { #ifdef SUNOS3_0 #ifdef DESELECT_ON_EXIT #undef DESELECT_ON_EXIT #endif #define DESELECT_ON_EXIT #endif #ifdef DESELECT_ON_EXIT outside_window = 1; if (m->selected_position != -1) { /* Remove old highlighting */ draw_item(pw, m, PIX_SRC, m->selected_position); m->selected_position = -1; } m->selected_position = -1; #else SELECT_ON_EXIT if (m->selected_position == -1) { pie_window_return(NULL, event, m); } else { pie_window_return(m->item_list[m->selected_position], event,m); } #endif } if (event_is_up(event) && event_is_button(event)) { /* Selecting an item */ Frame owner = (Frame)window_get(m->dynamic_info, WIN_OWNER); if (m->selected_position == -1) { if (m->parent) { /* finished a subwindow without a selection */ window_set(win, WIN_CLIENT_DATA, m->parent->parent, 0); /* this breaks: window_done(owner); */ /* and for some reason, this leaves the window visible: */ window_set(owner, WIN_SHOW, FALSE, 0); } else { /* this is the top */ pie_window_return(NULL, event,m); } } else if (! m->item_list[m->selected_position]->pullright) { pie_window_return(m->item_list[m->selected_position], event,m); } else { /* nesting temporarily disabled Frame newframe; struct menu *newm = (struct menu *)m->item_list[m->selected_position]->value; newframe = construct_pie_frame(x+window_get(owner, WIN_X), y+window_get(owner, WIN_Y), newm, 0); window_set(win, WIN_CLIENT_DATA, newm, 0); window_set(newframe, WIN_SHOW, TRUE, 0); */ } } new_cursor(m->selected_position*inc+(inc/2.0), m->selected_position); return NOTIFY_DONE; } static int pie_process_args(m) struct menu *m; { int itemnum = 0; struct menu_item **argv = m->item_list; struct pr_size size; int max_len = 0; while (itemnum++ < m->nitems) { struct image *im = (*argv)->image; if (im->pr) { /* got an image */ size = im->pr->pr_size; } else { if (! im->string) { im->string = "(null)"; } size = pf_textwidth(strlen(im->string), m->default_image.font, im->string); } im->width = size.x; im->height = size.y; if (size.x > max_len) max_len = size.x; if (size.y > max_len) max_len = size.y; argv++; } return max_len; } /* center for 5 bit radius deadzone */ short center_data[] = { #include "center.h" }; mpr_static(center_pr, 11, 11, 1, center_data); /* Serious abuse of the image structure here. 'left_margin' is used to store the x position 'right_margin' is used to store the y position */ init_pies(m, pw) struct menu *m; Pixwin *pw; { struct menu_item **pies = m->item_list; double diameter = (double)m->default_image.width; double fr = diameter/2; double fr2 = fr/2.0; int i; double inc = 2.0*M_PI/(double)m->nitems; double theta = inc/2.0; for(i = 0; i < m->nitems; i++) { /* compute x position of upper lefthand corner */ pies[i]->image->left_margin = fmax(0.0, fmin((double)(diameter-pies[i]->image->width+2), fmax(2.0, fr*sin(theta) + fr - pies[i]->image->width/2))); /* compute y position of upper lefthand corner */ pies[i]->image->right_margin = fmax(0.0, fmin((double)(diameter-pies[i]->image->height-2), fmax(2.0, fr*cos(theta) + fr - pies[i]->image->height/2))); theta += inc; draw_item(pw, m, PIX_SRC, i); } pw_rop(pw, m->default_image.width/2-5, m->default_image.height/2-5, 11, 11, PIX_SRC, ¢er_pr, 0, 0); } static min(x,y) { return x < y ? x : y; } static max(x,y) { return x < y ? y : x; } double fabs(x) double x; { return x < 0 ? -x : x; } double fmin(x,y) double x, y; { return x < y ? x : y; } double fmax(x,y) double x, y; { return x < y ? y : x; } draw_item(pw, m, op, item) Pixwin *pw; struct menu *m; int item; { struct image *im = m->item_list[item]->image; if (im->pr) { pw_rop(pw, im->left_margin, im->right_margin, im->width, im->height, op, im->pr, 0, 0); } else { pw_text(pw, im->left_margin, im->right_margin + im->height, /* text posn is from baseline */ op, m->default_image.font, im->string); } } /* Why all the extra levels of function calls? Well, at one time they all added value. But as the organization changed, more and action got pressed into the event_procs, and hollow shells were left up here. They could be removed... */ struct menu_item * menu_track(m, w, event, f, s) struct menu *m; Event *event; Window w; int (*f)(); { struct menu_item *rval; rval = pie_menu(m, event, event_x(event), event_y(event), w, f, s); return rval; } pie_window_return(rval,e,m) caddr_t rval; Event *e; struct menu *m; { caddr_t real_rval; window_set(mainframe, FRAME_NO_CONFIRM, TRUE, 0); window_destroy(mainframe); if (rval == NULL) real_rval = (caddr_t)rval; if (! m->notify_proc) { real_rval = (caddr_t)MENU_DEFAULT_NOTIFY_PROC(m, rval); } else { real_rval = (caddr_t)(m->notify_proc)(m, rval); } (*return_proc)(real_rval, e); } #define PIN(x) (max(0,min((x),15))) new_cursor(theta, item_num) double theta; int item_num; { int headx, heady; Cursor cursor = window_get(maincanvas, WIN_CURSOR); struct pixrect *pr = (struct pixrect *)cursor_get(cursor, CURSOR_IMAGE); if (item_num == -1) { pr_rop(pr, 0, 0, 16, 16, PIX_SRC, &pie_generic_cursor, 0, 0); cursor_set(cursor, CURSOR_XHOT, 8, CURSOR_YHOT, 8, 0); } else { pr_rop(pr, 0, 0, 16, 16, PIX_SRC, NULL, 0, 0); pr_vector(pr, 8+(int)(sin(theta)*(double)20.0), 8+(int)(cos(theta)*(double)20.0), 8+(int)(sin(theta)*(double)(-20.0)), 8+(int)(cos(theta)*(double)(-20.0)), PIX_SRC, 1); headx = PIN(7+(int)(sin(theta)*(double)8.0)); heady = PIN(7+(int)(cos(theta)*(double)8.0)); pr_rop(pr, headx, heady, 3, 3, PIX_NOT(PIX_SRC), NULL, 0, 0); cursor_set(cursor, CURSOR_XHOT, headx, CURSOR_YHOT, heady, 0); } cursor_set(cursor, CURSOR_IMAGE, pr, 0); window_set(maincanvas, WIN_CURSOR, cursor, 0); }