Annotation of micropolis/src/sim/w_piem.c, revision 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.