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 *--------------------------------------------------------------
396 ClientData clientData
, /* Main window associated with
398 Tcl_Interp
*interp
, /* Current interpreter. */
399 int argc
, /* Number of arguments. */
400 char **argv
/* Argument strings. */
403 Tk_Window tkwin
= (Tk_Window
) clientData
;
405 register PieMenu
*menuPtr
;
406 XSetWindowAttributes atts
;
409 Tcl_AppendResult(interp
, "wrong # args: should be \"",
410 argv
[0], " pathName ?options?\"", (char *) NULL
);
415 * Create the new window. Set override-redirect so the window
416 * manager won't add a border or argue about placement, and set
417 * save-under so that the window can pop up and down without a
421 new = Tk_CreateWindowFromPath(interp
, tkwin
, argv
[1], "");
425 atts
.override_redirect
= True
;
426 atts
.save_under
= True
;
427 Tk_ChangeWindowAttributes(new, CWOverrideRedirect
|CWSaveUnder
, &atts
);
430 * Initialize the data structure for the menu.
433 menuPtr
= (PieMenu
*) ckalloc(sizeof(PieMenu
));
434 menuPtr
->tkwin
= new;
435 menuPtr
->interp
= interp
;
436 menuPtr
->title
= NULL
;
437 menuPtr
->titleLength
= 0;
438 menuPtr
->preview
= NULL
;
439 menuPtr
->entries
= NULL
;
440 menuPtr
->numEntries
= 0;
441 menuPtr
->active
= -1;
442 menuPtr
->group
= NULL
;
445 menuPtr
->border
= NULL
;
446 menuPtr
->activeBorder
= NULL
;
447 menuPtr
->fontPtr
= NULL
;
448 menuPtr
->titlefontPtr
= NULL
;
450 menuPtr
->textGC
= None
;
451 menuPtr
->activeFg
= NULL
;
452 menuPtr
->activeGC
= None
;
455 menuPtr
->title_x
= 0;
456 menuPtr
->title_y
= 0;
457 menuPtr
->title_width
= 0;
458 menuPtr
->title_height
= 0;
459 menuPtr
->initial_angle
= 0;
460 menuPtr
->inactive_radius
= PIE_INACTIVE_RADIUS_NUM
;
461 menuPtr
->min_radius
= PIE_MIN_RADIUS_NUM
;
462 menuPtr
->extra_radius
= PIE_EXTRA_RADIUS_NUM
;
463 menuPtr
->fixed_radius
= 0;
464 menuPtr
->label_radius
= 0;
465 menuPtr
->center_x
= 0;
466 menuPtr
->center_y
= 0;
467 menuPtr
->segments
= NULL
;
468 menuPtr
->cursor
= None
;
469 menuPtr
->postedPie
= NULL
;
473 menuPtr
->popup_delay
= PIE_POPUP_DELAY_NUM
;
475 Tk_SetClass(new, "PieMenu");
476 Tk_CreateEventHandler(menuPtr
->tkwin
,
477 ExposureMask
| StructureNotifyMask
|
478 ButtonPressMask
| ButtonReleaseMask
|
480 PieMenuEventProc
, (ClientData
) menuPtr
);
481 Tcl_CreateCommand(interp
, Tk_PathName(menuPtr
->tkwin
), PieMenuWidgetCmd
,
482 (ClientData
) menuPtr
, (void (*)(int *)) NULL
);
483 if (ConfigurePieMenu(interp
, menuPtr
, argc
-2, argv
+2, 0) != TCL_OK
) {
487 interp
->result
= Tk_PathName(menuPtr
->tkwin
);
491 Tk_DestroyWindow(menuPtr
->tkwin
);
496 *--------------------------------------------------------------
498 * PieMenuWidgetCmd --
500 * This procedure is invoked to process the Tcl command
501 * that corresponds to a widget managed by this module.
502 * See the user documentation for details on what it does.
505 * A standard Tcl result.
508 * See the user documentation.
510 *--------------------------------------------------------------
515 ClientData clientData
, /* Information about menu widget. */
516 Tcl_Interp
*interp
, /* Current interpreter. */
517 int argc
, /* Number of arguments. */
518 char **argv
/* Argument strings. */
521 register PieMenu
*menuPtr
= (PieMenu
*) clientData
;
522 register PieMenuEntry
*mePtr
;
528 Tcl_AppendResult(interp
, "wrong # args: should be \"",
529 argv
[0], " option ?arg arg ...?\"", (char *) NULL
);
532 Tk_Preserve((ClientData
) menuPtr
);
534 length
= strlen(argv
[1]);
535 if ((c
== 'a') && (strncmp(argv
[1], "activate", length
) == 0)
540 Tcl_AppendResult(interp
, "wrong # args: should be \"",
541 argv
[0], " activate index\"", (char *) NULL
);
544 if (GetPieMenuIndex(interp
, menuPtr
, argv
[2], &index
) != TCL_OK
) {
547 if (menuPtr
->active
== index
) {
550 result
= ActivatePieMenuEntry(menuPtr
, index
, 1);
551 DeferPopupPieMenu(menuPtr
);
552 } else if ((c
== 's') && (strncmp(argv
[1], "show", length
) == 0)
555 Tcl_AppendResult(interp
, "wrong # args: should be \"",
556 argv
[0], " show\"", (char *) NULL
);
559 NowPopupPieMenu(menuPtr
);
560 } else if ((c
== 'p') && (strncmp(argv
[1], "pending", length
) == 0)
563 Tcl_AppendResult(interp
, "wrong # args: should be \"",
564 argv
[0], " pending\"", (char *) NULL
);
567 sprintf(interp
->result
, "%d",
568 (menuPtr
->flags
& POPUP_PENDING
) ? 1 : 0);
569 } else if ((c
== 'd') && (strncmp(argv
[1], "defer", length
) == 0)
572 Tcl_AppendResult(interp
, "wrong # args: should be \"",
573 argv
[0], " defer\"", (char *) NULL
);
576 DeferPopupPieMenu(menuPtr
);
577 } else if ((c
== 'a') && (strncmp(argv
[1], "add", length
) == 0)
579 PieMenuEntry
**newEntries
;
582 Tcl_AppendResult(interp
, "wrong # args: should be \"",
583 argv
[0], " add type ?options?\"", (char *) NULL
);
588 * Figure out the type of the new entry.
592 length
= strlen(argv
[2]);
593 if ((c
== 'c') && (strncmp(argv
[2], "command", length
) == 0)) {
594 type
= COMMAND_ENTRY
;
595 } else if ((c
== 'p') && (strncmp(argv
[2], "piemenu", length
) == 0)) {
596 type
= PIEMENU_ENTRY
;
598 Tcl_AppendResult(interp
, "bad menu entry type \"",
599 argv
[2], "\": must be command or piemenu",
605 * Add a new entry to the end of the menu's array of entries,
606 * and process options for it.
609 mePtr
= (PieMenuEntry
*) ckalloc(sizeof(PieMenuEntry
));
610 newEntries
= (PieMenuEntry
**) ckalloc((unsigned)
611 ((menuPtr
->numEntries
+1)*sizeof(PieMenuEntry
*)));
612 if (menuPtr
->numEntries
!= 0) {
613 memcpy((VOID
*) newEntries
, (VOID
*) menuPtr
->entries
,
614 menuPtr
->numEntries
*sizeof(PieMenuEntry
*));
615 ckfree((char *) menuPtr
->entries
);
617 menuPtr
->entries
= newEntries
;
618 menuPtr
->entries
[menuPtr
->numEntries
] = mePtr
;
619 menuPtr
->numEntries
++;
621 mePtr
->piemenuPtr
= menuPtr
;
623 mePtr
->labelLength
= 0;
624 mePtr
->bitmap
= None
;
631 mePtr
->border
= NULL
;
632 mePtr
->activeBorder
= NULL
;
633 mePtr
->fontPtr
= NULL
;
634 mePtr
->textGC
= None
;
635 mePtr
->activeGC
= None
;
640 mePtr
->subtend
= 0.0;
643 mePtr
->command
= NULL
;
644 mePtr
->preview
= NULL
;
647 if (ConfigurePieMenuEntry(interp
, menuPtr
, mePtr
,
648 menuPtr
->numEntries
-1,
649 argc
-3, argv
+3, 0) != TCL_OK
) {
650 DestroyPieMenuEntry((ClientData
) mePtr
);
651 menuPtr
->numEntries
--;
654 if (!(menuPtr
->flags
& RESIZE_PENDING
)) {
655 menuPtr
->flags
|= RESIZE_PENDING
;
656 Tk_DoWhenIdle(ComputePieMenuGeometry
, (ClientData
) menuPtr
);
658 } else if ((c
== 'c') && (strncmp(argv
[1], "configure", length
) == 0)) {
660 result
= Tk_ConfigureInfo(interp
, menuPtr
->tkwin
, configSpecs
,
661 (char *) menuPtr
, (char *) NULL
, 0);
662 } else if (argc
== 3) {
663 result
= Tk_ConfigureInfo(interp
, menuPtr
->tkwin
, configSpecs
,
664 (char *) menuPtr
, argv
[2], 0);
666 result
= ConfigurePieMenu(interp
, menuPtr
, argc
-2, argv
+2,
667 TK_CONFIG_ARGV_ONLY
);
669 } else if ((c
== 'd') && (strncmp(argv
[1], "delete", length
) == 0)
674 Tcl_AppendResult(interp
, "wrong # args: should be \"",
675 argv
[0], " delete index\"", (char *) NULL
);
678 if (GetPieMenuIndex(interp
, menuPtr
, argv
[2], &index
) != TCL_OK
) {
684 Tk_EventuallyFree((ClientData
) menuPtr
->entries
[index
],
685 DestroyPieMenuEntry
);
686 for (i
= index
; i
< menuPtr
->numEntries
-1; i
++) {
687 menuPtr
->entries
[i
] = menuPtr
->entries
[i
+1];
689 menuPtr
->numEntries
-= 1;
690 if (menuPtr
->active
== index
) {
691 menuPtr
->active
= -1;
692 } else if (menuPtr
->active
> index
) {
693 menuPtr
->active
-= 1;
695 if (!(menuPtr
->flags
& RESIZE_PENDING
)) {
696 menuPtr
->flags
|= RESIZE_PENDING
;
697 Tk_DoWhenIdle(ComputePieMenuGeometry
, (ClientData
) menuPtr
);
699 } else if ((c
== 'e') && (length
>= 3)
700 && (strncmp(argv
[1], "entryconfigure", length
) == 0)) {
704 Tcl_AppendResult(interp
, "wrong # args: should be \"",
705 argv
[0], " entryconfigure index ?option value ...?\"",
709 if (GetPieMenuIndex(interp
, menuPtr
, argv
[2], &index
) != TCL_OK
) {
715 mePtr
= menuPtr
->entries
[index
];
716 Tk_Preserve((ClientData
) mePtr
);
718 result
= Tk_ConfigureInfo(interp
, menuPtr
->tkwin
, entryConfigSpecs
,
719 (char *) mePtr
, (char *) NULL
,
720 COMMAND_MASK
<< mePtr
->type
);
721 } else if (argc
== 4) {
722 result
= Tk_ConfigureInfo(interp
, menuPtr
->tkwin
, entryConfigSpecs
,
723 (char *) mePtr
, argv
[3], COMMAND_MASK
<< mePtr
->type
);
725 result
= ConfigurePieMenuEntry(interp
, menuPtr
, mePtr
, index
,
727 TK_CONFIG_ARGV_ONLY
|
728 COMMAND_MASK
<< mePtr
->type
);
730 Tk_Release((ClientData
) mePtr
);
731 } else if ((c
== 'i') && (strncmp(argv
[1], "index", length
) == 0)
736 Tcl_AppendResult(interp
, "wrong # args: should be \"",
737 argv
[0], " index string\"", (char *) NULL
);
740 if (GetPieMenuIndex(interp
, menuPtr
, argv
[2], &index
) != TCL_OK
) {
744 interp
->result
= "none";
746 sprintf(interp
->result
, "%d", index
);
748 } else if ((c
== 'i') && (strncmp(argv
[1], "invoke", length
) == 0)
753 Tcl_AppendResult(interp
, "wrong # args: should be \"",
754 argv
[0], " invoke index\"", (char *) NULL
);
757 if (GetPieMenuIndex(interp
, menuPtr
, argv
[2], &index
) != TCL_OK
) {
763 mePtr
= menuPtr
->entries
[index
];
764 Tk_Preserve((ClientData
) mePtr
);
765 if (mePtr
->command
!= NULL
) {
766 result
= Tcl_GlobalEval(interp
, mePtr
->command
);
768 Tk_Release((ClientData
) mePtr
);
769 } else if ((c
== 'p') && (strncmp(argv
[1], "post", length
) == 0)) {
772 int ix
, iy
, tmp
, err
;
776 if ((argc
!= 4) && (argc
!= 5)) {
777 Tcl_AppendResult(interp
, "wrong # args: should be \"",
778 argv
[0], " post x y ?group?\"", (char *) NULL
);
781 if ((Tcl_GetInt(interp
, argv
[2], &x
) != TCL_OK
)
782 || (Tcl_GetInt(interp
, argv
[3], &y
) != TCL_OK
)) {
786 group
= Tk_GetUid(argv
[4]);
788 group
= Tk_GetUid("default");
792 * Adjust the position of the menu if necessary to keep it
796 x
-= menuPtr
->center_x
; y
-= menuPtr
->center_y
;
800 tmp
= WidthOfScreen(Tk_Screen(menuPtr
->tkwin
))
801 - Tk_Width(menuPtr
->tkwin
);
808 tmp
= HeightOfScreen(Tk_Screen(menuPtr
->tkwin
))
809 - Tk_Height(menuPtr
->tkwin
);
817 /* XXX: warp pointer by (x-ix, y-iy) upon popup */
820 Tk_MakeWindowExist(menuPtr
->tkwin
);
821 XRaiseWindow(Tk_Display(menuPtr
->tkwin
), Tk_WindowId(menuPtr
->tkwin
));
823 Tk_MoveWindow(menuPtr
->tkwin
, x
, y
);
824 menuPtr
->root_x
= x
+ menuPtr
->center_x
;
825 menuPtr
->root_y
= y
+ menuPtr
->center_y
;
827 if (Tk_IsMapped(menuPtr
->tkwin
)) {
828 if (group
!= menuPtr
->group
) {
829 Tk_UnshareEvents(menuPtr
->tkwin
, menuPtr
->group
);
830 Tk_ShareEvents(menuPtr
->tkwin
, group
);
833 Tk_ShareEvents(menuPtr
->tkwin
, group
);
834 EventuallyPopupPieMenu(menuPtr
);
835 result
= ActivatePieMenuEntry(menuPtr
, -1, 1);
837 menuPtr
->group
= group
;
838 } else if ((c
== 'u') && (strncmp(argv
[1], "unpost", length
) == 0)) {
840 Tcl_AppendResult(interp
, "wrong # args: should be \"",
841 argv
[0], " unpost\"", (char *) NULL
);
844 NeverPopupPieMenu(menuPtr
);
845 Tk_UnshareEvents(menuPtr
->tkwin
, menuPtr
->group
);
846 Tk_UnmapWindow(menuPtr
->tkwin
);
847 result
= ActivatePieMenuEntry(menuPtr
, -1, 0);
848 if (result
== TCL_OK
) {
849 result
= UnpostSubPieMenu(interp
, menuPtr
);
851 } else if ((c
== 'g') && (strncmp(argv
[1], "grab", length
) == 0)) {
856 ((tkwin
= Tk_NameToWindow(interp
, argv
[2],
857 menuPtr
->tkwin
)) == NULL
)) {
858 Tcl_AppendResult(interp
, "wrong # args: should be \"",
859 argv
[0], " grab window\"", (char *) NULL
);
864 XGrabPointer(Tk_Display(tkwin
),
867 ButtonPressMask
| ButtonReleaseMask
|
868 ButtonMotionMask
| PointerMotionMask
,
869 GrabModeAsync
, GrabModeAsync
, None
, None
,
870 TkCurrentTime(((TkWindow
*)tkwin
)->dispPtr
));
872 if (err
== GrabNotViewable
) {
873 interp
->result
= "grab failed: window not viewable";
874 } else if (err
== AlreadyGrabbed
) {
875 interp
->result
= "grab failed: another application has grab";
876 } else if (err
== GrabFrozen
) {
877 interp
->result
= "grab failed: keyboard or pointer frozen";
878 } else if (err
== GrabInvalidTime
) {
879 interp
->result
= "grab failed: invalid time";
883 sprintf(msg
, "grab failed for unknown reason (code %d)",
885 Tcl_AppendResult(interp
, msg
, (char *) NULL
);
889 } else if ((c
== 'u') && (strncmp(argv
[1], "ungrab", length
) == 0)) {
893 ((tkwin
= Tk_NameToWindow(interp
, argv
[2],
894 menuPtr
->tkwin
)) == NULL
)) {
895 Tcl_AppendResult(interp
, "wrong # args: should be \"",
896 argv
[0], " ungrab window\"", (char *) NULL
);
900 XUngrabPointer(Tk_Display(tkwin
),
901 TkCurrentTime(((TkWindow
*)tkwin
)->dispPtr
));
903 } else if ((c
== 'd') && (strncmp(argv
[1], "distance", length
) == 0)
908 Tcl_AppendResult(interp
, "wrong # args: should be \"",
909 argv
[0], " distance\"", (char *) NULL
);
912 distance
= (int)(sqrt((menuPtr
->dx
* menuPtr
->dx
) + (menuPtr
->dy
* menuPtr
->dy
)) + 0.499);
913 sprintf(interp
->result
, "%d", distance
);
914 } else if ((c
== 'd') && (strncmp(argv
[1], "direction", length
) == 0)
919 Tcl_AppendResult(interp
, "wrong # args: should be \"",
920 argv
[0], " direction\"", (char *) NULL
);
923 direction
= (int)(RAD_TO_DEG(atan2(menuPtr
->dy
, menuPtr
->dx
)) + 0.499);
924 if (direction
< 0) direction
+= 360;
925 sprintf(interp
->result
, "%d", direction
);
927 Tcl_AppendResult(interp
, "bad option \"", argv
[1],
928 "\": must be activate, show, add, configure, delete, ",
929 "entryconfigure, index, invoke, post, unpost, pending, ",
930 "defer, grab, or ungrab", (char *) NULL
);
934 Tk_Release((ClientData
) menuPtr
);
938 Tk_Release((ClientData
) menuPtr
);
943 *----------------------------------------------------------------------
947 * This procedure is invoked by Tk_EventuallyFree or Tk_Release
948 * to clean up the internal structure of a pie menu at a safe time
949 * (when no-one is using it anymore).
955 * Everything associated with the pie menu is freed up.
957 *----------------------------------------------------------------------
962 ClientData clientData
/* Info about menu widget. */
965 register PieMenu
*menuPtr
= (PieMenu
*) clientData
;
968 /* Should we delete the event handler? */
970 for (i
= 0; i
< menuPtr
->numEntries
; i
++) {
971 DestroyPieMenuEntry((ClientData
) menuPtr
->entries
[i
]);
973 if (menuPtr
->entries
!= NULL
) {
974 ckfree((char *) menuPtr
->entries
);
976 if (menuPtr
->border
!= NULL
) {
977 Tk_Free3DBorder(menuPtr
->border
);
979 if (menuPtr
->activeBorder
!= NULL
) {
980 Tk_Free3DBorder(menuPtr
->activeBorder
);
982 if (menuPtr
->fontPtr
!= NULL
) {
983 Tk_FreeFontStruct(menuPtr
->fontPtr
);
985 if (menuPtr
->fg
!= NULL
) {
986 Tk_FreeColor(menuPtr
->fg
);
988 if (menuPtr
->textGC
!= None
) {
989 Tk_FreeGC(menuPtr
->textGC
);
991 if (menuPtr
->activeFg
!= NULL
) {
992 Tk_FreeColor(menuPtr
->activeFg
);
994 if (menuPtr
->activeGC
!= None
) {
995 Tk_FreeGC(menuPtr
->activeGC
);
997 if (menuPtr
->cursor
!= None
) {
998 Tk_FreeCursor(menuPtr
->cursor
);
1000 ckfree((char *) menuPtr
);
1004 *----------------------------------------------------------------------
1006 * DestroyPieMenuEntry --
1008 * This procedure is invoked by Tk_EventuallyFree or Tk_Release
1009 * to clean up the internal structure of a pie menu entry at a safe
1010 * time (when no-one is using it anymore).
1016 * Everything associated with the pie menu entry is freed up.
1018 *----------------------------------------------------------------------
1022 DestroyPieMenuEntry (
1023 ClientData clientData
/* Pointer to entry to be freed. */
1026 register PieMenuEntry
*mePtr
= (PieMenuEntry
*) clientData
;
1027 PieMenu
*menuPtr
= mePtr
->piemenuPtr
;
1029 if (menuPtr
->postedPie
== mePtr
) {
1030 if (UnpostSubPieMenu(menuPtr
->interp
, menuPtr
)
1032 TkBindError(menuPtr
->interp
);
1035 if (mePtr
->label
!= NULL
) {
1036 ckfree(mePtr
->label
);
1038 if (mePtr
->bitmap
!= None
) {
1039 Tk_FreePixmap(mePtr
->bitmap
);
1041 if (mePtr
->border
!= NULL
) {
1042 Tk_Free3DBorder(mePtr
->border
);
1044 if (mePtr
->activeBorder
!= NULL
) {
1045 Tk_Free3DBorder(mePtr
->activeBorder
);
1047 if (mePtr
->fontPtr
!= NULL
) {
1048 Tk_FreeFontStruct(mePtr
->fontPtr
);
1050 if (mePtr
->textGC
!= NULL
) {
1051 Tk_FreeGC(mePtr
->textGC
);
1053 if (mePtr
->activeGC
!= NULL
) {
1054 Tk_FreeGC(mePtr
->activeGC
);
1056 if (mePtr
->command
!= NULL
) {
1057 ckfree(mePtr
->command
);
1059 if (mePtr
->name
!= NULL
) {
1060 ckfree(mePtr
->name
);
1062 ckfree((char *) mePtr
);
1066 *----------------------------------------------------------------------
1068 * ConfigurePieMenu --
1070 * This procedure is called to process an argv/argc list, plus
1071 * the Tk option database, in order to configure (or
1072 * reconfigure) a menu widget.
1075 * The return value is a standard Tcl result. If TCL_ERROR is
1076 * returned, then interp->result contains an error message.
1079 * Configuration information, such as colors, font, etc. get set
1080 * for menuPtr; old resources get freed, if there were any.
1082 *----------------------------------------------------------------------
1087 Tcl_Interp
*interp
, /* Used for error reporting. */
1088 register PieMenu
*menuPtr
, /* Information about widget; may or may
1089 * not already have values for some fields. */
1090 int argc
, /* Number of valid entries in argv. */
1091 char **argv
, /* Arguments. */
1092 int flags
/* Flags to pass to Tk_ConfigureWidget. */
1099 if (Tk_ConfigureWidget(interp
, menuPtr
->tkwin
, configSpecs
,
1100 argc
, argv
, (char *) menuPtr
, flags
) != TCL_OK
) {
1105 * A few options need special processing, such as setting the
1106 * background from a 3-D border, or filling in complicated
1107 * defaults that couldn't be specified to Tk_ConfigureWidget.
1110 if (menuPtr
->title
== NULL
) {
1111 menuPtr
->titleLength
= 0;
1113 menuPtr
->titleLength
= strlen(menuPtr
->title
);
1116 Tk_SetBackgroundFromBorder(menuPtr
->tkwin
, menuPtr
->border
);
1118 gcValues
.font
= menuPtr
->fontPtr
->fid
;
1119 gcValues
.foreground
= menuPtr
->fg
->pixel
;
1120 gcValues
.background
= Tk_3DBorderColor(menuPtr
->border
)->pixel
;
1121 newGC
= Tk_GetGC(menuPtr
->tkwin
, GCForeground
|GCBackground
|GCFont
,
1123 if (menuPtr
->textGC
!= None
) {
1124 Tk_FreeGC(menuPtr
->textGC
);
1126 menuPtr
->textGC
= newGC
;
1128 gcValues
.font
= menuPtr
->fontPtr
->fid
;
1129 gcValues
.foreground
= menuPtr
->activeFg
->pixel
;
1130 gcValues
.background
= Tk_3DBorderColor(menuPtr
->activeBorder
)->pixel
;
1131 newGC
= Tk_GetGC(menuPtr
->tkwin
, GCForeground
|GCBackground
|GCFont
,
1133 if (menuPtr
->activeGC
!= None
) {
1134 Tk_FreeGC(menuPtr
->activeGC
);
1136 menuPtr
->activeGC
= newGC
;
1139 * After reconfiguring a menu, we need to reconfigure all of the
1140 * entries in the menu, since some of the things in the children
1141 * (such as graphics contexts) may have to change to reflect changes
1145 for (i
= 0; i
< menuPtr
->numEntries
; i
++) {
1146 PieMenuEntry
*mePtr
;
1148 mePtr
= menuPtr
->entries
[i
];
1149 ConfigurePieMenuEntry(interp
, menuPtr
, mePtr
, i
, 0, (char **) NULL
,
1150 TK_CONFIG_ARGV_ONLY
| COMMAND_MASK
<< mePtr
->type
);
1153 if (!(menuPtr
->flags
& RESIZE_PENDING
)) {
1154 menuPtr
->flags
|= RESIZE_PENDING
;
1155 Tk_DoWhenIdle(ComputePieMenuGeometry
, (ClientData
) menuPtr
);
1162 *----------------------------------------------------------------------
1164 * ConfigurePieMenuEntry --
1166 * This procedure is called to process an argv/argc list, plus
1167 * the Tk option database, in order to configure (or
1168 * reconfigure) one entry in a menu.
1171 * The return value is a standard Tcl result. If TCL_ERROR is
1172 * returned, then interp->result contains an error message.
1175 * Configuration information such as label and accelerator get
1176 * set for mePtr; old resources get freed, if there were any.
1178 *----------------------------------------------------------------------
1182 ConfigurePieMenuEntry (
1183 Tcl_Interp
*interp
, /* Used for error reporting. */
1184 PieMenu
*menuPtr
, /* Information about whole menu. */
1185 register PieMenuEntry
*mePtr
, /* Information about menu entry; may
1186 * or may not already have values for
1188 int index
, /* Index of mePtr within menuPtr's
1190 int argc
, /* Number of valid entries in argv. */
1191 char **argv
, /* Arguments. */
1192 int flags
/* Additional flags to pass to
1193 * Tk_ConfigureWidget. */
1197 GC newGC
, newActiveGC
;
1200 * If this entry is a piemenu and the piemenu is posted, then unpost
1201 * it before reconfiguring the entry (otherwise the reconfigure might
1202 * change the name of the piemenu entry, leaving a posted menu
1206 if (menuPtr
->postedPie
== mePtr
) {
1207 if (UnpostSubPieMenu(menuPtr
->interp
, menuPtr
)
1209 TkBindError(menuPtr
->interp
);
1213 if (Tk_ConfigureWidget(interp
, menuPtr
->tkwin
, entryConfigSpecs
,
1214 argc
, argv
, (char *) mePtr
,
1215 flags
| (COMMAND_MASK
<< mePtr
->type
)) != TCL_OK
) {
1220 * The code below handles special configuration stuff not taken
1221 * care of by Tk_ConfigureWidget, such as special processing for
1222 * defaults, sizing strings, graphics contexts, etc.
1225 if (mePtr
->label
== NULL
) {
1226 mePtr
->labelLength
= 0;
1228 mePtr
->labelLength
= strlen(mePtr
->label
);
1231 if (index
!= menuPtr
->active
) {
1232 ActivatePieMenuEntry(menuPtr
, index
, 0);
1235 if ((mePtr
->fontPtr
!= NULL
) ||
1236 (mePtr
->type
== PIEMENU_ENTRY
)) {
1237 gcValues
.foreground
= menuPtr
->fg
->pixel
;
1238 gcValues
.background
= Tk_3DBorderColor(
1239 (mePtr
->border
!= NULL
) ? mePtr
->border
: menuPtr
->border
)
1241 if (mePtr
->fontPtr
!= NULL
) {
1242 gcValues
.font
= mePtr
->fontPtr
->fid
;
1244 if (menuPtr
->titlefontPtr
!= NULL
)
1245 gcValues
.font
= menuPtr
->titlefontPtr
->fid
;
1247 gcValues
.font
= menuPtr
->fontPtr
->fid
;
1251 * Note: disable GraphicsExpose events; we know there won't be
1252 * obscured areas when copying from an off-screen pixmap to the
1253 * screen and this gets rid of unnecessary events.
1256 gcValues
.graphics_exposures
= False
;
1257 newGC
= Tk_GetGC(menuPtr
->tkwin
,
1258 GCForeground
|GCBackground
|GCFont
|GCGraphicsExposures
,
1261 gcValues
.foreground
= menuPtr
->activeFg
->pixel
;
1262 gcValues
.background
= Tk_3DBorderColor(
1263 (mePtr
->activeBorder
!= NULL
) ? mePtr
->activeBorder
1264 : menuPtr
->activeBorder
)->pixel
;
1265 newActiveGC
= Tk_GetGC(menuPtr
->tkwin
,
1266 GCForeground
|GCBackground
|GCFont
|GCGraphicsExposures
,
1273 if (mePtr
->textGC
!= NULL
) {
1274 Tk_FreeGC(mePtr
->textGC
);
1276 mePtr
->textGC
= newGC
;
1278 if (mePtr
->activeGC
!= NULL
) {
1279 Tk_FreeGC(mePtr
->activeGC
);
1281 mePtr
->activeGC
= newActiveGC
;
1283 if (!(menuPtr
->flags
& RESIZE_PENDING
)) {
1284 menuPtr
->flags
|= RESIZE_PENDING
;
1285 Tk_DoWhenIdle(ComputePieMenuGeometry
, (ClientData
) menuPtr
);
1291 *--------------------------------------------------------------
1293 * ComputePieMenuGeometry --
1295 * This procedure is invoked to recompute the size and
1296 * layout of a menu. It is called as a when-idle handler so
1297 * that it only gets done once, even if a group of changes is
1304 * Fields of menu entries are changed to reflect their
1305 * current positions, and the size of the menu window
1306 * itself may be changed.
1308 *--------------------------------------------------------------
1312 ComputePieMenuGeometry (
1313 ClientData clientData
/* Structure describing menu. */
1316 PieMenu
*menuPtr
= (PieMenu
*) clientData
;
1318 if (menuPtr
->tkwin
== NULL
) {
1322 LayoutPieMenu(menuPtr
);
1324 if ((menuPtr
->width
!= Tk_ReqWidth(menuPtr
->tkwin
)) ||
1325 (menuPtr
->height
!= Tk_ReqHeight(menuPtr
->tkwin
))) {
1326 Tk_GeometryRequest(menuPtr
->tkwin
, menuPtr
->width
, menuPtr
->height
);
1329 * Must always force a redisplay here if the window is mapped
1330 * (even if the size didn't change, something else might have
1331 * changed in the menu, such as a label or accelerator). The
1332 * resize will force a redisplay above.
1335 EventuallyRedrawPieMenu(menuPtr
, -1);
1338 menuPtr
->flags
&= ~RESIZE_PENDING
;
1342 *----------------------------------------------------------------------
1346 * This procedure is invoked to display a pie menu widget.
1352 * Commands are output to X to display the pie menu in its
1355 *----------------------------------------------------------------------
1360 ClientData clientData
/* Information about widget. */
1363 register PieMenu
*menuPtr
= (PieMenu
*) clientData
;
1364 register Tk_Window tkwin
= menuPtr
->tkwin
;
1365 XFontStruct
*fontPtr
;
1367 menuPtr
->flags
&= ~REDRAW_PENDING
;
1368 if ((menuPtr
->tkwin
== NULL
) || !Tk_IsMapped(menuPtr
->tkwin
)) {
1372 if (menuPtr
->titlefontPtr
!= NULL
) {
1373 fontPtr
= menuPtr
->titlefontPtr
;
1375 fontPtr
= menuPtr
->fontPtr
;
1378 if (menuPtr
->titleLength
!= 0) {
1379 Tk_Draw3DRectangle(Tk_Display(tkwin
), Tk_WindowId(tkwin
),
1381 menuPtr
->borderWidth
, menuPtr
->borderWidth
,
1382 Tk_Width(tkwin
) - 2*menuPtr
->borderWidth
,
1383 menuPtr
->title_height
+ 2*menuPtr
->borderWidth
,
1384 menuPtr
->borderWidth
, TK_RELIEF_RAISED
);
1386 TkDisplayChars(Tk_Display(tkwin
), Tk_WindowId(tkwin
), menuPtr
->textGC
,
1387 fontPtr
, menuPtr
->title
, menuPtr
->titleLength
,
1388 menuPtr
->title_x
, menuPtr
->title_y
,
1389 TK_NEWLINES_NOT_SPECIAL
);
1392 if (menuPtr
->segments
) {
1393 XSetLineAttributes(Tk_Display(tkwin
), menuPtr
->textGC
,
1394 0, LineSolid
, CapButt
, JoinMiter
);
1395 XDrawSegments(Tk_Display(tkwin
), Tk_WindowId(tkwin
),
1396 menuPtr
->textGC
, menuPtr
->segments
, menuPtr
->numEntries
);
1399 Tk_Draw3DRectangle(Tk_Display(tkwin
), Tk_WindowId(tkwin
), menuPtr
->border
,
1400 0, 0, Tk_Width(tkwin
), Tk_Height(tkwin
),
1401 menuPtr
->borderWidth
, TK_RELIEF_RAISED
);
1403 UpdatePieMenuEntries(menuPtr
);
1407 *----------------------------------------------------------------------
1411 * This procedure is invoked to update a pie menu widget.
1417 * Commands are output to X to update the pie menu in its
1420 *----------------------------------------------------------------------
1425 ClientData clientData
/* Information about widget. */
1428 register PieMenu
*menuPtr
= (PieMenu
*) clientData
;
1430 menuPtr
->flags
&= ~UPDATE_PENDING
;
1431 if ((menuPtr
->tkwin
== NULL
) || !Tk_IsMapped(menuPtr
->tkwin
)) {
1435 UpdatePieMenuEntries(menuPtr
);
1440 UpdatePieMenuEntries(PieMenu
*menuPtr
)
1442 register PieMenuEntry
*mePtr
;
1443 register Tk_Window tkwin
= menuPtr
->tkwin
;
1444 XFontStruct
*fontPtr
;
1448 for (index
= 0; index
< menuPtr
->numEntries
; index
++) {
1449 mePtr
= menuPtr
->entries
[index
];
1450 if (!(mePtr
->flags
& ENTRY_NEEDS_REDISPLAY
)) {
1453 mePtr
->flags
&= ~ENTRY_NEEDS_REDISPLAY
;
1459 Tk_Fill3DRectangle(Tk_Display(tkwin
), Tk_WindowId(tkwin
),
1460 ((mePtr
->activeBorder
!= NULL
)
1461 ? mePtr
->activeBorder
1462 : menuPtr
->activeBorder
),
1464 mePtr
->width
, mePtr
->height
,
1465 menuPtr
->activeBorderWidth
,
1466 ((index
== menuPtr
->active
)
1468 : ((HaveShape
&& menuPtr
->shaped
)
1470 : TK_RELIEF_FLAT
)));
1474 gc
= menuPtr
->textGC
;
1478 * Draw label or bitmap for entry.
1481 fontPtr
= mePtr
->fontPtr
;
1482 if (fontPtr
== NULL
) {
1483 fontPtr
= menuPtr
->fontPtr
;
1485 if (mePtr
->bitmap
!= None
) {
1486 unsigned int width
, height
;
1488 Tk_SizeOfPixmap(mePtr
->bitmap
, &width
, &height
);
1489 XCopyArea(Tk_Display(tkwin
), mePtr
->bitmap
, Tk_WindowId(tkwin
),
1490 gc
, 0, 0, width
, height
,
1491 mePtr
->label_x
, mePtr
->label_y
);
1493 if (mePtr
->label
!= NULL
) {
1494 TkDisplayChars(Tk_Display(tkwin
), Tk_WindowId(tkwin
), gc
,
1495 fontPtr
, mePtr
->label
, mePtr
->labelLength
,
1496 mePtr
->label_x
, mePtr
->label_y
,
1497 TK_NEWLINES_NOT_SPECIAL
);
1504 *--------------------------------------------------------------
1506 * GetPieMenuIndex --
1508 * Parse a textual index into a pie menu and return the numerical
1509 * index of the indicated entry.
1512 * A standard Tcl result. If all went well, then *indexPtr is
1513 * filled in with the entry index corresponding to string
1514 * (ranges from -1 to the number of entries in the pie menu minus
1515 * one). Otherwise an error message is left in interp->result.
1520 *--------------------------------------------------------------
1525 Tcl_Interp
*interp
, /* For error messages. */
1526 PieMenu
*menuPtr
, /* Menu for which the index is being
1528 char *string
, /* Specification of an entry in menu. See
1529 * manual entry for valid .*/
1530 int *indexPtr
/* Where to store converted relief. */
1535 if ((string
[0] == 'a') && (strcmp(string
, "active") == 0)) {
1536 *indexPtr
= menuPtr
->active
;
1540 if ((string
[0] == 'l') && (strcmp(string
, "last") == 0)) {
1541 *indexPtr
= menuPtr
->numEntries
-1;
1545 if ((string
[0] == 'n') && (strcmp(string
, "none") == 0)) {
1550 if (string
[0] == '@') {
1551 char xstr
[32], ystr
[32];
1554 if ((sscanf(&string
[1], "%31[^,],%31[^,]", xstr
, ystr
) == 2) &&
1555 (Tcl_GetInt(interp
, xstr
, &x
) == TCL_OK
) &&
1556 (Tcl_GetInt(interp
, ystr
, &y
) == TCL_OK
)) {
1557 *indexPtr
= CalcPieMenuItem(menuPtr
, x
, y
);
1560 Tcl_SetResult(interp
, (char *) NULL
, TCL_STATIC
);
1564 if (isdigit(string
[0])) {
1565 if (Tcl_GetInt(interp
, string
, &i
) == TCL_OK
) {
1566 if ((i
< menuPtr
->numEntries
) && (i
>= 0)) {
1571 Tcl_SetResult(interp
, (char *) NULL
, TCL_STATIC
);
1575 for (i
= 0; i
< menuPtr
->numEntries
; i
++) {
1578 label
= menuPtr
->entries
[i
]->label
;
1580 && (Tcl_StringMatch(menuPtr
->entries
[i
]->label
, string
))) {
1586 Tcl_AppendResult(interp
, "bad menu entry index \"",
1587 string
, "\"", (char *) NULL
);
1592 *--------------------------------------------------------------
1594 * PieMenuEventProc --
1596 * This procedure is invoked by the Tk dispatcher for various
1597 * events on pie menus.
1603 * When the window gets deleted, internal structures get
1604 * cleaned up. When it gets exposed, it is redisplayed.
1606 *--------------------------------------------------------------
1611 ClientData clientData
, /* Information about window. */
1612 XEvent
*eventPtr
/* Information about event. */
1615 PieMenu
*menuPtr
= (PieMenu
*) clientData
;
1616 switch (eventPtr
->type
) {
1618 if (eventPtr
->xexpose
.count
== 0) {
1619 EventuallyRedrawPieMenu(menuPtr
, -1);
1623 Tcl_DeleteCommand(menuPtr
->interp
, Tk_PathName(menuPtr
->tkwin
));
1626 * Careful! Must delete the event-sharing information here
1627 * rather than in DestroyPieMenu. By the time that procedure
1628 * is called the tkwin may have been reused, resulting in some
1629 * other window accidentally being cut off from shared events.
1632 Tk_UnshareEvents(menuPtr
->tkwin
, menuPtr
->group
);
1633 menuPtr
->tkwin
= NULL
;
1634 if (menuPtr
->flags
& REDRAW_PENDING
) {
1635 Tk_CancelIdleCall(DisplayPieMenu
, (ClientData
) menuPtr
);
1637 if (menuPtr
->flags
& UPDATE_PENDING
) {
1638 Tk_CancelIdleCall(UpdatePieMenu
, (ClientData
) menuPtr
);
1640 if (menuPtr
->flags
& RESIZE_PENDING
) {
1641 Tk_CancelIdleCall(ComputePieMenuGeometry
, (ClientData
) menuPtr
);
1643 if (menuPtr
->flags
& POPUP_PENDING
) {
1644 Tk_CancelIdleCall(PopupPieMenu
, (ClientData
) menuPtr
);
1646 Tk_EventuallyFree((ClientData
) menuPtr
, DestroyPieMenu
);
1660 *----------------------------------------------------------------------
1662 * EventuallyRedrawPieMenu --
1664 * Arrange for an entry of a pie menu, or the whole pie menu,
1665 * to be redisplayed at some point in the future.
1671 * A when-idle hander is scheduled to do the redisplay, if there
1672 * isn't one already scheduled.
1674 *----------------------------------------------------------------------
1678 EventuallyRedrawPieMenu (
1679 register PieMenu
*menuPtr
, /* Information about menu to redraw. */
1680 int index
/* Which entry to redraw. If -1, then
1681 * all the entries in the menu are redrawn. */
1684 if (menuPtr
->tkwin
== NULL
) {
1688 menuPtr
->entries
[index
]->flags
|= ENTRY_NEEDS_REDISPLAY
;
1690 for (index
= 0; index
< menuPtr
->numEntries
; index
++) {
1691 menuPtr
->entries
[index
]->flags
|= ENTRY_NEEDS_REDISPLAY
;
1695 if ((menuPtr
->tkwin
== NULL
) || !Tk_IsMapped(menuPtr
->tkwin
)
1696 || (menuPtr
->flags
& REDRAW_PENDING
)) {
1701 if (menuPtr
->flags
& UPDATE_PENDING
) {
1702 Tk_CancelIdleCall(UpdatePieMenu
, (ClientData
) menuPtr
);
1704 Tk_DoWhenIdle(DisplayPieMenu
, (ClientData
) menuPtr
);
1705 menuPtr
->flags
|= REDRAW_PENDING
;
1707 Tk_DoWhenIdle(UpdatePieMenu
, (ClientData
) menuPtr
);
1708 menuPtr
->flags
|= UPDATE_PENDING
;
1715 ClientData clientData
/* Information about widget. */
1718 register PieMenu
*menuPtr
= (PieMenu
*) clientData
;
1720 NeverPopupPieMenu(menuPtr
);
1722 if (Tk_IsMapped(menuPtr
->tkwin
)) {
1726 ShapePieMenu(menuPtr
);
1727 Tk_MapWindow(menuPtr
->tkwin
);
1732 NowPopupPieMenu (register PieMenu
*menuPtr
)
1734 PopupPieMenu((ClientData
)menuPtr
);
1739 NeverPopupPieMenu (register PieMenu
*menuPtr
)
1741 if (menuPtr
->flags
& POPUP_PENDING
) {
1742 Tk_DeleteTimerHandler(menuPtr
->popup_timer_token
);
1743 menuPtr
->popup_timer_token
= 0;
1744 menuPtr
->flags
&= ~POPUP_PENDING
;
1750 EventuallyPopupPieMenu (register PieMenu
*menuPtr
)
1752 NeverPopupPieMenu(menuPtr
);
1754 if (Tk_IsMapped(menuPtr
->tkwin
)) {
1758 menuPtr
->popup_timer_token
=
1759 Tk_CreateTimerHandler(menuPtr
->popup_delay
,
1760 PopupPieMenu
, (ClientData
) menuPtr
);
1761 menuPtr
->flags
|= POPUP_PENDING
;
1766 DeferPopupPieMenu (register PieMenu
*menuPtr
)
1768 if (menuPtr
->flags
& POPUP_PENDING
) {
1769 EventuallyPopupPieMenu(menuPtr
);
1776 *--------------------------------------------------------------
1778 * UnpostSubPieMenu --
1780 * This procedure unposts any submenu.
1783 * A standard Tcl return result. Errors may occur in the
1784 * Tcl commands generated to unpost submenus.
1787 * If there is already a submenu posted, it is unposted.
1789 *--------------------------------------------------------------
1794 Tcl_Interp
*interp
, /* Used for invoking sub-commands and
1795 * reporting errors. */
1796 register PieMenu
*menuPtr
/* Information about menu as a whole. */
1801 if (menuPtr
->postedPie
== NULL
) {
1805 result
= Tcl_VarEval(interp
, menuPtr
->postedPie
->name
,
1806 " unpost", (char *) NULL
);
1807 menuPtr
->postedPie
= NULL
;
1813 *----------------------------------------------------------------------
1815 * ActivatePieMenuEntry --
1817 * This procedure is invoked to make a particular pie menu
1818 * entry the active one, deactivating any other entry that
1819 * might currently be active.
1822 * The return value is a standard Tcl result (errors can occur
1823 * while posting and unposting submenus).
1826 * Pie menu entries get redisplayed, and the active entry
1827 * changes. Submenus may get posted and unposted.
1829 *----------------------------------------------------------------------
1833 ActivatePieMenuEntry (
1834 register PieMenu
*menuPtr
, /* Menu in which to activate. */
1835 int index
, /* Index of entry to activate, or
1836 * -1 to deactivate all entries. */
1837 int preview
/* 1 to execute previewer */
1840 register PieMenuEntry
*mePtr
;
1841 int result
= TCL_OK
;
1843 if (menuPtr
->active
>= 0) {
1844 mePtr
= menuPtr
->entries
[menuPtr
->active
];
1846 EventuallyRedrawPieMenu(menuPtr
, menuPtr
->active
);
1848 menuPtr
->active
= index
;
1850 mePtr
= menuPtr
->entries
[index
];
1851 EventuallyRedrawPieMenu(menuPtr
, index
);
1853 Tk_Preserve((ClientData
) mePtr
);
1854 if (mePtr
->preview
!= NULL
) {
1855 result
= Tcl_GlobalEval(menuPtr
->interp
, mePtr
->preview
);
1857 Tk_Release((ClientData
) mePtr
);
1860 /* We're doing this in tcl these days, for finer control. */
1862 if (preview
&& menuPtr
->preview
) {
1863 result
= Tcl_GlobalEval(menuPtr
->interp
, menuPtr
->preview
);
1872 * This pie menu tracking code determines the slice the cursor
1873 * is in by representing slice edge angles as (quadrant, slope)
1874 * pairs that can be quickly computed and compared.
1876 * The slope is defined such that it is greater than or equal to zero,
1877 * less than infinity, and increasing counter-clockwise around the menu.
1878 * Each of the four quadrants encompasses one range of slope.
1883 * x<=0, y>0 <--+ y/x
1885 * quad 1 | quad 0 | X
1886 * -----+--------+--------+---->
1889 * x<0, y<=0 +--> x>=0, y<0
1893 * The quadrants and slopes of the item edges are all precalculated,
1894 * during menu layout.
1895 * The quadrant and slope of the cursor must be calculated frequently
1896 * during menu tracking, so we just calculate the numerator and
1897 * denominator of the slope, and avoid an unnecessary division.
1898 * Instead of calculating "slope = numerator / denominator" then
1899 * testing "slope < it->slope", every time the cursor moves, we can
1900 * just test "numerator < (denominator * it->slope)".
1902 * This algorithm works in a right-side-up coordinate space, but the final
1903 * results are tranformed into X-windows's up-side-down coordinate system
1904 * by subtracting the y values from the window height.
1908 #define CALC_QUADRANT_SLOPE(x, y, quadrant, numerator, denominator) \
1909 if ((y) > 0) (quadrant) = ((x) > 0 ? 0 : 1); \
1910 else if ((y) < 0) (quadrant) = ((x) < 0 ? 2 : 3); \
1911 else (quadrant) = ((x) > 0 ? 0 : 2); \
1912 if ((quadrant) & 1) { \
1913 (numerator) = ABS((x)); (denominator) = ABS((y)); \
1915 (numerator) = ABS((y)); (denominator) = ABS((x)); \
1920 CalcPieMenuItem(PieMenu
*menu
, int x
, int y
)
1922 register PieMenuEntry
*it
, *last_it
;
1923 int i
, order
= 0, quadrant
;
1924 int numerator
, denominator
;
1925 int first
, last_i
, last_order
;
1928 * Translate x and y from root window coordinates so they are
1929 * relative to the menu center, in right side up coordinates.
1932 menu
->dx
= x
= (x
- menu
->root_x
) + 1;
1933 menu
->dy
= y
= (menu
->root_y
- y
) - 1;
1936 * If there are no menu items,
1937 * or we are within the inactive region in the menu center,
1938 * then there is no item selected.
1940 if ((menu
->numEntries
== 0) ||
1941 ((x
* x
) + (y
* y
) <
1942 (menu
->inactive_radius
* menu
->inactive_radius
))) {
1947 * If there's only one item, then that must be it.
1949 if (menu
->numEntries
== 1) {
1954 * Calculate the quadrant, slope numerator, and slope denominator of
1955 * the cursor slope, to be used for comparisons.
1957 CALC_QUADRANT_SLOPE(x
, y
, quadrant
, numerator
, denominator
);
1960 * In most cases, during cursor tracking, the menu item that the
1961 * cursor is over will be the same as it was before (almost all
1962 * of the time), or one of the neighboring items (most of the
1963 * rest of the time). So we check those items first. But to keep
1964 * things simple, instead of actually checking the items in order of
1965 * frequency (the current, the two neighbors, then the rest), we just
1966 * start our loop around the menu items at the item *before* the
1967 * last selected menu item, so we still check the three most common
1968 * cases first (neighbor, current, neighbor, rest), without having
1969 * to complicate the code with special cases. Another strategy, that
1970 * might be good for menus with ridiculously many items, would be
1971 * [to check the current item first, then the two neighbors, then]
1972 * to do a binary search of the menu items (since they are ordered).
1973 * But that's more complicated and you shouldn't have that many menu
1978 * Start at the item before current one.
1980 first
= menu
->active
- 1;
1982 first
= menu
->numEntries
- 1;
1985 * Initialize last_order such that we will go through the loop
1986 * at least once, validating last_i, last_order, and last_it for
1987 * the next time through the loop.
1989 last_i
= last_order
= -1;
1992 it
= menu
->entries
[i
];
1996 /* Legend: c = cursor, e = edge
1997 <cursor quad>,<edge quad>
2003 /* Set order = 1, if shortest direction from edge to cursor is ccw */
2004 switch ((quadrant
- it
->quadrant
) & 3) {
2009 --+-- --+-- --+-- --+--
2012 /* slope >= it->slope */
2013 order
= ((float)numerator
>= (float)(denominator
* it
->slope
));
2019 --+-- --+-- --+-- --+--
2028 --+-- --+-- --+-- --+--
2031 /* slope < it->slope */
2032 order
= ((float)numerator
< (float)(denominator
* it
->slope
));
2038 --+-- --+-- --+-- --+--
2046 * If we were counter-clockwise of the last leading edge,
2047 * and we're clockwise of this leading edge,
2048 * then we were in the last menu item.
2049 * (Note: first time through this loop last_order = -1 so we'll
2050 * go back through the loop at least once, after validating
2051 * last_order, last_i, and last_it.)
2053 if ((last_order
== 1) && (order
== 0)) {
2059 * Remember this menu item index, and move on to the next one
2060 * counter-clockwise around the circle.
2062 last_i
= i
; last_it
= it
;
2063 if (++i
>= menu
->numEntries
) {
2066 it
= menu
->entries
[i
];
2069 * If we've checked all the others, then that must have been it.
2070 * This saves us from checking the leading edge of the first
2071 * item again (It's also insurance against layout bugs.)
2081 LayoutPieMenu(PieMenu
*menu
)
2084 int total_slice
, radius
;
2085 int minx
, miny
, maxx
, maxy
;
2087 PieMenuEntry
*it
, *last
;
2088 XFontStruct
*font
, *titlefont
;
2091 * Calculate the sum of the menu item slice sizes.
2092 * Each menu item will get a (slice / total_slice) sized slice of the pie.
2095 for (i
= 0; i
< menu
->numEntries
; i
++) {
2096 total_slice
+= menu
->entries
[i
]->slice
;
2099 if ((titlefont
= menu
->titlefontPtr
) == NULL
)
2100 titlefont
= menu
->fontPtr
;
2103 * Calculate the subtend, angle, cosine, sine, quadrant, slope,
2104 * and size of each menu item.
2106 angle
= DEG_TO_RAD(menu
->initial_angle
);
2107 for (i
= 0; i
< menu
->numEntries
; i
++) {
2108 register float edge_dx
, edge_dy
, numerator
, denominator
, twist
;
2109 register int quadrant
;
2111 it
= menu
->entries
[i
];
2112 if ((font
= it
->fontPtr
) == NULL
)
2113 font
= menu
->fontPtr
;
2115 if (it
->bitmap
!= None
) {
2116 unsigned int bitmapWidth
, bitmapHeight
;
2118 Tk_SizeOfPixmap(it
->bitmap
, &bitmapWidth
, &bitmapHeight
);
2119 it
->height
= bitmapHeight
;
2120 it
->width
= bitmapWidth
;
2122 it
->height
= font
->ascent
+ font
->descent
;
2123 if (it
->label
!= NULL
) {
2124 (void) TkMeasureChars(font
, it
->label
,
2125 it
->labelLength
, 0, (int) 100000,
2126 TK_NEWLINES_NOT_SPECIAL
, &it
->width
);
2131 it
->height
+= 2*menu
->activeBorderWidth
+ 2;
2132 it
->width
+= 2*menu
->activeBorderWidth
+ 2;
2134 it
->subtend
= TWO_PI
* it
->slice
/ total_slice
;
2135 twist
= it
->subtend
/ 2.0;
2136 if (i
!= 0) angle
+= twist
;
2138 it
->dx
= cos(angle
);
2139 it
->dy
= sin(angle
);
2140 edge_dx
= cos(angle
- twist
);
2141 edge_dy
= sin(angle
- twist
);
2142 CALC_QUADRANT_SLOPE(edge_dx
, edge_dy
, quadrant
, numerator
, denominator
);
2143 it
->quadrant
= quadrant
;
2144 it
->slope
= (float)numerator
/ (float)denominator
;
2148 if ((radius
= menu
->fixed_radius
) == 0) {
2149 radius
= menu
->min_radius
;
2150 if (menu
->numEntries
> 1) {
2151 last
= menu
->entries
[menu
->numEntries
- 1];
2152 for (i
= 0; i
< menu
->numEntries
; i
++) {
2153 float dx
, dy
, ldx
, ldy
;
2154 int width
, height
, lwidth
, lheight
;
2156 it
= menu
->entries
[i
];
2158 dx
= it
->dx
; dy
= it
->dy
;
2159 width
= it
->width
; height
= it
->height
;
2160 ldx
= last
->dx
; ldy
= last
->dy
;
2161 lwidth
= last
->width
; lheight
= last
->height
;
2163 register int x
, y
, lx
, ly
,
2164 x0max
, y0max
, x1min
, y1min
;
2166 x
= dx
* radius
+ it
->x_offset
;
2167 y
= dy
* radius
+ it
->y_offset
;
2168 lx
= ldx
* radius
+ last
->x_offset
;
2169 ly
= ldy
* radius
+ last
->y_offset
;
2171 /* Translate x y with respect to label size and position */
2192 /* Do rects (x y width height) and (lx ly lwidth lheight) overlap? */
2193 x0max
= x
> lx
? x
: lx
;
2194 y0max
= y
> ly
? y
: ly
;
2195 x1min
= x
+width
< lx
+lwidth
? x
+width
: lx
+lwidth
;
2196 y1min
= y
+height
< ly
+lheight
? y
+height
: ly
+lheight
;
2197 if (!((x0max
< x1min
) &&
2198 (y0max
< y1min
))) { /* If they don't overlap */
2199 /* They are far enough out, so move on. */
2202 /* Push the menu radius out a step and try again */
2205 /* Loop on to next menu item */
2209 radius
+= menu
->extra_radius
;
2211 menu
->label_radius
= radius
;
2213 /* Finally position all the menu labels at the same radius.
2214 Figure out the bounding box of the labels. */
2215 minx
= miny
= maxx
= maxy
= 0;
2216 for (i
= 0; i
< menu
->numEntries
; i
++) {
2217 it
= menu
->entries
[i
];
2219 it
->x
= radius
* it
->dx
+ it
->x_offset
;
2220 it
->y
= radius
* it
->dy
+ it
->y_offset
;
2222 /* Translate x y with respect to label size and position */
2223 if (ABS(it
->x
) <= 2) {
2224 it
->x
-= it
->width
/2;
2226 it
->y
-= it
->height
;
2230 it
->y
-= it
->height
/2;
2233 it
->label_x
= it
->x
+ menu
->activeBorderWidth
+ 1;
2234 it
->label_y
= it
->y
- menu
->activeBorderWidth
- 1;
2235 if (it
->bitmap
== None
) {
2236 it
->label_y
-= (it
->fontPtr
? it
->fontPtr
: menu
->fontPtr
)->ascent
;
2239 if (it
->x
< minx
) minx
= it
->x
;
2240 if ((it
->x
+ it
->width
) > maxx
) maxx
= (it
->x
+ it
->width
);
2241 if (it
->y
< miny
) miny
= it
->y
;
2242 if ((it
->y
+ it
->height
) > maxy
) maxy
= (it
->y
+ it
->height
);
2246 if (menu
->titleLength
!= 0) {
2247 menu
->title_height
= titlefont
->ascent
+ titlefont
->descent
+ 2;
2248 (void) TkMeasureChars(titlefont
, menu
->title
,
2249 menu
->titleLength
, 0, (int) 100000,
2250 TK_NEWLINES_NOT_SPECIAL
, &menu
->title_width
);
2251 menu
->title_width
+= 2;
2252 if (-(menu
->title_width
/ 2) < minx
)
2253 minx
= -(menu
->title_width
/ 2);
2254 if ((menu
->title_width
/ 2) > maxx
)
2255 maxx
= (menu
->title_width
/ 2);
2256 maxy
+= (2 * menu
->borderWidth
) + menu
->title_height
;
2258 menu
->title_width
= menu
->title_height
= 0;
2262 minx
-= 2*menu
->borderWidth
; miny
-= 2*menu
->borderWidth
;
2263 maxx
+= 2*menu
->borderWidth
; maxy
+= 2*menu
->borderWidth
;
2265 menu
->center_x
= -minx
;
2266 menu
->center_y
= maxy
; /* y flip */
2267 menu
->width
= maxx
- minx
;
2268 menu
->height
= maxy
- miny
;
2270 /* menu->title_x = (menu->width - menu->title_width) / 2 + 1; */
2271 menu
->title_x
= menu
->center_x
- menu
->title_width
/2 + 1;
2272 menu
->title_y
= 2*menu
->borderWidth
+ titlefont
->ascent
+ 1;
2274 /* Translate the menu items to the center of the menu, in X coordinates. */
2275 for (i
= 0; i
< menu
->numEntries
; i
++) {
2276 it
= menu
->entries
[i
];
2277 it
->x
= menu
->center_x
+ it
->x
;
2278 it
->y
= (menu
->center_y
- it
->y
) - it
->height
; /* y flip */
2279 it
->label_x
= menu
->center_x
+ it
->label_x
;
2280 it
->label_y
= (menu
->center_y
- it
->label_y
) - it
->height
; /* y flip */
2283 if (menu
->segments
!= NULL
) {
2284 ckfree((char *)menu
->segments
);
2286 menu
->segments
= (XSegment
*)
2287 ckalloc(menu
->numEntries
* sizeof(XSegment
));
2289 if (menu
->numEntries
> 1) {
2290 XSegment
*seg
= menu
->segments
;
2292 angle
= DEG_TO_RAD(menu
->initial_angle
) -
2293 (menu
->entries
[0]->subtend
/ 2.0);
2294 for (i
= 0; i
< menu
->numEntries
; i
++) {
2295 it
= menu
->entries
[i
];
2296 seg
->x1
= menu
->center_x
+ (cos(angle
) * menu
->inactive_radius
);
2297 seg
->y1
= menu
->center_y
- (sin(angle
) * menu
->inactive_radius
);
2298 seg
->x2
= menu
->center_x
+
2299 (cos(angle
) * (menu
->label_radius
- PIE_SPOKE_INSET
));
2300 seg
->y2
= menu
->center_y
-
2301 (sin(angle
) * (menu
->label_radius
- PIE_SPOKE_INSET
));
2303 angle
+= it
->subtend
;
2310 ShapePieMenu (PieMenu
*menuPtr
)
2322 if (menuPtr
->shaped
== 0) {
2326 dpy
= Tk_Display(menuPtr
->tkwin
);
2328 if (HaveShape
== -1) {
2330 if (XShapeQueryExtension(dpy
, &t1
, &t2
)) {
2338 Tk_MakeWindowExist(menuPtr
->tkwin
);
2339 win
= Tk_WindowId(menuPtr
->tkwin
);
2341 shape
= XCreatePixmap(dpy
, RootWindowOfScreen(Tk_Screen(menuPtr
->tkwin
)),
2342 menuPtr
->width
, menuPtr
->height
, 1);
2343 gc
= XCreateGC(dpy
, shape
, 0, &values
);
2346 XSetForeground(dpy
, gc
, 0);
2347 XFillRectangle(dpy
, shape
, gc
, 0, 0, menuPtr
->width
, menuPtr
->height
);
2349 XSetForeground(dpy
, gc
, 1);
2350 if (menuPtr
->titleLength
!= 0) {
2351 int bw
= menuPtr
->borderWidth
;
2353 XFillRectangle(dpy
, shape
, gc
, bw
, bw
, menuPtr
->width
- bw
*2, menuPtr
->title_height
+ bw
*2);
2356 for (i
= 0; i
< menuPtr
->numEntries
; i
++) {
2357 it
= menuPtr
->entries
[i
];
2358 XFillRectangle(dpy
, shape
, gc
, it
->x
, it
->y
, it
->width
, it
->height
);
2362 XShapeCombineMask(dpy
, win
, ShapeBounding
, 0, 0, shape
, ShapeSet
);