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
));
374 *--------------------------------------------------------------
378 * This procedure is invoked to process the "piemenu" Tcl
379 * command. Read the code and write some user documentation for
380 * details on what it does.
383 * A standard Tcl result.
386 * See the user documentation for "menu", which this was based on.
388 *--------------------------------------------------------------
392 Tk_PieMenuCmd(clientData
, interp
, argc
, argv
)
393 ClientData clientData
; /* Main window associated with
395 Tcl_Interp
*interp
; /* Current interpreter. */
396 int argc
; /* Number of arguments. */
397 char **argv
; /* Argument strings. */
399 Tk_Window tkwin
= (Tk_Window
) clientData
;
401 register PieMenu
*menuPtr
;
402 XSetWindowAttributes atts
;
405 Tcl_AppendResult(interp
, "wrong # args: should be \"",
406 argv
[0], " pathName ?options?\"", (char *) NULL
);
411 * Create the new window. Set override-redirect so the window
412 * manager won't add a border or argue about placement, and set
413 * save-under so that the window can pop up and down without a
417 new = Tk_CreateWindowFromPath(interp
, tkwin
, argv
[1], "");
421 atts
.override_redirect
= True
;
422 atts
.save_under
= True
;
423 Tk_ChangeWindowAttributes(new, CWOverrideRedirect
|CWSaveUnder
, &atts
);
426 * Initialize the data structure for the menu.
429 menuPtr
= (PieMenu
*) ckalloc(sizeof(PieMenu
));
430 menuPtr
->tkwin
= new;
431 menuPtr
->interp
= interp
;
432 menuPtr
->title
= NULL
;
433 menuPtr
->titleLength
= 0;
434 menuPtr
->preview
= NULL
;
435 menuPtr
->entries
= NULL
;
436 menuPtr
->numEntries
= 0;
437 menuPtr
->active
= -1;
438 menuPtr
->group
= NULL
;
441 menuPtr
->border
= NULL
;
442 menuPtr
->activeBorder
= NULL
;
443 menuPtr
->fontPtr
= NULL
;
444 menuPtr
->titlefontPtr
= NULL
;
446 menuPtr
->textGC
= None
;
447 menuPtr
->activeFg
= NULL
;
448 menuPtr
->activeGC
= None
;
451 menuPtr
->title_x
= 0;
452 menuPtr
->title_y
= 0;
453 menuPtr
->title_width
= 0;
454 menuPtr
->title_height
= 0;
455 menuPtr
->initial_angle
= 0;
456 menuPtr
->inactive_radius
= PIE_INACTIVE_RADIUS_NUM
;
457 menuPtr
->min_radius
= PIE_MIN_RADIUS_NUM
;
458 menuPtr
->extra_radius
= PIE_EXTRA_RADIUS_NUM
;
459 menuPtr
->fixed_radius
= 0;
460 menuPtr
->label_radius
= 0;
461 menuPtr
->center_x
= 0;
462 menuPtr
->center_y
= 0;
463 menuPtr
->segments
= NULL
;
464 menuPtr
->cursor
= None
;
465 menuPtr
->postedPie
= NULL
;
469 menuPtr
->popup_delay
= PIE_POPUP_DELAY_NUM
;
471 Tk_SetClass(new, "PieMenu");
472 Tk_CreateEventHandler(menuPtr
->tkwin
,
473 ExposureMask
| StructureNotifyMask
|
474 ButtonPressMask
| ButtonReleaseMask
|
476 PieMenuEventProc
, (ClientData
) menuPtr
);
477 Tcl_CreateCommand(interp
, Tk_PathName(menuPtr
->tkwin
), PieMenuWidgetCmd
,
478 (ClientData
) menuPtr
, (void (*)()) NULL
);
479 if (ConfigurePieMenu(interp
, menuPtr
, argc
-2, argv
+2, 0) != TCL_OK
) {
483 interp
->result
= Tk_PathName(menuPtr
->tkwin
);
487 Tk_DestroyWindow(menuPtr
->tkwin
);
492 *--------------------------------------------------------------
494 * PieMenuWidgetCmd --
496 * This procedure is invoked to process the Tcl command
497 * that corresponds to a widget managed by this module.
498 * See the user documentation for details on what it does.
501 * A standard Tcl result.
504 * See the user documentation.
506 *--------------------------------------------------------------
510 PieMenuWidgetCmd(clientData
, interp
, argc
, argv
)
511 ClientData clientData
; /* Information about menu widget. */
512 Tcl_Interp
*interp
; /* Current interpreter. */
513 int argc
; /* Number of arguments. */
514 char **argv
; /* Argument strings. */
516 register PieMenu
*menuPtr
= (PieMenu
*) clientData
;
517 register PieMenuEntry
*mePtr
;
523 Tcl_AppendResult(interp
, "wrong # args: should be \"",
524 argv
[0], " option ?arg arg ...?\"", (char *) NULL
);
527 Tk_Preserve((ClientData
) menuPtr
);
529 length
= strlen(argv
[1]);
530 if ((c
== 'a') && (strncmp(argv
[1], "activate", length
) == 0)
535 Tcl_AppendResult(interp
, "wrong # args: should be \"",
536 argv
[0], " activate index\"", (char *) NULL
);
539 if (GetPieMenuIndex(interp
, menuPtr
, argv
[2], &index
) != TCL_OK
) {
542 if (menuPtr
->active
== index
) {
545 result
= ActivatePieMenuEntry(menuPtr
, index
, 1);
546 DeferPopupPieMenu(menuPtr
);
547 } else if ((c
== 's') && (strncmp(argv
[1], "show", length
) == 0)
552 Tcl_AppendResult(interp
, "wrong # args: should be \"",
553 argv
[0], " show\"", (char *) NULL
);
556 NowPopupPieMenu(menuPtr
);
557 } else if ((c
== 'p') && (strncmp(argv
[1], "pending", length
) == 0)
562 Tcl_AppendResult(interp
, "wrong # args: should be \"",
563 argv
[0], " pending\"", (char *) NULL
);
566 sprintf(interp
->result
, "%d",
567 (menuPtr
->flags
& POPUP_PENDING
) ? 1 : 0);
568 } else if ((c
== 'd') && (strncmp(argv
[1], "defer", length
) == 0)
573 Tcl_AppendResult(interp
, "wrong # args: should be \"",
574 argv
[0], " defer\"", (char *) NULL
);
577 DeferPopupPieMenu(menuPtr
);
578 } else if ((c
== 'a') && (strncmp(argv
[1], "add", length
) == 0)
580 PieMenuEntry
**newEntries
;
583 Tcl_AppendResult(interp
, "wrong # args: should be \"",
584 argv
[0], " add type ?options?\"", (char *) NULL
);
589 * Figure out the type of the new entry.
593 length
= strlen(argv
[2]);
594 if ((c
== 'c') && (strncmp(argv
[2], "command", length
) == 0)) {
595 type
= COMMAND_ENTRY
;
596 } else if ((c
== 'p') && (strncmp(argv
[2], "piemenu", length
) == 0)) {
597 type
= PIEMENU_ENTRY
;
599 Tcl_AppendResult(interp
, "bad menu entry type \"",
600 argv
[2], "\": must be command or piemenu",
606 * Add a new entry to the end of the menu's array of entries,
607 * and process options for it.
610 mePtr
= (PieMenuEntry
*) ckalloc(sizeof(PieMenuEntry
));
611 newEntries
= (PieMenuEntry
**) ckalloc((unsigned)
612 ((menuPtr
->numEntries
+1)*sizeof(PieMenuEntry
*)));
613 if (menuPtr
->numEntries
!= 0) {
614 memcpy((VOID
*) newEntries
, (VOID
*) menuPtr
->entries
,
615 menuPtr
->numEntries
*sizeof(PieMenuEntry
*));
616 ckfree((char *) menuPtr
->entries
);
618 menuPtr
->entries
= newEntries
;
619 menuPtr
->entries
[menuPtr
->numEntries
] = mePtr
;
620 menuPtr
->numEntries
++;
622 mePtr
->piemenuPtr
= menuPtr
;
624 mePtr
->labelLength
= 0;
625 mePtr
->bitmap
= None
;
632 mePtr
->border
= NULL
;
633 mePtr
->activeBorder
= NULL
;
634 mePtr
->fontPtr
= NULL
;
635 mePtr
->textGC
= None
;
636 mePtr
->activeGC
= None
;
641 mePtr
->subtend
= 0.0;
644 mePtr
->command
= NULL
;
645 mePtr
->preview
= NULL
;
648 if (ConfigurePieMenuEntry(interp
, menuPtr
, mePtr
,
649 menuPtr
->numEntries
-1,
650 argc
-3, argv
+3, 0) != TCL_OK
) {
651 DestroyPieMenuEntry((ClientData
) mePtr
);
652 menuPtr
->numEntries
--;
655 if (!(menuPtr
->flags
& RESIZE_PENDING
)) {
656 menuPtr
->flags
|= RESIZE_PENDING
;
657 Tk_DoWhenIdle(ComputePieMenuGeometry
, (ClientData
) menuPtr
);
659 } else if ((c
== 'c') && (strncmp(argv
[1], "configure", length
) == 0)) {
661 result
= Tk_ConfigureInfo(interp
, menuPtr
->tkwin
, configSpecs
,
662 (char *) menuPtr
, (char *) NULL
, 0);
663 } else if (argc
== 3) {
664 result
= Tk_ConfigureInfo(interp
, menuPtr
->tkwin
, configSpecs
,
665 (char *) menuPtr
, argv
[2], 0);
667 result
= ConfigurePieMenu(interp
, menuPtr
, argc
-2, argv
+2,
668 TK_CONFIG_ARGV_ONLY
);
670 } else if ((c
== 'd') && (strncmp(argv
[1], "delete", length
) == 0)
675 Tcl_AppendResult(interp
, "wrong # args: should be \"",
676 argv
[0], " delete index\"", (char *) NULL
);
679 if (GetPieMenuIndex(interp
, menuPtr
, argv
[2], &index
) != TCL_OK
) {
685 Tk_EventuallyFree((ClientData
) menuPtr
->entries
[index
],
686 DestroyPieMenuEntry
);
687 for (i
= index
; i
< menuPtr
->numEntries
-1; i
++) {
688 menuPtr
->entries
[i
] = menuPtr
->entries
[i
+1];
690 menuPtr
->numEntries
-= 1;
691 if (menuPtr
->active
== index
) {
692 menuPtr
->active
= -1;
693 } else if (menuPtr
->active
> index
) {
694 menuPtr
->active
-= 1;
696 if (!(menuPtr
->flags
& RESIZE_PENDING
)) {
697 menuPtr
->flags
|= RESIZE_PENDING
;
698 Tk_DoWhenIdle(ComputePieMenuGeometry
, (ClientData
) menuPtr
);
700 } else if ((c
== 'e') && (length
>= 3)
701 && (strncmp(argv
[1], "entryconfigure", length
) == 0)) {
705 Tcl_AppendResult(interp
, "wrong # args: should be \"",
706 argv
[0], " entryconfigure index ?option value ...?\"",
710 if (GetPieMenuIndex(interp
, menuPtr
, argv
[2], &index
) != TCL_OK
) {
716 mePtr
= menuPtr
->entries
[index
];
717 Tk_Preserve((ClientData
) mePtr
);
719 result
= Tk_ConfigureInfo(interp
, menuPtr
->tkwin
, entryConfigSpecs
,
720 (char *) mePtr
, (char *) NULL
,
721 COMMAND_MASK
<< mePtr
->type
);
722 } else if (argc
== 4) {
723 result
= Tk_ConfigureInfo(interp
, menuPtr
->tkwin
, entryConfigSpecs
,
724 (char *) mePtr
, argv
[3], COMMAND_MASK
<< mePtr
->type
);
726 result
= ConfigurePieMenuEntry(interp
, menuPtr
, mePtr
, index
,
728 TK_CONFIG_ARGV_ONLY
|
729 COMMAND_MASK
<< mePtr
->type
);
731 Tk_Release((ClientData
) mePtr
);
732 } else if ((c
== 'i') && (strncmp(argv
[1], "index", length
) == 0)
737 Tcl_AppendResult(interp
, "wrong # args: should be \"",
738 argv
[0], " index string\"", (char *) NULL
);
741 if (GetPieMenuIndex(interp
, menuPtr
, argv
[2], &index
) != TCL_OK
) {
745 interp
->result
= "none";
747 sprintf(interp
->result
, "%d", index
);
749 } else if ((c
== 'i') && (strncmp(argv
[1], "invoke", length
) == 0)
754 Tcl_AppendResult(interp
, "wrong # args: should be \"",
755 argv
[0], " invoke index\"", (char *) NULL
);
758 if (GetPieMenuIndex(interp
, menuPtr
, argv
[2], &index
) != TCL_OK
) {
764 mePtr
= menuPtr
->entries
[index
];
765 Tk_Preserve((ClientData
) mePtr
);
766 if (mePtr
->command
!= NULL
) {
767 result
= Tcl_GlobalEval(interp
, mePtr
->command
);
769 Tk_Release((ClientData
) mePtr
);
770 } else if ((c
== 'p') && (strncmp(argv
[1], "post", length
) == 0)) {
771 int x
, y
, 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
;
1360 menuPtr
->flags
&= ~REDRAW_PENDING
;
1361 if ((menuPtr
->tkwin
== NULL
) || !Tk_IsMapped(menuPtr
->tkwin
)) {
1365 if (menuPtr
->titlefontPtr
!= NULL
) {
1366 fontPtr
= menuPtr
->titlefontPtr
;
1368 fontPtr
= menuPtr
->fontPtr
;
1371 if (menuPtr
->titleLength
!= 0) {
1372 Tk_Draw3DRectangle(Tk_Display(tkwin
), Tk_WindowId(tkwin
),
1374 menuPtr
->borderWidth
, menuPtr
->borderWidth
,
1375 Tk_Width(tkwin
) - 2*menuPtr
->borderWidth
,
1376 menuPtr
->title_height
+ 2*menuPtr
->borderWidth
,
1377 menuPtr
->borderWidth
, TK_RELIEF_RAISED
);
1379 TkDisplayChars(Tk_Display(tkwin
), Tk_WindowId(tkwin
), menuPtr
->textGC
,
1380 fontPtr
, menuPtr
->title
, menuPtr
->titleLength
,
1381 menuPtr
->title_x
, menuPtr
->title_y
,
1382 TK_NEWLINES_NOT_SPECIAL
);
1385 if (menuPtr
->segments
) {
1386 XSetLineAttributes(Tk_Display(tkwin
), menuPtr
->textGC
,
1387 0, LineSolid
, CapButt
, JoinMiter
);
1388 XDrawSegments(Tk_Display(tkwin
), Tk_WindowId(tkwin
),
1389 menuPtr
->textGC
, menuPtr
->segments
, menuPtr
->numEntries
);
1392 Tk_Draw3DRectangle(Tk_Display(tkwin
), Tk_WindowId(tkwin
), menuPtr
->border
,
1393 0, 0, Tk_Width(tkwin
), Tk_Height(tkwin
),
1394 menuPtr
->borderWidth
, TK_RELIEF_RAISED
);
1396 UpdatePieMenuEntries(menuPtr
);
1400 *----------------------------------------------------------------------
1404 * This procedure is invoked to update a pie menu widget.
1410 * Commands are output to X to update the pie menu in its
1413 *----------------------------------------------------------------------
1417 UpdatePieMenu(clientData
)
1418 ClientData clientData
; /* Information about widget. */
1420 register PieMenu
*menuPtr
= (PieMenu
*) clientData
;
1422 menuPtr
->flags
&= ~UPDATE_PENDING
;
1423 if ((menuPtr
->tkwin
== NULL
) || !Tk_IsMapped(menuPtr
->tkwin
)) {
1427 UpdatePieMenuEntries(menuPtr
);
1431 UpdatePieMenuEntries(menuPtr
)
1434 register PieMenuEntry
*mePtr
;
1435 register Tk_Window tkwin
= menuPtr
->tkwin
;
1436 XFontStruct
*fontPtr
;
1440 for (index
= 0; index
< menuPtr
->numEntries
; index
++) {
1441 mePtr
= menuPtr
->entries
[index
];
1442 if (!(mePtr
->flags
& ENTRY_NEEDS_REDISPLAY
)) {
1445 mePtr
->flags
&= ~ENTRY_NEEDS_REDISPLAY
;
1451 Tk_Fill3DRectangle(Tk_Display(tkwin
), Tk_WindowId(tkwin
),
1452 ((mePtr
->activeBorder
!= NULL
)
1453 ? mePtr
->activeBorder
1454 : menuPtr
->activeBorder
),
1456 mePtr
->width
, mePtr
->height
,
1457 menuPtr
->activeBorderWidth
,
1458 ((index
== menuPtr
->active
)
1460 : ((HaveShape
&& menuPtr
->shaped
)
1462 : TK_RELIEF_FLAT
)));
1466 gc
= menuPtr
->textGC
;
1470 * Draw label or bitmap for entry.
1473 fontPtr
= mePtr
->fontPtr
;
1474 if (fontPtr
== NULL
) {
1475 fontPtr
= menuPtr
->fontPtr
;
1477 if (mePtr
->bitmap
!= None
) {
1478 unsigned int width
, height
;
1480 Tk_SizeOfPixmap(mePtr
->bitmap
, &width
, &height
);
1481 XCopyArea(Tk_Display(tkwin
), mePtr
->bitmap
, Tk_WindowId(tkwin
),
1482 gc
, 0, 0, width
, height
,
1483 mePtr
->label_x
, mePtr
->label_y
);
1485 if (mePtr
->label
!= NULL
) {
1486 TkDisplayChars(Tk_Display(tkwin
), Tk_WindowId(tkwin
), gc
,
1487 fontPtr
, mePtr
->label
, mePtr
->labelLength
,
1488 mePtr
->label_x
, mePtr
->label_y
,
1489 TK_NEWLINES_NOT_SPECIAL
);
1496 *--------------------------------------------------------------
1498 * GetPieMenuIndex --
1500 * Parse a textual index into a pie menu and return the numerical
1501 * index of the indicated entry.
1504 * A standard Tcl result. If all went well, then *indexPtr is
1505 * filled in with the entry index corresponding to string
1506 * (ranges from -1 to the number of entries in the pie menu minus
1507 * one). Otherwise an error message is left in interp->result.
1512 *--------------------------------------------------------------
1516 GetPieMenuIndex(interp
, menuPtr
, string
, indexPtr
)
1517 Tcl_Interp
*interp
; /* For error messages. */
1518 PieMenu
*menuPtr
; /* Menu for which the index is being
1520 char *string
; /* Specification of an entry in menu. See
1521 * manual entry for valid .*/
1522 int *indexPtr
; /* Where to store converted relief. */
1526 if ((string
[0] == 'a') && (strcmp(string
, "active") == 0)) {
1527 *indexPtr
= menuPtr
->active
;
1531 if ((string
[0] == 'l') && (strcmp(string
, "last") == 0)) {
1532 *indexPtr
= menuPtr
->numEntries
-1;
1536 if ((string
[0] == 'n') && (strcmp(string
, "none") == 0)) {
1541 if (string
[0] == '@') {
1542 char xstr
[32], ystr
[32];
1545 if ((sscanf(&string
[1], "%31[^,],%31[^,]", xstr
, ystr
) == 2) &&
1546 (Tcl_GetInt(interp
, xstr
, &x
) == TCL_OK
) &&
1547 (Tcl_GetInt(interp
, ystr
, &y
) == TCL_OK
)) {
1548 *indexPtr
= CalcPieMenuItem(menuPtr
, x
, y
);
1551 Tcl_SetResult(interp
, (char *) NULL
, TCL_STATIC
);
1555 if (isdigit(string
[0])) {
1556 if (Tcl_GetInt(interp
, string
, &i
) == TCL_OK
) {
1557 if ((i
< menuPtr
->numEntries
) && (i
>= 0)) {
1562 Tcl_SetResult(interp
, (char *) NULL
, TCL_STATIC
);
1566 for (i
= 0; i
< menuPtr
->numEntries
; i
++) {
1569 label
= menuPtr
->entries
[i
]->label
;
1571 && (Tcl_StringMatch(menuPtr
->entries
[i
]->label
, string
))) {
1577 Tcl_AppendResult(interp
, "bad menu entry index \"",
1578 string
, "\"", (char *) NULL
);
1583 *--------------------------------------------------------------
1585 * PieMenuEventProc --
1587 * This procedure is invoked by the Tk dispatcher for various
1588 * events on pie menus.
1594 * When the window gets deleted, internal structures get
1595 * cleaned up. When it gets exposed, it is redisplayed.
1597 *--------------------------------------------------------------
1601 PieMenuEventProc(clientData
, eventPtr
)
1602 ClientData clientData
; /* Information about window. */
1603 XEvent
*eventPtr
; /* Information about event. */
1605 PieMenu
*menuPtr
= (PieMenu
*) clientData
;
1606 switch (eventPtr
->type
) {
1608 if (eventPtr
->xexpose
.count
== 0) {
1609 EventuallyRedrawPieMenu(menuPtr
, -1);
1613 Tcl_DeleteCommand(menuPtr
->interp
, Tk_PathName(menuPtr
->tkwin
));
1616 * Careful! Must delete the event-sharing information here
1617 * rather than in DestroyPieMenu. By the time that procedure
1618 * is called the tkwin may have been reused, resulting in some
1619 * other window accidentally being cut off from shared events.
1622 Tk_UnshareEvents(menuPtr
->tkwin
, menuPtr
->group
);
1623 menuPtr
->tkwin
= NULL
;
1624 if (menuPtr
->flags
& REDRAW_PENDING
) {
1625 Tk_CancelIdleCall(DisplayPieMenu
, (ClientData
) menuPtr
);
1627 if (menuPtr
->flags
& UPDATE_PENDING
) {
1628 Tk_CancelIdleCall(UpdatePieMenu
, (ClientData
) menuPtr
);
1630 if (menuPtr
->flags
& RESIZE_PENDING
) {
1631 Tk_CancelIdleCall(ComputePieMenuGeometry
, (ClientData
) menuPtr
);
1633 if (menuPtr
->flags
& POPUP_PENDING
) {
1634 Tk_CancelIdleCall(PopupPieMenu
, (ClientData
) menuPtr
);
1636 Tk_EventuallyFree((ClientData
) menuPtr
, DestroyPieMenu
);
1650 *----------------------------------------------------------------------
1652 * EventuallyRedrawPieMenu --
1654 * Arrange for an entry of a pie menu, or the whole pie menu,
1655 * to be redisplayed at some point in the future.
1661 * A when-idle hander is scheduled to do the redisplay, if there
1662 * isn't one already scheduled.
1664 *----------------------------------------------------------------------
1668 EventuallyRedrawPieMenu(menuPtr
, index
)
1669 register PieMenu
*menuPtr
; /* Information about menu to redraw. */
1670 int index
; /* Which entry to redraw. If -1, then
1671 * all the entries in the menu are redrawn. */
1673 if (menuPtr
->tkwin
== NULL
) {
1677 menuPtr
->entries
[index
]->flags
|= ENTRY_NEEDS_REDISPLAY
;
1679 for (index
= 0; index
< menuPtr
->numEntries
; index
++) {
1680 menuPtr
->entries
[index
]->flags
|= ENTRY_NEEDS_REDISPLAY
;
1684 if ((menuPtr
->tkwin
== NULL
) || !Tk_IsMapped(menuPtr
->tkwin
)
1685 || (menuPtr
->flags
& REDRAW_PENDING
)) {
1690 if (menuPtr
->flags
& UPDATE_PENDING
) {
1691 Tk_CancelIdleCall(UpdatePieMenu
, (ClientData
) menuPtr
);
1693 Tk_DoWhenIdle(DisplayPieMenu
, (ClientData
) menuPtr
);
1694 menuPtr
->flags
|= REDRAW_PENDING
;
1696 Tk_DoWhenIdle(UpdatePieMenu
, (ClientData
) menuPtr
);
1697 menuPtr
->flags
|= UPDATE_PENDING
;
1703 PopupPieMenu(clientData
)
1704 ClientData clientData
; /* Information about widget. */
1706 register PieMenu
*menuPtr
= (PieMenu
*) clientData
;
1708 NeverPopupPieMenu(menuPtr
);
1710 if (Tk_IsMapped(menuPtr
->tkwin
)) {
1714 ShapePieMenu(menuPtr
);
1715 Tk_MapWindow(menuPtr
->tkwin
);
1720 NowPopupPieMenu(menuPtr
)
1721 register PieMenu
*menuPtr
;
1723 PopupPieMenu((ClientData
)menuPtr
);
1728 NeverPopupPieMenu(menuPtr
)
1729 register PieMenu
*menuPtr
;
1731 if (menuPtr
->flags
& POPUP_PENDING
) {
1732 Tk_DeleteTimerHandler(menuPtr
->popup_timer_token
);
1733 menuPtr
->popup_timer_token
= 0;
1734 menuPtr
->flags
&= ~POPUP_PENDING
;
1740 EventuallyPopupPieMenu(menuPtr
)
1741 register PieMenu
*menuPtr
;
1743 NeverPopupPieMenu(menuPtr
);
1745 if (Tk_IsMapped(menuPtr
->tkwin
)) {
1749 menuPtr
->popup_timer_token
=
1750 Tk_CreateTimerHandler(menuPtr
->popup_delay
,
1751 PopupPieMenu
, (ClientData
) menuPtr
);
1752 menuPtr
->flags
|= POPUP_PENDING
;
1757 DeferPopupPieMenu(menuPtr
)
1758 register PieMenu
*menuPtr
;
1760 if (menuPtr
->flags
& POPUP_PENDING
) {
1761 EventuallyPopupPieMenu(menuPtr
);
1768 *--------------------------------------------------------------
1770 * UnpostSubPieMenu --
1772 * This procedure unposts any submenu.
1775 * A standard Tcl return result. Errors may occur in the
1776 * Tcl commands generated to unpost submenus.
1779 * If there is already a submenu posted, it is unposted.
1781 *--------------------------------------------------------------
1785 UnpostSubPieMenu(interp
, menuPtr
)
1786 Tcl_Interp
*interp
; /* Used for invoking sub-commands and
1787 * reporting errors. */
1788 register PieMenu
*menuPtr
; /* Information about menu as a whole. */
1791 int result
, x
, y
, win_x
, win_y
;
1792 unsigned int key_buttons
;
1795 if (menuPtr
->postedPie
== NULL
) {
1799 result
= Tcl_VarEval(interp
, menuPtr
->postedPie
->name
,
1800 " unpost", (char *) NULL
);
1801 menuPtr
->postedPie
= NULL
;
1807 *----------------------------------------------------------------------
1809 * ActivatePieMenuEntry --
1811 * This procedure is invoked to make a particular pie menu
1812 * entry the active one, deactivating any other entry that
1813 * might currently be active.
1816 * The return value is a standard Tcl result (errors can occur
1817 * while posting and unposting submenus).
1820 * Pie menu entries get redisplayed, and the active entry
1821 * changes. Submenus may get posted and unposted.
1823 *----------------------------------------------------------------------
1827 ActivatePieMenuEntry(menuPtr
, index
, preview
)
1828 register PieMenu
*menuPtr
; /* Menu in which to activate. */
1829 int index
; /* Index of entry to activate, or
1830 * -1 to deactivate all entries. */
1831 int preview
; /* 1 to execute previewer */
1833 register PieMenuEntry
*mePtr
;
1834 int result
= TCL_OK
;
1836 if (menuPtr
->active
>= 0) {
1837 mePtr
= menuPtr
->entries
[menuPtr
->active
];
1839 EventuallyRedrawPieMenu(menuPtr
, menuPtr
->active
);
1841 menuPtr
->active
= index
;
1843 mePtr
= menuPtr
->entries
[index
];
1844 EventuallyRedrawPieMenu(menuPtr
, index
);
1846 Tk_Preserve((ClientData
) mePtr
);
1847 if (mePtr
->preview
!= NULL
) {
1848 result
= Tcl_GlobalEval(menuPtr
->interp
, mePtr
->preview
);
1850 Tk_Release((ClientData
) mePtr
);
1853 /* We're doing this in tcl these days, for finer control. */
1855 if (preview
&& menuPtr
->preview
) {
1856 result
= Tcl_GlobalEval(menuPtr
->interp
, menuPtr
->preview
);
1865 * This pie menu tracking code determines the slice the cursor
1866 * is in by representing slice edge angles as (quadrant, slope)
1867 * pairs that can be quickly computed and compared.
1869 * The slope is defined such that it is greater than or equal to zero,
1870 * less than infinity, and increasing counter-clockwise around the menu.
1871 * Each of the four quadrants encompasses one range of slope.
1876 * x<=0, y>0 <--+ y/x
1878 * quad 1 | quad 0 | X
1879 * -----+--------+--------+---->
1882 * x<0, y<=0 +--> x>=0, y<0
1886 * The quadrants and slopes of the item edges are all precalculated,
1887 * during menu layout.
1888 * The quadrant and slope of the cursor must be calculated frequently
1889 * during menu tracking, so we just calculate the numerator and
1890 * denominator of the slope, and avoid an unnecessary division.
1891 * Instead of calculating "slope = numerator / denominator" then
1892 * testing "slope < it->slope", every time the cursor moves, we can
1893 * just test "numerator < (denominator * it->slope)".
1895 * This algorithm works in a right-side-up coordinate space, but the final
1896 * results are tranformed into X-windows's up-side-down coordinate system
1897 * by subtracting the y values from the window height.
1901 #define CALC_QUADRANT_SLOPE(x, y, quadrant, numerator, denominator) \
1902 if ((y) > 0) (quadrant) = ((x) > 0 ? 0 : 1); \
1903 else if ((y) < 0) (quadrant) = ((x) < 0 ? 2 : 3); \
1904 else (quadrant) = ((x) > 0 ? 0 : 2); \
1905 if ((quadrant) & 1) { \
1906 (numerator) = ABS((x)); (denominator) = ABS((y)); \
1908 (numerator) = ABS((y)); (denominator) = ABS((x)); \
1913 CalcPieMenuItem(menu
, x
, y
)
1917 register PieMenuEntry
*it
, *last_it
;
1918 int i
, j
, order
, quadrant
;
1919 int numerator
, denominator
;
1920 int first
, last_i
, last_order
;
1923 * Translate x and y from root window coordinates so they are
1924 * relative to the menu center, in right side up coordinates.
1927 menu
->dx
= x
= (x
- menu
->root_x
) + 1;
1928 menu
->dy
= y
= (menu
->root_y
- y
) - 1;
1931 * If there are no menu items,
1932 * or we are within the inactive region in the menu center,
1933 * then there is no item selected.
1935 if ((menu
->numEntries
== 0) ||
1936 ((x
* x
) + (y
* y
) <
1937 (menu
->inactive_radius
* menu
->inactive_radius
))) {
1942 * If there's only one item, then that must be it.
1944 if (menu
->numEntries
== 1) {
1949 * Calculate the quadrant, slope numerator, and slope denominator of
1950 * the cursor slope, to be used for comparisons.
1952 CALC_QUADRANT_SLOPE(x
, y
, quadrant
, numerator
, denominator
);
1955 * In most cases, during cursor tracking, the menu item that the
1956 * cursor is over will be the same as it was before (almost all
1957 * of the time), or one of the neighboring items (most of the
1958 * rest of the time). So we check those items first. But to keep
1959 * things simple, instead of actually checking the items in order of
1960 * frequency (the current, the two neighbors, then the rest), we just
1961 * start our loop around the menu items at the item *before* the
1962 * last selected menu item, so we still check the three most common
1963 * cases first (neighbor, current, neighbor, rest), without having
1964 * to complicate the code with special cases. Another strategy, that
1965 * might be good for menus with ridiculously many items, would be
1966 * [to check the current item first, then the two neighbors, then]
1967 * to do a binary search of the menu items (since they are ordered).
1968 * But that's more complicated and you shouldn't have that many menu
1973 * Start at the item before current one.
1975 first
= menu
->active
- 1;
1977 first
= menu
->numEntries
- 1;
1980 * Initialize last_order such that we will go through the loop
1981 * at least once, validating last_i, last_order, and last_it for
1982 * the next time through the loop.
1984 last_i
= last_order
= -1;
1987 it
= menu
->entries
[i
];
1991 /* Legend: c = cursor, e = edge
1992 <cursor quad>,<edge quad>
1998 /* Set order = 1, if shortest direction from edge to cursor is ccw */
1999 switch ((quadrant
- it
->quadrant
) & 3) {
2004 --+-- --+-- --+-- --+--
2007 /* slope >= it->slope */
2008 order
= ((float)numerator
>= (float)(denominator
* it
->slope
));
2014 --+-- --+-- --+-- --+--
2023 --+-- --+-- --+-- --+--
2026 /* slope < it->slope */
2027 order
= ((float)numerator
< (float)(denominator
* it
->slope
));
2033 --+-- --+-- --+-- --+--
2041 * If we were counter-clockwise of the last leading edge,
2042 * and we're clockwise of this leading edge,
2043 * then we were in the last menu item.
2044 * (Note: first time through this loop last_order = -1 so we'll
2045 * go back through the loop at least once, after validating
2046 * last_order, last_i, and last_it.)
2048 if ((last_order
== 1) && (order
== 0)) {
2054 * Remember this menu item index, and move on to the next one
2055 * counter-clockwise around the circle.
2057 last_i
= i
; last_it
= it
;
2058 if (++i
>= menu
->numEntries
) {
2061 it
= menu
->entries
[i
];
2064 * If we've checked all the others, then that must have been it.
2065 * This saves us from checking the leading edge of the first
2066 * item again (It's also insurance against layout bugs.)
2079 int total_slice
, radius
;
2080 int minx
, miny
, maxx
, maxy
;
2082 PieMenuEntry
*it
, *last
;
2083 XFontStruct
*font
, *titlefont
;
2086 * Calculate the sum of the menu item slice sizes.
2087 * Each menu item will get a (slice / total_slice) sized slice of the pie.
2090 for (i
= 0; i
< menu
->numEntries
; i
++) {
2091 total_slice
+= menu
->entries
[i
]->slice
;
2094 if ((titlefont
= menu
->titlefontPtr
) == NULL
)
2095 titlefont
= menu
->fontPtr
;
2098 * Calculate the subtend, angle, cosine, sine, quadrant, slope,
2099 * and size of each menu item.
2101 angle
= DEG_TO_RAD(menu
->initial_angle
);
2102 for (i
= 0; i
< menu
->numEntries
; i
++) {
2103 register float edge_dx
, edge_dy
, numerator
, denominator
, twist
;
2104 register int quadrant
;
2106 it
= menu
->entries
[i
];
2107 if ((font
= it
->fontPtr
) == NULL
)
2108 font
= menu
->fontPtr
;
2110 if (it
->bitmap
!= None
) {
2111 unsigned int bitmapWidth
, bitmapHeight
;
2113 Tk_SizeOfPixmap(it
->bitmap
, &bitmapWidth
, &bitmapHeight
);
2114 it
->height
= bitmapHeight
;
2115 it
->width
= bitmapWidth
;
2117 it
->height
= font
->ascent
+ font
->descent
;
2118 if (it
->label
!= NULL
) {
2119 (void) TkMeasureChars(font
, it
->label
,
2120 it
->labelLength
, 0, (int) 100000,
2121 TK_NEWLINES_NOT_SPECIAL
, &it
->width
);
2126 it
->height
+= 2*menu
->activeBorderWidth
+ 2;
2127 it
->width
+= 2*menu
->activeBorderWidth
+ 2;
2129 it
->subtend
= TWO_PI
* it
->slice
/ total_slice
;
2130 twist
= it
->subtend
/ 2.0;
2131 if (i
!= 0) angle
+= twist
;
2133 it
->dx
= cos(angle
);
2134 it
->dy
= sin(angle
);
2135 edge_dx
= cos(angle
- twist
);
2136 edge_dy
= sin(angle
- twist
);
2137 CALC_QUADRANT_SLOPE(edge_dx
, edge_dy
, quadrant
, numerator
, denominator
);
2138 it
->quadrant
= quadrant
;
2139 it
->slope
= (float)numerator
/ (float)denominator
;
2143 if ((radius
= menu
->fixed_radius
) == 0) {
2144 radius
= menu
->min_radius
;
2145 if (menu
->numEntries
> 1) {
2146 last
= menu
->entries
[menu
->numEntries
- 1];
2147 for (i
= 0; i
< menu
->numEntries
; i
++) {
2148 float dx
, dy
, ldx
, ldy
;
2149 int width
, height
, lwidth
, lheight
;
2151 it
= menu
->entries
[i
];
2153 dx
= it
->dx
; dy
= it
->dy
;
2154 width
= it
->width
; height
= it
->height
;
2155 ldx
= last
->dx
; ldy
= last
->dy
;
2156 lwidth
= last
->width
; lheight
= last
->height
;
2158 register int x
, y
, lx
, ly
,
2159 x0max
, y0max
, x1min
, y1min
;
2161 x
= dx
* radius
+ it
->x_offset
;
2162 y
= dy
* radius
+ it
->y_offset
;
2163 lx
= ldx
* radius
+ last
->x_offset
;
2164 ly
= ldy
* radius
+ last
->y_offset
;
2166 /* Translate x y with respect to label size and position */
2187 /* Do rects (x y width height) and (lx ly lwidth lheight) overlap? */
2188 x0max
= x
> lx
? x
: lx
;
2189 y0max
= y
> ly
? y
: ly
;
2190 x1min
= x
+width
< lx
+lwidth
? x
+width
: lx
+lwidth
;
2191 y1min
= y
+height
< ly
+lheight
? y
+height
: ly
+lheight
;
2192 if (!((x0max
< x1min
) &&
2193 (y0max
< y1min
))) { /* If they don't overlap */
2194 /* They are far enough out, so move on. */
2197 /* Push the menu radius out a step and try again */
2200 /* Loop on to next menu item */
2204 radius
+= menu
->extra_radius
;
2206 menu
->label_radius
= radius
;
2208 /* Finally position all the menu labels at the same radius.
2209 Figure out the bounding box of the labels. */
2210 minx
= miny
= maxx
= maxy
= 0;
2211 for (i
= 0; i
< menu
->numEntries
; i
++) {
2212 it
= menu
->entries
[i
];
2214 it
->x
= radius
* it
->dx
+ it
->x_offset
;
2215 it
->y
= radius
* it
->dy
+ it
->y_offset
;
2217 /* Translate x y with respect to label size and position */
2218 if (ABS(it
->x
) <= 2) {
2219 it
->x
-= it
->width
/2;
2221 it
->y
-= it
->height
;
2225 it
->y
-= it
->height
/2;
2228 it
->label_x
= it
->x
+ menu
->activeBorderWidth
+ 1;
2229 it
->label_y
= it
->y
- menu
->activeBorderWidth
- 1;
2230 if (it
->bitmap
== None
) {
2231 it
->label_y
-= (it
->fontPtr
? it
->fontPtr
: menu
->fontPtr
)->ascent
;
2234 if (it
->x
< minx
) minx
= it
->x
;
2235 if ((it
->x
+ it
->width
) > maxx
) maxx
= (it
->x
+ it
->width
);
2236 if (it
->y
< miny
) miny
= it
->y
;
2237 if ((it
->y
+ it
->height
) > maxy
) maxy
= (it
->y
+ it
->height
);
2241 if (menu
->titleLength
!= 0) {
2242 menu
->title_height
= titlefont
->ascent
+ titlefont
->descent
+ 2;
2243 (void) TkMeasureChars(titlefont
, menu
->title
,
2244 menu
->titleLength
, 0, (int) 100000,
2245 TK_NEWLINES_NOT_SPECIAL
, &menu
->title_width
);
2246 menu
->title_width
+= 2;
2247 if (-(menu
->title_width
/ 2) < minx
)
2248 minx
= -(menu
->title_width
/ 2);
2249 if ((menu
->title_width
/ 2) > maxx
)
2250 maxx
= (menu
->title_width
/ 2);
2251 maxy
+= (2 * menu
->borderWidth
) + menu
->title_height
;
2253 menu
->title_width
= menu
->title_height
= 0;
2257 minx
-= 2*menu
->borderWidth
; miny
-= 2*menu
->borderWidth
;
2258 maxx
+= 2*menu
->borderWidth
; maxy
+= 2*menu
->borderWidth
;
2260 menu
->center_x
= -minx
;
2261 menu
->center_y
= maxy
; /* y flip */
2262 menu
->width
= maxx
- minx
;
2263 menu
->height
= maxy
- miny
;
2265 /* menu->title_x = (menu->width - menu->title_width) / 2 + 1; */
2266 menu
->title_x
= menu
->center_x
- menu
->title_width
/2 + 1;
2267 menu
->title_y
= 2*menu
->borderWidth
+ titlefont
->ascent
+ 1;
2269 /* Translate the menu items to the center of the menu, in X coordinates. */
2270 for (i
= 0; i
< menu
->numEntries
; i
++) {
2271 it
= menu
->entries
[i
];
2272 it
->x
= menu
->center_x
+ it
->x
;
2273 it
->y
= (menu
->center_y
- it
->y
) - it
->height
; /* y flip */
2274 it
->label_x
= menu
->center_x
+ it
->label_x
;
2275 it
->label_y
= (menu
->center_y
- it
->label_y
) - it
->height
; /* y flip */
2278 if (menu
->segments
!= NULL
) {
2279 ckfree((char *)menu
->segments
);
2281 menu
->segments
= (XSegment
*)
2282 ckalloc(menu
->numEntries
* sizeof(XSegment
));
2284 if (menu
->numEntries
> 1) {
2285 XSegment
*seg
= menu
->segments
;
2287 angle
= DEG_TO_RAD(menu
->initial_angle
) -
2288 (menu
->entries
[0]->subtend
/ 2.0);
2289 for (i
= 0; i
< menu
->numEntries
; i
++) {
2290 it
= menu
->entries
[i
];
2291 seg
->x1
= menu
->center_x
+ (cos(angle
) * menu
->inactive_radius
);
2292 seg
->y1
= menu
->center_y
- (sin(angle
) * menu
->inactive_radius
);
2293 seg
->x2
= menu
->center_x
+
2294 (cos(angle
) * (menu
->label_radius
- PIE_SPOKE_INSET
));
2295 seg
->y2
= menu
->center_y
-
2296 (sin(angle
) * (menu
->label_radius
- PIE_SPOKE_INSET
));
2298 angle
+= it
->subtend
;
2305 ShapePieMenu(menuPtr
)
2318 if (menuPtr
->shaped
== 0) {
2322 dpy
= Tk_Display(menuPtr
->tkwin
);
2324 if (HaveShape
== -1) {
2326 if (XShapeQueryExtension(dpy
, &t1
, &t2
)) {
2334 Tk_MakeWindowExist(menuPtr
->tkwin
);
2335 win
= Tk_WindowId(menuPtr
->tkwin
);
2337 shape
= XCreatePixmap(dpy
, RootWindowOfScreen(Tk_Screen(menuPtr
->tkwin
)),
2338 menuPtr
->width
, menuPtr
->height
, 1);
2339 gc
= XCreateGC(dpy
, shape
, 0, &values
);
2342 XSetForeground(dpy
, gc
, 0);
2343 XFillRectangle(dpy
, shape
, gc
, 0, 0, menuPtr
->width
, menuPtr
->height
);
2345 XSetForeground(dpy
, gc
, 1);
2346 if (menuPtr
->titleLength
!= 0) {
2347 int bw
= menuPtr
->borderWidth
;
2349 XFillRectangle(dpy
, shape
, gc
, bw
, bw
, menuPtr
->width
- bw
*2, menuPtr
->title_height
+ bw
*2);
2352 for (i
= 0; i
< menuPtr
->numEntries
; i
++) {
2353 it
= menuPtr
->entries
[i
];
2354 XFillRectangle(dpy
, shape
, gc
, it
->x
, it
->y
, it
->width
, it
->height
);
2358 XShapeCombineMask(dpy
, win
, ShapeBounding
, 0, 0, shape
, ShapeSet
);