1/* Copyright    Massachusetts Institute of Technology    1985	*/
2
3#include "copyright.h"
4
5/*
6Copyright (C) 1993, 1996, 2001, 2002, 2003, 2004, 2005, 2006,
7  2007 Free Software Foundation, Inc.
8
9This program is free software; you can redistribute it and/or modify
10it under the terms of the GNU General Public License as published by
11the Free Software Foundation; either version 2, or (at your option)
12any later version.
13
14This program is distributed in the hope that it will be useful,
15but WITHOUT ANY WARRANTY; without even the implied warranty of
16MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17GNU General Public License for more details.
18
19You should have received a copy of the GNU General Public License
20along with this program; see the file COPYING.  If not, write to the
21Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22Boston, MA 02110-1301, USA. */
23
24
25/*
26 * XMenu:	MIT Project Athena, X Window system menu package
27 *
28 * 	XMenuInternal.c - XMenu internal (not user visible) routines.
29 *
30 *	Author:		Tony Della Fera, DEC
31 *			November, 1985
32 *
33 */
34
35#include <config.h>
36#include "XMenuInt.h"
37
38/*
39 * Toggle color macro.
40 */
41#define toggle_color(x) \
42	((x) == menu->bkgnd_color ? menu->s_frg_color : menu->bkgnd_color)
43
44/*
45 * Internal Window creation queue sizes.
46 */
47#define S_QUE_SIZE	300
48#define P_QUE_SIZE	20
49#define BUFFER_SIZE	(S_QUE_SIZE >= P_QUE_SIZE ? S_QUE_SIZE : P_QUE_SIZE)
50
51
52/*
53 * XMWinQue - Internal window creation queue datatype.
54 */
55typedef struct _xmwinquedef {
56    int sq_size;
57    XMSelect *sq[S_QUE_SIZE];
58    XMSelect **sq_ptr;
59    int pq_size;
60    XMPane *pq[P_QUE_SIZE];
61    XMPane **pq_ptr;
62} XMWinQue;
63
64/*
65 * _XMWinQue - Internal static window creation queue.
66 */
67static Bool _XMWinQueIsInit = False;
68static XMWinQue _XMWinQue;
69
70/*
71 * _XMErrorCode - Global XMenu error code.
72 */
73int _XMErrorCode = XME_NO_ERROR;
74/*
75 * _XMErrorList - Global XMenu error code description strings.
76 */
77char *
78_XMErrorList[XME_CODE_COUNT] = {
79    "No error",				/* XME_NO_ERROR */
80    "Menu not initialized",		/* XME_NOT_INIT */
81    "Argument out of bounds",		/* XME_ARG_BOUNDS */
82    "Pane not found",			/* XME_P_NOT_FOUND */
83    "Selection not found",		/* XME_S_NOT_FOUND */
84    "Invalid menu style parameter",	/* XME_STYLE_PARAM */
85    "Unable to grab mouse",		/* XME_GRAB_MOUSE */
86    "Unable to interpret locator",	/* XME_INTERP_LOC */
87    "Unable to calloc memory",		/* XME_CALLOC */
88    "Unable to create XAssocTable",	/* XME_CREATE_ASSOC */
89    "Unable to store bitmap",		/* XME_STORE_BITMAP */
90    "Unable to make tile pixmaps",	/* XME_MAKE_TILES */
91    "Unable to make pixmap",		/* XME_MAKE_PIXMAP */
92    "Unable to create cursor",		/* XME_CREATE_CURSOR */
93    "Unable to open font",		/* XME_OPEN_FONT */
94    "Unable to create windows",		/* XME_CREATE_WINDOW */
95    "Unable to create transparencies",	/* XME_CREATE_TRANSP */
96};
97
98/*
99 * _XMEventHandler - Internal event handler variable.
100 */
101int (*_XMEventHandler)() = NULL;
102
103
104
105/*
106 * _XMWinQueInit - Internal routine to initialize the window
107 *		   queue.
108 */
109_XMWinQueInit()
110{
111    /*
112     * If the queue is not initialized initialize it.
113     */
114    if (!_XMWinQueIsInit) {
115	/*
116	 * Blank the queue structure.
117	 */
118	register int i;
119
120	for (i = 0; i < S_QUE_SIZE; i++)
121	  _XMWinQue.sq[i] = 0;
122
123	for (i = 0; i < P_QUE_SIZE; i++)
124	  _XMWinQue.pq[i] = 0;
125
126	_XMWinQue.sq_size = _XMWinQue.pq_size = 0;
127
128	/*
129	 * Initialize the next free location pointers.
130	 */
131	_XMWinQue.sq_ptr = _XMWinQue.sq;
132	_XMWinQue.pq_ptr = _XMWinQue.pq;
133    }
134}
135
136
137
138/*
139 * _XMWinQueAddPane - Internal routine to add a pane to the pane
140 *		      window queue.
141 */
142int
143_XMWinQueAddPane(display, menu, p_ptr)
144    register Display *display;
145    register XMenu *menu;	/* Menu being manipulated. */
146    register XMPane *p_ptr;	/* XMPane being queued. */
147{
148    /*
149     * If the queue is currently full then flush it.
150     */
151    if (_XMWinQue.pq_size == P_QUE_SIZE) {
152	if (_XMWinQueFlush(display, menu, 0, 0) == _FAILURE) return(_FAILURE);
153    }
154
155    /*
156     * Insert the new XMPane pointer and increment the queue pointer
157     * and the queue size.
158     */
159    *_XMWinQue.pq_ptr = p_ptr;
160    _XMWinQue.pq_ptr++;
161    _XMWinQue.pq_size++;
162
163    /*
164     * All went well, return successfully.
165     */
166    _XMErrorCode = XME_NO_ERROR;
167    return(_SUCCESS);
168}
169
170
171
172/*
173 * _XMWinQueAddSelection - Internal routine to add a selection to
174 *			   the selection window queue.
175 */
176int
177_XMWinQueAddSelection(display, menu, s_ptr)
178    register Display *display;
179    register XMenu *menu;	/* Menu being manipulated. */
180    register XMSelect *s_ptr;	/* XMSelection being queued. */
181{
182    /*
183     * If this entry will overflow the queue then flush it.
184     */
185    if (_XMWinQue.sq_size == S_QUE_SIZE) {
186	if (_XMWinQueFlush(display, menu, 0, 0) == _FAILURE) return(_FAILURE);
187    }
188
189    /*
190     * Insert the new XMSelect pointer and increment the queue pointer
191     * and the queue size.
192     */
193    *_XMWinQue.sq_ptr = s_ptr;
194    _XMWinQue.sq_ptr++;
195    _XMWinQue.sq_size++;
196
197    /*
198     * All went well, return successfully.
199     */
200    _XMErrorCode = XME_NO_ERROR;
201    return(_SUCCESS);
202}
203
204
205
206/*
207 * _XMWinQueFlush - Internal routine to flush the pane and
208 *		    selection window queues.
209 */
210int
211_XMWinQueFlush(display, menu, pane, select)
212    register Display *display;
213    register XMenu *menu;		/* Menu being manipulated. */
214    register XMPane *pane;		/* Current pane. */
215{
216    register int pq_index;		/* Pane queue index. */
217    register int sq_index;		/* Selection queue index. */
218    register XMPane *p_ptr;		/* XMPane pointer. */
219    register XMSelect *s_ptr;   	/* XMSelect pointer. */
220    unsigned long valuemask;    	/* Which attributes to set. */
221    XSetWindowAttributes *attributes; 	/* Attributes to be set. */
222
223    /*
224     * If the pane window queue is not empty...
225     */
226
227    if (_XMWinQue.pq_size > 0) {
228	/*
229	 * set up attributes for pane window to be created.
230	 */
231	valuemask = (CWBackPixmap | CWBorderPixel | CWOverrideRedirect);
232	attributes = (XSetWindowAttributes *)malloc(sizeof(XSetWindowAttributes));
233	attributes->border_pixel = menu->p_bdr_color;
234	attributes->background_pixmap = menu->inact_pixmap;
235	attributes->override_redirect = True;
236
237	/*
238	 * Create all the pending panes in order, so that the
239	 * current pane will be on top, with the others
240	 * stacked appropriately under it.
241	 */
242	for (pq_index = _XMWinQue.pq_size - 1;
243	     pq_index >= 0;
244	     pq_index--)
245	  {
246	      p_ptr = _XMWinQue.pq[pq_index];  /* Retrieve next pane. */
247	      if (p_ptr == pane) break;
248	      p_ptr->window = XCreateWindow(display,
249					    menu->parent,
250					    p_ptr->window_x,
251					    p_ptr->window_y,
252					    p_ptr->window_w,
253					    p_ptr->window_h,
254					    menu->p_bdr_width,
255					    CopyFromParent,
256					    InputOutput,
257					    CopyFromParent,
258					    valuemask,
259					    attributes);
260	      XMakeAssoc(display, menu->assoc_tab, p_ptr->window, p_ptr);
261	      XSelectInput(display, p_ptr->window, menu->p_events);
262	  }
263	for (pq_index = 0;
264	     pq_index < _XMWinQue.pq_size;
265	     pq_index++)
266	  {
267	      p_ptr = _XMWinQue.pq[pq_index];	/* Retrieve next pane. */
268	      p_ptr->window = XCreateWindow(display,
269					    menu->parent,
270					    p_ptr->window_x,
271					    p_ptr->window_y,
272					    p_ptr->window_w,
273					    p_ptr->window_h,
274					    menu->p_bdr_width,
275					    CopyFromParent,
276					    InputOutput,
277					    CopyFromParent,
278					    valuemask,
279					    attributes);
280	      XMakeAssoc(display, menu->assoc_tab, p_ptr->window, p_ptr);
281	      XSelectInput(display, p_ptr->window, menu->p_events);
282	      if (p_ptr == pane) break;
283	}
284
285	/*
286	 * Reset the pane queue pointer and size.
287	 */
288	_XMWinQue.pq_size = 0;
289	_XMWinQue.pq_ptr = _XMWinQue.pq;
290    }
291
292    /*
293     * If the selection window queue is not empty...
294     */
295
296    if (_XMWinQue.sq_size > 0) {
297
298	for (sq_index = 0; sq_index < _XMWinQue.sq_size; sq_index++) {
299	    /*
300	     * Retrieve the XMSelect pointer.
301	     */
302	    s_ptr = _XMWinQue.sq[sq_index];
303	    s_ptr->window = XCreateWindow(display,
304				   s_ptr->parent_p->window,
305				   s_ptr->window_x,
306				   s_ptr->window_y,
307				   s_ptr->window_w,
308				   s_ptr->window_h,
309				   0,                /* border width*/
310				   CopyFromParent,
311				   InputOnly,
312				   CopyFromParent,
313				   0,
314				   attributes);
315
316	    /*
317	     * Insert the new window id and its
318	     * associated XMSelect structure into the
319	     * association table.
320	     */
321	    XMakeAssoc(display, menu->assoc_tab, s_ptr->window, s_ptr);
322	    XSelectInput(display, s_ptr->window, menu->s_events);
323	}
324
325	/*
326	 * Reset the selection queue pointer and size.
327	 */
328	_XMWinQue.sq_size = 0;
329	_XMWinQue.sq_ptr = _XMWinQue.sq;
330    }
331
332    /*
333     * Flush X's internal queues.
334     */
335    XFlush(display);
336
337    /*
338     * All went well, return successfully.
339     */
340    _XMErrorCode = XME_NO_ERROR;
341    return(_SUCCESS);
342}
343
344
345
346/*
347 * _XMGetPanePtr - 	Given a menu pointer and a pane index number, return
348 *			a pane pointer that points to the indexed pane.
349 */
350XMPane *
351_XMGetPanePtr(menu, p_num)
352    register XMenu *menu;	/* Menu to find the pane in. */
353    register int p_num;		/* Index number of pane to find. */
354{
355    register XMPane *p_ptr;	/* Pane pointer to be returned. */
356    register int i;		/* Loop counter. */
357
358    /*
359     * Is the pane number out of range?
360     */
361    if ((p_num < 0) || (p_num > (menu->p_count - 1))) {
362	_XMErrorCode = XME_P_NOT_FOUND;
363	return(NULL);
364    }
365
366    /*
367     * Find the right pane.
368     */
369    p_ptr = menu->p_list->next;
370    for (i = 0; i < p_num; i++) p_ptr = p_ptr->next;
371
372    /*
373     * Return successfully.
374     */
375    _XMErrorCode = XME_NO_ERROR;
376    return(p_ptr);
377}
378
379
380
381/*
382 * _XMGetSelectionPtr -	Given pane pointer and a selection index number,
383 *			return a selection pointer that points to the
384 *			indexed selection.
385 */
386XMSelect *
387_XMGetSelectionPtr(p_ptr, s_num)
388    register XMPane *p_ptr;	/* Pane to find the selection in. */
389    register int s_num;		/* Index number of the selection to find. */
390{
391    register XMSelect *s_ptr;	/* Selection pointer to be returned. */
392    register int i;		/* Loop counter. */
393
394    /*
395     * Is the selection number out of range?
396     */
397    if ((s_num < 0) || (s_num > (p_ptr->s_count - 1))) {
398	_XMErrorCode = XME_S_NOT_FOUND;
399	return(NULL);
400    }
401
402    /*
403     * Find the right selection.
404     */
405    s_ptr = p_ptr->s_list->next;
406    for (i = 0; i < s_num; i++) s_ptr = s_ptr->next;
407
408    /*
409     * Return successfully.
410     */
411    _XMErrorCode = XME_NO_ERROR;
412    return(s_ptr);
413}
414
415
416
417/*
418 * _XMRecomputeGlobals - Internal subroutine to recompute menu wide
419 *			 global values.
420 */
421_XMRecomputeGlobals(display, menu)
422    register Display *display; /*X11 display variable. */
423    register XMenu *menu;	/* Menu object to compute from. */
424{
425    register XMPane *p_ptr;	/* Pane pointer. */
426    register XMSelect *s_ptr;	/* Selection pointer. */
427
428    register int max_p_label = 0;	/* Maximum pane label width. */
429    register int max_s_label = 0;	/* Maximum selection label width. */
430    register int s_count = 0;		/* Maximum selection count. */
431
432    int p_s_pad;		/* Pane <-> selection padding. */
433    int p_s_diff;		/* Pane <-> selection separation. */
434
435    int p_height;		/* Pane window height. */
436    int p_width;		/* Pane window width. */
437    int s_width;		/* Selection window width. */
438
439    int screen;			/* DefaultScreen holder. */
440
441    /*
442     * For each pane...
443     */
444    for (
445	p_ptr = menu->p_list->next;
446	p_ptr != menu->p_list;
447	p_ptr = p_ptr->next
448    ){
449
450	/*
451	 * Recompute maximum pane label width.
452	 */
453	max_p_label = max(max_p_label, p_ptr->label_width);
454
455	/*
456	 * Recompute maximum selection count.
457	 */
458	s_count = max(s_count, p_ptr->s_count);
459
460	/*
461	 * For each selection in the current pane...
462	 */
463	for (
464	    s_ptr = p_ptr->s_list->next;
465	    s_ptr != p_ptr->s_list;
466	    s_ptr = s_ptr->next
467	){
468
469	    /*
470	     * Recompute maximum selection label width.
471	     */
472	    max_s_label = max(max_s_label, s_ptr->label_width);
473	}
474    }
475
476    /*
477     * Recompute pane height.
478     */
479    p_height = (menu->flag_height << 1) + (menu->s_y_off * s_count);
480
481    /*
482     * Recompute horizontal padding between the pane window and the
483     * selection windows.
484     */
485    p_s_pad = menu->p_x_off << 1;
486
487    /*
488     * Recompute pane and selection window widths.
489     * This is done by first computing the window sizes from the maximum
490     * label widths.  If the spacing between the selection window and the
491     * containing pane window is less than the pane selection padding value
492     * (twice the pane X offset) then change the size of the pane to be
493     * the size of the selection window plus the padding.  If, however the
494     * spacing between the selection window and the containing pane window
495     * is more than the pane selection padding value increase the size of
496     * the selection to its maximum possible value (the pane width minus
497     * the pane selection padding value).
498     */
499    p_width = max_p_label + p_s_pad;
500    s_width = max_s_label + (menu->s_fnt_pad << 1) + (menu->s_bdr_width << 1);
501    p_s_diff = p_width - s_width;
502    if (p_s_diff < p_s_pad) {
503	p_width = s_width + p_s_pad;
504    }
505    else if (p_s_diff > p_s_pad) {
506	s_width = p_width - p_s_pad;
507    }
508
509    /*
510     * Reset menu wide global values.
511     */
512    menu->s_count = s_count;
513    menu->p_height = p_height;
514    menu->p_width = p_width;
515    menu->s_width = s_width;
516
517    /*
518     * Ensure that the origin of the menu is placed so that
519     * None of the panes ore selections are off the screen.
520     */
521    screen = DefaultScreen(display);
522    if (menu->x_pos + menu->width > DisplayWidth(display, screen))
523	    menu->x_pos = DisplayWidth(display, screen) - menu->width;
524    else if (menu->x_pos < 0) menu->x_pos = 0;
525    if(menu->y_pos + menu->height > DisplayHeight(display, screen))
526	    menu->y_pos = DisplayHeight(display, screen) - menu->height;
527    else if (menu->y_pos < 0) menu->y_pos = 0;
528}
529
530
531/*
532 * _XMRecomputePane - Internal subroutine to recompute pane
533 *		      window dependencies.
534 */
535int
536_XMRecomputePane(display, menu, p_ptr, p_num)
537    register Display *display;	/* Standard X display variable. */
538    register XMenu *menu;	/* Menu object being recomputed. */
539    register XMPane *p_ptr;	/* Pane pointer. */
540    register int p_num;		/* Pane sequence number. */
541{
542    register int window_x;	/* Recomputed window X coordinate. */
543    register int window_y;	/* Recomputed window Y coordinate. */
544
545    unsigned long change_mask;	/* Value mask to reconfigure window. */
546    XWindowChanges *changes;	/* Values to use in configure window. */
547
548    register Bool config_p = False;	/* Reconfigure pane window? */
549
550    /*
551     * Update the pane serial number.
552     */
553    p_ptr->serial = p_num;
554
555    /*
556     * Recompute window X and Y coordinates.
557     */
558    switch (menu->menu_style) {
559	case LEFT:
560	    window_x = menu->p_x_off * ((menu->p_count - 1) - p_num);
561	    window_y = menu->p_y_off * ((menu->p_count - 1) - p_num);
562	    break;
563	case RIGHT:
564	    window_x = menu->p_x_off * p_num;
565	    window_y = menu->p_y_off * ((menu->p_count - 1) - p_num);
566	    break;
567	case CENTER:
568	    window_x = 0;
569	    window_y = menu->p_y_off * ((menu->p_count - 1) - p_num);
570	    break;
571	default:
572	    /* Error! Invalid style parameter. */
573	    _XMErrorCode = XME_STYLE_PARAM;
574	    return(_FAILURE);
575    }
576    window_x += menu->x_pos;
577    window_y += menu->y_pos;
578
579    /*
580     * If the newly compute pane coordinates differ from the
581     * current coordinates, reset the current coordinates and
582     * reconfigure the pane.
583     */
584    if (
585	(window_x != p_ptr->window_x) ||
586	(window_y != p_ptr->window_y)
587    ){
588	/*
589	 * Reset the coordinates and schedule
590	 * the pane for reconfiguration.
591	 */
592	p_ptr->window_x = window_x;
593	p_ptr->window_y = window_y;
594	config_p = True;
595    }
596
597    /*
598     * If the local pane width and height differs from the
599     * menu pane width and height, reset the local values.
600     */
601    if (
602	(p_ptr->window_w != menu->p_width) ||
603	(p_ptr->window_h != menu->p_height)
604    ){
605	/*
606	 * Reset window width and height and schedule
607	 * the pane for reconfiguration.
608	 */
609	p_ptr->window_w = menu->p_width;
610	p_ptr->window_h = menu->p_height;
611	config_p = True;
612    }
613
614    /*
615     * If we need to reconfigure the pane window do it now.
616     */
617    if (config_p == True) {
618	/*
619	 * If the pane window has already been created then
620	 * reconfigure the existing window, otherwise queue
621	 * it for creation with the new configuration.
622	 */
623	if (p_ptr->window) {
624	    change_mask = (CWX | CWY | CWWidth | CWHeight);
625	    changes = (XWindowChanges *)malloc(sizeof(XWindowChanges));
626	    changes->x = p_ptr->window_x;
627	    changes->y = p_ptr->window_y;
628	    changes->width = p_ptr->window_w;
629	    changes->height = p_ptr->window_h;
630
631	    XConfigureWindow(
632			     display,
633			     p_ptr->window,
634			     change_mask,
635			     changes
636			     );
637	    free(changes);
638
639	}
640	else {
641	    if (_XMWinQueAddPane(display, menu, p_ptr) == _FAILURE) {
642		return(_FAILURE);
643	    }
644	}
645    }
646
647    /*
648     * Recompute label X position.
649     */
650    switch (menu->p_style) {
651	case LEFT:
652	    p_ptr->label_x = menu->p_x_off + menu->p_fnt_pad;
653	    break;
654	case RIGHT:
655	    p_ptr->label_x = menu->p_width -
656		(p_ptr->label_width + menu->p_x_off + menu->p_fnt_pad);
657	    break;
658	case CENTER:
659	    p_ptr->label_x = (menu->p_width - p_ptr->label_width) >> 1;
660	    break;
661	default:
662	    /* Error! Invalid style parameter. */
663	    _XMErrorCode = XME_STYLE_PARAM;
664	    return(_FAILURE);
665    }
666    /*
667     * Recompute label Y positions.
668     */
669    p_ptr->label_uy = menu->p_fnt_pad + menu->p_fnt_info->max_bounds.ascent;
670    p_ptr->label_ly = (menu->p_height - menu->p_fnt_pad - menu->p_fnt_info->max_bounds.descent);
671
672    /*
673     * All went well, return successfully.
674     */
675    _XMErrorCode = XME_NO_ERROR;
676    return(_SUCCESS);
677}
678
679
680
681/*
682 * _XMRecomputeSelection - Internal subroutine to recompute
683 *			   selection window dependencies.
684 */
685int
686_XMRecomputeSelection(display, menu, s_ptr, s_num)
687    register Display *display;
688    register XMenu *menu;	/* Menu object being recomputed. */
689    register XMSelect *s_ptr;	/* Selection pointer. */
690    register int s_num;		/* Selection sequence number. */
691{
692    register Bool config_s = False;	/* Reconfigure selection window? */
693    XWindowChanges *changes;		/* Values to change in configure. */
694    unsigned long change_mask;		/* Value mask for XConfigureWindow. */
695
696    /*
697     * If the selection serial numbers are out of order, begin
698     * resequencing selections.  Recompute selection window coordinates
699     * and serial number.
700     *
701     * When selections are created they are given a serial number of
702     * -1, this causes this routine to give a new selection
703     * its initial coordinates and serial number.
704     */
705    if (s_ptr->serial != s_num) {
706	/*
707	 * Fix the sequence number.
708	 */
709	s_ptr->serial = s_num;
710	/*
711	 * Recompute window X and Y coordinates.
712	 */
713	s_ptr->window_x = menu->s_x_off;
714	s_ptr->window_y = menu->flag_height + (menu->s_y_off * s_num);
715	/*
716	 * We must reconfigure the window.
717	 */
718	config_s = True;
719    }
720
721    /*
722     * If the local selection width and height differs from the
723     * menu selection width and height, reset the local values.
724     */
725    if (
726	(s_ptr->window_w != menu->s_width) ||
727	(s_ptr->window_h != menu->s_height)
728    ){
729	/*
730	 * We must reconfigure the window.
731	 */
732	config_s = True;
733	/*
734	 * Reset window width and height.
735	 */
736	s_ptr->window_w = menu->s_width;
737	s_ptr->window_h = menu->s_height;
738    }
739
740    /*
741     * If we need to reconfigure the selection window do it now.
742     */
743    if (config_s == True) {
744	/*
745	 * If the selection window has already been created then
746	 * reconfigure the existing window, otherwise queue it
747	 * for creation with the new configuration.
748	 */
749	if (s_ptr->window) {
750	    changes = (XWindowChanges *)malloc(sizeof(XWindowChanges));
751	    change_mask = (CWX | CWY | CWWidth | CWHeight);
752	    changes = (XWindowChanges *)malloc(sizeof(XWindowChanges));
753	    changes->x = s_ptr->window_x;
754	    changes->y = s_ptr->window_y;
755	    changes->width = s_ptr->window_w;
756	    changes->height = s_ptr->window_h;
757
758	    XConfigureWindow(
759			     display,
760			     s_ptr->window,
761			     change_mask,
762			     changes
763			     );
764	    free(changes);
765
766	}
767	else {
768	    if (_XMWinQueAddSelection(display, menu, s_ptr) == _FAILURE) {
769		return(_FAILURE);
770	    }
771	}
772    }
773
774    /*
775     * Recompute label X position.
776     */
777    switch (menu->s_style) {
778	case LEFT:
779	    s_ptr->label_x = menu->s_bdr_width + menu->s_fnt_pad + s_ptr->window_x;
780	    break;
781	case RIGHT:
782	    s_ptr->label_x = s_ptr->window_x + menu->s_width -
783		(s_ptr->label_width + menu->s_bdr_width + menu->s_fnt_pad);
784	    break;
785	case CENTER:
786	    s_ptr->label_x = s_ptr->window_x + ((menu->s_width - s_ptr->label_width) >> 1);
787	    break;
788	default:
789	    /* Error! Invalid style parameter. */
790	    _XMErrorCode = XME_STYLE_PARAM;
791	    return(_FAILURE);
792    }
793    /*
794     * Recompute label Y position.
795     */
796    s_ptr->label_y = s_ptr->window_y + menu->s_fnt_info->max_bounds.ascent + menu->s_fnt_pad + menu->s_bdr_width;
797
798    /*
799     * All went well, return successfully.
800     */
801    _XMErrorCode = XME_NO_ERROR;
802    return(_SUCCESS);
803}
804
805
806
807/*
808 * _XMTransToOrigin - Internal subroutine to translate the point at
809 *		      the center of the current pane and selection to the
810 *		      the menu origin.
811 *
812 *	WARNING! ******	Be certain that all menu dependencies have been
813 *			recomputed before calling this routine or
814 *			unpredictable results will follow.
815 */
816_XMTransToOrigin(display, menu, p_ptr, s_ptr, x_pos, y_pos, orig_x, orig_y)
817    Display *display;		/* Not used. Included for consistency. */
818    register XMenu *menu;	/* Menu being computed against. */
819    register XMPane *p_ptr;	/* Current pane pointer. */
820    register XMSelect *s_ptr;	/* Current selection pointer. */
821    int x_pos;			/* X coordinate of point to translate. */
822    int y_pos;			/* Y coordinate of point to translate. */
823    int *orig_x;		/* Return value X coord. of the menu origin. */
824    int *orig_y;		/* Return value Y coord. of the menu origin. */
825{
826    register int l_orig_x;	/* Local X coordinate of the menu origin. */
827    register int l_orig_y;	/* Local Y coordinate of the menu origin. */
828
829    /*
830     * Translate the menu origin such that the cursor hot point will be in the
831     * center of the desired current selection and pane.
832     * If the current selection pointer is NULL then assume that the hot point
833     * will be in the center of the current pane flag.
834     */
835
836    if (s_ptr == NULL) {
837	/*
838	 * Translate from the center of the pane flag to the upper left
839	 * of the current pane window.
840	 */
841	l_orig_x = x_pos - (menu->p_width >> 1) - menu->p_bdr_width;
842	l_orig_y = y_pos - (menu->flag_height >> 1) - menu->p_bdr_width;
843    }
844    else {
845	/*
846	 * First translate from the center of the current selection
847	 * to the upper left of the current selection window.
848	 */
849	l_orig_x = x_pos - (menu->s_width >> 1);
850	l_orig_y = y_pos - (menu->s_height >> 1);
851
852	/*
853	 * Then translate to the upper left of the current pane window.
854	 */
855	l_orig_x -= (s_ptr->window_x + menu->p_bdr_width);
856	l_orig_y -= (s_ptr->window_y + menu->p_bdr_width);
857    }
858
859    /*
860     * Finally translate to the upper left of the menu.
861     */
862    l_orig_x -= (p_ptr->window_x - menu->x_pos);
863    l_orig_y -= (p_ptr->window_y - menu->y_pos);
864
865    /*
866     * Set the return values.
867     */
868    *orig_x = l_orig_x;
869    *orig_y = l_orig_y;
870}
871
872/*
873 * _XMRefreshPane - Internal subroutine to completely refresh
874 *		    the contents of a pane.
875 */
876_XMRefreshPane(display, menu, pane)
877    register Display *display;
878    register XMenu *menu;
879    register XMPane *pane;
880{
881    register XMSelect *s_list = pane->s_list;
882    register XMSelect *s_ptr;
883
884    /*
885     * First clear the pane.
886     */
887    XClearWindow(display, pane->window);
888    if (!pane->activated) {
889	XFillRectangle(display,
890		       pane->window,
891		       menu->inverse_select_GC,
892		       pane->label_x - menu->p_fnt_pad,
893		       pane->label_uy - menu->p_fnt_info->max_bounds.ascent - menu->p_fnt_pad,
894		       pane->label_width + (menu->p_fnt_pad << 1),
895		       menu->flag_height);
896
897	XFillRectangle(display,
898		       pane->window,
899		       menu->inverse_select_GC,
900		       pane->label_x - menu->p_fnt_pad,
901		       pane->label_ly - menu->p_fnt_info->max_bounds.ascent - menu->p_fnt_pad,
902		       pane->label_width + (menu->p_fnt_pad << 1),
903		       menu->flag_height);
904    }
905    if (!pane->active) {
906	XDrawString(display,
907		    pane->window,
908		    menu->inact_GC,
909		    pane->label_x, pane->label_uy,
910		    pane->label, pane->label_length);
911	XDrawString(display,
912		    pane->window,
913		    menu->inact_GC,
914		    pane->label_x, pane->label_ly,
915		    pane->label, pane->label_length);
916    }
917    else {
918	XDrawString(display,
919			 pane->window,
920			 menu->pane_GC,
921			 pane->label_x, pane->label_uy,
922			 pane->label, pane->label_length);
923	XDrawString(display,
924			 pane->window,
925			 menu->pane_GC,
926			 pane->label_x, pane->label_ly,
927			 pane->label, pane->label_length);
928
929	/*
930	 * Finally refresh each selection if the pane is activated.
931	 */
932	if (pane->activated) {
933	    for (s_ptr = s_list->next; s_ptr != s_list; s_ptr = s_ptr->next)
934		_XMRefreshSelection(display, menu, s_ptr);
935	}
936    }
937}
938
939
940
941
942/*
943 * _XMRefreshSelection - Internal subroutine that refreshes
944 *			 a single selection window.
945 */
946_XMRefreshSelection(display, menu, select)
947    register Display *display;
948    register XMenu *menu;
949    register XMSelect *select;
950{
951    register int width = select->window_w;
952    register int height = select->window_h;
953    register int bdr_width = menu->s_bdr_width;
954
955    if (select->type == SEPARATOR) {
956        XDrawLine(display,
957                  select->parent_p->window,
958                  menu->normal_select_GC,
959                  select->window_x,
960                  select->window_y + height / 2,
961                  select->window_x + width,
962                  select->window_y + height / 2);
963    }
964    else if (select->activated) {
965	if (menu->menu_mode == INVERT) {
966	    XFillRectangle(display,
967			   select->parent_p->window,
968			   menu->normal_select_GC,
969			   select->window_x, select->window_y,
970			   width, height);
971	    XDrawString(display,
972			select->parent_p->window,
973			menu->inverse_select_GC,
974			select->label_x,
975			select->label_y,
976			select->label, select->label_length);
977	}
978        else {
979            /*
980	     * Using BOX mode.
981             * Since most drawing routines with arbitrary width lines
982	     * are slow compared to raster-ops lets use a raster-op to
983	     * draw the boxes.
984             */
985
986	    XDrawRectangle(display,
987			   select->parent_p->window,
988			   menu->normal_select_GC,
989			   select->window_x + (bdr_width >> 1),
990			   select->window_y + (bdr_width >> 1 ),
991			   width - bdr_width,
992			   height - bdr_width);
993	    XDrawString(display,
994			select->parent_p->window,
995			menu->normal_select_GC,
996			select->label_x,
997			select->label_y,
998			select->label, select->label_length);
999        }
1000    }
1001    else {
1002	XClearArea(display,
1003		   select->parent_p->window,
1004		   select->window_x, select->window_y,
1005		   width, height,
1006		   False);
1007	if (select->active) {
1008	    XDrawString(display,
1009			select->parent_p->window,
1010			menu->normal_select_GC,
1011			select->label_x,
1012			select->label_y,
1013			select->label, select->label_length);
1014	}
1015	else {
1016	    XDrawString(display,
1017			select->parent_p->window,
1018			menu->inact_GC,
1019			select->label_x,
1020			select->label_y,
1021			select->label, select->label_length);
1022	}
1023    }
1024}
1025
1026/* arch-tag: 3ac61957-0852-4e72-8b88-7dfab1a5dee9
1027   (do not change this comment) */
1028