|
|
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: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.