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
);