7  * Copyright (C) 1992 by Don Hopkins. 
   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  
  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.  
  24 /* workaround to make gcc work on suns */ 
  29 typedef unsigned int size_t; 
  37 #include <X11/extensions/shape.h> 
  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 
  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) 
  67 #define MAX(x,y) ((x)>(y)?(x):(y)) 
  68 #define MIN(x,y) ((x)<(y)?(x):(y)) 
  70 #define ABS(x)   (((x)<0)?(-(x)):(x)) 
  72 static int HaveShape 
= -1; 
  75  * One of the following data structures is kept for each entry of each 
  76  * pie menu managed by this file: 
  79 typedef struct PieMenuEntry 
{ 
  81     struct PieMenu 
*piemenuPtr
; 
  87      * Information related to displaying entry: 
  92     int x_offset
, y_offset
; 
  96     Tk_3DBorder activeBorder
; 
 102      * Information used for pie menu layout & tracking: 
 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 */ 
 113      * Information used to implement this entry's action: 
 121      * Miscellaneous information: 
 124     int flags
;                  /* Various flags. See below for definitions. */ 
 128  * Flag values defined for menu entries: 
 130  * ENTRY_NEEDS_REDISPLAY:       Non-zero means the entry should be redisplayed. 
 133 #define ENTRY_NEEDS_REDISPLAY   1 
 136  * Types defined for PieMenuEntries: 
 139 #define COMMAND_ENTRY           0 
 140 #define PIEMENU_ENTRY           1 
 143  * Mask bits for above types: 
 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) 
 151  * Configuration specs for individual menu entries: 
 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
), 
 167     {TK_CONFIG_STRING
, "-preview", (char *) NULL
, (char *) NULL
, 
 168         (char *) NULL
, Tk_Offset(PieMenuEntry
, preview
), 
 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
), 
 176     {TK_CONFIG_STRING
, "-piemenu", (char *) NULL
, (char *) NULL
, 
 177         (char *) NULL
, Tk_Offset(PieMenuEntry
, name
), 
 179     {TK_CONFIG_INT
, "-xoffset", "xOffset", "XOffset", 
 180         "0", Tk_Offset(PieMenuEntry
, x_offset
), 
 182     {TK_CONFIG_INT
, "-yoffset", "yOffset", "YOffset", 
 183         "0", Tk_Offset(PieMenuEntry
, y_offset
), 
 185     {TK_CONFIG_END
, (char *) NULL
, (char *) NULL
, (char *) NULL
, 
 190  * A data structure of the following type is kept for each 
 191  * pie menu managed by this file: 
 194 typedef struct PieMenu 
{ 
 200     PieMenuEntry 
**entries
; 
 208      * Information used when displaying widget: 
 213     Tk_3DBorder activeBorder
; 
 214     int activeBorderWidth
; 
 215     XFontStruct 
*fontPtr
; 
 216     XFontStruct 
*titlefontPtr
; 
 223      * Information used to layout pie menu: 
 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 */ 
 239      * Miscellaneous information: 
 242     Tk_TimerToken popup_timer_token
; 
 244     PieMenuEntry 
*postedPie
; 
 247     int popup_delay
;                    /* Delay before popup */ 
 248     int shaped
;                         /* Use SHAPE extension */ 
 252  * Flag bits for menus: 
 254  * REDRAW_PENDING:              Non-zero means a DoWhenIdle handler 
 255  *                              has already been queued to redraw 
 257  * UPDATE_PENDING:              Non-zero means a DoWhenIdle handler 
 258  *                              has already been queued to update 
 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. 
 266 #define REDRAW_PENDING          1 
 267 #define UPDATE_PENDING          2 
 268 #define RESIZE_PENDING          4 
 269 #define POPUP_PENDING           8 
 272  * Configuration specs valid for the menu as a whole: 
 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
, 
 335  * Forward declarations for procedures defined later in this file: 
 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
, 
 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
, 
 357 static int      GetPieMenuIndex 
_ANSI_ARGS_((Tcl_Interp 
*interp
, 
 358                     PieMenu 
*menuPtr
, char *string
, int *indexPtr
)); 
 359 static void     PieMenuEventProc 
_ANSI_ARGS_((ClientData clientData
, 
 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
, 
 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 static void     LayoutPieMenu(PieMenu 
*menu
); 
 372 static void     UpdatePieMenuEntries(PieMenu 
*menuPtr
); 
 373 static int      CalcPieMenuItem(PieMenu 
*menu
, int x
, int y
); 
 377  *-------------------------------------------------------------- 
 381  *      This procedure is invoked to process the "piemenu" Tcl 
 382  *      command.  Read the code and write some user documentation for 
 383  *      details on what it does. 
 386  *      A standard Tcl result. 
 389  *      See the user documentation for "menu", which this was based on. 
 391  *-------------------------------------------------------------- 
 395 Tk_PieMenuCmd(clientData
, interp
, argc
, argv
) 
 396     ClientData clientData
;      /* Main window associated with 
 398     Tcl_Interp 
*interp
;         /* Current interpreter. */ 
 399     int argc
;                   /* Number of arguments. */ 
 400     char **argv
;                /* Argument strings. */ 
 402     Tk_Window tkwin 
= (Tk_Window
) clientData
; 
 404     register PieMenu 
*menuPtr
; 
 405     XSetWindowAttributes atts
; 
 408         Tcl_AppendResult(interp
, "wrong # args: should be \"", 
 409                 argv
[0], " pathName ?options?\"", (char *) NULL
); 
 414      * Create the new window.  Set override-redirect so the window 
 415      * manager won't add a border or argue about placement, and set 
 416      * save-under so that the window can pop up and down without a 
 420     new = Tk_CreateWindowFromPath(interp
, tkwin
, argv
[1], ""); 
 424     atts
.override_redirect 
= True
; 
 425     atts
.save_under 
= True
; 
 426     Tk_ChangeWindowAttributes(new, CWOverrideRedirect
|CWSaveUnder
, &atts
); 
 429      * Initialize the data structure for the menu. 
 432     menuPtr 
= (PieMenu 
*) ckalloc(sizeof(PieMenu
)); 
 433     menuPtr
->tkwin 
= new; 
 434     menuPtr
->interp 
= interp
; 
 435     menuPtr
->title 
= NULL
; 
 436     menuPtr
->titleLength 
= 0; 
 437     menuPtr
->preview 
= NULL
; 
 438     menuPtr
->entries 
= NULL
; 
 439     menuPtr
->numEntries 
= 0; 
 440     menuPtr
->active 
= -1; 
 441     menuPtr
->group 
= NULL
;  
 444     menuPtr
->border 
= NULL
; 
 445     menuPtr
->activeBorder 
= NULL
; 
 446     menuPtr
->fontPtr 
= NULL
; 
 447     menuPtr
->titlefontPtr 
= NULL
; 
 449     menuPtr
->textGC 
= None
; 
 450     menuPtr
->activeFg 
= NULL
; 
 451     menuPtr
->activeGC 
= None
; 
 454     menuPtr
->title_x 
= 0; 
 455     menuPtr
->title_y 
= 0; 
 456     menuPtr
->title_width 
= 0; 
 457     menuPtr
->title_height 
= 0; 
 458     menuPtr
->initial_angle 
= 0; 
 459     menuPtr
->inactive_radius 
= PIE_INACTIVE_RADIUS_NUM
; 
 460     menuPtr
->min_radius 
= PIE_MIN_RADIUS_NUM
; 
 461     menuPtr
->extra_radius 
= PIE_EXTRA_RADIUS_NUM
; 
 462     menuPtr
->fixed_radius 
= 0; 
 463     menuPtr
->label_radius 
= 0; 
 464     menuPtr
->center_x 
= 0; 
 465     menuPtr
->center_y 
= 0; 
 466     menuPtr
->segments 
= NULL
; 
 467     menuPtr
->cursor 
= None
; 
 468     menuPtr
->postedPie 
= NULL
; 
 472     menuPtr
->popup_delay 
= PIE_POPUP_DELAY_NUM
; 
 474     Tk_SetClass(new, "PieMenu"); 
 475     Tk_CreateEventHandler(menuPtr
->tkwin
, 
 476                           ExposureMask 
| StructureNotifyMask 
| 
 477                           ButtonPressMask 
| ButtonReleaseMask 
| 
 479                           PieMenuEventProc
, (ClientData
) menuPtr
); 
 480     Tcl_CreateCommand(interp
, Tk_PathName(menuPtr
->tkwin
), PieMenuWidgetCmd
, 
 481             (ClientData
) menuPtr
, (void (*)()) NULL
); 
 482     if (ConfigurePieMenu(interp
, menuPtr
, argc
-2, argv
+2, 0) != TCL_OK
) { 
 486     interp
->result 
= Tk_PathName(menuPtr
->tkwin
); 
 490     Tk_DestroyWindow(menuPtr
->tkwin
); 
 495  *-------------------------------------------------------------- 
 497  * PieMenuWidgetCmd -- 
 499  *      This procedure is invoked to process the Tcl command 
 500  *      that corresponds to a widget managed by this module. 
 501  *      See the user documentation for details on what it does. 
 504  *      A standard Tcl result. 
 507  *      See the user documentation. 
 509  *-------------------------------------------------------------- 
 513 PieMenuWidgetCmd(clientData
, interp
, argc
, argv
) 
 514     ClientData clientData
;      /* Information about menu widget. */ 
 515     Tcl_Interp 
*interp
;         /* Current interpreter. */ 
 516     int argc
;                   /* Number of arguments. */ 
 517     char **argv
;                /* Argument strings. */ 
 519     register PieMenu 
*menuPtr 
= (PieMenu 
*) clientData
; 
 520     register PieMenuEntry 
*mePtr
; 
 526         Tcl_AppendResult(interp
, "wrong # args: should be \"", 
 527                 argv
[0], " option ?arg arg ...?\"", (char *) NULL
); 
 530     Tk_Preserve((ClientData
) menuPtr
); 
 532     length 
= strlen(argv
[1]); 
 533     if ((c 
== 'a') && (strncmp(argv
[1], "activate", length
) == 0) 
 538             Tcl_AppendResult(interp
, "wrong # args: should be \"", 
 539                     argv
[0], " activate index\"", (char *) NULL
); 
 542         if (GetPieMenuIndex(interp
, menuPtr
, argv
[2], &index
) != TCL_OK
) { 
 545         if (menuPtr
->active 
== index
) { 
 548         result 
= ActivatePieMenuEntry(menuPtr
, index
, 1); 
 549         DeferPopupPieMenu(menuPtr
); 
 550     } else if ((c 
== 's') && (strncmp(argv
[1], "show", length
) == 0) 
 553             Tcl_AppendResult(interp
, "wrong # args: should be \"", 
 554                     argv
[0], " show\"", (char *) NULL
); 
 557         NowPopupPieMenu(menuPtr
); 
 558     } else if ((c 
== 'p') && (strncmp(argv
[1], "pending", length
) == 0) 
 561             Tcl_AppendResult(interp
, "wrong # args: should be \"", 
 562                     argv
[0], " pending\"", (char *) NULL
); 
 565         sprintf(interp
->result
, "%d", 
 566                 (menuPtr
->flags 
& POPUP_PENDING
) ? 1 : 0); 
 567     } else if ((c 
== 'd') && (strncmp(argv
[1], "defer", length
) == 0) 
 570             Tcl_AppendResult(interp
, "wrong # args: should be \"", 
 571                     argv
[0], " defer\"", (char *) NULL
); 
 574         DeferPopupPieMenu(menuPtr
); 
 575     } else if ((c 
== 'a') && (strncmp(argv
[1], "add", length
) == 0) 
 577         PieMenuEntry 
**newEntries
; 
 580             Tcl_AppendResult(interp
, "wrong # args: should be \"", 
 581                     argv
[0], " add type ?options?\"", (char *) NULL
); 
 586          * Figure out the type of the new entry. 
 590         length 
= strlen(argv
[2]); 
 591         if ((c 
== 'c') && (strncmp(argv
[2], "command", length
) == 0)) { 
 592             type 
= COMMAND_ENTRY
; 
 593         } else if ((c 
== 'p') && (strncmp(argv
[2], "piemenu", length
) == 0)) { 
 594             type 
= PIEMENU_ENTRY
; 
 596             Tcl_AppendResult(interp
, "bad menu entry type \"", 
 597                              argv
[2], "\":  must be command or piemenu", 
 603          * Add a new entry to the end of the menu's array of entries, 
 604          * and process options for it. 
 607         mePtr 
= (PieMenuEntry 
*) ckalloc(sizeof(PieMenuEntry
)); 
 608         newEntries 
= (PieMenuEntry 
**) ckalloc((unsigned) 
 609                 ((menuPtr
->numEntries
+1)*sizeof(PieMenuEntry 
*))); 
 610         if (menuPtr
->numEntries 
!= 0) { 
 611             memcpy((VOID 
*) newEntries
, (VOID 
*) menuPtr
->entries
, 
 612                     menuPtr
->numEntries
*sizeof(PieMenuEntry 
*)); 
 613             ckfree((char *) menuPtr
->entries
); 
 615         menuPtr
->entries 
= newEntries
; 
 616         menuPtr
->entries
[menuPtr
->numEntries
] = mePtr
; 
 617         menuPtr
->numEntries
++; 
 619         mePtr
->piemenuPtr 
= menuPtr
; 
 621         mePtr
->labelLength 
= 0; 
 622         mePtr
->bitmap 
= None
; 
 629         mePtr
->border 
= NULL
; 
 630         mePtr
->activeBorder 
= NULL
; 
 631         mePtr
->fontPtr 
= NULL
; 
 632         mePtr
->textGC 
= None
; 
 633         mePtr
->activeGC 
= None
; 
 638         mePtr
->subtend 
= 0.0; 
 641         mePtr
->command 
= NULL
; 
 642         mePtr
->preview 
= NULL
; 
 645         if (ConfigurePieMenuEntry(interp
, menuPtr
, mePtr
, 
 646                                   menuPtr
->numEntries
-1, 
 647                                   argc
-3, argv
+3, 0) != TCL_OK
) { 
 648             DestroyPieMenuEntry((ClientData
) mePtr
); 
 649             menuPtr
->numEntries
--; 
 652         if (!(menuPtr
->flags 
& RESIZE_PENDING
)) { 
 653             menuPtr
->flags 
|= RESIZE_PENDING
; 
 654             Tk_DoWhenIdle(ComputePieMenuGeometry
, (ClientData
) menuPtr
); 
 656     } else if ((c 
== 'c') && (strncmp(argv
[1], "configure", length
) == 0)) { 
 658             result 
= Tk_ConfigureInfo(interp
, menuPtr
->tkwin
, configSpecs
, 
 659                     (char *) menuPtr
, (char *) NULL
, 0); 
 660         } else if (argc 
== 3) { 
 661             result 
= Tk_ConfigureInfo(interp
, menuPtr
->tkwin
, configSpecs
, 
 662                     (char *) menuPtr
, argv
[2], 0); 
 664             result 
= ConfigurePieMenu(interp
, menuPtr
, argc
-2, argv
+2, 
 665                     TK_CONFIG_ARGV_ONLY
); 
 667     } else if ((c 
== 'd') && (strncmp(argv
[1], "delete", length
) == 0) 
 672             Tcl_AppendResult(interp
, "wrong # args: should be \"", 
 673                     argv
[0], " delete index\"", (char *) NULL
); 
 676         if (GetPieMenuIndex(interp
, menuPtr
, argv
[2], &index
) != TCL_OK
) { 
 682         Tk_EventuallyFree((ClientData
) menuPtr
->entries
[index
], 
 683                           DestroyPieMenuEntry
); 
 684         for (i 
= index
; i 
< menuPtr
->numEntries
-1; i
++) { 
 685             menuPtr
->entries
[i
] = menuPtr
->entries
[i
+1]; 
 687         menuPtr
->numEntries 
-= 1; 
 688         if (menuPtr
->active 
== index
) { 
 689             menuPtr
->active 
= -1; 
 690         } else if (menuPtr
->active 
> index
) { 
 691             menuPtr
->active 
-= 1; 
 693         if (!(menuPtr
->flags 
& RESIZE_PENDING
)) { 
 694             menuPtr
->flags 
|= RESIZE_PENDING
; 
 695             Tk_DoWhenIdle(ComputePieMenuGeometry
, (ClientData
) menuPtr
); 
 697     } else if ((c 
== 'e') && (length 
>= 3) 
 698             && (strncmp(argv
[1], "entryconfigure", length
) == 0)) { 
 702             Tcl_AppendResult(interp
, "wrong # args: should be \"", 
 703                     argv
[0], " entryconfigure index ?option value ...?\"", 
 707         if (GetPieMenuIndex(interp
, menuPtr
, argv
[2], &index
) != TCL_OK
) { 
 713         mePtr 
= menuPtr
->entries
[index
]; 
 714         Tk_Preserve((ClientData
) mePtr
); 
 716             result 
= Tk_ConfigureInfo(interp
, menuPtr
->tkwin
, entryConfigSpecs
, 
 717                     (char *) mePtr
, (char *) NULL
, 
 718                     COMMAND_MASK 
<< mePtr
->type
); 
 719         } else if (argc 
== 4) { 
 720             result 
= Tk_ConfigureInfo(interp
, menuPtr
->tkwin
, entryConfigSpecs
, 
 721                     (char *) mePtr
, argv
[3], COMMAND_MASK 
<< mePtr
->type
); 
 723             result 
= ConfigurePieMenuEntry(interp
, menuPtr
, mePtr
, index
, 
 725                                            TK_CONFIG_ARGV_ONLY 
| 
 726                                              COMMAND_MASK 
<< mePtr
->type
); 
 728         Tk_Release((ClientData
) mePtr
); 
 729     } else if ((c 
== 'i') && (strncmp(argv
[1], "index", length
) == 0) 
 734             Tcl_AppendResult(interp
, "wrong # args: should be \"", 
 735                     argv
[0], " index string\"", (char *) NULL
); 
 738         if (GetPieMenuIndex(interp
, menuPtr
, argv
[2], &index
) != TCL_OK
) { 
 742             interp
->result 
= "none"; 
 744             sprintf(interp
->result
, "%d", index
); 
 746     } else if ((c 
== 'i') && (strncmp(argv
[1], "invoke", length
) == 0) 
 751             Tcl_AppendResult(interp
, "wrong # args: should be \"", 
 752                     argv
[0], " invoke index\"", (char *) NULL
); 
 755         if (GetPieMenuIndex(interp
, menuPtr
, argv
[2], &index
) != TCL_OK
) { 
 761         mePtr 
= menuPtr
->entries
[index
]; 
 762         Tk_Preserve((ClientData
) mePtr
); 
 763         if (mePtr
->command 
!= NULL
) { 
 764             result 
= Tcl_GlobalEval(interp
, mePtr
->command
); 
 766         Tk_Release((ClientData
) mePtr
); 
 767     } else if ((c 
== 'p') && (strncmp(argv
[1], "post", length
) == 0)) { 
 770         int ix
, iy
, tmp
, err
; 
 774         if ((argc 
!= 4) && (argc 
!= 5)) { 
 775             Tcl_AppendResult(interp
, "wrong # args: should be \"", 
 776                     argv
[0], " post x y ?group?\"", (char *) NULL
); 
 779         if ((Tcl_GetInt(interp
, argv
[2], &x
) != TCL_OK
) 
 780                 || (Tcl_GetInt(interp
, argv
[3], &y
) != TCL_OK
)) { 
 784             group 
= Tk_GetUid(argv
[4]); 
 786             group 
= Tk_GetUid("default"); 
 790          * Adjust the position of the menu if necessary to keep it 
 794         x 
-= menuPtr
->center_x
; y 
-= menuPtr
->center_y
; 
 798         tmp 
= WidthOfScreen(Tk_Screen(menuPtr
->tkwin
)) 
 799                 - Tk_Width(menuPtr
->tkwin
); 
 806         tmp 
= HeightOfScreen(Tk_Screen(menuPtr
->tkwin
)) 
 807                 - Tk_Height(menuPtr
->tkwin
); 
 815         /* XXX: warp pointer by (x-ix, y-iy) upon popup */ 
 818         Tk_MakeWindowExist(menuPtr
->tkwin
); 
 819         XRaiseWindow(Tk_Display(menuPtr
->tkwin
), Tk_WindowId(menuPtr
->tkwin
)); 
 821         Tk_MoveWindow(menuPtr
->tkwin
, x
, y
); 
 822         menuPtr
->root_x 
= x 
+ menuPtr
->center_x
; 
 823         menuPtr
->root_y 
= y 
+ menuPtr
->center_y
; 
 825         if (Tk_IsMapped(menuPtr
->tkwin
)) { 
 826             if (group 
!= menuPtr
->group
) { 
 827                 Tk_UnshareEvents(menuPtr
->tkwin
, menuPtr
->group
); 
 828                 Tk_ShareEvents(menuPtr
->tkwin
, group
); 
 831             Tk_ShareEvents(menuPtr
->tkwin
, group
); 
 832             EventuallyPopupPieMenu(menuPtr
); 
 833             result 
= ActivatePieMenuEntry(menuPtr
, -1, 1); 
 835         menuPtr
->group 
= group
; 
 836     } else if ((c 
== 'u') && (strncmp(argv
[1], "unpost", length
) == 0)) { 
 838             Tcl_AppendResult(interp
, "wrong # args: should be \"", 
 839                     argv
[0], " unpost\"", (char *) NULL
); 
 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
); 
 849     } else if ((c 
== 'g') && (strncmp(argv
[1], "grab", length
) == 0)) { 
 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
); 
 862         XGrabPointer(Tk_Display(tkwin
), 
 865                      ButtonPressMask 
| ButtonReleaseMask 
| 
 866                      ButtonMotionMask 
| PointerMotionMask
, 
 867                      GrabModeAsync
, GrabModeAsync
, None
, None
, 
 868                      TkCurrentTime(((TkWindow 
*)tkwin
)->dispPtr
)); 
 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"; 
 881           sprintf(msg
, "grab failed for unknown reason (code %d)", 
 883           Tcl_AppendResult(interp
, msg
, (char *) NULL
); 
 887     } else if ((c 
== 'u') && (strncmp(argv
[1], "ungrab", length
) == 0)) { 
 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
); 
 898       XUngrabPointer(Tk_Display(tkwin
), 
 899                      TkCurrentTime(((TkWindow 
*)tkwin
)->dispPtr
)); 
 901     } else if ((c 
== 'd') && (strncmp(argv
[1], "distance", length
) == 0) 
 906             Tcl_AppendResult(interp
, "wrong # args: should be \"", 
 907                     argv
[0], " distance\"", (char *) NULL
); 
 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) 
 917             Tcl_AppendResult(interp
, "wrong # args: should be \"", 
 918                     argv
[0], " direction\"", (char *) NULL
); 
 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
); 
 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
); 
 932     Tk_Release((ClientData
) menuPtr
); 
 936     Tk_Release((ClientData
) menuPtr
); 
 941  *---------------------------------------------------------------------- 
 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). 
 953  *      Everything associated with the pie menu is freed up. 
 955  *---------------------------------------------------------------------- 
 959 DestroyPieMenu(clientData
) 
 960     ClientData clientData
;      /* Info about menu widget. */ 
 962     register PieMenu 
*menuPtr 
= (PieMenu 
*) clientData
; 
 965     /* Should we delete the event handler? */ 
 967     for (i 
= 0; i 
< menuPtr
->numEntries
; i
++) { 
 968         DestroyPieMenuEntry((ClientData
) menuPtr
->entries
[i
]); 
 970     if (menuPtr
->entries 
!= NULL
) { 
 971         ckfree((char *) menuPtr
->entries
); 
 973     if (menuPtr
->border 
!= NULL
) { 
 974         Tk_Free3DBorder(menuPtr
->border
); 
 976     if (menuPtr
->activeBorder 
!= NULL
) { 
 977         Tk_Free3DBorder(menuPtr
->activeBorder
); 
 979     if (menuPtr
->fontPtr 
!= NULL
) { 
 980         Tk_FreeFontStruct(menuPtr
->fontPtr
); 
 982     if (menuPtr
->fg 
!= NULL
) { 
 983         Tk_FreeColor(menuPtr
->fg
); 
 985     if (menuPtr
->textGC 
!= None
) { 
 986         Tk_FreeGC(menuPtr
->textGC
); 
 988     if (menuPtr
->activeFg 
!= NULL
) { 
 989         Tk_FreeColor(menuPtr
->activeFg
); 
 991     if (menuPtr
->activeGC 
!= None
) { 
 992         Tk_FreeGC(menuPtr
->activeGC
); 
 994     if (menuPtr
->cursor 
!= None
) { 
 995         Tk_FreeCursor(menuPtr
->cursor
); 
 997     ckfree((char *) menuPtr
); 
1001  *---------------------------------------------------------------------- 
1003  * DestroyPieMenuEntry -- 
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). 
1013  *      Everything associated with the pie menu entry is freed up. 
1015  *---------------------------------------------------------------------- 
1019 DestroyPieMenuEntry(clientData
) 
1020     ClientData clientData
;              /* Pointer to entry to be freed. */ 
1022     register PieMenuEntry 
*mePtr 
= (PieMenuEntry 
*) clientData
; 
1023     PieMenu 
*menuPtr 
= mePtr
->piemenuPtr
; 
1025     if (menuPtr
->postedPie 
== mePtr
) { 
1026         if (UnpostSubPieMenu(menuPtr
->interp
, menuPtr
) 
1028             TkBindError(menuPtr
->interp
); 
1031     if (mePtr
->label 
!= NULL
) { 
1032         ckfree(mePtr
->label
); 
1034     if (mePtr
->bitmap 
!= None
) { 
1035         Tk_FreePixmap(mePtr
->bitmap
); 
1037     if (mePtr
->border 
!= NULL
) { 
1038         Tk_Free3DBorder(mePtr
->border
); 
1040     if (mePtr
->activeBorder 
!= NULL
) { 
1041         Tk_Free3DBorder(mePtr
->activeBorder
); 
1043     if (mePtr
->fontPtr 
!= NULL
) { 
1044         Tk_FreeFontStruct(mePtr
->fontPtr
); 
1046     if (mePtr
->textGC 
!= NULL
) { 
1047         Tk_FreeGC(mePtr
->textGC
); 
1049     if (mePtr
->activeGC 
!= NULL
) { 
1050         Tk_FreeGC(mePtr
->activeGC
); 
1052     if (mePtr
->command 
!= NULL
) { 
1053         ckfree(mePtr
->command
); 
1055     if (mePtr
->name 
!= NULL
) { 
1056         ckfree(mePtr
->name
); 
1058     ckfree((char *) mePtr
); 
1062  *---------------------------------------------------------------------- 
1064  * ConfigurePieMenu -- 
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. 
1071  *      The return value is a standard Tcl result.  If TCL_ERROR is 
1072  *      returned, then interp->result contains an error message. 
1075  *      Configuration information, such as colors, font, etc. get set 
1076  *      for menuPtr;  old resources get freed, if there were any. 
1078  *---------------------------------------------------------------------- 
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. */ 
1094     if (Tk_ConfigureWidget(interp
, menuPtr
->tkwin
, configSpecs
, 
1095             argc
, argv
, (char *) menuPtr
, flags
) != TCL_OK
) { 
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. 
1105     if (menuPtr
->title 
== NULL
) { 
1106         menuPtr
->titleLength 
= 0; 
1108         menuPtr
->titleLength 
= strlen(menuPtr
->title
); 
1111     Tk_SetBackgroundFromBorder(menuPtr
->tkwin
, menuPtr
->border
); 
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
, 
1118     if (menuPtr
->textGC 
!= None
) { 
1119         Tk_FreeGC(menuPtr
->textGC
); 
1121     menuPtr
->textGC 
= newGC
; 
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
, 
1128     if (menuPtr
->activeGC 
!= None
) { 
1129         Tk_FreeGC(menuPtr
->activeGC
); 
1131     menuPtr
->activeGC 
= newGC
; 
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 
1140     for (i 
= 0; i 
< menuPtr
->numEntries
; i
++) { 
1141         PieMenuEntry 
*mePtr
; 
1143         mePtr 
= menuPtr
->entries
[i
]; 
1144         ConfigurePieMenuEntry(interp
, menuPtr
, mePtr
, i
, 0, (char **) NULL
, 
1145                 TK_CONFIG_ARGV_ONLY 
| COMMAND_MASK 
<< mePtr
->type
); 
1148     if (!(menuPtr
->flags 
& RESIZE_PENDING
)) { 
1149         menuPtr
->flags 
|= RESIZE_PENDING
; 
1150         Tk_DoWhenIdle(ComputePieMenuGeometry
, (ClientData
) menuPtr
); 
1157  *---------------------------------------------------------------------- 
1159  * ConfigurePieMenuEntry -- 
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. 
1166  *      The return value is a standard Tcl result.  If TCL_ERROR is 
1167  *      returned, then interp->result contains an error message. 
1170  *      Configuration information such as label and accelerator get 
1171  *      set for mePtr;  old resources get freed, if there were any. 
1173  *---------------------------------------------------------------------- 
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 
1183     int index
;                          /* Index of mePtr within menuPtr's 
1185     int argc
;                           /* Number of valid entries in argv. */ 
1186     char **argv
;                        /* Arguments. */ 
1187     int flags
;                          /* Additional flags to pass to 
1188                                          * Tk_ConfigureWidget. */ 
1191     GC newGC
, newActiveGC
; 
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 
1200     if (menuPtr
->postedPie 
== mePtr
) { 
1201         if (UnpostSubPieMenu(menuPtr
->interp
, menuPtr
) 
1203             TkBindError(menuPtr
->interp
); 
1207     if (Tk_ConfigureWidget(interp
, menuPtr
->tkwin
, entryConfigSpecs
, 
1208             argc
, argv
, (char *) mePtr
, 
1209             flags 
| (COMMAND_MASK 
<< mePtr
->type
)) != TCL_OK
) { 
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. 
1219     if (mePtr
->label 
== NULL
) { 
1220         mePtr
->labelLength 
= 0; 
1222         mePtr
->labelLength 
= strlen(mePtr
->label
); 
1225     if (index 
!= menuPtr
->active
) { 
1226         ActivatePieMenuEntry(menuPtr
, index
, 0); 
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
) 
1235         if (mePtr
->fontPtr 
!= NULL
) { 
1236           gcValues
.font 
= mePtr
->fontPtr
->fid
; 
1238           if (menuPtr
->titlefontPtr 
!= NULL
) 
1239             gcValues
.font 
= menuPtr
->titlefontPtr
->fid
; 
1241             gcValues
.font 
= menuPtr
->fontPtr
->fid
; 
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. 
1250         gcValues
.graphics_exposures 
= False
; 
1251         newGC 
= Tk_GetGC(menuPtr
->tkwin
, 
1252                 GCForeground
|GCBackground
|GCFont
|GCGraphicsExposures
, 
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
, 
1267     if (mePtr
->textGC 
!= NULL
) { 
1268             Tk_FreeGC(mePtr
->textGC
); 
1270     mePtr
->textGC 
= newGC
; 
1272     if (mePtr
->activeGC 
!= NULL
) { 
1273             Tk_FreeGC(mePtr
->activeGC
); 
1275     mePtr
->activeGC 
= newActiveGC
; 
1277     if (!(menuPtr
->flags 
& RESIZE_PENDING
)) { 
1278         menuPtr
->flags 
|= RESIZE_PENDING
; 
1279         Tk_DoWhenIdle(ComputePieMenuGeometry
, (ClientData
) menuPtr
); 
1285  *-------------------------------------------------------------- 
1287  * ComputePieMenuGeometry -- 
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 
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. 
1302  *-------------------------------------------------------------- 
1306 ComputePieMenuGeometry(clientData
) 
1307     ClientData clientData
;              /* Structure describing menu. */ 
1309     PieMenu 
*menuPtr 
= (PieMenu 
*) clientData
; 
1311     if (menuPtr
->tkwin 
== NULL
) { 
1315     LayoutPieMenu(menuPtr
); 
1317     if ((menuPtr
->width 
!= Tk_ReqWidth(menuPtr
->tkwin
)) || 
1318         (menuPtr
->height 
!= Tk_ReqHeight(menuPtr
->tkwin
))) { 
1319         Tk_GeometryRequest(menuPtr
->tkwin
, menuPtr
->width
, menuPtr
->height
); 
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. 
1328         EventuallyRedrawPieMenu(menuPtr
, -1); 
1331     menuPtr
->flags 
&= ~RESIZE_PENDING
; 
1335  *---------------------------------------------------------------------- 
1339  *      This procedure is invoked to display a pie menu widget. 
1345  *      Commands are output to X to display the pie menu in its 
1348  *---------------------------------------------------------------------- 
1352 DisplayPieMenu(clientData
) 
1353     ClientData clientData
;      /* Information about widget. */ 
1355     register PieMenu 
*menuPtr 
= (PieMenu 
*) clientData
; 
1356     register Tk_Window tkwin 
= menuPtr
->tkwin
; 
1357     XFontStruct 
*fontPtr
; 
1359     menuPtr
->flags 
&= ~REDRAW_PENDING
; 
1360     if ((menuPtr
->tkwin 
== NULL
) || !Tk_IsMapped(menuPtr
->tkwin
)) { 
1364     if (menuPtr
->titlefontPtr 
!= NULL
) { 
1365         fontPtr 
= menuPtr
->titlefontPtr
; 
1367         fontPtr 
= menuPtr
->fontPtr
; 
1370     if (menuPtr
->titleLength 
!= 0) { 
1371       Tk_Draw3DRectangle(Tk_Display(tkwin
), Tk_WindowId(tkwin
), 
1373                          menuPtr
->borderWidth
, menuPtr
->borderWidth
, 
1374                          Tk_Width(tkwin
) - 2*menuPtr
->borderWidth
, 
1375                          menuPtr
->title_height 
+ 2*menuPtr
->borderWidth
, 
1376                          menuPtr
->borderWidth
, TK_RELIEF_RAISED
); 
1378       TkDisplayChars(Tk_Display(tkwin
), Tk_WindowId(tkwin
), menuPtr
->textGC
, 
1379                      fontPtr
, menuPtr
->title
, menuPtr
->titleLength
, 
1380                      menuPtr
->title_x
, menuPtr
->title_y
, 
1381                      TK_NEWLINES_NOT_SPECIAL
); 
1384     if (menuPtr
->segments
) {  
1385       XSetLineAttributes(Tk_Display(tkwin
), menuPtr
->textGC
, 
1386                          0, LineSolid
, CapButt
, JoinMiter
); 
1387       XDrawSegments(Tk_Display(tkwin
), Tk_WindowId(tkwin
), 
1388                     menuPtr
->textGC
, menuPtr
->segments
, menuPtr
->numEntries
); 
1391     Tk_Draw3DRectangle(Tk_Display(tkwin
), Tk_WindowId(tkwin
), menuPtr
->border
, 
1392                        0, 0, Tk_Width(tkwin
), Tk_Height(tkwin
), 
1393                        menuPtr
->borderWidth
, TK_RELIEF_RAISED
); 
1395     UpdatePieMenuEntries(menuPtr
); 
1399  *---------------------------------------------------------------------- 
1403  *      This procedure is invoked to update a pie menu widget. 
1409  *      Commands are output to X to update the pie menu in its 
1412  *---------------------------------------------------------------------- 
1416 UpdatePieMenu(clientData
) 
1417     ClientData clientData
;      /* Information about widget. */ 
1419     register PieMenu 
*menuPtr 
= (PieMenu 
*) clientData
; 
1421     menuPtr
->flags 
&= ~UPDATE_PENDING
; 
1422     if ((menuPtr
->tkwin 
== NULL
) || !Tk_IsMapped(menuPtr
->tkwin
)) { 
1426     UpdatePieMenuEntries(menuPtr
); 
1431 UpdatePieMenuEntries(PieMenu 
*menuPtr
) 
1433     register PieMenuEntry 
*mePtr
; 
1434     register Tk_Window tkwin 
= menuPtr
->tkwin
; 
1435     XFontStruct 
*fontPtr
; 
1439     for (index 
= 0; index 
< menuPtr
->numEntries
; index
++) { 
1440         mePtr 
= menuPtr
->entries
[index
]; 
1441         if (!(mePtr
->flags 
& ENTRY_NEEDS_REDISPLAY
)) { 
1444         mePtr
->flags 
&= ~ENTRY_NEEDS_REDISPLAY
; 
1450         Tk_Fill3DRectangle(Tk_Display(tkwin
), Tk_WindowId(tkwin
), 
1451                            ((mePtr
->activeBorder 
!= NULL
) 
1452                               ? mePtr
->activeBorder
 
1453                               : menuPtr
->activeBorder
), 
1455                            mePtr
->width
, mePtr
->height
, 
1456                            menuPtr
->activeBorderWidth
, 
1457                            ((index 
== menuPtr
->active
) 
1459                               : ((HaveShape 
&& menuPtr
->shaped
) 
1461                                    : TK_RELIEF_FLAT
))); 
1465             gc 
= menuPtr
->textGC
; 
1469          * Draw label or bitmap for entry. 
1472         fontPtr 
= mePtr
->fontPtr
; 
1473         if (fontPtr 
== NULL
) { 
1474             fontPtr 
= menuPtr
->fontPtr
; 
1476         if (mePtr
->bitmap 
!= None
) { 
1477             unsigned int width
, height
; 
1479             Tk_SizeOfPixmap(mePtr
->bitmap
, &width
, &height
); 
1480             XCopyArea(Tk_Display(tkwin
), mePtr
->bitmap
, Tk_WindowId(tkwin
), 
1481                       gc
, 0, 0, width
, height
, 
1482                       mePtr
->label_x
, mePtr
->label_y
); 
1484             if (mePtr
->label 
!= NULL
) { 
1485                 TkDisplayChars(Tk_Display(tkwin
), Tk_WindowId(tkwin
), gc
, 
1486                                fontPtr
, mePtr
->label
, mePtr
->labelLength
, 
1487                                mePtr
->label_x
, mePtr
->label_y
, 
1488                                TK_NEWLINES_NOT_SPECIAL
); 
1495  *-------------------------------------------------------------- 
1497  * GetPieMenuIndex -- 
1499  *      Parse a textual index into a pie menu and return the numerical 
1500  *      index of the indicated entry. 
1503  *      A standard Tcl result.  If all went well, then *indexPtr is 
1504  *      filled in with the entry index corresponding to string 
1505  *      (ranges from -1 to the number of entries in the pie menu minus 
1506  *      one).  Otherwise an error message is left in interp->result. 
1511  *-------------------------------------------------------------- 
1515 GetPieMenuIndex(interp
, menuPtr
, string
, indexPtr
) 
1516     Tcl_Interp 
*interp
;         /* For error messages. */ 
1517     PieMenu 
*menuPtr
;           /* Menu for which the index is being 
1519     char *string
;               /* Specification of an entry in menu.  See 
1520                                  * manual entry for valid .*/ 
1521     int *indexPtr
;              /* Where to store converted relief. */ 
1525     if ((string
[0] == 'a') && (strcmp(string
, "active") == 0)) { 
1526         *indexPtr 
= menuPtr
->active
; 
1530     if ((string
[0] == 'l') && (strcmp(string
, "last") == 0)) { 
1531         *indexPtr 
= menuPtr
->numEntries
-1; 
1535     if ((string
[0] == 'n') && (strcmp(string
, "none") == 0)) { 
1540     if (string
[0] == '@') { 
1541         char xstr
[32], ystr
[32]; 
1544         if ((sscanf(&string
[1], "%31[^,],%31[^,]", xstr
, ystr
) == 2) && 
1545             (Tcl_GetInt(interp
, xstr
,  &x
) == TCL_OK
) && 
1546             (Tcl_GetInt(interp
, ystr
,  &y
) == TCL_OK
)) { 
1547             *indexPtr 
= CalcPieMenuItem(menuPtr
, x
, y
); 
1550             Tcl_SetResult(interp
, (char *) NULL
, TCL_STATIC
); 
1554     if (isdigit(string
[0])) { 
1555         if (Tcl_GetInt(interp
, string
,  &i
) == TCL_OK
) { 
1556             if ((i 
< menuPtr
->numEntries
) && (i 
>= 0)) { 
1561             Tcl_SetResult(interp
, (char *) NULL
, TCL_STATIC
); 
1565     for (i 
= 0; i 
< menuPtr
->numEntries
; i
++) { 
1568         label 
= menuPtr
->entries
[i
]->label
; 
1570                 && (Tcl_StringMatch(menuPtr
->entries
[i
]->label
, string
))) { 
1576     Tcl_AppendResult(interp
, "bad menu entry index \"", 
1577                      string
, "\"", (char *) NULL
); 
1582  *-------------------------------------------------------------- 
1584  * PieMenuEventProc -- 
1586  *      This procedure is invoked by the Tk dispatcher for various 
1587  *      events on pie menus. 
1593  *      When the window gets deleted, internal structures get 
1594  *      cleaned up.  When it gets exposed, it is redisplayed. 
1596  *-------------------------------------------------------------- 
1600 PieMenuEventProc(clientData
, eventPtr
) 
1601     ClientData clientData
;      /* Information about window. */ 
1602     XEvent 
*eventPtr
;           /* Information about event. */ 
1604     PieMenu 
*menuPtr 
= (PieMenu 
*) clientData
; 
1605     switch (eventPtr
->type
) { 
1607         if (eventPtr
->xexpose
.count 
== 0) { 
1608             EventuallyRedrawPieMenu(menuPtr
, -1); 
1612         Tcl_DeleteCommand(menuPtr
->interp
, Tk_PathName(menuPtr
->tkwin
)); 
1615          * Careful!  Must delete the event-sharing information here 
1616          * rather than in DestroyPieMenu.  By the time that procedure 
1617          * is called the tkwin may have been reused, resulting in some 
1618          * other window accidentally being cut off from shared events. 
1621         Tk_UnshareEvents(menuPtr
->tkwin
, menuPtr
->group
); 
1622         menuPtr
->tkwin 
= NULL
; 
1623         if (menuPtr
->flags 
& REDRAW_PENDING
) { 
1624             Tk_CancelIdleCall(DisplayPieMenu
, (ClientData
) menuPtr
); 
1626         if (menuPtr
->flags 
& UPDATE_PENDING
) { 
1627             Tk_CancelIdleCall(UpdatePieMenu
, (ClientData
) menuPtr
); 
1629         if (menuPtr
->flags 
& RESIZE_PENDING
) { 
1630             Tk_CancelIdleCall(ComputePieMenuGeometry
, (ClientData
) menuPtr
); 
1632         if (menuPtr
->flags 
& POPUP_PENDING
) { 
1633             Tk_CancelIdleCall(PopupPieMenu
, (ClientData
) menuPtr
); 
1635         Tk_EventuallyFree((ClientData
) menuPtr
, DestroyPieMenu
); 
1649  *---------------------------------------------------------------------- 
1651  * EventuallyRedrawPieMenu -- 
1653  *      Arrange for an entry of a pie menu, or the whole pie menu,  
1654  *      to be redisplayed at some point in the future. 
1660  *      A when-idle hander is scheduled to do the redisplay, if there 
1661  *      isn't one already scheduled. 
1663  *---------------------------------------------------------------------- 
1667 EventuallyRedrawPieMenu(menuPtr
, index
) 
1668     register PieMenu 
*menuPtr
;  /* Information about menu to redraw. */ 
1669     int index
;                  /* Which entry to redraw.  If -1, then 
1670                                  * all the entries in the menu are redrawn. */ 
1672     if (menuPtr
->tkwin 
== NULL
) { 
1676         menuPtr
->entries
[index
]->flags 
|= ENTRY_NEEDS_REDISPLAY
; 
1678         for (index 
= 0; index 
< menuPtr
->numEntries
; index
++) { 
1679             menuPtr
->entries
[index
]->flags 
|= ENTRY_NEEDS_REDISPLAY
; 
1683     if ((menuPtr
->tkwin 
== NULL
) || !Tk_IsMapped(menuPtr
->tkwin
) 
1684             || (menuPtr
->flags 
& REDRAW_PENDING
)) { 
1689         if (menuPtr
->flags 
& UPDATE_PENDING
) { 
1690             Tk_CancelIdleCall(UpdatePieMenu
, (ClientData
) menuPtr
); 
1692         Tk_DoWhenIdle(DisplayPieMenu
, (ClientData
) menuPtr
); 
1693         menuPtr
->flags 
|= REDRAW_PENDING
; 
1695         Tk_DoWhenIdle(UpdatePieMenu
, (ClientData
) menuPtr
); 
1696         menuPtr
->flags 
|= UPDATE_PENDING
; 
1702 PopupPieMenu(clientData
) 
1703     ClientData clientData
;      /* Information about widget. */ 
1705   register PieMenu 
*menuPtr 
= (PieMenu 
*) clientData
; 
1707   NeverPopupPieMenu(menuPtr
); 
1709   if (Tk_IsMapped(menuPtr
->tkwin
)) { 
1713   ShapePieMenu(menuPtr
); 
1714   Tk_MapWindow(menuPtr
->tkwin
); 
1719 NowPopupPieMenu(menuPtr
) 
1720     register PieMenu 
*menuPtr
; 
1722   PopupPieMenu((ClientData
)menuPtr
); 
1727 NeverPopupPieMenu(menuPtr
) 
1728     register PieMenu 
*menuPtr
; 
1730   if (menuPtr
->flags 
& POPUP_PENDING
) { 
1731     Tk_DeleteTimerHandler(menuPtr
->popup_timer_token
); 
1732     menuPtr
->popup_timer_token 
= 0; 
1733     menuPtr
->flags 
&= ~POPUP_PENDING
; 
1739 EventuallyPopupPieMenu(menuPtr
) 
1740     register PieMenu 
*menuPtr
; 
1742   NeverPopupPieMenu(menuPtr
); 
1744   if (Tk_IsMapped(menuPtr
->tkwin
)) { 
1748   menuPtr
->popup_timer_token 
= 
1749     Tk_CreateTimerHandler(menuPtr
->popup_delay
, 
1750                           PopupPieMenu
, (ClientData
) menuPtr
); 
1751   menuPtr
->flags 
|= POPUP_PENDING
; 
1756 DeferPopupPieMenu(menuPtr
) 
1757     register PieMenu 
*menuPtr
; 
1759   if (menuPtr
->flags 
& POPUP_PENDING
) { 
1760     EventuallyPopupPieMenu(menuPtr
); 
1767  *-------------------------------------------------------------- 
1769  * UnpostSubPieMenu -- 
1771  *      This procedure unposts any submenu. 
1774  *      A standard Tcl return result.  Errors may occur in the 
1775  *      Tcl commands generated to unpost submenus. 
1778  *      If there is already a submenu posted, it is unposted. 
1780  *-------------------------------------------------------------- 
1784 UnpostSubPieMenu(interp
, menuPtr
) 
1785     Tcl_Interp 
*interp
;         /* Used for invoking sub-commands and 
1786                                  * reporting errors. */ 
1787     register PieMenu 
*menuPtr
;  /* Information about menu as a whole. */ 
1791     if (menuPtr
->postedPie 
== NULL
) { 
1795     result 
= Tcl_VarEval(interp
, menuPtr
->postedPie
->name
, 
1796                          " unpost", (char *) NULL
); 
1797     menuPtr
->postedPie 
= NULL
; 
1803  *---------------------------------------------------------------------- 
1805  * ActivatePieMenuEntry -- 
1807  *      This procedure is invoked to make a particular pie menu  
1808  *      entry the active one, deactivating any other entry that  
1809  *      might currently be active. 
1812  *      The return value is a standard Tcl result (errors can occur 
1813  *      while posting and unposting submenus). 
1816  *      Pie menu entries get redisplayed, and the active entry  
1817  *      changes.  Submenus may get posted and unposted. 
1819  *---------------------------------------------------------------------- 
1823 ActivatePieMenuEntry(menuPtr
, index
, preview
) 
1824     register PieMenu 
*menuPtr
;          /* Menu in which to activate. */ 
1825     int index
;                          /* Index of entry to activate, or 
1826                                          * -1 to deactivate all entries. */ 
1827     int preview
;                        /* 1 to execute previewer */ 
1829     register PieMenuEntry 
*mePtr
; 
1830     int result 
= TCL_OK
; 
1832     if (menuPtr
->active 
>= 0) { 
1833         mePtr 
= menuPtr
->entries
[menuPtr
->active
]; 
1835         EventuallyRedrawPieMenu(menuPtr
, menuPtr
->active
); 
1837     menuPtr
->active 
= index
; 
1839         mePtr 
= menuPtr
->entries
[index
]; 
1840         EventuallyRedrawPieMenu(menuPtr
, index
); 
1842           Tk_Preserve((ClientData
) mePtr
); 
1843           if (mePtr
->preview 
!= NULL
) { 
1844               result 
= Tcl_GlobalEval(menuPtr
->interp
, mePtr
->preview
); 
1846           Tk_Release((ClientData
) mePtr
); 
1849 /* We're doing this in tcl these days, for finer control. */ 
1851       if (preview 
&& menuPtr
->preview
) { 
1852             result 
= Tcl_GlobalEval(menuPtr
->interp
, menuPtr
->preview
); 
1861  * This pie menu tracking code determines the slice the cursor  
1862  * is in by representing slice edge angles as (quadrant, slope)  
1863  * pairs that can be quickly computed and compared.  
1865  * The slope is defined such that it is greater than or equal to zero, 
1866  * less than infinity, and increasing counter-clockwise around the menu.  
1867  * Each of the four quadrants encompasses one range of slope. 
1872  *  x<=0, y>0 <--+       y/x 
1874  *        quad 1 | quad 0 |     X 
1875  * -----+--------+--------+---->  
1878  *   x<0, y<=0   +--> x>=0, y<0 
1882  * The quadrants and slopes of the item edges are all precalculated, 
1883  * during menu layout. 
1884  * The quadrant and slope of the cursor must be calculated frequently 
1885  * during menu tracking, so we just calculate the numerator and 
1886  * denominator of the slope, and avoid an unnecessary division. 
1887  * Instead of calculating "slope = numerator / denominator" then 
1888  * testing "slope < it->slope", every time the cursor moves, we can 
1889  * just test "numerator < (denominator * it->slope)". 
1891  * This algorithm works in a right-side-up coordinate space, but the final 
1892  * results are tranformed into X-windows's up-side-down coordinate system  
1893  * by subtracting the y values from the window height.  
1897 #define CALC_QUADRANT_SLOPE(x, y, quadrant, numerator, denominator) \ 
1898     if ((y) > 0) (quadrant) = ((x) > 0 ? 0 : 1); \ 
1899     else if ((y) < 0) (quadrant) = ((x) < 0 ? 2 : 3); \ 
1900     else (quadrant) = ((x) > 0 ? 0 : 2); \ 
1901     if ((quadrant) & 1) { \ 
1902         (numerator) = ABS((x)); (denominator) = ABS((y)); \ 
1904         (numerator) = ABS((y)); (denominator) = ABS((x)); \ 
1909 CalcPieMenuItem(PieMenu 
*menu
, int x
, int y
) 
1911   register PieMenuEntry 
*it
, *last_it
; 
1912   int i
, order 
= 0, quadrant
; 
1913   int numerator
, denominator
; 
1914   int first
, last_i
, last_order
; 
1917    * Translate x and y from root window coordinates so they are  
1918    * relative to the menu center, in right side up coordinates. 
1921   menu
->dx 
= x 
= (x 
- menu
->root_x
) + 1; 
1922   menu
->dy 
= y 
= (menu
->root_y 
- y
) - 1; 
1925    * If there are no menu items, 
1926    * or we are within the inactive region in the menu center, 
1927    * then there is no item selected. 
1929   if ((menu
->numEntries 
== 0) || 
1930       ((x 
* x
) + (y 
* y
) < 
1931        (menu
->inactive_radius 
* menu
->inactive_radius
))) { 
1936    * If there's only one item, then that must be it.  
1938   if (menu
->numEntries 
== 1) { 
1943    * Calculate the quadrant, slope numerator, and slope denominator of 
1944    * the cursor slope, to be used for comparisons. 
1946   CALC_QUADRANT_SLOPE(x
, y
, quadrant
, numerator
, denominator
); 
1949    * In most cases, during cursor tracking, the menu item that the 
1950    * cursor is over will be the same as it was before (almost all 
1951    * of the time), or one of the neighboring items (most of the 
1952    * rest of the time). So we check those items first. But to keep 
1953    * things simple, instead of actually checking the items in order of 
1954    * frequency (the current, the two neighbors, then the rest), we just 
1955    * start our loop around the menu items at the item *before* the 
1956    * last selected menu item, so we still check the three most common 
1957    * cases first (neighbor, current, neighbor, rest), without having  
1958    * to complicate the code with special cases. Another strategy, that 
1959    * might be good for menus with ridiculously many items, would be 
1960    * [to check the current item first, then the two neighbors, then] 
1961    * to do a binary search of the menu items (since they are ordered). 
1962    * But that's more complicated and you shouldn't have that many menu 
1967    * Start at the item before current one. 
1969   first 
= menu
->active 
- 1; 
1971     first 
= menu
->numEntries 
- 1; 
1974    * Initialize last_order such that we will go through the loop 
1975    * at least once, validating last_i, last_order, and last_it for 
1976    * the next time through the loop. 
1978   last_i 
= last_order 
= -1; 
1981   it 
= menu
->entries
[i
]; 
1985 /* Legend: c = cursor, e = edge 
1986    <cursor quad>,<edge quad> 
1992     /* Set order = 1, if shortest direction from edge to cursor is ccw */ 
1993     switch ((quadrant 
- it
->quadrant
) & 3) { 
1998                 --+--   --+--   --+--   --+--    
2001       /* slope >= it->slope */ 
2002       order 
= ((float)numerator 
>= (float)(denominator 
* it
->slope
)); 
2008                 --+--   --+--   --+--   --+--    
2017                 --+--   --+--   --+--   --+--    
2020       /* slope < it->slope */ 
2021       order 
= ((float)numerator 
< (float)(denominator 
* it
->slope
)); 
2027                 --+--   --+--   --+--   --+--    
2035      * If we were counter-clockwise of the last leading edge, 
2036      * and we're clockwise of this leading edge, 
2037      * then we were in the last menu item. 
2038      * (Note: first time through this loop last_order = -1 so we'll 
2039      * go back through the loop at least once, after validating 
2040      * last_order, last_i, and last_it.) 
2042     if ((last_order 
== 1) && (order 
== 0)) { 
2048      * Remember this menu item index, and move on to the next one 
2049      * counter-clockwise around the circle. 
2051     last_i 
= i
; last_it 
= it
; 
2052     if (++i 
>= menu
->numEntries
) { 
2055     it 
= menu
->entries
[i
]; 
2058      * If we've checked all the others, then that must have been it.  
2059      * This saves us from checking the leading edge of the first 
2060      * item again (It's also insurance against layout bugs.) 
2070 LayoutPieMenu(PieMenu 
*menu
) 
2073   int total_slice
, radius
; 
2074   int minx
, miny
, maxx
, maxy
; 
2076   PieMenuEntry 
*it
, *last
; 
2077   XFontStruct 
*font
, *titlefont
; 
2080    * Calculate the sum of the menu item slice sizes. 
2081    * Each menu item will get a (slice / total_slice) sized slice of the pie. 
2084   for (i 
= 0; i 
< menu
->numEntries
; i
++) { 
2085     total_slice 
+= menu
->entries
[i
]->slice
; 
2088   if ((titlefont 
= menu
->titlefontPtr
) == NULL
) 
2089     titlefont 
= menu
->fontPtr
; 
2092    * Calculate the subtend, angle, cosine, sine, quadrant, slope, 
2093    * and size of each menu item. 
2095   angle 
= DEG_TO_RAD(menu
->initial_angle
); 
2096   for (i 
= 0; i 
< menu
->numEntries
; i
++) { 
2097     register float edge_dx
, edge_dy
, numerator
, denominator
, twist
; 
2098     register int quadrant
; 
2100     it 
= menu
->entries
[i
]; 
2101     if ((font 
= it
->fontPtr
) == NULL
) 
2102       font 
= menu
->fontPtr
; 
2104     if (it
->bitmap 
!= None
) { 
2105       unsigned int bitmapWidth
, bitmapHeight
; 
2107       Tk_SizeOfPixmap(it
->bitmap
, &bitmapWidth
, &bitmapHeight
); 
2108       it
->height 
= bitmapHeight
; 
2109       it
->width 
= bitmapWidth
; 
2111       it
->height 
= font
->ascent 
+ font
->descent
; 
2112       if (it
->label 
!= NULL
) { 
2113         (void) TkMeasureChars(font
, it
->label
, 
2114                               it
->labelLength
, 0, (int) 100000, 
2115                               TK_NEWLINES_NOT_SPECIAL
, &it
->width
); 
2120     it
->height 
+= 2*menu
->activeBorderWidth 
+ 2; 
2121     it
->width 
+= 2*menu
->activeBorderWidth 
+ 2; 
2123     it
->subtend 
= TWO_PI 
* it
->slice 
/ total_slice
; 
2124     twist 
= it
->subtend 
/ 2.0; 
2125     if (i 
!= 0) angle 
+= twist
; 
2127     it
->dx 
= cos(angle
); 
2128     it
->dy 
= sin(angle
); 
2129     edge_dx 
= cos(angle 
- twist
); 
2130     edge_dy 
= sin(angle 
- twist
); 
2131     CALC_QUADRANT_SLOPE(edge_dx
, edge_dy
, quadrant
, numerator
, denominator
); 
2132     it
->quadrant 
= quadrant
; 
2133     it
->slope 
= (float)numerator 
/ (float)denominator
; 
2137   if ((radius 
= menu
->fixed_radius
) == 0) { 
2138     radius 
= menu
->min_radius
; 
2139     if (menu
->numEntries 
> 1) { 
2140       last 
= menu
->entries
[menu
->numEntries 
- 1]; 
2141       for (i 
= 0; i 
< menu
->numEntries
; i
++) { 
2142         float dx
, dy
, ldx
, ldy
; 
2143         int width
, height
, lwidth
, lheight
; 
2145         it 
= menu
->entries
[i
]; 
2147         dx 
= it
->dx
;  dy 
= it
->dy
; 
2148         width 
= it
->width
;  height 
= it
->height
; 
2149         ldx 
= last
->dx
;  ldy 
= last
->dy
; 
2150         lwidth 
= last
->width
;  lheight 
= last
->height
; 
2152           register int x
, y
, lx
, ly
,  
2153                        x0max
, y0max
, x1min
, y1min
; 
2155           x 
= dx 
* radius 
+ it
->x_offset
; 
2156           y 
= dy 
* radius 
+ it
->y_offset
; 
2157           lx 
= ldx 
* radius 
+ last
->x_offset
; 
2158           ly 
= ldy 
* radius 
+ last
->y_offset
; 
2160           /* Translate x y with respect to label size and position */ 
2181           /* Do rects (x y width height) and (lx ly lwidth lheight) overlap? */ 
2182           x0max 
= x 
> lx 
? x 
: lx
; 
2183           y0max 
= y 
> ly 
? y 
: ly
; 
2184           x1min 
= x
+width 
< lx
+lwidth 
? x
+width 
: lx
+lwidth
; 
2185           y1min 
= y
+height 
< ly
+lheight 
? y
+height 
: ly
+lheight
; 
2186           if (!((x0max 
< x1min
) && 
2187                 (y0max 
< y1min
))) { /* If they don't overlap */ 
2188             /* They are far enough out, so move on. */ 
2191           /* Push the menu radius out a step and try again */ 
2194         /* Loop on to next menu item */ 
2198     radius 
+= menu
->extra_radius
; 
2200   menu
->label_radius 
= radius
; 
2202   /* Finally position all the menu labels at the same radius. 
2203      Figure out the bounding box of the labels. */ 
2204   minx 
= miny 
= maxx 
= maxy 
= 0; 
2205   for (i 
= 0; i 
< menu
->numEntries
; i
++) { 
2206     it 
= menu
->entries
[i
]; 
2208     it
->x 
= radius 
* it
->dx 
+ it
->x_offset
; 
2209     it
->y 
= radius 
* it
->dy 
+ it
->y_offset
; 
2211     /* Translate x y with respect to label size and position */ 
2212     if (ABS(it
->x
) <= 2) { 
2213       it
->x 
-= it
->width
/2; 
2215         it
->y 
-= it
->height
; 
2219       it
->y 
-= it
->height
/2; 
2222     it
->label_x 
= it
->x 
+ menu
->activeBorderWidth 
+ 1; 
2223     it
->label_y 
= it
->y 
- menu
->activeBorderWidth 
- 1; 
2224     if (it
->bitmap 
== None
) { 
2225       it
->label_y 
-= (it
->fontPtr 
? it
->fontPtr 
: menu
->fontPtr
)->ascent
; 
2228     if (it
->x 
< minx
) minx 
= it
->x
; 
2229     if ((it
->x 
+ it
->width
) > maxx
) maxx 
= (it
->x 
+ it
->width
); 
2230     if (it
->y 
< miny
) miny 
= it
->y
; 
2231     if ((it
->y 
+ it
->height
) > maxy
) maxy 
= (it
->y 
+ it
->height
); 
2235   if (menu
->titleLength 
!= 0) { 
2236     menu
->title_height 
= titlefont
->ascent 
+ titlefont
->descent 
+ 2; 
2237     (void) TkMeasureChars(titlefont
, menu
->title
, 
2238                           menu
->titleLength
, 0, (int) 100000, 
2239                           TK_NEWLINES_NOT_SPECIAL
, &menu
->title_width
); 
2240     menu
->title_width 
+= 2; 
2241     if (-(menu
->title_width 
/ 2) < minx
) 
2242       minx 
= -(menu
->title_width 
/ 2); 
2243     if ((menu
->title_width 
/ 2) > maxx
) 
2244       maxx 
= (menu
->title_width 
/ 2); 
2245     maxy 
+= (2 * menu
->borderWidth
) + menu
->title_height
; 
2247     menu
->title_width 
= menu
->title_height 
= 0; 
2251   minx 
-= 2*menu
->borderWidth
;  miny 
-= 2*menu
->borderWidth
; 
2252   maxx 
+= 2*menu
->borderWidth
;  maxy 
+= 2*menu
->borderWidth
; 
2254   menu
->center_x 
= -minx
; 
2255   menu
->center_y 
= maxy
; /* y flip */ 
2256   menu
->width 
= maxx 
- minx
; 
2257   menu
->height 
= maxy 
- miny
; 
2259 /*  menu->title_x = (menu->width - menu->title_width) / 2 + 1; */ 
2260   menu
->title_x 
= menu
->center_x 
- menu
->title_width
/2 + 1; 
2261   menu
->title_y 
= 2*menu
->borderWidth 
+ titlefont
->ascent 
+ 1; 
2263   /* Translate the menu items to the center of the menu, in X coordinates. */ 
2264   for (i 
= 0; i 
< menu
->numEntries
; i
++) { 
2265     it 
= menu
->entries
[i
]; 
2266     it
->x 
= menu
->center_x 
+ it
->x
; 
2267     it
->y 
= (menu
->center_y 
- it
->y
) - it
->height
; /* y flip */ 
2268     it
->label_x 
= menu
->center_x 
+ it
->label_x
; 
2269     it
->label_y 
= (menu
->center_y 
- it
->label_y
) - it
->height
; /* y flip */ 
2272   if (menu
->segments 
!= NULL
) { 
2273       ckfree((char *)menu
->segments
); 
2275   menu
->segments 
= (XSegment 
*) 
2276     ckalloc(menu
->numEntries 
* sizeof(XSegment
)); 
2278   if (menu
->numEntries 
> 1) { 
2279     XSegment 
*seg 
= menu
->segments
; 
2281     angle 
= DEG_TO_RAD(menu
->initial_angle
) - 
2282             (menu
->entries
[0]->subtend 
/ 2.0); 
2283     for (i 
= 0; i 
< menu
->numEntries
; i
++) { 
2284       it 
= menu
->entries
[i
]; 
2285       seg
->x1 
= menu
->center_x 
+ (cos(angle
) * menu
->inactive_radius
); 
2286       seg
->y1 
= menu
->center_y 
- (sin(angle
) * menu
->inactive_radius
); 
2287       seg
->x2 
= menu
->center_x 
+ 
2288         (cos(angle
) * (menu
->label_radius 
- PIE_SPOKE_INSET
)); 
2289       seg
->y2 
= menu
->center_y 
- 
2290         (sin(angle
) * (menu
->label_radius 
- PIE_SPOKE_INSET
)); 
2292       angle 
+= it
->subtend
; 
2299 ShapePieMenu(menuPtr
) 
2312   if (menuPtr
->shaped 
== 0) { 
2316   dpy 
= Tk_Display(menuPtr
->tkwin
); 
2318   if (HaveShape 
== -1) { 
2320     if (XShapeQueryExtension(dpy
, &t1
, &t2
)) { 
2328   Tk_MakeWindowExist(menuPtr
->tkwin
); 
2329   win 
= Tk_WindowId(menuPtr
->tkwin
); 
2331   shape 
= XCreatePixmap(dpy
, RootWindowOfScreen(Tk_Screen(menuPtr
->tkwin
)), 
2332                         menuPtr
->width
, menuPtr
->height
, 1); 
2333   gc 
= XCreateGC(dpy
, shape
, 0, &values
); 
2336   XSetForeground(dpy
, gc
, 0); 
2337   XFillRectangle(dpy
, shape
, gc
, 0, 0, menuPtr
->width
, menuPtr
->height
); 
2339   XSetForeground(dpy
, gc
, 1); 
2340   if (menuPtr
->titleLength 
!= 0) { 
2341     int bw 
= menuPtr
->borderWidth
; 
2343     XFillRectangle(dpy
, shape
, gc
, bw
, bw
, menuPtr
->width 
- bw
*2, menuPtr
->title_height 
+ bw
*2); 
2346   for (i 
= 0; i 
< menuPtr
->numEntries
; i
++) { 
2347     it 
= menuPtr
->entries
[i
]; 
2348     XFillRectangle(dpy
, shape
, gc
, it
->x
, it
->y
, it
->width
, it
->height
); 
2352   XShapeCombineMask(dpy
, win
, ShapeBounding
, 0, 0, shape
, ShapeSet
);