Annotation of micropolis/src/sim/w_piem.c, revision 1.1.1.1

1.1       root        1: /* w_piem.c:  Pie Menus
                      2:  */
                      3: 
                      4: /*
                      5:  *
                      6:  * Pie Menus for Tk.
                      7:  * Copyright (C) 1992 by Don Hopkins.
                      8:  *
                      9:  * This program is provided for unrestricted use, provided that this 
                     10:  * copyright message is preserved. There is no warranty, and no author 
                     11:  * or distributer accepts responsibility for any damage caused by this 
                     12:  * program. 
                     13:  * 
                     14:  * This code and the ideas behind it were developed over time by Don Hopkins 
                     15:  * with the support of the University of Maryland, UniPress Software, Sun
                     16:  * Microsystems, DUX Software, the Turing Institute, and Carnegie Mellon
                     17:  * University.  Pie menus are NOT patented or restricted, and the interface 
                     18:  * and algorithms may be freely copied and improved upon. 
                     19:  *
                     20:  */
                     21: 
                     22: 
                     23: #if 0
                     24: /* workaround to make gcc work on suns */
                     25: #ifndef SOLARIS2
                     26: #define _PTRDIFF_T
                     27: #define _SIZE_T
                     28: #ifndef sgi
                     29: typedef unsigned int size_t;
                     30: #endif
                     31: #endif
                     32: #endif
                     33: 
                     34: #include "tkconfig.h"
                     35: #include "default.h"
                     36: #include "tkint.h"
                     37: #include <X11/extensions/shape.h>
                     38: 
                     39: #define PI 3.1415926535897932
                     40: #define TWO_PI 6.2831853071795865
                     41: #define DEG_TO_RAD(d) (((d) * TWO_PI) / 360.0)
                     42: #define RAD_TO_DEG(d) (((d) * 360.0) / TWO_PI)
                     43: #define PIE_SPOKE_INSET 6
                     44: #define PIE_BG_COLOR "#bfbfbf"
                     45: #define PIE_BG_MONO WHITE
                     46: #define PIE_ACTIVE_FG_COLOR BLACK
                     47: #define PIE_ACTIVE_FG_MONO BLACK
                     48: #define PIE_ACTIVE_BG_COLOR "#bfbfbf"
                     49: #define PIE_ACTIVE_BG_MONO WHITE
                     50: #define PIE_FG BLACK
                     51: #define PIE_FONT "-Adobe-Helvetica-Bold-R-Normal-*-120-*"
                     52: #define PIE_ACTIVE_BORDER_WIDTH "2"
                     53: #define PIE_INACTIVE_RADIUS "8"
                     54: #define PIE_INACTIVE_RADIUS_NUM 8
                     55: #define PIE_MIN_RADIUS "16"
                     56: #define PIE_MIN_RADIUS_NUM 16
                     57: #define PIE_EXTRA_RADIUS "2"
                     58: #define PIE_EXTRA_RADIUS_NUM 2
                     59: #define PIE_BORDER_WIDTH "2"
                     60: #define PIE_POPUP_DELAY "250"
                     61: #define PIE_POPUP_DELAY_NUM 250
                     62: #define PIE_ENTRY_ACTIVE_BG ((char *) NULL)
                     63: #define PIE_ENTRY_BG ((char *) NULL)
                     64: #define PIE_ENTRY_FONT ((char *) NULL)
                     65: 
                     66: #ifndef MAX
                     67: #define MAX(x,y) ((x)>(y)?(x):(y))
                     68: #define MIN(x,y) ((x)<(y)?(x):(y))
                     69: #endif
                     70: #define ABS(x)  (((x)<0)?(-(x)):(x))
                     71: 
                     72: static int HaveShape = -1;
                     73: 
                     74: /*
                     75:  * One of the following data structures is kept for each entry of each
                     76:  * pie menu managed by this file:
                     77:  */
                     78: 
                     79: typedef struct PieMenuEntry {
                     80:     int type;
                     81:     struct PieMenu *piemenuPtr;
                     82:     char *label;
                     83:     int labelLength;
                     84:     Pixmap bitmap;
                     85: 
                     86:     /*
                     87:      * Information related to displaying entry:
                     88:      */
                     89: 
                     90:     int width, height;
                     91:     int x, y;
                     92:     int x_offset, y_offset;
                     93:     int label_x, label_y;
                     94: 
                     95:     Tk_3DBorder border;
                     96:     Tk_3DBorder activeBorder;
                     97:     XFontStruct *fontPtr;
                     98:     GC textGC;
                     99:     GC activeGC;
                    100: 
                    101:     /*
                    102:      * Information used for pie menu layout & tracking:
                    103:      */
                    104: 
                    105:     int slice;                 /* Desired relative slice size */
                    106:     float angle;               /* Angle through center of slice */
                    107:     float dx, dy;              /* Cosine and sine of angle */
                    108:     float subtend;             /* Angle subtended by slice */
                    109:     int quadrant;              /* Quadrant of leading edge */
                    110:     float slope;               /* Slope of leading edge */
                    111: 
                    112:     /*
                    113:      * Information used to implement this entry's action:
                    114:      */
                    115: 
                    116:     char *command;
                    117:     char *preview;
                    118:     char *name;
                    119: 
                    120:     /*
                    121:      * Miscellaneous information:
                    122:      */
                    123: 
                    124:     int flags;                 /* Various flags. See below for definitions. */
                    125: } PieMenuEntry;
                    126: 
                    127: /*
                    128:  * Flag values defined for menu entries:
                    129:  *
                    130:  * ENTRY_NEEDS_REDISPLAY:      Non-zero means the entry should be redisplayed.
                    131:  */
                    132: 
                    133: #define ENTRY_NEEDS_REDISPLAY  1
                    134: 
                    135: /*
                    136:  * Types defined for PieMenuEntries:
                    137:  */
                    138: 
                    139: #define COMMAND_ENTRY          0
                    140: #define PIEMENU_ENTRY          1
                    141: 
                    142: /*
                    143:  * Mask bits for above types:
                    144:  */
                    145: 
                    146: #define COMMAND_MASK           TK_CONFIG_USER_BIT
                    147: #define PIEMENU_MASK           (TK_CONFIG_USER_BIT << 1)
                    148: #define ALL_MASK               (COMMAND_MASK | PIEMENU_MASK)
                    149: 
                    150: /*
                    151:  * Configuration specs for individual menu entries:
                    152:  */
                    153: 
                    154: static Tk_ConfigSpec entryConfigSpecs[] = {
                    155:     {TK_CONFIG_BORDER, "-activebackground", (char *) NULL, (char *) NULL,
                    156:        PIE_ENTRY_ACTIVE_BG, Tk_Offset(PieMenuEntry, activeBorder),
                    157:        ALL_MASK|TK_CONFIG_NULL_OK},
                    158:     {TK_CONFIG_BORDER, "-background", (char *) NULL, (char *) NULL,
                    159:        PIE_ENTRY_BG, Tk_Offset(PieMenuEntry, border),
                    160:        ALL_MASK|TK_CONFIG_NULL_OK},
                    161:     {TK_CONFIG_PIXMAP, "-bitmap", (char *) NULL, (char *) NULL,
                    162:        (char *) NULL, Tk_Offset(PieMenuEntry, bitmap),
                    163:        ALL_MASK|TK_CONFIG_NULL_OK},
                    164:     {TK_CONFIG_STRING, "-command", (char *) NULL, (char *) NULL,
                    165:        (char *) NULL, Tk_Offset(PieMenuEntry, command),
                    166:        COMMAND_MASK},
                    167:     {TK_CONFIG_STRING, "-preview", (char *) NULL, (char *) NULL,
                    168:        (char *) NULL, Tk_Offset(PieMenuEntry, preview),
                    169:        ALL_MASK},
                    170:     {TK_CONFIG_FONT, "-font", (char *) NULL, (char *) NULL,
                    171:        PIE_ENTRY_FONT, Tk_Offset(PieMenuEntry, fontPtr),
                    172:        ALL_MASK|TK_CONFIG_NULL_OK},
                    173:     {TK_CONFIG_STRING, "-label", (char *) NULL, (char *) NULL,
                    174:        (char *) NULL, Tk_Offset(PieMenuEntry, label),
                    175:        ALL_MASK},
                    176:     {TK_CONFIG_STRING, "-piemenu", (char *) NULL, (char *) NULL,
                    177:         (char *) NULL, Tk_Offset(PieMenuEntry, name),
                    178:         ALL_MASK},
                    179:     {TK_CONFIG_INT, "-xoffset", "xOffset", "XOffset",
                    180:        "0", Tk_Offset(PieMenuEntry, x_offset),
                    181:         ALL_MASK},
                    182:     {TK_CONFIG_INT, "-yoffset", "yOffset", "YOffset",
                    183:        "0", Tk_Offset(PieMenuEntry, y_offset),
                    184:         ALL_MASK},
                    185:     {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
                    186:        (char *) NULL, 0, 0}
                    187: };
                    188: 
                    189: /*
                    190:  * A data structure of the following type is kept for each
                    191:  * pie menu managed by this file:
                    192:  */
                    193: 
                    194: typedef struct PieMenu {
                    195:     Tk_Window tkwin;
                    196:     Tcl_Interp *interp;
                    197:     char *title;
                    198:     int titleLength;
                    199:     char *preview;
                    200:     PieMenuEntry **entries;
                    201:     int numEntries;
                    202:     int active;
                    203:     Tk_Uid group;
                    204:     int root_x, root_y;
                    205:     int dx, dy;
                    206: 
                    207:     /*
                    208:      * Information used when displaying widget:
                    209:      */
                    210: 
                    211:     Tk_3DBorder border;
                    212:     int borderWidth;
                    213:     Tk_3DBorder activeBorder;
                    214:     int activeBorderWidth;
                    215:     XFontStruct *fontPtr;
                    216:     XFontStruct *titlefontPtr;
                    217:     XColor *fg;
                    218:     GC textGC;
                    219:     XColor *activeFg;
                    220:     GC activeGC;
                    221: 
                    222:     /*
                    223:      * Information used to layout pie menu:
                    224:      */
                    225: 
                    226:     int width, height;                 /* size of the pie menu */
                    227:     int title_x, title_y;              /* position of menu title */
                    228:     int title_width, title_height;     /* size of menu title */
                    229:     int initial_angle;                 /* pie menu initial angle in radians */
                    230:     int inactive_radius;               /* inactive inner radius */
                    231:     int min_radius;                    /* minimum label radius */
                    232:     int fixed_radius;                  /* fixed label radius */
                    233:     int extra_radius;                  /* extra label radius pad */
                    234:     int label_radius;                  /* Radius of labels from menu center */
                    235:     int center_x, center_y;            /* Menu center */
                    236:     XSegment *segments;                        /* Line segments to draw */
                    237: 
                    238:     /*
                    239:      * Miscellaneous information:
                    240:      */
                    241: 
                    242:     Tk_TimerToken popup_timer_token;
                    243:     Cursor cursor;
                    244:     PieMenuEntry *postedPie;
                    245:     int flags;
                    246:     int phase;
                    247:     int popup_delay;                   /* Delay before popup */
                    248:     int shaped;                                /* Use SHAPE extension */
                    249: } PieMenu;
                    250: 
                    251: /*
                    252:  * Flag bits for menus:
                    253:  *
                    254:  * REDRAW_PENDING:             Non-zero means a DoWhenIdle handler
                    255:  *                             has already been queued to redraw
                    256:  *                             this window.
                    257:  * UPDATE_PENDING:             Non-zero means a DoWhenIdle handler
                    258:  *                             has already been queued to update
                    259:  *                             this window.
                    260:  * RESIZE_PENDING:             Non-zero means a call to ComputeMenuGeometry
                    261:  *                             has already been scheduled.
                    262:  * POPUP_PENDING:              Non-zero means a call to PopupPieMenu has
                    263:  *                             already been scheduled.
                    264:  */
                    265: 
                    266: #define REDRAW_PENDING         1
                    267: #define UPDATE_PENDING         2
                    268: #define RESIZE_PENDING         4
                    269: #define POPUP_PENDING          8
                    270: 
                    271: /*
                    272:  * Configuration specs valid for the menu as a whole:
                    273:  */
                    274: 
                    275: static Tk_ConfigSpec configSpecs[] = {
                    276:     {TK_CONFIG_BORDER, "-activebackground", "activeBackground", "Foreground",
                    277:        PIE_ACTIVE_BG_COLOR, Tk_Offset(PieMenu, activeBorder),
                    278:        TK_CONFIG_COLOR_ONLY},
                    279:     {TK_CONFIG_BORDER, "-activebackground", "activeBackground", "Foreground",
                    280:        PIE_ACTIVE_BG_MONO, Tk_Offset(PieMenu, activeBorder),
                    281:        TK_CONFIG_MONO_ONLY},
                    282:     {TK_CONFIG_PIXELS, "-activeborderwidth", "activeBorderWidth", "BorderWidth",
                    283:        PIE_ACTIVE_BORDER_WIDTH, Tk_Offset(PieMenu, activeBorderWidth), 0},
                    284:     {TK_CONFIG_COLOR, "-activeforeground", "activeForeground", "Background",
                    285:        PIE_ACTIVE_FG_COLOR, Tk_Offset(PieMenu, activeFg),
                    286:        TK_CONFIG_COLOR_ONLY},
                    287:     {TK_CONFIG_COLOR, "-activeforeground", "activeForeground", "Background",
                    288:        PIE_ACTIVE_FG_MONO, Tk_Offset(PieMenu, activeFg),
                    289:        TK_CONFIG_MONO_ONLY},
                    290:     {TK_CONFIG_BORDER, "-background", "background", "Background",
                    291:        PIE_BG_COLOR, Tk_Offset(PieMenu, border), TK_CONFIG_COLOR_ONLY},
                    292:     {TK_CONFIG_BORDER, "-background", "background", "Background",
                    293:        PIE_BG_MONO, Tk_Offset(PieMenu, border), TK_CONFIG_MONO_ONLY},
                    294:     {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *) NULL,
                    295:        (char *) NULL, 0, 0},
                    296:     {TK_CONFIG_SYNONYM, "-bg", "background", (char *) NULL,
                    297:        (char *) NULL, 0, 0},
                    298:     {TK_CONFIG_PIXELS, "-borderwidth", "borderWidth", "BorderWidth",
                    299:        PIE_BORDER_WIDTH, Tk_Offset(PieMenu, borderWidth), 0},
                    300:     {TK_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor",
                    301:        "circle", Tk_Offset(PieMenu, cursor), TK_CONFIG_NULL_OK},
                    302:     {TK_CONFIG_SYNONYM, "-fg", "foreground", (char *) NULL,
                    303:        (char *) NULL, 0, 0},
                    304:     {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
                    305:         PIE_FG, Tk_Offset(PieMenu, fg), 0},
                    306:     {TK_CONFIG_FONT, "-font", "font", "Font",
                    307:        PIE_FONT, Tk_Offset(PieMenu, fontPtr), 0},
                    308:     {TK_CONFIG_STRING, "-title", (char *) NULL, (char *) NULL,
                    309:        "", Tk_Offset(PieMenu, title), 0},
                    310:     {TK_CONFIG_STRING, "-preview", (char *) NULL, (char *) NULL,
                    311:        "", Tk_Offset(PieMenu, preview), 0},
                    312:     {TK_CONFIG_FONT, "-titlefont", "font", "Font",
                    313:        PIE_FONT, Tk_Offset(PieMenu, titlefontPtr), 0},
                    314:     {TK_CONFIG_INT, "-initialangle", "initialAngle", "InitialAngle",
                    315:        "0", Tk_Offset(PieMenu, initial_angle), 0},
                    316:     {TK_CONFIG_INT, "-inactiveradius", "inactiveRadius", "InactiveRadius",
                    317:        PIE_INACTIVE_RADIUS, Tk_Offset(PieMenu, inactive_radius), 0},
                    318:     {TK_CONFIG_INT, "-minradius", "minRadius", "MinRadius",
                    319:         PIE_MIN_RADIUS, Tk_Offset(PieMenu, min_radius), 0},
                    320:     {TK_CONFIG_INT, "-extraradius", "extraRadius", "ExtraRadius",
                    321:         PIE_EXTRA_RADIUS, Tk_Offset(PieMenu, extra_radius), 0},
                    322:     {TK_CONFIG_INT, "-fixedradius", "fixedRadius", "FixedRadius",
                    323:        "0", Tk_Offset(PieMenu, fixed_radius), 0},
                    324:     {TK_CONFIG_INT, "-active", "active", "Active",
                    325:        "-1", Tk_Offset(PieMenu, active), 0},
                    326:     {TK_CONFIG_INT, "-popupdelay", "popupDelay", "PopupDelay",
                    327:        PIE_POPUP_DELAY, Tk_Offset(PieMenu, popup_delay), 0},
                    328:     {TK_CONFIG_INT, "-shaped", "shaped", "Shaped",
                    329:        "1", Tk_Offset(PieMenu, shaped), 0},
                    330:     {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
                    331:        (char *) NULL, 0, 0}
                    332: };
                    333: 
                    334: /*
                    335:  * Forward declarations for procedures defined later in this file:
                    336:  */
                    337: 
                    338: int            Tk_PieMenuCmd(ClientData clientData, Tcl_Interp *interp,
                    339:                              int argc, char **argv);
                    340: static int     ActivatePieMenuEntry _ANSI_ARGS_((PieMenu *menuPtr,
                    341:                    int index, int preview));
                    342: static void    ComputePieMenuGeometry _ANSI_ARGS_((
                    343:                    ClientData clientData));
                    344: static int     ConfigurePieMenu _ANSI_ARGS_((Tcl_Interp *interp,
                    345:                    PieMenu *menuPtr, int argc, char **argv,
                    346:                    int flags));
                    347: static int     ConfigurePieMenuEntry _ANSI_ARGS_((Tcl_Interp *interp,
                    348:                    PieMenu *menuPtr, PieMenuEntry *mePtr, int index,
                    349:                    int argc, char **argv, int flags));
                    350: static void    DestroyPieMenu _ANSI_ARGS_((ClientData clientData));
                    351: static void    DestroyPieMenuEntry _ANSI_ARGS_((ClientData clientData));
                    352: static void    DisplayPieMenu _ANSI_ARGS_((ClientData clientData));
                    353: static void    UpdatePieMenu _ANSI_ARGS_((ClientData clientData));
                    354: static void    PopupPieMenu _ANSI_ARGS_((ClientData clientData));
                    355: static void    EventuallyRedrawPieMenu _ANSI_ARGS_((PieMenu *menuPtr,
                    356:                    int index));
                    357: static int     GetPieMenuIndex _ANSI_ARGS_((Tcl_Interp *interp,
                    358:                    PieMenu *menuPtr, char *string, int *indexPtr));
                    359: static void    PieMenuEventProc _ANSI_ARGS_((ClientData clientData,
                    360:                    XEvent *eventPtr));
                    361: static int     PieMenuWidgetCmd _ANSI_ARGS_((ClientData clientData,
                    362:                    Tcl_Interp *interp, int argc, char **argv));
                    363: static int     UnpostSubPieMenu _ANSI_ARGS_((Tcl_Interp *interp,
                    364:                    PieMenu *menuPtr));
                    365: static void    PopupPieMenu _ANSI_ARGS_((ClientData clientData));
                    366: static void    NowPopupPieMenu _ANSI_ARGS_((PieMenu *menuPtr));
                    367: static void    NeverPopupPieMenu _ANSI_ARGS_((PieMenu *menuPtr));
                    368: static void    EventuallyPopupPieMenu _ANSI_ARGS_((PieMenu *menuPtr));
                    369: static void    DeferPopupPieMenu _ANSI_ARGS_((PieMenu *menuPtr));
                    370: static void    ShapePieMenu _ANSI_ARGS_((PieMenu *menuPtr));
                    371: 
                    372: 
                    373: /*
                    374:  *--------------------------------------------------------------
                    375:  *
                    376:  * Tk_PieMenuCmd --
                    377:  *
                    378:  *     This procedure is invoked to process the "piemenu" Tcl
                    379:  *     command.  Read the code and write some user documentation for
                    380:  *     details on what it does.
                    381:  *
                    382:  * Results:
                    383:  *     A standard Tcl result.
                    384:  *
                    385:  * Side effects:
                    386:  *     See the user documentation for "menu", which this was based on.
                    387:  *
                    388:  *--------------------------------------------------------------
                    389:  */
                    390: 
                    391: int
                    392: Tk_PieMenuCmd(clientData, interp, argc, argv)
                    393:     ClientData clientData;     /* Main window associated with
                    394:                                 * interpreter. */
                    395:     Tcl_Interp *interp;                /* Current interpreter. */
                    396:     int argc;                  /* Number of arguments. */
                    397:     char **argv;               /* Argument strings. */
                    398: {
                    399:     Tk_Window tkwin = (Tk_Window) clientData;
                    400:     Tk_Window new;
                    401:     register PieMenu *menuPtr;
                    402:     XSetWindowAttributes atts;
                    403: 
                    404:     if (argc < 2) {
                    405:        Tcl_AppendResult(interp, "wrong # args: should be \"",
                    406:                argv[0], " pathName ?options?\"", (char *) NULL);
                    407:        return TCL_ERROR;
                    408:     }
                    409: 
                    410:     /*
                    411:      * Create the new window.  Set override-redirect so the window
                    412:      * manager won't add a border or argue about placement, and set
                    413:      * save-under so that the window can pop up and down without a
                    414:      * lot of re-drawing.
                    415:      */
                    416: 
                    417:     new = Tk_CreateWindowFromPath(interp, tkwin, argv[1], "");
                    418:     if (new == NULL) {
                    419:        return TCL_ERROR;
                    420:     }
                    421:     atts.override_redirect = True;
                    422:     atts.save_under = True;
                    423:     Tk_ChangeWindowAttributes(new, CWOverrideRedirect|CWSaveUnder, &atts);
                    424: 
                    425:     /*
                    426:      * Initialize the data structure for the menu.
                    427:      */
                    428: 
                    429:     menuPtr = (PieMenu *) ckalloc(sizeof(PieMenu));
                    430:     menuPtr->tkwin = new;
                    431:     menuPtr->interp = interp;
                    432:     menuPtr->title = NULL;
                    433:     menuPtr->titleLength = 0;
                    434:     menuPtr->preview = NULL;
                    435:     menuPtr->entries = NULL;
                    436:     menuPtr->numEntries = 0;
                    437:     menuPtr->active = -1;
                    438:     menuPtr->group = NULL; 
                    439:     menuPtr->root_x = 0;
                    440:     menuPtr->root_y = 0; 
                    441:     menuPtr->border = NULL;
                    442:     menuPtr->activeBorder = NULL;
                    443:     menuPtr->fontPtr = NULL;
                    444:     menuPtr->titlefontPtr = NULL;
                    445:     menuPtr->fg = NULL;
                    446:     menuPtr->textGC = None;
                    447:     menuPtr->activeFg = NULL;
                    448:     menuPtr->activeGC = None;
                    449:     menuPtr->width = 0;
                    450:     menuPtr->height = 0;
                    451:     menuPtr->title_x = 0;
                    452:     menuPtr->title_y = 0;
                    453:     menuPtr->title_width = 0;
                    454:     menuPtr->title_height = 0;
                    455:     menuPtr->initial_angle = 0;
                    456:     menuPtr->inactive_radius = PIE_INACTIVE_RADIUS_NUM;
                    457:     menuPtr->min_radius = PIE_MIN_RADIUS_NUM;
                    458:     menuPtr->extra_radius = PIE_EXTRA_RADIUS_NUM;
                    459:     menuPtr->fixed_radius = 0;
                    460:     menuPtr->label_radius = 0;
                    461:     menuPtr->center_x = 0;
                    462:     menuPtr->center_y = 0;
                    463:     menuPtr->segments = NULL;
                    464:     menuPtr->cursor = None;
                    465:     menuPtr->postedPie = NULL;
                    466:     menuPtr->flags = 0;
                    467:     menuPtr->phase = 0;
                    468:     menuPtr->shaped = 1;
                    469:     menuPtr->popup_delay = PIE_POPUP_DELAY_NUM;
                    470: 
                    471:     Tk_SetClass(new, "PieMenu");
                    472:     Tk_CreateEventHandler(menuPtr->tkwin,
                    473:                          ExposureMask | StructureNotifyMask |
                    474:                          ButtonPressMask | ButtonReleaseMask |
                    475:                          PointerMotionMask,
                    476:                          PieMenuEventProc, (ClientData) menuPtr);
                    477:     Tcl_CreateCommand(interp, Tk_PathName(menuPtr->tkwin), PieMenuWidgetCmd,
                    478:            (ClientData) menuPtr, (void (*)()) NULL);
                    479:     if (ConfigurePieMenu(interp, menuPtr, argc-2, argv+2, 0) != TCL_OK) {
                    480:        goto error;
                    481:     }
                    482: 
                    483:     interp->result = Tk_PathName(menuPtr->tkwin);
                    484:     return TCL_OK;
                    485: 
                    486:     error:
                    487:     Tk_DestroyWindow(menuPtr->tkwin);
                    488:     return TCL_ERROR;
                    489: }
                    490: 
                    491: /*
                    492:  *--------------------------------------------------------------
                    493:  *
                    494:  * PieMenuWidgetCmd --
                    495:  *
                    496:  *     This procedure is invoked to process the Tcl command
                    497:  *     that corresponds to a widget managed by this module.
                    498:  *     See the user documentation for details on what it does.
                    499:  *
                    500:  * Results:
                    501:  *     A standard Tcl result.
                    502:  *
                    503:  * Side effects:
                    504:  *     See the user documentation.
                    505:  *
                    506:  *--------------------------------------------------------------
                    507:  */
                    508: 
                    509: static int
                    510: PieMenuWidgetCmd(clientData, interp, argc, argv)
                    511:     ClientData clientData;     /* Information about menu widget. */
                    512:     Tcl_Interp *interp;                /* Current interpreter. */
                    513:     int argc;                  /* Number of arguments. */
                    514:     char **argv;               /* Argument strings. */
                    515: {
                    516:     register PieMenu *menuPtr = (PieMenu *) clientData;
                    517:     register PieMenuEntry *mePtr;
                    518:     int result = TCL_OK;
                    519:     int length, type;
                    520:     char c;
                    521: 
                    522:     if (argc < 2) {
                    523:        Tcl_AppendResult(interp, "wrong # args: should be \"",
                    524:                argv[0], " option ?arg arg ...?\"", (char *) NULL);
                    525:        return TCL_ERROR;
                    526:     }
                    527:     Tk_Preserve((ClientData) menuPtr);
                    528:     c = argv[1][0];
                    529:     length = strlen(argv[1]);
                    530:     if ((c == 'a') && (strncmp(argv[1], "activate", length) == 0)
                    531:            && (length >= 2)) {
                    532:        int index;
                    533: 
                    534:        if (argc != 3) {
                    535:            Tcl_AppendResult(interp, "wrong # args: should be \"",
                    536:                    argv[0], " activate index\"", (char *) NULL);
                    537:            goto error;
                    538:        }
                    539:        if (GetPieMenuIndex(interp, menuPtr, argv[2], &index) != TCL_OK) {
                    540:            goto error;
                    541:        }
                    542:        if (menuPtr->active == index) {
                    543:            goto done;
                    544:        }
                    545:        result = ActivatePieMenuEntry(menuPtr, index, 1);
                    546:        DeferPopupPieMenu(menuPtr);
                    547:     } else if ((c == 's') && (strncmp(argv[1], "show", length) == 0)
                    548:            && (length >= 2)) {
                    549:        int index;
                    550: 
                    551:        if (argc != 2) {
                    552:            Tcl_AppendResult(interp, "wrong # args: should be \"",
                    553:                    argv[0], " show\"", (char *) NULL);
                    554:            goto error;
                    555:        }
                    556:        NowPopupPieMenu(menuPtr);
                    557:     } else if ((c == 'p') && (strncmp(argv[1], "pending", length) == 0)
                    558:            && (length >= 2)) {
                    559:        int index;
                    560: 
                    561:        if (argc != 2) {
                    562:            Tcl_AppendResult(interp, "wrong # args: should be \"",
                    563:                    argv[0], " pending\"", (char *) NULL);
                    564:            goto error;
                    565:        }
                    566:        sprintf(interp->result, "%d",
                    567:                (menuPtr->flags & POPUP_PENDING) ? 1 : 0);
                    568:     } else if ((c == 'd') && (strncmp(argv[1], "defer", length) == 0)
                    569:            && (length >= 2)) {
                    570:        int index;
                    571: 
                    572:        if (argc != 2) {
                    573:            Tcl_AppendResult(interp, "wrong # args: should be \"",
                    574:                    argv[0], " defer\"", (char *) NULL);
                    575:            goto error;
                    576:        }
                    577:        DeferPopupPieMenu(menuPtr);
                    578:     } else if ((c == 'a') && (strncmp(argv[1], "add", length) == 0)
                    579:            && (length >= 2)) {
                    580:        PieMenuEntry **newEntries;
                    581: 
                    582:        if (argc < 3) {
                    583:            Tcl_AppendResult(interp, "wrong # args: should be \"",
                    584:                    argv[0], " add type ?options?\"", (char *) NULL);
                    585:            goto error;
                    586:        }
                    587: 
                    588:        /*
                    589:         * Figure out the type of the new entry.
                    590:         */
                    591: 
                    592:        c = argv[2][0];
                    593:        length = strlen(argv[2]);
                    594:        if ((c == 'c') && (strncmp(argv[2], "command", length) == 0)) {
                    595:            type = COMMAND_ENTRY;
                    596:        } else if ((c == 'p') && (strncmp(argv[2], "piemenu", length) == 0)) {
                    597:            type = PIEMENU_ENTRY;
                    598:        } else {
                    599:            Tcl_AppendResult(interp, "bad menu entry type \"",
                    600:                             argv[2], "\":  must be command or piemenu",
                    601:                             (char *) NULL);
                    602:            goto error;
                    603:        }
                    604: 
                    605:        /*
                    606:         * Add a new entry to the end of the menu's array of entries,
                    607:         * and process options for it.
                    608:         */
                    609: 
                    610:        mePtr = (PieMenuEntry *) ckalloc(sizeof(PieMenuEntry));
                    611:        newEntries = (PieMenuEntry **) ckalloc((unsigned)
                    612:                ((menuPtr->numEntries+1)*sizeof(PieMenuEntry *)));
                    613:        if (menuPtr->numEntries != 0) {
                    614:            memcpy((VOID *) newEntries, (VOID *) menuPtr->entries,
                    615:                    menuPtr->numEntries*sizeof(PieMenuEntry *));
                    616:            ckfree((char *) menuPtr->entries);
                    617:        }
                    618:        menuPtr->entries = newEntries;
                    619:        menuPtr->entries[menuPtr->numEntries] = mePtr;
                    620:        menuPtr->numEntries++;
                    621:        mePtr->type = type;
                    622:        mePtr->piemenuPtr = menuPtr;
                    623:        mePtr->label = NULL;
                    624:        mePtr->labelLength = 0;
                    625:        mePtr->bitmap = None;
                    626:        mePtr->width = 0;
                    627:        mePtr->height = 0;
                    628:        mePtr->x_offset = 0;
                    629:        mePtr->y_offset = 0;
                    630:        mePtr->label_x = 0;
                    631:        mePtr->label_y = 0;
                    632:        mePtr->border = NULL;
                    633:        mePtr->activeBorder = NULL;
                    634:        mePtr->fontPtr = NULL;
                    635:        mePtr->textGC = None;
                    636:        mePtr->activeGC = None;
                    637:        mePtr->slice = 1.0;
                    638:        mePtr->angle = 0.0;
                    639:        mePtr->dx = 0.0;
                    640:        mePtr->dy = 0.0;
                    641:        mePtr->subtend = 0.0;
                    642:        mePtr->quadrant = 0;
                    643:        mePtr->slope = 0.0;
                    644:        mePtr->command = NULL;
                    645:        mePtr->preview = NULL;
                    646:        mePtr->name = NULL;
                    647:        mePtr->flags = 0;
                    648:        if (ConfigurePieMenuEntry(interp, menuPtr, mePtr,
                    649:                                  menuPtr->numEntries-1,
                    650:                                  argc-3, argv+3, 0) != TCL_OK) {
                    651:            DestroyPieMenuEntry((ClientData) mePtr);
                    652:            menuPtr->numEntries--;
                    653:            goto error;
                    654:        }
                    655:        if (!(menuPtr->flags & RESIZE_PENDING)) {
                    656:            menuPtr->flags |= RESIZE_PENDING;
                    657:            Tk_DoWhenIdle(ComputePieMenuGeometry, (ClientData) menuPtr);
                    658:        }
                    659:     } else if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)) {
                    660:        if (argc == 2) {
                    661:            result = Tk_ConfigureInfo(interp, menuPtr->tkwin, configSpecs,
                    662:                    (char *) menuPtr, (char *) NULL, 0);
                    663:        } else if (argc == 3) {
                    664:            result = Tk_ConfigureInfo(interp, menuPtr->tkwin, configSpecs,
                    665:                    (char *) menuPtr, argv[2], 0);
                    666:        } else {
                    667:            result = ConfigurePieMenu(interp, menuPtr, argc-2, argv+2,
                    668:                    TK_CONFIG_ARGV_ONLY);
                    669:        }
                    670:     } else if ((c == 'd') && (strncmp(argv[1], "delete", length) == 0)
                    671:            && (length >= 2)) {
                    672:        int index, i;
                    673: 
                    674:        if (argc != 3) {
                    675:            Tcl_AppendResult(interp, "wrong # args: should be \"",
                    676:                    argv[0], " delete index\"", (char *) NULL);
                    677:            goto error;
                    678:        }
                    679:        if (GetPieMenuIndex(interp, menuPtr, argv[2], &index) != TCL_OK) {
                    680:            goto error;
                    681:        }
                    682:        if (index < 0) {
                    683:            goto done;
                    684:        }
                    685:        Tk_EventuallyFree((ClientData) menuPtr->entries[index],
                    686:                          DestroyPieMenuEntry);
                    687:        for (i = index; i < menuPtr->numEntries-1; i++) {
                    688:            menuPtr->entries[i] = menuPtr->entries[i+1];
                    689:        }
                    690:        menuPtr->numEntries -= 1;
                    691:        if (menuPtr->active == index) {
                    692:            menuPtr->active = -1;
                    693:        } else if (menuPtr->active > index) {
                    694:            menuPtr->active -= 1;
                    695:        }
                    696:        if (!(menuPtr->flags & RESIZE_PENDING)) {
                    697:            menuPtr->flags |= RESIZE_PENDING;
                    698:            Tk_DoWhenIdle(ComputePieMenuGeometry, (ClientData) menuPtr);
                    699:        }
                    700:     } else if ((c == 'e') && (length >= 3)
                    701:            && (strncmp(argv[1], "entryconfigure", length) == 0)) {
                    702:        int index;
                    703: 
                    704:        if (argc < 3) {
                    705:            Tcl_AppendResult(interp, "wrong # args: should be \"",
                    706:                    argv[0], " entryconfigure index ?option value ...?\"",
                    707:                    (char *) NULL);
                    708:            goto error;
                    709:        }
                    710:        if (GetPieMenuIndex(interp, menuPtr, argv[2], &index) != TCL_OK) {
                    711:            goto error;
                    712:        }
                    713:        if (index < 0) {
                    714:            goto done;
                    715:        }
                    716:        mePtr = menuPtr->entries[index];
                    717:        Tk_Preserve((ClientData) mePtr);
                    718:        if (argc == 3) {
                    719:            result = Tk_ConfigureInfo(interp, menuPtr->tkwin, entryConfigSpecs,
                    720:                    (char *) mePtr, (char *) NULL,
                    721:                    COMMAND_MASK << mePtr->type);
                    722:        } else if (argc == 4) {
                    723:            result = Tk_ConfigureInfo(interp, menuPtr->tkwin, entryConfigSpecs,
                    724:                    (char *) mePtr, argv[3], COMMAND_MASK << mePtr->type);
                    725:        } else {
                    726:            result = ConfigurePieMenuEntry(interp, menuPtr, mePtr, index,
                    727:                                           argc-3, argv+3,
                    728:                                           TK_CONFIG_ARGV_ONLY |
                    729:                                             COMMAND_MASK << mePtr->type);
                    730:        }
                    731:        Tk_Release((ClientData) mePtr);
                    732:     } else if ((c == 'i') && (strncmp(argv[1], "index", length) == 0)
                    733:            && (length >= 3)) {
                    734:        int index;
                    735: 
                    736:        if (argc != 3) {
                    737:            Tcl_AppendResult(interp, "wrong # args: should be \"",
                    738:                    argv[0], " index string\"", (char *) NULL);
                    739:            goto error;
                    740:        }
                    741:        if (GetPieMenuIndex(interp, menuPtr, argv[2], &index) != TCL_OK) {
                    742:            goto error;
                    743:        }
                    744:        if (index < 0) {
                    745:            interp->result = "none";
                    746:        } else {
                    747:            sprintf(interp->result, "%d", index);
                    748:        }
                    749:     } else if ((c == 'i') && (strncmp(argv[1], "invoke", length) == 0)
                    750:            && (length >= 3)) {
                    751:        int index;
                    752: 
                    753:        if (argc != 3) {
                    754:            Tcl_AppendResult(interp, "wrong # args: should be \"",
                    755:                    argv[0], " invoke index\"", (char *) NULL);
                    756:            goto error;
                    757:        }
                    758:        if (GetPieMenuIndex(interp, menuPtr, argv[2], &index) != TCL_OK) {
                    759:            goto error;
                    760:        }
                    761:        if (index < 0) {
                    762:            goto done;
                    763:        }
                    764:        mePtr = menuPtr->entries[index];
                    765:        Tk_Preserve((ClientData) mePtr);
                    766:        if (mePtr->command != NULL) {
                    767:            result = Tcl_GlobalEval(interp, mePtr->command);
                    768:        }
                    769:        Tk_Release((ClientData) mePtr);
                    770:     } else if ((c == 'p') && (strncmp(argv[1], "post", length) == 0)) {
                    771:        int x, y, ix, iy, tmp, err;
                    772:        Tk_Uid group;
                    773: 
                    774:        if ((argc != 4) && (argc != 5)) {
                    775:            Tcl_AppendResult(interp, "wrong # args: should be \"",
                    776:                    argv[0], " post x y ?group?\"", (char *) NULL);
                    777:            goto error;
                    778:        }
                    779:        if ((Tcl_GetInt(interp, argv[2], &x) != TCL_OK)
                    780:                || (Tcl_GetInt(interp, argv[3], &y) != TCL_OK)) {
                    781:            goto error;
                    782:        }
                    783:        if (argc == 5) {
                    784:            group = Tk_GetUid(argv[4]);
                    785:        } else {
                    786:            group = Tk_GetUid("default");
                    787:        }
                    788: 
                    789:        /*
                    790:         * Adjust the position of the menu if necessary to keep it
                    791:         * on-screen.
                    792:         */
                    793: 
                    794:        x -= menuPtr->center_x; y -= menuPtr->center_y;
                    795: #if 0
                    796:        ix = x; iy = y;
                    797: 
                    798:        tmp = WidthOfScreen(Tk_Screen(menuPtr->tkwin))
                    799:                - Tk_Width(menuPtr->tkwin);
                    800:        if (x > tmp) {
                    801:            x = tmp;
                    802:        }
                    803:        if (x < 0) {
                    804:            x = 0;
                    805:        }
                    806:        tmp = HeightOfScreen(Tk_Screen(menuPtr->tkwin))
                    807:                - Tk_Height(menuPtr->tkwin);
                    808:        if (y > tmp) {
                    809:            y = tmp;
                    810:        }
                    811:        if (y < 0) {
                    812:            y = 0;
                    813:        }
                    814: 
                    815:        /* XXX: warp pointer by (x-ix, y-iy) upon popup */
                    816: #endif
                    817: 
                    818:        Tk_MakeWindowExist(menuPtr->tkwin);
                    819:        XRaiseWindow(Tk_Display(menuPtr->tkwin), Tk_WindowId(menuPtr->tkwin));
                    820: 
                    821:        Tk_MoveWindow(menuPtr->tkwin, x, y);
                    822:        menuPtr->root_x = x + menuPtr->center_x;
                    823:        menuPtr->root_y = y + menuPtr->center_y;
                    824:        
                    825:        if (Tk_IsMapped(menuPtr->tkwin)) {
                    826:            if (group != menuPtr->group) {
                    827:                Tk_UnshareEvents(menuPtr->tkwin, menuPtr->group);
                    828:                Tk_ShareEvents(menuPtr->tkwin, group);
                    829:            }
                    830:        } else {
                    831:            Tk_ShareEvents(menuPtr->tkwin, group);
                    832:            EventuallyPopupPieMenu(menuPtr);
                    833:            result = ActivatePieMenuEntry(menuPtr, -1, 1);
                    834:        }
                    835:        menuPtr->group = group;
                    836:     } else if ((c == 'u') && (strncmp(argv[1], "unpost", length) == 0)) {
                    837:        if (argc != 2) {
                    838:            Tcl_AppendResult(interp, "wrong # args: should be \"",
                    839:                    argv[0], " unpost\"", (char *) NULL);
                    840:            goto error;
                    841:        }
                    842:        NeverPopupPieMenu(menuPtr);
                    843:        Tk_UnshareEvents(menuPtr->tkwin, menuPtr->group);
                    844:        Tk_UnmapWindow(menuPtr->tkwin);
                    845:        result = ActivatePieMenuEntry(menuPtr, -1, 0);
                    846:        if (result == TCL_OK) {
                    847:            result = UnpostSubPieMenu(interp, menuPtr);
                    848:        }
                    849:     } else if ((c == 'g') && (strncmp(argv[1], "grab", length) == 0)) {
                    850:       Tk_Window tkwin;
                    851:       int err;
                    852: 
                    853:       if ((argc != 3) ||
                    854:          ((tkwin = Tk_NameToWindow(interp, argv[2],
                    855:                                    menuPtr->tkwin)) == NULL)) {
                    856:        Tcl_AppendResult(interp, "wrong # args: should be \"",
                    857:                         argv[0], " grab window\"", (char *) NULL);
                    858:        goto error;
                    859:       }
                    860:       
                    861:       err =
                    862:        XGrabPointer(Tk_Display(tkwin),
                    863:                     Tk_WindowId(tkwin),
                    864:                     False,
                    865:                     ButtonPressMask | ButtonReleaseMask |
                    866:                     ButtonMotionMask | PointerMotionMask,
                    867:                     GrabModeAsync, GrabModeAsync, None, None,
                    868:                     TkCurrentTime(((TkWindow *)tkwin)->dispPtr));
                    869:       if (err != 0) {
                    870:        if (err == GrabNotViewable) {
                    871:          interp->result = "grab failed: window not viewable";
                    872:        } else if (err == AlreadyGrabbed) {
                    873:          interp->result = "grab failed: another application has grab";
                    874:        } else if (err == GrabFrozen) {
                    875:          interp->result = "grab failed: keyboard or pointer frozen";
                    876:        } else if (err == GrabInvalidTime) {
                    877:          interp->result = "grab failed: invalid time";
                    878:        } else {
                    879:          char msg[100];
                    880:          
                    881:          sprintf(msg, "grab failed for unknown reason (code %d)",
                    882:                  err);
                    883:          Tcl_AppendResult(interp, msg, (char *) NULL);
                    884:        }
                    885:        return TCL_ERROR;
                    886:       }
                    887:     } else if ((c == 'u') && (strncmp(argv[1], "ungrab", length) == 0)) {
                    888:       Tk_Window tkwin;
                    889: 
                    890:       if ((argc != 3) ||
                    891:          ((tkwin = Tk_NameToWindow(interp, argv[2],
                    892:                                    menuPtr->tkwin)) == NULL)) {
                    893:        Tcl_AppendResult(interp, "wrong # args: should be \"",
                    894:                         argv[0], " ungrab window\"", (char *) NULL);
                    895:        goto error;
                    896:       }
                    897: 
                    898:       XUngrabPointer(Tk_Display(tkwin),
                    899:                     TkCurrentTime(((TkWindow *)tkwin)->dispPtr));
                    900: 
                    901:     } else if ((c == 'd') && (strncmp(argv[1], "distance", length) == 0)
                    902:            && (length >= 3)) {
                    903:        int distance;
                    904: 
                    905:        if (argc != 2) {
                    906:            Tcl_AppendResult(interp, "wrong # args: should be \"",
                    907:                    argv[0], " distance\"", (char *) NULL);
                    908:            goto error;
                    909:        }
                    910:        distance = (int)(sqrt((menuPtr->dx * menuPtr->dx) + (menuPtr->dy * menuPtr->dy)) + 0.499);
                    911:        sprintf(interp->result, "%d", distance);
                    912:     } else if ((c == 'd') && (strncmp(argv[1], "direction", length) == 0)
                    913:            && (length >= 3)) {
                    914:        int direction;
                    915: 
                    916:        if (argc != 2) {
                    917:            Tcl_AppendResult(interp, "wrong # args: should be \"",
                    918:                    argv[0], " direction\"", (char *) NULL);
                    919:            goto error;
                    920:        }
                    921:        direction = (int)(RAD_TO_DEG(atan2(menuPtr->dy, menuPtr->dx)) + 0.499);
                    922:        if (direction < 0) direction += 360;
                    923:        sprintf(interp->result, "%d", direction);
                    924:     } else {
                    925:        Tcl_AppendResult(interp, "bad option \"", argv[1],
                    926:                "\": must be activate, show, add, configure, delete, ",
                    927:                "entryconfigure, index, invoke, post, unpost, pending, ",
                    928:                "defer, grab, or ungrab", (char *) NULL);
                    929:        goto error;
                    930:     }
                    931:     done:
                    932:     Tk_Release((ClientData) menuPtr);
                    933:     return result;
                    934: 
                    935:     error:
                    936:     Tk_Release((ClientData) menuPtr);
                    937:     return TCL_ERROR;
                    938: }
                    939: 
                    940: /*
                    941:  *----------------------------------------------------------------------
                    942:  *
                    943:  * DestroyPieMenu --
                    944:  *
                    945:  *     This procedure is invoked by Tk_EventuallyFree or Tk_Release
                    946:  *     to clean up the internal structure of a pie menu at a safe time
                    947:  *     (when no-one is using it anymore).
                    948:  *
                    949:  * Results:
                    950:  *     None.
                    951:  *
                    952:  * Side effects:
                    953:  *     Everything associated with the pie menu is freed up.
                    954:  *
                    955:  *----------------------------------------------------------------------
                    956:  */
                    957: 
                    958: static void
                    959: DestroyPieMenu(clientData)
                    960:     ClientData clientData;     /* Info about menu widget. */
                    961: {
                    962:     register PieMenu *menuPtr = (PieMenu *) clientData;
                    963:     int i;
                    964: 
                    965:     /* Should we delete the event handler? */
                    966: 
                    967:     for (i = 0; i < menuPtr->numEntries; i++) {
                    968:        DestroyPieMenuEntry((ClientData) menuPtr->entries[i]);
                    969:     }
                    970:     if (menuPtr->entries != NULL) {
                    971:        ckfree((char *) menuPtr->entries);
                    972:     }
                    973:     if (menuPtr->border != NULL) {
                    974:        Tk_Free3DBorder(menuPtr->border);
                    975:     }
                    976:     if (menuPtr->activeBorder != NULL) {
                    977:        Tk_Free3DBorder(menuPtr->activeBorder);
                    978:     }
                    979:     if (menuPtr->fontPtr != NULL) {
                    980:        Tk_FreeFontStruct(menuPtr->fontPtr);
                    981:     }
                    982:     if (menuPtr->fg != NULL) {
                    983:        Tk_FreeColor(menuPtr->fg);
                    984:     }
                    985:     if (menuPtr->textGC != None) {
                    986:        Tk_FreeGC(menuPtr->textGC);
                    987:     }
                    988:     if (menuPtr->activeFg != NULL) {
                    989:        Tk_FreeColor(menuPtr->activeFg);
                    990:     }
                    991:     if (menuPtr->activeGC != None) {
                    992:        Tk_FreeGC(menuPtr->activeGC);
                    993:     }
                    994:     if (menuPtr->cursor != None) {
                    995:        Tk_FreeCursor(menuPtr->cursor);
                    996:     }
                    997:     ckfree((char *) menuPtr);
                    998: }
                    999: 
                   1000: /*
                   1001:  *----------------------------------------------------------------------
                   1002:  *
                   1003:  * DestroyPieMenuEntry --
                   1004:  *
                   1005:  *     This procedure is invoked by Tk_EventuallyFree or Tk_Release
                   1006:  *     to clean up the internal structure of a pie menu entry at a safe 
                   1007:  *     time (when no-one is using it anymore).
                   1008:  *
                   1009:  * Results:
                   1010:  *     None.
                   1011:  *
                   1012:  * Side effects:
                   1013:  *     Everything associated with the pie menu entry is freed up.
                   1014:  *
                   1015:  *----------------------------------------------------------------------
                   1016:  */
                   1017: 
                   1018: static void
                   1019: DestroyPieMenuEntry(clientData)
                   1020:     ClientData clientData;             /* Pointer to entry to be freed. */
                   1021: {
                   1022:     register PieMenuEntry *mePtr = (PieMenuEntry *) clientData;
                   1023:     PieMenu *menuPtr = mePtr->piemenuPtr;
                   1024: 
                   1025:     if (menuPtr->postedPie == mePtr) {
                   1026:        if (UnpostSubPieMenu(menuPtr->interp, menuPtr)
                   1027:                != TCL_OK) {
                   1028:            TkBindError(menuPtr->interp);
                   1029:        }
                   1030:     }
                   1031:     if (mePtr->label != NULL) {
                   1032:        ckfree(mePtr->label);
                   1033:     }
                   1034:     if (mePtr->bitmap != None) {
                   1035:        Tk_FreePixmap(mePtr->bitmap);
                   1036:     }
                   1037:     if (mePtr->border != NULL) {
                   1038:        Tk_Free3DBorder(mePtr->border);
                   1039:     }
                   1040:     if (mePtr->activeBorder != NULL) {
                   1041:        Tk_Free3DBorder(mePtr->activeBorder);
                   1042:     }
                   1043:     if (mePtr->fontPtr != NULL) {
                   1044:        Tk_FreeFontStruct(mePtr->fontPtr);
                   1045:     }
                   1046:     if (mePtr->textGC != NULL) {
                   1047:        Tk_FreeGC(mePtr->textGC);
                   1048:     }
                   1049:     if (mePtr->activeGC != NULL) {
                   1050:        Tk_FreeGC(mePtr->activeGC);
                   1051:     }
                   1052:     if (mePtr->command != NULL) {
                   1053:        ckfree(mePtr->command);
                   1054:     }
                   1055:     if (mePtr->name != NULL) {
                   1056:        ckfree(mePtr->name);
                   1057:     }
                   1058:     ckfree((char *) mePtr);
                   1059: }
                   1060: 
                   1061: /*
                   1062:  *----------------------------------------------------------------------
                   1063:  *
                   1064:  * ConfigurePieMenu --
                   1065:  *
                   1066:  *     This procedure is called to process an argv/argc list, plus
                   1067:  *     the Tk option database, in order to configure (or
                   1068:  *     reconfigure) a menu widget.
                   1069:  *
                   1070:  * Results:
                   1071:  *     The return value is a standard Tcl result.  If TCL_ERROR is
                   1072:  *     returned, then interp->result contains an error message.
                   1073:  *
                   1074:  * Side effects:
                   1075:  *     Configuration information, such as colors, font, etc. get set
                   1076:  *     for menuPtr;  old resources get freed, if there were any.
                   1077:  *
                   1078:  *----------------------------------------------------------------------
                   1079:  */
                   1080: 
                   1081: static int
                   1082: ConfigurePieMenu(interp, menuPtr, argc, argv, flags)
                   1083:     Tcl_Interp *interp;                /* Used for error reporting. */
                   1084:     register PieMenu *menuPtr; /* Information about widget;  may or may
                   1085:                                 * not already have values for some fields. */
                   1086:     int argc;                  /* Number of valid entries in argv. */
                   1087:     char **argv;               /* Arguments. */
                   1088:     int flags;                 /* Flags to pass to Tk_ConfigureWidget. */
                   1089: {
                   1090:     XGCValues gcValues;
                   1091:     GC newGC;
                   1092:     int i;
                   1093: 
                   1094:     if (Tk_ConfigureWidget(interp, menuPtr->tkwin, configSpecs,
                   1095:            argc, argv, (char *) menuPtr, flags) != TCL_OK) {
                   1096:        return TCL_ERROR;
                   1097:     }
                   1098: 
                   1099:     /*
                   1100:      * A few options need special processing, such as setting the
                   1101:      * background from a 3-D border, or filling in complicated
                   1102:      * defaults that couldn't be specified to Tk_ConfigureWidget.
                   1103:      */
                   1104: 
                   1105:     if (menuPtr->title == NULL) {
                   1106:        menuPtr->titleLength = 0;
                   1107:     } else {
                   1108:        menuPtr->titleLength = strlen(menuPtr->title);
                   1109:     }
                   1110: 
                   1111:     Tk_SetBackgroundFromBorder(menuPtr->tkwin, menuPtr->border);
                   1112: 
                   1113:     gcValues.font = menuPtr->fontPtr->fid;
                   1114:     gcValues.foreground = menuPtr->fg->pixel;
                   1115:     gcValues.background = Tk_3DBorderColor(menuPtr->border)->pixel;
                   1116:     newGC = Tk_GetGC(menuPtr->tkwin, GCForeground|GCBackground|GCFont,
                   1117:            &gcValues);
                   1118:     if (menuPtr->textGC != None) {
                   1119:        Tk_FreeGC(menuPtr->textGC);
                   1120:     }
                   1121:     menuPtr->textGC = newGC;
                   1122: 
                   1123:     gcValues.font = menuPtr->fontPtr->fid;
                   1124:     gcValues.foreground = menuPtr->activeFg->pixel;
                   1125:     gcValues.background = Tk_3DBorderColor(menuPtr->activeBorder)->pixel;
                   1126:     newGC = Tk_GetGC(menuPtr->tkwin, GCForeground|GCBackground|GCFont,
                   1127:            &gcValues);
                   1128:     if (menuPtr->activeGC != None) {
                   1129:        Tk_FreeGC(menuPtr->activeGC);
                   1130:     }
                   1131:     menuPtr->activeGC = newGC;
                   1132: 
                   1133:     /*
                   1134:      * After reconfiguring a menu, we need to reconfigure all of the
                   1135:      * entries in the menu, since some of the things in the children
                   1136:      * (such as graphics contexts) may have to change to reflect changes
                   1137:      * in the parent.
                   1138:      */
                   1139: 
                   1140:     for (i = 0; i < menuPtr->numEntries; i++) {
                   1141:        PieMenuEntry *mePtr;
                   1142: 
                   1143:        mePtr = menuPtr->entries[i];
                   1144:        ConfigurePieMenuEntry(interp, menuPtr, mePtr, i, 0, (char **) NULL,
                   1145:                TK_CONFIG_ARGV_ONLY | COMMAND_MASK << mePtr->type);
                   1146:     }
                   1147: 
                   1148:     if (!(menuPtr->flags & RESIZE_PENDING)) {
                   1149:        menuPtr->flags |= RESIZE_PENDING;
                   1150:        Tk_DoWhenIdle(ComputePieMenuGeometry, (ClientData) menuPtr);
                   1151:     }
                   1152: 
                   1153:     return TCL_OK;
                   1154: }
                   1155: 
                   1156: /*
                   1157:  *----------------------------------------------------------------------
                   1158:  *
                   1159:  * ConfigurePieMenuEntry --
                   1160:  *
                   1161:  *     This procedure is called to process an argv/argc list, plus
                   1162:  *     the Tk option database, in order to configure (or
                   1163:  *     reconfigure) one entry in a menu.
                   1164:  *
                   1165:  * Results:
                   1166:  *     The return value is a standard Tcl result.  If TCL_ERROR is
                   1167:  *     returned, then interp->result contains an error message.
                   1168:  *
                   1169:  * Side effects:
                   1170:  *     Configuration information such as label and accelerator get
                   1171:  *     set for mePtr;  old resources get freed, if there were any.
                   1172:  *
                   1173:  *----------------------------------------------------------------------
                   1174:  */
                   1175: 
                   1176: static int
                   1177: ConfigurePieMenuEntry(interp, menuPtr, mePtr, index, argc, argv, flags)
                   1178:     Tcl_Interp *interp;                        /* Used for error reporting. */
                   1179:     PieMenu *menuPtr;                  /* Information about whole menu. */
                   1180:     register PieMenuEntry *mePtr;      /* Information about menu entry;  may
                   1181:                                         * or may not already have values for
                   1182:                                         * some fields. */
                   1183:     int index;                         /* Index of mePtr within menuPtr's
                   1184:                                         * entries. */
                   1185:     int argc;                          /* Number of valid entries in argv. */
                   1186:     char **argv;                       /* Arguments. */
                   1187:     int flags;                         /* Additional flags to pass to
                   1188:                                         * Tk_ConfigureWidget. */
                   1189: {
                   1190:     XGCValues gcValues;
                   1191:     GC newGC, newActiveGC;
                   1192: 
                   1193:     /*
                   1194:      * If this entry is a piemenu and the piemenu is posted, then unpost
                   1195:      * it before reconfiguring the entry (otherwise the reconfigure might
                   1196:      * change the name of the piemenu entry, leaving a posted menu
                   1197:      * high and dry).
                   1198:      */
                   1199: 
                   1200:     if (menuPtr->postedPie == mePtr) {
                   1201:        if (UnpostSubPieMenu(menuPtr->interp, menuPtr)
                   1202:                != TCL_OK) {
                   1203:            TkBindError(menuPtr->interp);
                   1204:        }
                   1205:     }
                   1206: 
                   1207:     if (Tk_ConfigureWidget(interp, menuPtr->tkwin, entryConfigSpecs,
                   1208:            argc, argv, (char *) mePtr,
                   1209:            flags | (COMMAND_MASK << mePtr->type)) != TCL_OK) {
                   1210:        return TCL_ERROR;
                   1211:     }
                   1212: 
                   1213:     /*
                   1214:      * The code below handles special configuration stuff not taken
                   1215:      * care of by Tk_ConfigureWidget, such as special processing for
                   1216:      * defaults, sizing strings, graphics contexts, etc.
                   1217:      */
                   1218: 
                   1219:     if (mePtr->label == NULL) {
                   1220:        mePtr->labelLength = 0;
                   1221:     } else {
                   1222:        mePtr->labelLength = strlen(mePtr->label);
                   1223:     }
                   1224: 
                   1225:     if (index != menuPtr->active) {
                   1226:        ActivatePieMenuEntry(menuPtr, index, 0);
                   1227:     }
                   1228: 
                   1229:     if ((mePtr->fontPtr != NULL) ||
                   1230:        (mePtr->type == PIEMENU_ENTRY)) {
                   1231:        gcValues.foreground = menuPtr->fg->pixel;
                   1232:        gcValues.background = Tk_3DBorderColor(
                   1233:                (mePtr->border != NULL) ? mePtr->border : menuPtr->border)
                   1234:                ->pixel;
                   1235:        if (mePtr->fontPtr != NULL) {
                   1236:          gcValues.font = mePtr->fontPtr->fid;
                   1237:        } else {
                   1238:          if (menuPtr->titlefontPtr != NULL)
                   1239:            gcValues.font = menuPtr->titlefontPtr->fid;
                   1240:          else
                   1241:            gcValues.font = menuPtr->fontPtr->fid;
                   1242:        }
                   1243: 
                   1244:        /*
                   1245:         * Note: disable GraphicsExpose events;  we know there won't be
                   1246:         * obscured areas when copying from an off-screen pixmap to the
                   1247:         * screen and this gets rid of unnecessary events.
                   1248:         */
                   1249: 
                   1250:        gcValues.graphics_exposures = False;
                   1251:        newGC = Tk_GetGC(menuPtr->tkwin,
                   1252:                GCForeground|GCBackground|GCFont|GCGraphicsExposures,
                   1253:                &gcValues);
                   1254: 
                   1255:        gcValues.foreground = menuPtr->activeFg->pixel;
                   1256:        gcValues.background = Tk_3DBorderColor(
                   1257:                (mePtr->activeBorder != NULL) ? mePtr->activeBorder
                   1258:                : menuPtr->activeBorder)->pixel;
                   1259:        newActiveGC = Tk_GetGC(menuPtr->tkwin,
                   1260:                GCForeground|GCBackground|GCFont|GCGraphicsExposures,
                   1261:                &gcValues);
                   1262:     } else {
                   1263:        newGC = NULL;
                   1264:        newActiveGC = NULL;
                   1265:     }
                   1266: 
                   1267:     if (mePtr->textGC != NULL) {
                   1268:            Tk_FreeGC(mePtr->textGC);
                   1269:     }
                   1270:     mePtr->textGC = newGC;
                   1271: 
                   1272:     if (mePtr->activeGC != NULL) {
                   1273:            Tk_FreeGC(mePtr->activeGC);
                   1274:     }
                   1275:     mePtr->activeGC = newActiveGC;
                   1276: 
                   1277:     if (!(menuPtr->flags & RESIZE_PENDING)) {
                   1278:        menuPtr->flags |= RESIZE_PENDING;
                   1279:        Tk_DoWhenIdle(ComputePieMenuGeometry, (ClientData) menuPtr);
                   1280:     }
                   1281:     return TCL_OK;
                   1282: }
                   1283: 
                   1284: /*
                   1285:  *--------------------------------------------------------------
                   1286:  *
                   1287:  * ComputePieMenuGeometry --
                   1288:  *
                   1289:  *     This procedure is invoked to recompute the size and
                   1290:  *     layout of a menu.  It is called as a when-idle handler so
                   1291:  *     that it only gets done once, even if a group of changes is
                   1292:  *     made to the menu.
                   1293:  *
                   1294:  * Results:
                   1295:  *     None.
                   1296:  *
                   1297:  * Side effects:
                   1298:  *     Fields of menu entries are changed to reflect their
                   1299:  *     current positions, and the size of the menu window
                   1300:  *     itself may be changed.
                   1301:  *
                   1302:  *--------------------------------------------------------------
                   1303:  */
                   1304: 
                   1305: static void
                   1306: ComputePieMenuGeometry(clientData)
                   1307:     ClientData clientData;             /* Structure describing menu. */
                   1308: {
                   1309:     PieMenu *menuPtr = (PieMenu *) clientData;
                   1310: 
                   1311:     if (menuPtr->tkwin == NULL) {
                   1312:        return;
                   1313:     }
                   1314: 
                   1315:     LayoutPieMenu(menuPtr);
                   1316: 
                   1317:     if ((menuPtr->width != Tk_ReqWidth(menuPtr->tkwin)) ||
                   1318:        (menuPtr->height != Tk_ReqHeight(menuPtr->tkwin))) {
                   1319:        Tk_GeometryRequest(menuPtr->tkwin, menuPtr->width, menuPtr->height);
                   1320:     } else {
                   1321:        /*
                   1322:         * Must always force a redisplay here if the window is mapped
                   1323:         * (even if the size didn't change, something else might have
                   1324:         * changed in the menu, such as a label or accelerator).  The
                   1325:         * resize will force a redisplay above.
                   1326:         */
                   1327: 
                   1328:        EventuallyRedrawPieMenu(menuPtr, -1);
                   1329:     }
                   1330: 
                   1331:     menuPtr->flags &= ~RESIZE_PENDING;
                   1332: }
                   1333: 
                   1334: /*
                   1335:  *----------------------------------------------------------------------
                   1336:  *
                   1337:  * DisplayPieMenu --
                   1338:  *
                   1339:  *     This procedure is invoked to display a pie menu widget.
                   1340:  *
                   1341:  * Results:
                   1342:  *     None.
                   1343:  *
                   1344:  * Side effects:
                   1345:  *     Commands are output to X to display the pie menu in its
                   1346:  *     current mode.
                   1347:  *
                   1348:  *----------------------------------------------------------------------
                   1349:  */
                   1350: 
                   1351: static void
                   1352: DisplayPieMenu(clientData)
                   1353:     ClientData clientData;     /* Information about widget. */
                   1354: {
                   1355:     register PieMenu *menuPtr = (PieMenu *) clientData;
                   1356:     register Tk_Window tkwin = menuPtr->tkwin;
                   1357:     XFontStruct *fontPtr;
                   1358:     int index;
                   1359: 
                   1360:     menuPtr->flags &= ~REDRAW_PENDING;
                   1361:     if ((menuPtr->tkwin == NULL) || !Tk_IsMapped(menuPtr->tkwin)) {
                   1362:        return;
                   1363:     }
                   1364: 
                   1365:     if (menuPtr->titlefontPtr != NULL) {
                   1366:        fontPtr = menuPtr->titlefontPtr;
                   1367:     } else {
                   1368:        fontPtr = menuPtr->fontPtr;
                   1369:     }
                   1370: 
                   1371:     if (menuPtr->titleLength != 0) {
                   1372:       Tk_Draw3DRectangle(Tk_Display(tkwin), Tk_WindowId(tkwin),
                   1373:                         menuPtr->border,
                   1374:                         menuPtr->borderWidth, menuPtr->borderWidth,
                   1375:                         Tk_Width(tkwin) - 2*menuPtr->borderWidth,
                   1376:                         menuPtr->title_height + 2*menuPtr->borderWidth,
                   1377:                         menuPtr->borderWidth, TK_RELIEF_RAISED);
                   1378: 
                   1379:       TkDisplayChars(Tk_Display(tkwin), Tk_WindowId(tkwin), menuPtr->textGC,
                   1380:                     fontPtr, menuPtr->title, menuPtr->titleLength,
                   1381:                     menuPtr->title_x, menuPtr->title_y,
                   1382:                     TK_NEWLINES_NOT_SPECIAL);
                   1383:     }
                   1384: 
                   1385:     if (menuPtr->segments) { 
                   1386:       XSetLineAttributes(Tk_Display(tkwin), menuPtr->textGC,
                   1387:                         0, LineSolid, CapButt, JoinMiter);
                   1388:       XDrawSegments(Tk_Display(tkwin), Tk_WindowId(tkwin),
                   1389:                    menuPtr->textGC, menuPtr->segments, menuPtr->numEntries);
                   1390:     }
                   1391:     
                   1392:     Tk_Draw3DRectangle(Tk_Display(tkwin), Tk_WindowId(tkwin), menuPtr->border,
                   1393:                       0, 0, Tk_Width(tkwin), Tk_Height(tkwin),
                   1394:                       menuPtr->borderWidth, TK_RELIEF_RAISED);
                   1395: 
                   1396:     UpdatePieMenuEntries(menuPtr);
                   1397: }
                   1398: 
                   1399: /*
                   1400:  *----------------------------------------------------------------------
                   1401:  *
                   1402:  * UpdatePieMenu --
                   1403:  *
                   1404:  *     This procedure is invoked to update a pie menu widget.
                   1405:  *
                   1406:  * Results:
                   1407:  *     None.
                   1408:  *
                   1409:  * Side effects:
                   1410:  *     Commands are output to X to update the pie menu in its
                   1411:  *     current mode.
                   1412:  *
                   1413:  *----------------------------------------------------------------------
                   1414:  */
                   1415: 
                   1416: static void
                   1417: UpdatePieMenu(clientData)
                   1418:     ClientData clientData;     /* Information about widget. */
                   1419: {
                   1420:     register PieMenu *menuPtr = (PieMenu *) clientData;
                   1421: 
                   1422:     menuPtr->flags &= ~UPDATE_PENDING;
                   1423:     if ((menuPtr->tkwin == NULL) || !Tk_IsMapped(menuPtr->tkwin)) {
                   1424:        return;
                   1425:     }
                   1426: 
                   1427:     UpdatePieMenuEntries(menuPtr);
                   1428: }
                   1429: 
                   1430: 
                   1431: UpdatePieMenuEntries(menuPtr)
                   1432:     PieMenu *menuPtr;
                   1433: {
                   1434:     register PieMenuEntry *mePtr;
                   1435:     register Tk_Window tkwin = menuPtr->tkwin;
                   1436:     XFontStruct *fontPtr;
                   1437:     int index;
                   1438:     GC gc;
                   1439: 
                   1440:     for (index = 0; index < menuPtr->numEntries; index++) {
                   1441:        mePtr = menuPtr->entries[index];
                   1442:        if (!(mePtr->flags & ENTRY_NEEDS_REDISPLAY)) {
                   1443:            continue;
                   1444:        }
                   1445:        mePtr->flags &= ~ENTRY_NEEDS_REDISPLAY;
                   1446: 
                   1447:        /*
                   1448:         * Background.
                   1449:         */
                   1450: 
                   1451:        Tk_Fill3DRectangle(Tk_Display(tkwin), Tk_WindowId(tkwin),
                   1452:                           ((mePtr->activeBorder != NULL)
                   1453:                              ? mePtr->activeBorder
                   1454:                              : menuPtr->activeBorder),
                   1455:                           mePtr->x, mePtr->y,
                   1456:                           mePtr->width, mePtr->height,
                   1457:                           menuPtr->activeBorderWidth,
                   1458:                           ((index == menuPtr->active)
                   1459:                              ? TK_RELIEF_SUNKEN
                   1460:                              : ((HaveShape && menuPtr->shaped)
                   1461:                                   ? TK_RELIEF_RAISED
                   1462:                                   : TK_RELIEF_FLAT)));
                   1463: 
                   1464:        gc = mePtr->textGC;
                   1465:        if (gc == NULL) {
                   1466:            gc = menuPtr->textGC;
                   1467:        }
                   1468: 
                   1469:        /*
                   1470:         * Draw label or bitmap for entry.
                   1471:         */
                   1472: 
                   1473:        fontPtr = mePtr->fontPtr;
                   1474:        if (fontPtr == NULL) {
                   1475:            fontPtr = menuPtr->fontPtr;
                   1476:        }
                   1477:        if (mePtr->bitmap != None) {
                   1478:            unsigned int width, height;
                   1479: 
                   1480:            Tk_SizeOfPixmap(mePtr->bitmap, &width, &height);
                   1481:             XCopyArea(Tk_Display(tkwin), mePtr->bitmap, Tk_WindowId(tkwin),
                   1482:                      gc, 0, 0, width, height,
                   1483:                      mePtr->label_x, mePtr->label_y);
                   1484:        } else {
                   1485:            if (mePtr->label != NULL) {
                   1486:                TkDisplayChars(Tk_Display(tkwin), Tk_WindowId(tkwin), gc,
                   1487:                               fontPtr, mePtr->label, mePtr->labelLength,
                   1488:                               mePtr->label_x, mePtr->label_y,
                   1489:                               TK_NEWLINES_NOT_SPECIAL);
                   1490:            }
                   1491:        }
                   1492:     }
                   1493: }
                   1494: 
                   1495: /*
                   1496:  *--------------------------------------------------------------
                   1497:  *
                   1498:  * GetPieMenuIndex --
                   1499:  *
                   1500:  *     Parse a textual index into a pie menu and return the numerical
                   1501:  *     index of the indicated entry.
                   1502:  *
                   1503:  * Results:
                   1504:  *     A standard Tcl result.  If all went well, then *indexPtr is
                   1505:  *     filled in with the entry index corresponding to string
                   1506:  *     (ranges from -1 to the number of entries in the pie menu minus
                   1507:  *     one).  Otherwise an error message is left in interp->result.
                   1508:  *
                   1509:  * Side effects:
                   1510:  *     None.
                   1511:  *
                   1512:  *--------------------------------------------------------------
                   1513:  */
                   1514: 
                   1515: static int
                   1516: GetPieMenuIndex(interp, menuPtr, string, indexPtr)
                   1517:     Tcl_Interp *interp;                /* For error messages. */
                   1518:     PieMenu *menuPtr;          /* Menu for which the index is being
                   1519:                                 * specified. */
                   1520:     char *string;              /* Specification of an entry in menu.  See
                   1521:                                 * manual entry for valid .*/
                   1522:     int *indexPtr;             /* Where to store converted relief. */
                   1523: {
                   1524:     int i, y;
                   1525: 
                   1526:     if ((string[0] == 'a') && (strcmp(string, "active") == 0)) {
                   1527:        *indexPtr = menuPtr->active;
                   1528:        return TCL_OK;
                   1529:     }
                   1530: 
                   1531:     if ((string[0] == 'l') && (strcmp(string, "last") == 0)) {
                   1532:        *indexPtr = menuPtr->numEntries-1;
                   1533:        return TCL_OK;
                   1534:     }
                   1535: 
                   1536:     if ((string[0] == 'n') && (strcmp(string, "none") == 0)) {
                   1537:        *indexPtr = -1;
                   1538:        return TCL_OK;
                   1539:     }
                   1540: 
                   1541:     if (string[0] == '@') {
                   1542:         char xstr[32], ystr[32];
                   1543:         int x, y;
                   1544: 
                   1545:        if ((sscanf(&string[1], "%31[^,],%31[^,]", xstr, ystr) == 2) &&
                   1546:            (Tcl_GetInt(interp, xstr,  &x) == TCL_OK) &&
                   1547:            (Tcl_GetInt(interp, ystr,  &y) == TCL_OK)) {
                   1548:            *indexPtr = CalcPieMenuItem(menuPtr, x, y);
                   1549:            return TCL_OK;
                   1550:        } else {
                   1551:            Tcl_SetResult(interp, (char *) NULL, TCL_STATIC);
                   1552:        }
                   1553:     }
                   1554: 
                   1555:     if (isdigit(string[0])) {
                   1556:        if (Tcl_GetInt(interp, string,  &i) == TCL_OK) {
                   1557:            if ((i < menuPtr->numEntries) && (i >= 0)) {
                   1558:                *indexPtr = i;
                   1559:                return TCL_OK;
                   1560:            }
                   1561:        } else {
                   1562:            Tcl_SetResult(interp, (char *) NULL, TCL_STATIC);
                   1563:        }
                   1564:     }
                   1565: 
                   1566:     for (i = 0; i < menuPtr->numEntries; i++) {
                   1567:        char *label;
                   1568: 
                   1569:        label = menuPtr->entries[i]->label;
                   1570:        if ((label != NULL)
                   1571:                && (Tcl_StringMatch(menuPtr->entries[i]->label, string))) {
                   1572:            *indexPtr = i;
                   1573:            return TCL_OK;
                   1574:        }
                   1575:     }
                   1576: 
                   1577:     Tcl_AppendResult(interp, "bad menu entry index \"",
                   1578:                     string, "\"", (char *) NULL);
                   1579:     return TCL_ERROR;
                   1580: }
                   1581: 
                   1582: /*
                   1583:  *--------------------------------------------------------------
                   1584:  *
                   1585:  * PieMenuEventProc --
                   1586:  *
                   1587:  *     This procedure is invoked by the Tk dispatcher for various
                   1588:  *     events on pie menus.
                   1589:  *
                   1590:  * Results:
                   1591:  *     None.
                   1592:  *
                   1593:  * Side effects:
                   1594:  *     When the window gets deleted, internal structures get
                   1595:  *     cleaned up.  When it gets exposed, it is redisplayed.
                   1596:  *
                   1597:  *--------------------------------------------------------------
                   1598:  */
                   1599: 
                   1600: static void
                   1601: PieMenuEventProc(clientData, eventPtr)
                   1602:     ClientData clientData;     /* Information about window. */
                   1603:     XEvent *eventPtr;          /* Information about event. */
                   1604: {
                   1605:     PieMenu *menuPtr = (PieMenu *) clientData;
                   1606:     switch (eventPtr->type) {
                   1607:     case Expose:
                   1608:        if (eventPtr->xexpose.count == 0) {
                   1609:            EventuallyRedrawPieMenu(menuPtr, -1);
                   1610:        }
                   1611:        break;
                   1612:     case DestroyNotify:
                   1613:        Tcl_DeleteCommand(menuPtr->interp, Tk_PathName(menuPtr->tkwin));
                   1614: 
                   1615:        /*
                   1616:         * Careful!  Must delete the event-sharing information here
                   1617:         * rather than in DestroyPieMenu.  By the time that procedure
                   1618:         * is called the tkwin may have been reused, resulting in some
                   1619:         * other window accidentally being cut off from shared events.
                   1620:         */
                   1621: 
                   1622:        Tk_UnshareEvents(menuPtr->tkwin, menuPtr->group);
                   1623:        menuPtr->tkwin = NULL;
                   1624:        if (menuPtr->flags & REDRAW_PENDING) {
                   1625:            Tk_CancelIdleCall(DisplayPieMenu, (ClientData) menuPtr);
                   1626:        }
                   1627:        if (menuPtr->flags & UPDATE_PENDING) {
                   1628:            Tk_CancelIdleCall(UpdatePieMenu, (ClientData) menuPtr);
                   1629:        }
                   1630:        if (menuPtr->flags & RESIZE_PENDING) {
                   1631:            Tk_CancelIdleCall(ComputePieMenuGeometry, (ClientData) menuPtr);
                   1632:        }
                   1633:        if (menuPtr->flags & POPUP_PENDING) {
                   1634:            Tk_CancelIdleCall(PopupPieMenu, (ClientData) menuPtr);
                   1635:        }
                   1636:        Tk_EventuallyFree((ClientData) menuPtr, DestroyPieMenu);
                   1637:        break;
                   1638:     case MotionNotify:
                   1639:        break;
                   1640:     case ButtonPress:
                   1641:        break;
                   1642:     case ButtonRelease:
                   1643:        break;
                   1644:     }
                   1645: }
                   1646: 
                   1647: 
                   1648: 
                   1649: /*
                   1650:  *----------------------------------------------------------------------
                   1651:  *
                   1652:  * EventuallyRedrawPieMenu --
                   1653:  *
                   1654:  *     Arrange for an entry of a pie menu, or the whole pie menu, 
                   1655:  *     to be redisplayed at some point in the future.
                   1656:  *
                   1657:  * Results:
                   1658:  *     None.
                   1659:  *
                   1660:  * Side effects:
                   1661:  *     A when-idle hander is scheduled to do the redisplay, if there
                   1662:  *     isn't one already scheduled.
                   1663:  *
                   1664:  *----------------------------------------------------------------------
                   1665:  */
                   1666: 
                   1667: static void
                   1668: EventuallyRedrawPieMenu(menuPtr, index)
                   1669:     register PieMenu *menuPtr; /* Information about menu to redraw. */
                   1670:     int index;                 /* Which entry to redraw.  If -1, then
                   1671:                                 * all the entries in the menu are redrawn. */
                   1672: {
                   1673:     if (menuPtr->tkwin == NULL) {
                   1674:        return;
                   1675:     }
                   1676:     if (index != -1) {
                   1677:        menuPtr->entries[index]->flags |= ENTRY_NEEDS_REDISPLAY;
                   1678:     } else {
                   1679:        for (index = 0; index < menuPtr->numEntries; index++) {
                   1680:            menuPtr->entries[index]->flags |= ENTRY_NEEDS_REDISPLAY;
                   1681:        }
                   1682:        index = -1;
                   1683:     }
                   1684:     if ((menuPtr->tkwin == NULL) || !Tk_IsMapped(menuPtr->tkwin)
                   1685:            || (menuPtr->flags & REDRAW_PENDING)) {
                   1686:        return;
                   1687:     }
                   1688: 
                   1689:     if (index == -1) {
                   1690:        if (menuPtr->flags & UPDATE_PENDING) {
                   1691:            Tk_CancelIdleCall(UpdatePieMenu, (ClientData) menuPtr);
                   1692:        }
                   1693:        Tk_DoWhenIdle(DisplayPieMenu, (ClientData) menuPtr);
                   1694:        menuPtr->flags |= REDRAW_PENDING;
                   1695:     } else {
                   1696:        Tk_DoWhenIdle(UpdatePieMenu, (ClientData) menuPtr);
                   1697:        menuPtr->flags |= UPDATE_PENDING;
                   1698:     }
                   1699: }
                   1700: 
                   1701: 
                   1702: static void
                   1703: PopupPieMenu(clientData)
                   1704:     ClientData clientData;     /* Information about widget. */
                   1705: {
                   1706:   register PieMenu *menuPtr = (PieMenu *) clientData;
                   1707: 
                   1708:   NeverPopupPieMenu(menuPtr);
                   1709: 
                   1710:   if (Tk_IsMapped(menuPtr->tkwin)) {
                   1711:     return;
                   1712:   }
                   1713: 
                   1714:   ShapePieMenu(menuPtr);
                   1715:   Tk_MapWindow(menuPtr->tkwin);
                   1716: }
                   1717: 
                   1718: 
                   1719: static void
                   1720: NowPopupPieMenu(menuPtr)
                   1721:     register PieMenu *menuPtr;
                   1722: {
                   1723:   PopupPieMenu((ClientData)menuPtr);
                   1724: }
                   1725: 
                   1726: 
                   1727: static void
                   1728: NeverPopupPieMenu(menuPtr)
                   1729:     register PieMenu *menuPtr;
                   1730: {
                   1731:   if (menuPtr->flags & POPUP_PENDING) {
                   1732:     Tk_DeleteTimerHandler(menuPtr->popup_timer_token);
                   1733:     menuPtr->popup_timer_token = 0;
                   1734:     menuPtr->flags &= ~POPUP_PENDING;
                   1735:   }
                   1736: }
                   1737: 
                   1738: 
                   1739: static void
                   1740: EventuallyPopupPieMenu(menuPtr)
                   1741:     register PieMenu *menuPtr;
                   1742: {
                   1743:   NeverPopupPieMenu(menuPtr);
                   1744: 
                   1745:   if (Tk_IsMapped(menuPtr->tkwin)) {
                   1746:     return;
                   1747:   }
                   1748: 
                   1749:   menuPtr->popup_timer_token =
                   1750:     Tk_CreateTimerHandler(menuPtr->popup_delay,
                   1751:                          PopupPieMenu, (ClientData) menuPtr);
                   1752:   menuPtr->flags |= POPUP_PENDING;
                   1753: }
                   1754: 
                   1755: 
                   1756: static void
                   1757: DeferPopupPieMenu(menuPtr)
                   1758:     register PieMenu *menuPtr;
                   1759: {
                   1760:   if (menuPtr->flags & POPUP_PENDING) {
                   1761:     EventuallyPopupPieMenu(menuPtr);
                   1762:   }
                   1763: }
                   1764: 
                   1765: 
                   1766: 
                   1767: /*
                   1768:  *--------------------------------------------------------------
                   1769:  *
                   1770:  * UnpostSubPieMenu --
                   1771:  *
                   1772:  *     This procedure unposts any submenu.
                   1773:  *
                   1774:  * Results:
                   1775:  *     A standard Tcl return result.  Errors may occur in the
                   1776:  *     Tcl commands generated to unpost submenus.
                   1777:  *
                   1778:  * Side effects:
                   1779:  *     If there is already a submenu posted, it is unposted.
                   1780:  *
                   1781:  *--------------------------------------------------------------
                   1782:  */
                   1783: 
                   1784: static int
                   1785: UnpostSubPieMenu(interp, menuPtr)
                   1786:     Tcl_Interp *interp;                /* Used for invoking sub-commands and
                   1787:                                 * reporting errors. */
                   1788:     register PieMenu *menuPtr; /* Information about menu as a whole. */
                   1789: {
                   1790:     char string[30];
                   1791:     int result, x, y, win_x, win_y;
                   1792:     unsigned int key_buttons;
                   1793:     Window root, child;
                   1794: 
                   1795:     if (menuPtr->postedPie == NULL) {
                   1796:        return TCL_OK;
                   1797:     }
                   1798: 
                   1799:     result = Tcl_VarEval(interp, menuPtr->postedPie->name,
                   1800:                         " unpost", (char *) NULL);
                   1801:     menuPtr->postedPie = NULL;
                   1802: 
                   1803:     return result;
                   1804: }
                   1805: 
                   1806: /*
                   1807:  *----------------------------------------------------------------------
                   1808:  *
                   1809:  * ActivatePieMenuEntry --
                   1810:  *
                   1811:  *     This procedure is invoked to make a particular pie menu 
                   1812:  *     entry the active one, deactivating any other entry that 
                   1813:  *     might currently be active.
                   1814:  *
                   1815:  * Results:
                   1816:  *     The return value is a standard Tcl result (errors can occur
                   1817:  *     while posting and unposting submenus).
                   1818:  *
                   1819:  * Side effects:
                   1820:  *     Pie menu entries get redisplayed, and the active entry 
                   1821:  *     changes.  Submenus may get posted and unposted.
                   1822:  *
                   1823:  *----------------------------------------------------------------------
                   1824:  */
                   1825: 
                   1826: static int
                   1827: ActivatePieMenuEntry(menuPtr, index, preview)
                   1828:     register PieMenu *menuPtr;         /* Menu in which to activate. */
                   1829:     int index;                         /* Index of entry to activate, or
                   1830:                                         * -1 to deactivate all entries. */
                   1831:     int preview;                       /* 1 to execute previewer */
                   1832: {
                   1833:     register PieMenuEntry *mePtr;
                   1834:     int result = TCL_OK;
                   1835: 
                   1836:     if (menuPtr->active >= 0) {
                   1837:        mePtr = menuPtr->entries[menuPtr->active];
                   1838: 
                   1839:        EventuallyRedrawPieMenu(menuPtr, menuPtr->active);
                   1840:     }
                   1841:     menuPtr->active = index;
                   1842:     if (index >= 0) {
                   1843:        mePtr = menuPtr->entries[index];
                   1844:        EventuallyRedrawPieMenu(menuPtr, index);
                   1845:        if (preview) {
                   1846:          Tk_Preserve((ClientData) mePtr);
                   1847:          if (mePtr->preview != NULL) {
                   1848:              result = Tcl_GlobalEval(menuPtr->interp, mePtr->preview);
                   1849:          }
                   1850:          Tk_Release((ClientData) mePtr);
                   1851:        }
                   1852:     } else {
                   1853: /* We're doing this in tcl these days, for finer control. */
                   1854: #if 0
                   1855:       if (preview && menuPtr->preview) {
                   1856:            result = Tcl_GlobalEval(menuPtr->interp, menuPtr->preview);
                   1857:       }
                   1858: #endif
                   1859:     }
                   1860:     return result;
                   1861: }
                   1862: 
                   1863: 
                   1864: /*
                   1865:  * This pie menu tracking code determines the slice the cursor 
                   1866:  * is in by representing slice edge angles as (quadrant, slope) 
                   1867:  * pairs that can be quickly computed and compared. 
                   1868:  *
                   1869:  * The slope is defined such that it is greater than or equal to zero,
                   1870:  * less than infinity, and increasing counter-clockwise around the menu. 
                   1871:  * Each of the four quadrants encompasses one range of slope.
                   1872:  *
                   1873:  *                 Y
                   1874:  *               ^
                   1875:  *               |     x>0, y>=0
                   1876:  *  x<=0, y>0 <--+       y/x
                   1877:  *    -x/y       |        ^
                   1878:  *        quad 1 | quad 0 |     X
                   1879:  * -----+--------+--------+----> 
                   1880:  *      | quad 2 | quad 3
                   1881:  *      V        |      -x/y
                   1882:  *   x<0, y<=0   +--> x>=0, y<0
                   1883:  *     y/x       |
                   1884:  *               |
                   1885:  * 
                   1886:  * The quadrants and slopes of the item edges are all precalculated,
                   1887:  * during menu layout.
                   1888:  * The quadrant and slope of the cursor must be calculated frequently
                   1889:  * during menu tracking, so we just calculate the numerator and
                   1890:  * denominator of the slope, and avoid an unnecessary division.
                   1891:  * Instead of calculating "slope = numerator / denominator" then
                   1892:  * testing "slope < it->slope", every time the cursor moves, we can
                   1893:  * just test "numerator < (denominator * it->slope)".
                   1894:  *
                   1895:  * This algorithm works in a right-side-up coordinate space, but the final
                   1896:  * results are tranformed into X-windows's up-side-down coordinate system 
                   1897:  * by subtracting the y values from the window height. 
                   1898:  */
                   1899: 
                   1900: 
                   1901: #define CALC_QUADRANT_SLOPE(x, y, quadrant, numerator, denominator) \
                   1902:     if ((y) > 0) (quadrant) = ((x) > 0 ? 0 : 1); \
                   1903:     else if ((y) < 0) (quadrant) = ((x) < 0 ? 2 : 3); \
                   1904:     else (quadrant) = ((x) > 0 ? 0 : 2); \
                   1905:     if ((quadrant) & 1) { \
                   1906:        (numerator) = ABS((x)); (denominator) = ABS((y)); \
                   1907:     } else { \
                   1908:        (numerator) = ABS((y)); (denominator) = ABS((x)); \
                   1909:     }
                   1910: 
                   1911: 
                   1912: int
                   1913: CalcPieMenuItem(menu, x, y)
                   1914:   PieMenu *menu;
                   1915:   int x, y;
                   1916: {
                   1917:   register PieMenuEntry *it, *last_it;
                   1918:   int i, j, order, quadrant;
                   1919:   int numerator, denominator;
                   1920:   int first, last_i, last_order;
                   1921:   
                   1922:   /*
                   1923:    * Translate x and y from root window coordinates so they are 
                   1924:    * relative to the menu center, in right side up coordinates.
                   1925:    */
                   1926:   
                   1927:   menu->dx = x = (x - menu->root_x) + 1;
                   1928:   menu->dy = y = (menu->root_y - y) - 1;
                   1929:   
                   1930:   /*
                   1931:    * If there are no menu items,
                   1932:    * or we are within the inactive region in the menu center,
                   1933:    * then there is no item selected.
                   1934:    */
                   1935:   if ((menu->numEntries == 0) ||
                   1936:       ((x * x) + (y * y) <
                   1937:        (menu->inactive_radius * menu->inactive_radius))) {
                   1938:     return(-1);
                   1939:   }
                   1940:   
                   1941:   /*
                   1942:    * If there's only one item, then that must be it. 
                   1943:    */
                   1944:   if (menu->numEntries == 1) {
                   1945:     return(0);
                   1946:   }
                   1947:   
                   1948:   /*
                   1949:    * Calculate the quadrant, slope numerator, and slope denominator of
                   1950:    * the cursor slope, to be used for comparisons.
                   1951:    */
                   1952:   CALC_QUADRANT_SLOPE(x, y, quadrant, numerator, denominator);
                   1953:   
                   1954:   /*
                   1955:    * In most cases, during cursor tracking, the menu item that the
                   1956:    * cursor is over will be the same as it was before (almost all
                   1957:    * of the time), or one of the neighboring items (most of the
                   1958:    * rest of the time). So we check those items first. But to keep
                   1959:    * things simple, instead of actually checking the items in order of
                   1960:    * frequency (the current, the two neighbors, then the rest), we just
                   1961:    * start our loop around the menu items at the item *before* the
                   1962:    * last selected menu item, so we still check the three most common
                   1963:    * cases first (neighbor, current, neighbor, rest), without having 
                   1964:    * to complicate the code with special cases. Another strategy, that
                   1965:    * might be good for menus with ridiculously many items, would be
                   1966:    * [to check the current item first, then the two neighbors, then]
                   1967:    * to do a binary search of the menu items (since they are ordered).
                   1968:    * But that's more complicated and you shouldn't have that many menu
                   1969:    * items anyway.
                   1970:    */
                   1971:   
                   1972:   /*
                   1973:    * Start at the item before current one.
                   1974:    */
                   1975:   first = menu->active - 1;
                   1976:   if (first < 0)
                   1977:     first = menu->numEntries - 1;
                   1978:   
                   1979:   /*
                   1980:    * Initialize last_order such that we will go through the loop
                   1981:    * at least once, validating last_i, last_order, and last_it for
                   1982:    * the next time through the loop.
                   1983:    */
                   1984:   last_i = last_order = -1;
                   1985:   i = first;
                   1986:   
                   1987:   it = menu->entries[i];
                   1988:   
                   1989:   while (1) {
                   1990: 
                   1991: /* Legend: c = cursor, e = edge
                   1992:    <cursor quad>,<edge quad>
                   1993:          quad 1 | quad 0
                   1994:          -------+-------
                   1995:          quad 2 | quad 3
                   1996: */
                   1997: 
                   1998:     /* Set order = 1, if shortest direction from edge to cursor is ccw */
                   1999:     switch ((quadrant - it->quadrant) & 3) {
                   2000: 
                   2001: case 0: /*
                   2002:                 0,0     1,1     2,2     3,3
                   2003:                  |ce   ce|       |       |     
                   2004:                --+--   --+--   --+--   --+--   
                   2005:                  |       |     ce|       |ce
                   2006:        */
                   2007:       /* slope >= it->slope */
                   2008:       order = ((float)numerator >= (float)(denominator * it->slope));
                   2009:       break;
                   2010: 
                   2011: case 1: /*
                   2012:                 1,0     2,1     3,2     0,3
                   2013:                 c|e     e|       |       |c
                   2014:                --+--   --+--   --+--   --+--   
                   2015:                  |      c|      e|c      |e
                   2016:        */
                   2017:       order = 1;
                   2018:       break;
                   2019: 
                   2020: case 2: /*
                   2021:                 2,0     3,1     0,2     1,3
                   2022:                  |e     e|       |c     c|
                   2023:                --+--   --+--   --+--   --+--   
                   2024:                 c|       |c     e|       |e
                   2025:        */
                   2026:       /* slope < it->slope */
                   2027:       order = ((float)numerator < (float)(denominator * it->slope));
                   2028:       break;
                   2029: 
                   2030: case 3: /*
                   2031:                 3,0     0,1     1,2     2,3
                   2032:                  |e     e|c     c|       |
                   2033:                --+--   --+--   --+--   --+--   
                   2034:                  |c      |      e|      c|e
                   2035:        */
                   2036:       order = 0;
                   2037:       break;
                   2038:     }
                   2039: 
                   2040:     /*
                   2041:      * If we were counter-clockwise of the last leading edge,
                   2042:      * and we're clockwise of this leading edge,
                   2043:      * then we were in the last menu item.
                   2044:      * (Note: first time through this loop last_order = -1 so we'll
                   2045:      * go back through the loop at least once, after validating
                   2046:      * last_order, last_i, and last_it.)
                   2047:      */
                   2048:     if ((last_order == 1) && (order == 0)) {
                   2049:       return(last_i);
                   2050:     }
                   2051:     last_order = order;
                   2052: 
                   2053:     /*
                   2054:      * Remember this menu item index, and move on to the next one
                   2055:      * counter-clockwise around the circle.
                   2056:      */
                   2057:     last_i = i; last_it = it;
                   2058:     if (++i >= menu->numEntries) {
                   2059:       i = 0;
                   2060:     }
                   2061:     it = menu->entries[i];
                   2062: 
                   2063:     /* 
                   2064:      * If we've checked all the others, then that must have been it. 
                   2065:      * This saves us from checking the leading edge of the first
                   2066:      * item again (It's also insurance against layout bugs.)
                   2067:      */
                   2068:     if (i == first) {
                   2069:       return(last_i);
                   2070:     }
                   2071:   }
                   2072: }
                   2073: 
                   2074: 
                   2075: LayoutPieMenu(menu)
                   2076:   PieMenu *menu;
                   2077: {
                   2078:   int i;
                   2079:   int total_slice, radius;
                   2080:   int minx, miny, maxx, maxy;
                   2081:   float angle;
                   2082:   PieMenuEntry *it, *last;
                   2083:   XFontStruct *font, *titlefont;
                   2084: 
                   2085:   /*
                   2086:    * Calculate the sum of the menu item slice sizes.
                   2087:    * Each menu item will get a (slice / total_slice) sized slice of the pie.
                   2088:    */
                   2089:   total_slice = 0;
                   2090:   for (i = 0; i < menu->numEntries; i++) {
                   2091:     total_slice += menu->entries[i]->slice;
                   2092:   }
                   2093: 
                   2094:   if ((titlefont = menu->titlefontPtr) == NULL)
                   2095:     titlefont = menu->fontPtr;
                   2096: 
                   2097:   /*
                   2098:    * Calculate the subtend, angle, cosine, sine, quadrant, slope,
                   2099:    * and size of each menu item.
                   2100:    */
                   2101:   angle = DEG_TO_RAD(menu->initial_angle);
                   2102:   for (i = 0; i < menu->numEntries; i++) {
                   2103:     register float edge_dx, edge_dy, numerator, denominator, twist;
                   2104:     register int quadrant;
                   2105: 
                   2106:     it = menu->entries[i];
                   2107:     if ((font = it->fontPtr) == NULL)
                   2108:       font = menu->fontPtr;
                   2109: 
                   2110:     if (it->bitmap != None) {
                   2111:       unsigned int bitmapWidth, bitmapHeight;
                   2112: 
                   2113:       Tk_SizeOfPixmap(it->bitmap, &bitmapWidth, &bitmapHeight);
                   2114:       it->height = bitmapHeight;
                   2115:       it->width = bitmapWidth;
                   2116:     } else {
                   2117:       it->height = font->ascent + font->descent;
                   2118:       if (it->label != NULL) {
                   2119:        (void) TkMeasureChars(font, it->label,
                   2120:                              it->labelLength, 0, (int) 100000,
                   2121:                              TK_NEWLINES_NOT_SPECIAL, &it->width);
                   2122:       } else {
                   2123:        it->width = 0;
                   2124:       }
                   2125:     }
                   2126:     it->height += 2*menu->activeBorderWidth + 2;
                   2127:     it->width += 2*menu->activeBorderWidth + 2;
                   2128: 
                   2129:     it->subtend = TWO_PI * it->slice / total_slice;
                   2130:     twist = it->subtend / 2.0;
                   2131:     if (i != 0) angle += twist;
                   2132:     it->angle = angle;
                   2133:     it->dx = cos(angle);
                   2134:     it->dy = sin(angle);
                   2135:     edge_dx = cos(angle - twist);
                   2136:     edge_dy = sin(angle - twist);
                   2137:     CALC_QUADRANT_SLOPE(edge_dx, edge_dy, quadrant, numerator, denominator);
                   2138:     it->quadrant = quadrant;
                   2139:     it->slope = (float)numerator / (float)denominator;
                   2140:     angle += twist;
                   2141:   }
                   2142: 
                   2143:   if ((radius = menu->fixed_radius) == 0) {
                   2144:     radius = menu->min_radius;
                   2145:     if (menu->numEntries > 1) {
                   2146:       last = menu->entries[menu->numEntries - 1];
                   2147:       for (i = 0; i < menu->numEntries; i++) {
                   2148:        float dx, dy, ldx, ldy;
                   2149:        int width, height, lwidth, lheight;
                   2150: 
                   2151:        it = menu->entries[i];
                   2152: 
                   2153:        dx = it->dx;  dy = it->dy;
                   2154:        width = it->width;  height = it->height;
                   2155:        ldx = last->dx;  ldy = last->dy;
                   2156:        lwidth = last->width;  lheight = last->height;
                   2157:        while (1) {
                   2158:          register int x, y, lx, ly, 
                   2159:                       x0max, y0max, x1min, y1min;
                   2160: 
                   2161:          x = dx * radius + it->x_offset;
                   2162:          y = dy * radius + it->y_offset;
                   2163:          lx = ldx * radius + last->x_offset;
                   2164:          ly = ldy * radius + last->y_offset;
                   2165: 
                   2166:          /* Translate x y with respect to label size and position */
                   2167:          if (ABS(x) <= 2) {
                   2168:            x -= width/2;
                   2169:            if (y < 0)
                   2170:              y -= height;
                   2171:          } else {
                   2172:            if (x < 0)
                   2173:              x -= width;
                   2174:            y -= height/2;
                   2175:          }
                   2176: 
                   2177:          if (ABS(lx) <= 2) {
                   2178:            lx -= lwidth/2;
                   2179:            if (ly < 0)
                   2180:              ly -= lheight;
                   2181:          } else {
                   2182:            if (lx < 0)
                   2183:              lx -= lwidth;
                   2184:            ly -= lheight/2;
                   2185:          }
                   2186: 
                   2187:          /* Do rects (x y width height) and (lx ly lwidth lheight) overlap? */
                   2188:          x0max = x > lx ? x : lx;
                   2189:          y0max = y > ly ? y : ly;
                   2190:          x1min = x+width < lx+lwidth ? x+width : lx+lwidth;
                   2191:          y1min = y+height < ly+lheight ? y+height : ly+lheight;
                   2192:          if (!((x0max < x1min) &&
                   2193:                (y0max < y1min))) { /* If they don't overlap */
                   2194:            /* They are far enough out, so move on. */
                   2195:            break;
                   2196:          }
                   2197:          /* Push the menu radius out a step and try again */
                   2198:          radius++;
                   2199:        }
                   2200:        /* Loop on to next menu item */
                   2201:        last = it;
                   2202:       }
                   2203:     }
                   2204:     radius += menu->extra_radius;
                   2205:   }
                   2206:   menu->label_radius = radius;
                   2207: 
                   2208:   /* Finally position all the menu labels at the same radius.
                   2209:      Figure out the bounding box of the labels. */
                   2210:   minx = miny = maxx = maxy = 0;
                   2211:   for (i = 0; i < menu->numEntries; i++) {
                   2212:     it = menu->entries[i];
                   2213: 
                   2214:     it->x = radius * it->dx + it->x_offset;
                   2215:     it->y = radius * it->dy + it->y_offset;
                   2216: 
                   2217:     /* Translate x y with respect to label size and position */
                   2218:     if (ABS(it->x) <= 2) {
                   2219:       it->x -= it->width/2;
                   2220:       if (it->y < 0)
                   2221:        it->y -= it->height;
                   2222:     } else {
                   2223:       if (it->x < 0)
                   2224:        it->x -= it->width;
                   2225:       it->y -= it->height/2;
                   2226:     }
                   2227: 
                   2228:     it->label_x = it->x + menu->activeBorderWidth + 1;
                   2229:     it->label_y = it->y - menu->activeBorderWidth - 1;
                   2230:     if (it->bitmap == None) {
                   2231:       it->label_y -= (it->fontPtr ? it->fontPtr : menu->fontPtr)->ascent;
                   2232:     }
                   2233: 
                   2234:     if (it->x < minx) minx = it->x;
                   2235:     if ((it->x + it->width) > maxx) maxx = (it->x + it->width);
                   2236:     if (it->y < miny) miny = it->y;
                   2237:     if ((it->y + it->height) > maxy) maxy = (it->y + it->height);
                   2238:   }
                   2239: 
                   2240:   
                   2241:   if (menu->titleLength != 0) {
                   2242:     menu->title_height = titlefont->ascent + titlefont->descent + 2;
                   2243:     (void) TkMeasureChars(titlefont, menu->title,
                   2244:                          menu->titleLength, 0, (int) 100000,
                   2245:                          TK_NEWLINES_NOT_SPECIAL, &menu->title_width);
                   2246:     menu->title_width += 2;
                   2247:     if (-(menu->title_width / 2) < minx)
                   2248:       minx = -(menu->title_width / 2);
                   2249:     if ((menu->title_width / 2) > maxx)
                   2250:       maxx = (menu->title_width / 2);
                   2251:     maxy += (2 * menu->borderWidth) + menu->title_height;
                   2252:   } else {
                   2253:     menu->title_width = menu->title_height = 0;
                   2254:   }
                   2255: 
                   2256: 
                   2257:   minx -= 2*menu->borderWidth;  miny -= 2*menu->borderWidth;
                   2258:   maxx += 2*menu->borderWidth;  maxy += 2*menu->borderWidth;
                   2259: 
                   2260:   menu->center_x = -minx;
                   2261:   menu->center_y = maxy; /* y flip */
                   2262:   menu->width = maxx - minx;
                   2263:   menu->height = maxy - miny;
                   2264: 
                   2265: /*  menu->title_x = (menu->width - menu->title_width) / 2 + 1; */
                   2266:   menu->title_x = menu->center_x - menu->title_width/2 + 1;
                   2267:   menu->title_y = 2*menu->borderWidth + titlefont->ascent + 1;
                   2268: 
                   2269:   /* Translate the menu items to the center of the menu, in X coordinates. */
                   2270:   for (i = 0; i < menu->numEntries; i++) {
                   2271:     it = menu->entries[i];
                   2272:     it->x = menu->center_x + it->x;
                   2273:     it->y = (menu->center_y - it->y) - it->height; /* y flip */
                   2274:     it->label_x = menu->center_x + it->label_x;
                   2275:     it->label_y = (menu->center_y - it->label_y) - it->height; /* y flip */
                   2276:   }
                   2277: 
                   2278:   if (menu->segments != NULL) {
                   2279:       ckfree((char *)menu->segments);
                   2280:   }
                   2281:   menu->segments = (XSegment *)
                   2282:     ckalloc(menu->numEntries * sizeof(XSegment));
                   2283: 
                   2284:   if (menu->numEntries > 1) {
                   2285:     XSegment *seg = menu->segments;
                   2286: 
                   2287:     angle = DEG_TO_RAD(menu->initial_angle) -
                   2288:             (menu->entries[0]->subtend / 2.0);
                   2289:     for (i = 0; i < menu->numEntries; i++) {
                   2290:       it = menu->entries[i];
                   2291:       seg->x1 = menu->center_x + (cos(angle) * menu->inactive_radius);
                   2292:       seg->y1 = menu->center_y - (sin(angle) * menu->inactive_radius);
                   2293:       seg->x2 = menu->center_x +
                   2294:        (cos(angle) * (menu->label_radius - PIE_SPOKE_INSET));
                   2295:       seg->y2 = menu->center_y -
                   2296:        (sin(angle) * (menu->label_radius - PIE_SPOKE_INSET));
                   2297:       seg++;
                   2298:       angle += it->subtend;
                   2299:     }
                   2300:   }
                   2301: }
                   2302: 
                   2303: 
                   2304: static void
                   2305: ShapePieMenu(menuPtr)
                   2306: PieMenu *menuPtr;
                   2307: {
                   2308:   Display *dpy;
                   2309:   Window win, shape;
                   2310:   GC gc;
                   2311:   XGCValues values;
                   2312:   PieMenuEntry *it;
                   2313:   int i;
                   2314: 
                   2315:   if (HaveShape == 0)
                   2316:     return;
                   2317: 
                   2318:   if (menuPtr->shaped == 0) {
                   2319:     return;
                   2320:   }
                   2321: 
                   2322:   dpy = Tk_Display(menuPtr->tkwin);
                   2323: 
                   2324:   if (HaveShape == -1) {
                   2325:     int t1, t2;
                   2326:     if (XShapeQueryExtension(dpy, &t1, &t2)) {
                   2327:       HaveShape = 1;
                   2328:     } else {
                   2329:       HaveShape = 0;
                   2330:       return;
                   2331:     }
                   2332:   }
                   2333: 
                   2334:   Tk_MakeWindowExist(menuPtr->tkwin);
                   2335:   win = Tk_WindowId(menuPtr->tkwin);
                   2336: 
                   2337:   shape = XCreatePixmap(dpy, RootWindowOfScreen(Tk_Screen(menuPtr->tkwin)),
                   2338:                        menuPtr->width, menuPtr->height, 1);
                   2339:   gc = XCreateGC(dpy, shape, 0, &values);
                   2340: 
                   2341: 
                   2342:   XSetForeground(dpy, gc, 0);
                   2343:   XFillRectangle(dpy, shape, gc, 0, 0, menuPtr->width, menuPtr->height);
                   2344: 
                   2345:   XSetForeground(dpy, gc, 1);
                   2346:   if (menuPtr->titleLength != 0) {
                   2347:     int bw = menuPtr->borderWidth;
                   2348: 
                   2349:     XFillRectangle(dpy, shape, gc, bw, bw, menuPtr->width - bw*2, menuPtr->title_height + bw*2);
                   2350:   }
                   2351: 
                   2352:   for (i = 0; i < menuPtr->numEntries; i++) {
                   2353:     it = menuPtr->entries[i];
                   2354:     XFillRectangle(dpy, shape, gc, it->x, it->y, it->width, it->height);
                   2355:   }
                   2356: 
                   2357:   XFreeGC(dpy, gc);
                   2358:   XShapeCombineMask(dpy, win, ShapeBounding, 0, 0, shape, ShapeSet);
                   2359: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.