]> granicus.if.org Git - imagemagick/blob - MagickCore/widget.c
(no commit message)
[imagemagick] / MagickCore / widget.c
1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 %                                                                             %
4 %                                                                             %
5 %                                                                             %
6 %                                                                             %
7 %                  W   W  IIIII  DDDD    GGGG  EEEEE  TTTTT                   %
8 %                  W   W    I    D   D  G      E        T                     %
9 %                  W W W    I    D   D  G  GG  EEE      T                     %
10 %                  WW WW    I    D   D  G   G  E        T                     %
11 %                  W   W  IIIII  DDDD    GGGG  EEEEE    T                     %
12 %                                                                             %
13 %                                                                             %
14 %                   MagickCore X11 User Interface Methods                     %
15 %                                                                             %
16 %                              Software Design                                %
17 %                                John Cristy                                  %
18 %                              September 1993                                 %
19 %                                                                             %
20 %                                                                             %
21 %  Copyright 1999-2013 ImageMagick Studio LLC, a non-profit organization      %
22 %  dedicated to making software imaging solutions freely available.           %
23 %                                                                             %
24 %  You may not use this file except in compliance with the License.  You may  %
25 %  obtain a copy of the License at                                            %
26 %                                                                             %
27 %    http://www.imagemagick.org/script/license.php                            %
28 %                                                                             %
29 %  Unless required by applicable law or agreed to in writing, software        %
30 %  distributed under the License is distributed on an "AS IS" BASIS,          %
31 %  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
32 %  See the License for the specific language governing permissions and        %
33 %  limitations under the License.                                             %
34 %                                                                             %
35 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
36 %
37 %
38 */
39 \f
40 /*
41   Include declarations.
42 */
43 #include "MagickCore/studio.h"
44 #include "MagickCore/color.h"
45 #include "MagickCore/color-private.h"
46 #include "MagickCore/exception.h"
47 #include "MagickCore/exception-private.h"
48 #include "MagickCore/image.h"
49 #include "MagickCore/magick.h"
50 #include "MagickCore/memory_.h"
51 #include "MagickCore/PreRvIcccm.h"
52 #include "MagickCore/string_.h"
53 #include "MagickCore/token.h"
54 #include "MagickCore/token-private.h"
55 #include "MagickCore/utility.h"
56 #include "MagickCore/utility-private.h"
57 #include "MagickCore/xwindow-private.h"
58 #include "MagickCore/widget.h"
59 #include "MagickCore/widget-private.h"
60
61 #if defined(MAGICKCORE_X11_DELEGATE)
62 \f
63 /*
64   Define declarations.
65 */
66 #define AreaIsActive(matte_info,position)  ( \
67   ((position.y >= (int) (matte_info.y-matte_info.bevel_width)) &&  \
68    (position.y < (int) (matte_info.y+matte_info.height+matte_info.bevel_width))) \
69    ? MagickTrue : MagickFalse)
70 #define Extent(s)  ((int) strlen(s))
71 #define MatteIsActive(matte_info,position)  ( \
72   ((position.x >= (int) (matte_info.x-matte_info.bevel_width)) && \
73    (position.y >= (int) (matte_info.y-matte_info.bevel_width)) &&  \
74    (position.x < (int) (matte_info.x+matte_info.width+matte_info.bevel_width)) &&  \
75    (position.y < (int) (matte_info.y+matte_info.height+matte_info.bevel_width))) \
76    ? MagickTrue : MagickFalse)
77 #define MaxTextWidth  ((unsigned int) (255*XTextWidth(font_info,"_",1)))
78 #define MinTextWidth  (26*XTextWidth(font_info,"_",1))
79 #define QuantumMargin   MagickMax(font_info->max_bounds.width,12)
80 #define WidgetTextWidth(font_info,text)  \
81   ((unsigned int) XTextWidth(font_info,text,Extent(text)))
82 #define WindowIsActive(window_info,position)  ( \
83   ((position.x >= 0) && (position.y >= 0) &&  \
84    (position.x < (int) window_info.width) &&  \
85    (position.y < (int) window_info.height)) ? MagickTrue : MagickFalse)
86 \f
87 /*
88   Enum declarations.
89 */
90 typedef enum
91 {
92   ControlState = 0x0001,
93   InactiveWidgetState = 0x0004,
94   JumpListState = 0x0008,
95   RedrawActionState = 0x0010,
96   RedrawListState = 0x0020,
97   RedrawWidgetState = 0x0040,
98   UpdateListState = 0x0100
99 } WidgetState;
100
101 /*
102   Typedef declarations.
103 */
104 typedef struct _XWidgetInfo
105 {
106   char
107     *cursor,
108     *text,
109     *marker;
110
111   int
112     id;
113
114   unsigned int
115     bevel_width,
116     width,
117     height;
118
119   int
120     x,
121     y,
122     min_y,
123     max_y;
124
125   MagickStatusType
126     raised,
127     active,
128     center,
129     trough,
130     highlight;
131 } XWidgetInfo;
132 \f
133 /*
134   Variable declarations.
135 */
136 static XWidgetInfo
137   monitor_info =
138   {
139     (char *) NULL, (char *) NULL, (char *) NULL, 0, 0, 0, 0, 0, 0, 0, 0,
140     MagickFalse, MagickFalse, MagickFalse, MagickFalse, MagickFalse
141   },
142   submenu_info =
143   {
144     (char *) NULL, (char *) NULL, (char *) NULL, 0, 0, 0, 0, 0, 0, 0, 0,
145     MagickFalse, MagickFalse, MagickFalse, MagickFalse, MagickFalse
146   },
147   *selection_info = (XWidgetInfo *) NULL,
148   toggle_info =
149   {
150     (char *) NULL, (char *) NULL, (char *) NULL, 0, 0, 0, 0, 0, 0, 0, 0,
151     MagickFalse, MagickFalse, MagickFalse, MagickFalse, MagickFalse
152   };
153 \f
154 /*
155   Constant declarations.
156 */
157 static const int
158   BorderOffset = 4,
159   DoubleClick = 250;
160 \f
161 /*
162   Method prototypes.
163 */
164 static void
165   XDrawMatte(Display *,const XWindowInfo *,const XWidgetInfo *),
166   XSetBevelColor(Display *,const XWindowInfo *,const MagickStatusType),
167   XSetMatteColor(Display *,const XWindowInfo *,const MagickStatusType),
168   XSetTextColor(Display *,const XWindowInfo *,const MagickStatusType);
169 \f
170 /*
171 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
172 %                                                                             %
173 %                                                                             %
174 %                                                                             %
175 %   D e s t r o y X W i d g e t                                               %
176 %                                                                             %
177 %                                                                             %
178 %                                                                             %
179 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
180 %
181 %  DestroyXWidget() destroys resources associated with the X widget.
182 %
183 %  The format of the DestroyXWidget method is:
184 %
185 %      void DestroyXWidget()
186 %
187 %  A description of each parameter follows:
188 %
189 */
190 MagickPrivate void DestroyXWidget(void)
191 {
192   if (selection_info != (XWidgetInfo *) NULL)
193     selection_info=(XWidgetInfo *) RelinquishMagickMemory(selection_info);
194 }
195 \f
196 /*
197 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
198 %                                                                             %
199 %                                                                             %
200 %                                                                             %
201 +   X D r a w B e v e l                                                       %
202 %                                                                             %
203 %                                                                             %
204 %                                                                             %
205 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
206 %
207 %  XDrawBevel() "sets off" an area with a highlighted upper and left bevel and
208 %  a shadowed lower and right bevel.  The highlighted and shadowed bevels
209 %  create a 3-D effect.
210 %
211 %  The format of the XDrawBevel function is:
212 %
213 %      XDrawBevel(display,window_info,bevel_info)
214 %
215 %  A description of each parameter follows:
216 %
217 %    o display: Specifies a pointer to the Display structure;  returned from
218 %      XOpenDisplay.
219 %
220 %    o window_info: Specifies a pointer to a X11 XWindowInfo structure.
221 %
222 %    o bevel_info: Specifies a pointer to a XWidgetInfo structure.  It
223 %      contains the extents of the bevel.
224 %
225 */
226 static void XDrawBevel(Display *display,const XWindowInfo *window_info,
227   const XWidgetInfo *bevel_info)
228 {
229   int
230     x1,
231     x2,
232     y1,
233     y2;
234
235   unsigned int
236     bevel_width;
237
238   XPoint
239     points[6];
240
241   /*
242     Draw upper and left beveled border.
243   */
244   x1=bevel_info->x;
245   y1=bevel_info->y+bevel_info->height;
246   x2=bevel_info->x+bevel_info->width;
247   y2=bevel_info->y;
248   bevel_width=bevel_info->bevel_width;
249   points[0].x=x1;
250   points[0].y=y1;
251   points[1].x=x1;
252   points[1].y=y2;
253   points[2].x=x2;
254   points[2].y=y2;
255   points[3].x=x2+bevel_width;
256   points[3].y=y2-bevel_width;
257   points[4].x=x1-bevel_width;
258   points[4].y=y2-bevel_width;
259   points[5].x=x1-bevel_width;
260   points[5].y=y1+bevel_width;
261   XSetBevelColor(display,window_info,bevel_info->raised);
262   (void) XFillPolygon(display,window_info->id,window_info->widget_context,
263     points,6,Complex,CoordModeOrigin);
264   /*
265     Draw lower and right beveled border.
266   */
267   points[0].x=x1;
268   points[0].y=y1;
269   points[1].x=x2;
270   points[1].y=y1;
271   points[2].x=x2;
272   points[2].y=y2;
273   points[3].x=x2+bevel_width;
274   points[3].y=y2-bevel_width;
275   points[4].x=x2+bevel_width;
276   points[4].y=y1+bevel_width;
277   points[5].x=x1-bevel_width;
278   points[5].y=y1+bevel_width;
279   XSetBevelColor(display,window_info,!bevel_info->raised);
280   (void) XFillPolygon(display,window_info->id,window_info->widget_context,
281     points,6,Complex,CoordModeOrigin);
282   (void) XSetFillStyle(display,window_info->widget_context,FillSolid);
283 }
284 \f
285 /*
286 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
287 %                                                                             %
288 %                                                                             %
289 %                                                                             %
290 +   X D r a w B e v e l e d B u t t o n                                       %
291 %                                                                             %
292 %                                                                             %
293 %                                                                             %
294 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
295 %
296 %  XDrawBeveledButton() draws a button with a highlighted upper and left bevel
297 %  and a shadowed lower and right bevel.  The highlighted and shadowed bevels
298 %  create a 3-D effect.
299 %
300 %  The format of the XDrawBeveledButton function is:
301 %
302 %      XDrawBeveledButton(display,window_info,button_info)
303 %
304 %  A description of each parameter follows:
305 %
306 %    o display: Specifies a pointer to the Display structure;  returned from
307 %      XOpenDisplay.
308 %
309 %    o window_info: Specifies a pointer to a X11 XWindowInfo structure.
310 %
311 %    o button_info: Specifies a pointer to a XWidgetInfo structure.  It
312 %      contains the extents of the button.
313 %
314 */
315
316 static inline int MagickAbsoluteValue(const int x)
317 {
318   if (x < 0)
319     return(-x);
320   return(x);
321 }
322
323 static inline int MagickMax(const int x,const int y)
324 {
325   if (x > y)
326     return(x);
327   return(y);
328 }
329
330 static inline int MagickMin(const int x,const int y)
331 {
332   if (x < y)
333     return(x);
334   return(y);
335 }
336
337 static void XDrawBeveledButton(Display *display,const XWindowInfo *window_info,
338   const XWidgetInfo *button_info)
339 {
340   int
341     x,
342     y;
343
344   unsigned int
345     width;
346
347   XFontStruct
348     *font_info;
349
350   XRectangle
351     crop_info;
352
353   /*
354     Draw matte.
355   */
356   XDrawBevel(display,window_info,button_info);
357   XSetMatteColor(display,window_info,button_info->raised);
358   (void) XFillRectangle(display,window_info->id,window_info->widget_context,
359     button_info->x,button_info->y,button_info->width,button_info->height);
360   x=button_info->x-button_info->bevel_width-1;
361   y=button_info->y-button_info->bevel_width-1;
362   (void) XSetForeground(display,window_info->widget_context,
363     window_info->pixel_info->trough_color.pixel);
364   if (button_info->raised || (window_info->depth == 1))
365     (void) XDrawRectangle(display,window_info->id,window_info->widget_context,
366       x,y,button_info->width+(button_info->bevel_width << 1)+1,
367       button_info->height+(button_info->bevel_width << 1)+1);
368   if (button_info->text == (char *) NULL)
369     return;
370   /*
371     Set cropping region.
372   */
373   crop_info.width=(unsigned short) button_info->width;
374   crop_info.height=(unsigned short) button_info->height;
375   crop_info.x=button_info->x;
376   crop_info.y=button_info->y;
377   /*
378     Draw text.
379   */
380   font_info=window_info->font_info;
381   width=WidgetTextWidth(font_info,button_info->text);
382   x=button_info->x+(QuantumMargin >> 1);
383   if (button_info->center)
384     x=button_info->x+(button_info->width >> 1)-(width >> 1);
385   y=button_info->y+((button_info->height-
386     (font_info->ascent+font_info->descent)) >> 1)+font_info->ascent;
387   if ((int) button_info->width == (QuantumMargin >> 1))
388     {
389       /*
390         Option button-- write label to right of button.
391       */
392       XSetTextColor(display,window_info,MagickTrue);
393       x=button_info->x+button_info->width+button_info->bevel_width+
394         (QuantumMargin >> 1);
395       (void) XDrawString(display,window_info->id,window_info->widget_context,
396         x,y,button_info->text,Extent(button_info->text));
397       return;
398     }
399   (void) XSetClipRectangles(display,window_info->widget_context,0,0,&crop_info,
400     1,Unsorted);
401   XSetTextColor(display,window_info,button_info->raised);
402   (void) XDrawString(display,window_info->id,window_info->widget_context,x,y,
403     button_info->text,Extent(button_info->text));
404   (void) XSetClipMask(display,window_info->widget_context,None);
405   if (button_info->raised == MagickFalse)
406     XDelay(display,SuspendTime << 2);
407 }
408 \f
409 /*
410 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
411 %                                                                             %
412 %                                                                             %
413 %                                                                             %
414 +   X D r a w B e v e l e d M a t t e                                         %
415 %                                                                             %
416 %                                                                             %
417 %                                                                             %
418 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
419 %
420 %  XDrawBeveledMatte() draws a matte with a shadowed upper and left bevel and
421 %  a highlighted lower and right bevel.  The highlighted and shadowed bevels
422 %  create a 3-D effect.
423 %
424 %  The format of the XDrawBeveledMatte function is:
425 %
426 %      XDrawBeveledMatte(display,window_info,matte_info)
427 %
428 %  A description of each parameter follows:
429 %
430 %    o display: Specifies a pointer to the Display structure;  returned from
431 %      XOpenDisplay.
432 %
433 %    o window_info: Specifies a pointer to a X11 XWindowInfo structure.
434 %
435 %    o matte_info: Specifies a pointer to a XWidgetInfo structure.  It
436 %      contains the extents of the matte.
437 %
438 */
439 static void XDrawBeveledMatte(Display *display,const XWindowInfo *window_info,
440   const XWidgetInfo *matte_info)
441 {
442   /*
443     Draw matte.
444   */
445   XDrawBevel(display,window_info,matte_info);
446   XDrawMatte(display,window_info,matte_info);
447 }
448 \f
449 /*
450 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
451 %                                                                             %
452 %                                                                             %
453 %                                                                             %
454 +   X D r a w M a t t e                                                       %
455 %                                                                             %
456 %                                                                             %
457 %                                                                             %
458 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
459 %
460 %  XDrawMatte() fills a rectangular area with the matte color.
461 %
462 %  The format of the XDrawMatte function is:
463 %
464 %      XDrawMatte(display,window_info,matte_info)
465 %
466 %  A description of each parameter follows:
467 %
468 %    o display: Specifies a pointer to the Display structure;  returned from
469 %      XOpenDisplay.
470 %
471 %    o window_info: Specifies a pointer to a X11 XWindowInfo structure.
472 %
473 %    o matte_info: Specifies a pointer to a XWidgetInfo structure.  It
474 %      contains the extents of the matte.
475 %
476 */
477 static void XDrawMatte(Display *display,const XWindowInfo *window_info,
478   const XWidgetInfo *matte_info)
479 {
480   /*
481     Draw matte.
482   */
483   if ((matte_info->trough == MagickFalse) || (window_info->depth == 1))
484     (void) XFillRectangle(display,window_info->id,
485       window_info->highlight_context,matte_info->x,matte_info->y,
486       matte_info->width,matte_info->height);
487   else
488     {
489       (void) XSetForeground(display,window_info->widget_context,
490         window_info->pixel_info->trough_color.pixel);
491       (void) XFillRectangle(display,window_info->id,window_info->widget_context,
492         matte_info->x,matte_info->y,matte_info->width,matte_info->height);
493     }
494 }
495 \f
496 /*
497 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
498 %                                                                             %
499 %                                                                             %
500 %                                                                             %
501 +   X D r a w M a t t e T e x t                                               %
502 %                                                                             %
503 %                                                                             %
504 %                                                                             %
505 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
506 %
507 %  XDrawMatteText() draws a matte with text.  If the text exceeds the extents
508 %  of the text, a portion of the text relative to the cursor is displayed.
509 %
510 %  The format of the XDrawMatteText function is:
511 %
512 %      XDrawMatteText(display,window_info,text_info)
513 %
514 %  A description of each parameter follows:
515 %
516 %    o display: Specifies a pointer to the Display structure;  returned from
517 %      XOpenDisplay.
518 %
519 %    o window_info: Specifies a pointer to a X11 XWindowInfo structure.
520 %
521 %    o text_info: Specifies a pointer to a XWidgetInfo structure.  It
522 %      contains the extents of the text.
523 %
524 */
525 static void XDrawMatteText(Display *display,const XWindowInfo *window_info,
526   XWidgetInfo *text_info)
527 {
528   const char
529     *text;
530
531   int
532     n,
533     x,
534     y;
535
536   register int
537     i;
538
539   unsigned int
540     height,
541     width;
542
543   XFontStruct
544     *font_info;
545
546   XRectangle
547     crop_info;
548
549   /*
550     Clear the text area.
551   */
552   XSetMatteColor(display,window_info,MagickFalse);
553   (void) XFillRectangle(display,window_info->id,window_info->widget_context,
554     text_info->x,text_info->y,text_info->width,text_info->height);
555   if (text_info->text == (char *) NULL)
556     return;
557   XSetTextColor(display,window_info,text_info->highlight);
558   font_info=window_info->font_info;
559   x=text_info->x+(QuantumMargin >> 2);
560   y=text_info->y+font_info->ascent+(text_info->height >> 2);
561   width=text_info->width-(QuantumMargin >> 1);
562   height=(unsigned int) (font_info->ascent+font_info->descent);
563   if (*text_info->text == '\0')
564     {
565       /*
566         No text-- just draw cursor.
567       */
568       (void) XDrawLine(display,window_info->id,window_info->annotate_context,
569         x,y+3,x,y-height+3);
570       return;
571     }
572   /*
573     Set cropping region.
574   */
575   crop_info.width=(unsigned short) text_info->width;
576   crop_info.height=(unsigned short) text_info->height;
577   crop_info.x=text_info->x;
578   crop_info.y=text_info->y;
579   /*
580     Determine beginning of the visible text.
581   */
582   if (text_info->cursor < text_info->marker)
583     text_info->marker=text_info->cursor;
584   else
585     {
586       text=text_info->marker;
587       if (XTextWidth(font_info,(char *) text,(int) (text_info->cursor-text)) >
588           (int) width)
589         {
590           text=text_info->text;
591           for (i=0; i < Extent(text); i++)
592           {
593             n=XTextWidth(font_info,(char *) text+i,(int)
594               (text_info->cursor-text-i));
595             if (n <= (int) width)
596               break;
597           }
598           text_info->marker=(char *) text+i;
599         }
600     }
601   /*
602     Draw text and cursor.
603   */
604   if (text_info->highlight == MagickFalse)
605     {
606       (void) XSetClipRectangles(display,window_info->widget_context,0,0,
607         &crop_info,1,Unsorted);
608       (void) XDrawString(display,window_info->id,window_info->widget_context,
609         x,y,text_info->marker,Extent(text_info->marker));
610       (void) XSetClipMask(display,window_info->widget_context,None);
611     }
612   else
613     {
614       (void) XSetClipRectangles(display,window_info->annotate_context,0,0,
615         &crop_info,1,Unsorted);
616       width=WidgetTextWidth(font_info,text_info->marker);
617       (void) XFillRectangle(display,window_info->id,
618         window_info->annotate_context,x,y-font_info->ascent,width,height);
619       (void) XSetClipMask(display,window_info->annotate_context,None);
620       (void) XSetClipRectangles(display,window_info->highlight_context,0,0,
621         &crop_info,1,Unsorted);
622       (void) XDrawString(display,window_info->id,
623         window_info->highlight_context,x,y,text_info->marker,
624         Extent(text_info->marker));
625       (void) XSetClipMask(display,window_info->highlight_context,None);
626     }
627   x+=XTextWidth(font_info,text_info->marker,(int)
628     (text_info->cursor-text_info->marker));
629   (void) XDrawLine(display,window_info->id,window_info->annotate_context,x,y+3,
630     x,y-height+3);
631 }
632 \f
633 /*
634 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
635 %                                                                             %
636 %                                                                             %
637 %                                                                             %
638 +   X D r a w T r i a n g l e E a s t                                         %
639 %                                                                             %
640 %                                                                             %
641 %                                                                             %
642 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
643 %
644 %  XDrawTriangleEast() draws a triangle with a highlighted left bevel and a
645 %  shadowed right and lower bevel.  The highlighted and shadowed bevels create
646 %  a 3-D effect.
647 %
648 %  The format of the XDrawTriangleEast function is:
649 %
650 %      XDrawTriangleEast(display,window_info,triangle_info)
651 %
652 %  A description of each parameter follows:
653 %
654 %    o display: Specifies a pointer to the Display structure;  returned from
655 %      XOpenDisplay.
656 %
657 %    o window_info: Specifies a pointer to a X11 XWindowInfo structure.
658 %
659 %    o triangle_info: Specifies a pointer to a XWidgetInfo structure.  It
660 %      contains the extents of the triangle.
661 %
662 */
663 static void XDrawTriangleEast(Display *display,const XWindowInfo *window_info,
664   const XWidgetInfo *triangle_info)
665 {
666   int
667     x1,
668     x2,
669     x3,
670     y1,
671     y2,
672     y3;
673
674   unsigned int
675     bevel_width;
676
677   XFontStruct
678     *font_info;
679
680   XPoint
681     points[4];
682
683   /*
684     Draw triangle matte.
685   */
686   x1=triangle_info->x;
687   y1=triangle_info->y;
688   x2=triangle_info->x+triangle_info->width;
689   y2=triangle_info->y+(triangle_info->height >> 1);
690   x3=triangle_info->x;
691   y3=triangle_info->y+triangle_info->height;
692   bevel_width=triangle_info->bevel_width;
693   points[0].x=x1;
694   points[0].y=y1;
695   points[1].x=x2;
696   points[1].y=y2;
697   points[2].x=x3;
698   points[2].y=y3;
699   XSetMatteColor(display,window_info,triangle_info->raised);
700   (void) XFillPolygon(display,window_info->id,window_info->widget_context,
701     points,3,Complex,CoordModeOrigin);
702   /*
703     Draw bottom bevel.
704   */
705   points[0].x=x2;
706   points[0].y=y2;
707   points[1].x=x3;
708   points[1].y=y3;
709   points[2].x=x3-bevel_width;
710   points[2].y=y3+bevel_width;
711   points[3].x=x2+bevel_width;
712   points[3].y=y2;
713   XSetBevelColor(display,window_info,!triangle_info->raised);
714   (void) XFillPolygon(display,window_info->id,window_info->widget_context,
715     points,4,Complex,CoordModeOrigin);
716   /*
717     Draw Left bevel.
718   */
719   points[0].x=x3;
720   points[0].y=y3;
721   points[1].x=x1;
722   points[1].y=y1;
723   points[2].x=x1-bevel_width+1;
724   points[2].y=y1-bevel_width;
725   points[3].x=x3-bevel_width+1;
726   points[3].y=y3+bevel_width;
727   XSetBevelColor(display,window_info,triangle_info->raised);
728   (void) XFillPolygon(display,window_info->id,window_info->widget_context,
729     points,4,Complex,CoordModeOrigin);
730   /*
731     Draw top bevel.
732   */
733   points[0].x=x1;
734   points[0].y=y1;
735   points[1].x=x2;
736   points[1].y=y2;
737   points[2].x=x2+bevel_width;
738   points[2].y=y2;
739   points[3].x=x1-bevel_width;
740   points[3].y=y1-bevel_width;
741   (void) XFillPolygon(display,window_info->id,window_info->widget_context,
742     points,4,Complex,CoordModeOrigin);
743   (void) XSetFillStyle(display,window_info->widget_context,FillSolid);
744   if (triangle_info->text == (char *) NULL)
745     return;
746   /*
747     Write label to right of triangle.
748   */
749   font_info=window_info->font_info;
750   XSetTextColor(display,window_info,MagickTrue);
751   x1=triangle_info->x+triangle_info->width+triangle_info->bevel_width+
752     (QuantumMargin >> 1);
753   y1=triangle_info->y+((triangle_info->height-
754     (font_info->ascent+font_info->descent)) >> 1)+font_info->ascent;
755   (void) XDrawString(display,window_info->id,window_info->widget_context,x1,y1,
756     triangle_info->text,Extent(triangle_info->text));
757 }
758 \f
759 /*
760 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
761 %                                                                             %
762 %                                                                             %
763 %                                                                             %
764 +   X D r a w T r i a n g l e N o r t h                                       %
765 %                                                                             %
766 %                                                                             %
767 %                                                                             %
768 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
769 %
770 %  XDrawTriangleNorth() draws a triangle with a highlighted left bevel and a
771 %  shadowed right and lower bevel.  The highlighted and shadowed bevels create
772 %  a 3-D effect.
773 %
774 %  The format of the XDrawTriangleNorth function is:
775 %
776 %      XDrawTriangleNorth(display,window_info,triangle_info)
777 %
778 %  A description of each parameter follows:
779 %
780 %    o display: Specifies a pointer to the Display structure;  returned from
781 %      XOpenDisplay.
782 %
783 %    o window_info: Specifies a pointer to a X11 XWindowInfo structure.
784 %
785 %    o triangle_info: Specifies a pointer to a XWidgetInfo structure.  It
786 %      contains the extents of the triangle.
787 %
788 */
789 static void XDrawTriangleNorth(Display *display,const XWindowInfo *window_info,
790   const XWidgetInfo *triangle_info)
791 {
792   int
793     x1,
794     x2,
795     x3,
796     y1,
797     y2,
798     y3;
799
800   unsigned int
801     bevel_width;
802
803   XPoint
804     points[4];
805
806   /*
807     Draw triangle matte.
808   */
809   x1=triangle_info->x;
810   y1=triangle_info->y+triangle_info->height;
811   x2=triangle_info->x+(triangle_info->width >> 1);
812   y2=triangle_info->y;
813   x3=triangle_info->x+triangle_info->width;
814   y3=triangle_info->y+triangle_info->height;
815   bevel_width=triangle_info->bevel_width;
816   points[0].x=x1;
817   points[0].y=y1;
818   points[1].x=x2;
819   points[1].y=y2;
820   points[2].x=x3;
821   points[2].y=y3;
822   XSetMatteColor(display,window_info,triangle_info->raised);
823   (void) XFillPolygon(display,window_info->id,window_info->widget_context,
824     points,3,Complex,CoordModeOrigin);
825   /*
826     Draw left bevel.
827   */
828   points[0].x=x1;
829   points[0].y=y1;
830   points[1].x=x2;
831   points[1].y=y2;
832   points[2].x=x2;
833   points[2].y=y2-bevel_width-2;
834   points[3].x=x1-bevel_width-1;
835   points[3].y=y1+bevel_width;
836   XSetBevelColor(display,window_info,triangle_info->raised);
837   (void) XFillPolygon(display,window_info->id,window_info->widget_context,
838     points,4,Complex,CoordModeOrigin);
839   /*
840     Draw right bevel.
841   */
842   points[0].x=x2;
843   points[0].y=y2;
844   points[1].x=x3;
845   points[1].y=y3;
846   points[2].x=x3+bevel_width;
847   points[2].y=y3+bevel_width;
848   points[3].x=x2;
849   points[3].y=y2-bevel_width;
850   XSetBevelColor(display,window_info,!triangle_info->raised);
851   (void) XFillPolygon(display,window_info->id,window_info->widget_context,
852     points,4,Complex,CoordModeOrigin);
853   /*
854     Draw lower bevel.
855   */
856   points[0].x=x3;
857   points[0].y=y3;
858   points[1].x=x1;
859   points[1].y=y1;
860   points[2].x=x1-bevel_width;
861   points[2].y=y1+bevel_width;
862   points[3].x=x3+bevel_width;
863   points[3].y=y3+bevel_width;
864   (void) XFillPolygon(display,window_info->id,window_info->widget_context,
865     points,4,Complex,CoordModeOrigin);
866   (void) XSetFillStyle(display,window_info->widget_context,FillSolid);
867 }
868 \f
869 /*
870 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
871 %                                                                             %
872 %                                                                             %
873 %                                                                             %
874 +   X D r a w T r i a n g l e S o u t h                                       %
875 %                                                                             %
876 %                                                                             %
877 %                                                                             %
878 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
879 %
880 %  XDrawTriangleSouth() draws a border with a highlighted left and right bevel
881 %  and a shadowed lower bevel.  The highlighted and shadowed bevels create a
882 %  3-D effect.
883 %
884 %  The format of the XDrawTriangleSouth function is:
885 %
886 %      XDrawTriangleSouth(display,window_info,triangle_info)
887 %
888 %  A description of each parameter follows:
889 %
890 %    o display: Specifies a pointer to the Display structure;  returned from
891 %      XOpenDisplay.
892 %
893 %    o window_info: Specifies a pointer to a X11 XWindowInfo structure.
894 %
895 %    o triangle_info: Specifies a pointer to a XWidgetInfo structure.  It
896 %      contains the extents of the triangle.
897 %
898 */
899 static void XDrawTriangleSouth(Display *display,const XWindowInfo *window_info,
900   const XWidgetInfo *triangle_info)
901 {
902   int
903     x1,
904     x2,
905     x3,
906     y1,
907     y2,
908     y3;
909
910   unsigned int
911     bevel_width;
912
913   XPoint
914     points[4];
915
916   /*
917     Draw triangle matte.
918   */
919   x1=triangle_info->x;
920   y1=triangle_info->y;
921   x2=triangle_info->x+(triangle_info->width >> 1);
922   y2=triangle_info->y+triangle_info->height;
923   x3=triangle_info->x+triangle_info->width;
924   y3=triangle_info->y;
925   bevel_width=triangle_info->bevel_width;
926   points[0].x=x1;
927   points[0].y=y1;
928   points[1].x=x2;
929   points[1].y=y2;
930   points[2].x=x3;
931   points[2].y=y3;
932   XSetMatteColor(display,window_info,triangle_info->raised);
933   (void) XFillPolygon(display,window_info->id,window_info->widget_context,
934     points,3,Complex,CoordModeOrigin);
935   /*
936     Draw top bevel.
937   */
938   points[0].x=x3;
939   points[0].y=y3;
940   points[1].x=x1;
941   points[1].y=y1;
942   points[2].x=x1-bevel_width;
943   points[2].y=y1-bevel_width;
944   points[3].x=x3+bevel_width;
945   points[3].y=y3-bevel_width;
946   XSetBevelColor(display,window_info,triangle_info->raised);
947   (void) XFillPolygon(display,window_info->id,window_info->widget_context,
948     points,4,Complex,CoordModeOrigin);
949   /*
950     Draw right bevel.
951   */
952   points[0].x=x2;
953   points[0].y=y2;
954   points[1].x=x3+1;
955   points[1].y=y3-bevel_width;
956   points[2].x=x3+bevel_width;
957   points[2].y=y3-bevel_width;
958   points[3].x=x2;
959   points[3].y=y2+bevel_width;
960   XSetBevelColor(display,window_info,!triangle_info->raised);
961   (void) XFillPolygon(display,window_info->id,window_info->widget_context,
962     points,4,Complex,CoordModeOrigin);
963   /*
964     Draw left bevel.
965   */
966   points[0].x=x1;
967   points[0].y=y1;
968   points[1].x=x2;
969   points[1].y=y2;
970   points[2].x=x2;
971   points[2].y=y2+bevel_width;
972   points[3].x=x1-bevel_width;
973   points[3].y=y1-bevel_width;
974   XSetBevelColor(display,window_info,triangle_info->raised);
975   (void) XFillPolygon(display,window_info->id,window_info->widget_context,
976     points,4,Complex,CoordModeOrigin);
977   (void) XSetFillStyle(display,window_info->widget_context,FillSolid);
978 }
979 \f
980 /*
981 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
982 %                                                                             %
983 %                                                                             %
984 %                                                                             %
985 +   X D r a w W i d g e t T e x t                                             %
986 %                                                                             %
987 %                                                                             %
988 %                                                                             %
989 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
990 %
991 %  XDrawWidgetText() first clears the widget and draws a text string justifed
992 %  left (or center) in the x-direction and centered within the y-direction.
993 %
994 %  The format of the XDrawWidgetText function is:
995 %
996 %      XDrawWidgetText(display,window_info,text_info)
997 %
998 %  A description of each parameter follows:
999 %
1000 %    o display: Specifies a pointer to the Display structure;  returned from
1001 %      XOpenDisplay.
1002 %
1003 %    o window_info: Specifies a pointer to a XWindowText structure.
1004 %
1005 %    o text_info: Specifies a pointer to XWidgetInfo structure.
1006 %
1007 */
1008 static void XDrawWidgetText(Display *display,const XWindowInfo *window_info,
1009   XWidgetInfo *text_info)
1010 {
1011   GC
1012     widget_context;
1013
1014   int
1015     x,
1016     y;
1017
1018   unsigned int
1019     height,
1020     width;
1021
1022   XFontStruct
1023     *font_info;
1024
1025   XRectangle
1026     crop_info;
1027
1028   /*
1029     Clear the text area.
1030   */
1031   widget_context=window_info->annotate_context;
1032   if (text_info->raised)
1033     (void) XClearArea(display,window_info->id,text_info->x,text_info->y,
1034       text_info->width,text_info->height,MagickFalse);
1035   else
1036     {
1037       (void) XFillRectangle(display,window_info->id,widget_context,text_info->x,
1038         text_info->y,text_info->width,text_info->height);
1039       widget_context=window_info->highlight_context;
1040     }
1041   if (text_info->text == (char *) NULL)
1042     return;
1043   if (*text_info->text == '\0')
1044     return;
1045   /*
1046     Set cropping region.
1047   */
1048   font_info=window_info->font_info;
1049   crop_info.width=(unsigned short) text_info->width;
1050   crop_info.height=(unsigned short) text_info->height;
1051   crop_info.x=text_info->x;
1052   crop_info.y=text_info->y;
1053   /*
1054     Draw text.
1055   */
1056   width=WidgetTextWidth(font_info,text_info->text);
1057   x=text_info->x+(QuantumMargin >> 1);
1058   if (text_info->center)
1059     x=text_info->x+(text_info->width >> 1)-(width >> 1);
1060   if (text_info->raised)
1061     if (width > (text_info->width-QuantumMargin))
1062       x+=(text_info->width-QuantumMargin-width);
1063   height=(unsigned int) (font_info->ascent+font_info->descent);
1064   y=text_info->y+((text_info->height-height) >> 1)+font_info->ascent;
1065   (void) XSetClipRectangles(display,widget_context,0,0,&crop_info,1,Unsorted);
1066   (void) XDrawString(display,window_info->id,widget_context,x,y,text_info->text,
1067     Extent(text_info->text));
1068   (void) XSetClipMask(display,widget_context,None);
1069   if (x < text_info->x)
1070     (void) XDrawLine(display,window_info->id,window_info->annotate_context,
1071       text_info->x,text_info->y,text_info->x,text_info->y+text_info->height-1);
1072 }
1073 \f
1074 /*
1075 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1076 %                                                                             %
1077 %                                                                             %
1078 %                                                                             %
1079 +   X E d i t T e x t                                                         %
1080 %                                                                             %
1081 %                                                                             %
1082 %                                                                             %
1083 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1084 %
1085 %  XEditText() edits a text string as indicated by the key symbol.
1086 %
1087 %  The format of the XEditText function is:
1088 %
1089 %      XEditText(display,text_info,key_symbol,text,state)
1090 %
1091 %  A description of each parameter follows:
1092 %
1093 %    o display: Specifies a connection to an X server;  returned from
1094 %      XOpenDisplay.
1095 %
1096 %    o text_info: Specifies a pointer to a XWidgetInfo structure.  It
1097 %      contains the extents of the text.
1098 %
1099 %    o key_symbol:  A X11 KeySym that indicates what editing function to
1100 %      perform to the text.
1101 %
1102 %    o text: A character string to insert into the text.
1103 %
1104 %    o state:  An size_t that indicates whether the key symbol is a
1105 %      control character or not.
1106 %
1107 */
1108 static void XEditText(Display *display,XWidgetInfo *text_info,
1109   const KeySym key_symbol,char *text,const size_t state)
1110 {
1111   switch ((int) key_symbol)
1112   {
1113     case XK_BackSpace:
1114     case XK_Delete:
1115     {
1116       if (text_info->highlight)
1117         {
1118           /*
1119             Erase the entire line of text.
1120           */
1121           *text_info->text='\0';
1122           text_info->cursor=text_info->text;
1123           text_info->marker=text_info->text;
1124           text_info->highlight=MagickFalse;
1125         }
1126       /*
1127         Erase one character.
1128       */
1129       if (text_info->cursor != text_info->text)
1130         {
1131           text_info->cursor--;
1132           (void) CopyMagickString(text_info->cursor,text_info->cursor+1,
1133             MaxTextExtent);
1134           text_info->highlight=MagickFalse;
1135           break;
1136         }
1137     }
1138     case XK_Left:
1139     case XK_KP_Left:
1140     {
1141       /*
1142         Move cursor one position left.
1143       */
1144       if (text_info->cursor == text_info->text)
1145         break;
1146       text_info->cursor--;
1147       break;
1148     }
1149     case XK_Right:
1150     case XK_KP_Right:
1151     {
1152       /*
1153         Move cursor one position right.
1154       */
1155       if (text_info->cursor == (text_info->text+Extent(text_info->text)))
1156         break;
1157       text_info->cursor++;
1158       break;
1159     }
1160     default:
1161     {
1162       register char
1163         *p,
1164         *q;
1165
1166       register int
1167         i;
1168
1169       if (state & ControlState)
1170         break;
1171       if (*text == '\0')
1172         break;
1173       if ((Extent(text_info->text)+1) >= (int) MaxTextExtent)
1174         (void) XBell(display,0);
1175       else
1176         {
1177           if (text_info->highlight)
1178             {
1179               /*
1180                 Erase the entire line of text.
1181               */
1182               *text_info->text='\0';
1183               text_info->cursor=text_info->text;
1184               text_info->marker=text_info->text;
1185               text_info->highlight=MagickFalse;
1186             }
1187           /*
1188             Insert a string into the text.
1189           */
1190           q=text_info->text+Extent(text_info->text)+strlen(text);
1191           for (i=0; i <= Extent(text_info->cursor); i++)
1192           {
1193             *q=(*(q-Extent(text)));
1194             q--;
1195           }
1196           p=text;
1197           for (i=0; i < Extent(text); i++)
1198             *text_info->cursor++=(*p++);
1199         }
1200       break;
1201     }
1202   }
1203 }
1204 \f
1205 /*
1206 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1207 %                                                                             %
1208 %                                                                             %
1209 %                                                                             %
1210 +   X G e t W i d g e t I n f o                                               %
1211 %                                                                             %
1212 %                                                                             %
1213 %                                                                             %
1214 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1215 %
1216 %  XGetWidgetInfo() initializes the XWidgetInfo structure.
1217 %
1218 %  The format of the XGetWidgetInfo function is:
1219 %
1220 %      XGetWidgetInfo(text,widget_info)
1221 %
1222 %  A description of each parameter follows:
1223 %
1224 %    o text: A string of characters associated with the widget.
1225 %
1226 %    o widget_info: Specifies a pointer to a X11 XWidgetInfo structure.
1227 %
1228 */
1229 static void XGetWidgetInfo(const char *text,XWidgetInfo *widget_info)
1230 {
1231   /*
1232     Initialize widget info.
1233   */
1234   widget_info->id=(~0);
1235   widget_info->bevel_width=3;
1236   widget_info->width=1;
1237   widget_info->height=1;
1238   widget_info->x=0;
1239   widget_info->y=0;
1240   widget_info->min_y=0;
1241   widget_info->max_y=0;
1242   widget_info->raised=MagickTrue;
1243   widget_info->active=MagickFalse;
1244   widget_info->center=MagickTrue;
1245   widget_info->trough=MagickFalse;
1246   widget_info->highlight=MagickFalse;
1247   widget_info->text=(char *) text;
1248   widget_info->cursor=(char *) text;
1249   if (text != (char *) NULL)
1250     widget_info->cursor+=Extent(text);
1251   widget_info->marker=(char *) text;
1252 }
1253 \f
1254 /*
1255 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1256 %                                                                             %
1257 %                                                                             %
1258 %                                                                             %
1259 +   X H i g h l i g h t W i d g e t                                           %
1260 %                                                                             %
1261 %                                                                             %
1262 %                                                                             %
1263 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1264 %
1265 %  XHighlightWidget() draws a highlighted border around a window.
1266 %
1267 %  The format of the XHighlightWidget function is:
1268 %
1269 %      XHighlightWidget(display,window_info,x,y)
1270 %
1271 %  A description of each parameter follows:
1272 %
1273 %    o display: Specifies a pointer to the Display structure;  returned from
1274 %      XOpenDisplay.
1275 %
1276 %    o window_info: Specifies a pointer to a X11 XWindowInfo structure.
1277 %
1278 %    o x: Specifies an integer representing the rectangle offset in the
1279 %      x-direction.
1280 %
1281 %    o y: Specifies an integer representing the rectangle offset in the
1282 %      y-direction.
1283 %
1284 */
1285 static void XHighlightWidget(Display *display,const XWindowInfo *window_info,
1286   const int x,const int y)
1287 {
1288   /*
1289     Draw the widget highlighting rectangle.
1290   */
1291   XSetBevelColor(display,window_info,MagickTrue);
1292   (void) XDrawRectangle(display,window_info->id,window_info->widget_context,x,y,
1293     window_info->width-(x << 1),window_info->height-(y << 1));
1294   (void) XDrawRectangle(display,window_info->id,window_info->widget_context,
1295     x-1,y-1,window_info->width-(x << 1)+1,window_info->height-(y << 1)+1);
1296   XSetBevelColor(display,window_info,MagickFalse);
1297   (void) XDrawRectangle(display,window_info->id,window_info->widget_context,
1298     x-1,y-1,window_info->width-(x << 1),window_info->height-(y << 1));
1299   (void) XSetFillStyle(display,window_info->widget_context,FillSolid);
1300 }
1301 \f
1302 /*
1303 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1304 %                                                                             %
1305 %                                                                             %
1306 %                                                                             %
1307 +   X S c r e e n E v e n t                                                   %
1308 %                                                                             %
1309 %                                                                             %
1310 %                                                                             %
1311 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1312 %
1313 %  XScreenEvent() returns MagickTrue if the any event on the X server queue is
1314 %  associated with the widget window.
1315 %
1316 %  The format of the XScreenEvent function is:
1317 %
1318 %      int XScreenEvent(Display *display,XEvent *event,char *data)
1319 %
1320 %  A description of each parameter follows:
1321 %
1322 %    o display: Specifies a pointer to the Display structure;  returned from
1323 %      XOpenDisplay.
1324 %
1325 %    o event: Specifies a pointer to a X11 XEvent structure.
1326 %
1327 %    o data: Specifies a pointer to a XWindows structure.
1328 %
1329 */
1330
1331 #if defined(__cplusplus) || defined(c_plusplus)
1332 extern "C" {
1333 #endif
1334
1335 static int XScreenEvent(Display *display,XEvent *event,char *data)
1336 {
1337   XWindows
1338     *windows;
1339
1340   windows=(XWindows *) data;
1341   if (event->xany.window == windows->popup.id)
1342     {
1343       if (event->type == MapNotify)
1344         windows->popup.mapped=MagickTrue;
1345       if (event->type == UnmapNotify)
1346         windows->popup.mapped=MagickFalse;
1347       return(MagickTrue);
1348     }
1349   if (event->xany.window == windows->widget.id)
1350     {
1351       if (event->type == MapNotify)
1352         windows->widget.mapped=MagickTrue;
1353       if (event->type == UnmapNotify)
1354         windows->widget.mapped=MagickFalse;
1355       return(MagickTrue);
1356     }
1357   switch (event->type)
1358   {
1359     case ButtonPress:
1360     {
1361       if ((event->xbutton.button == Button3) &&
1362           (event->xbutton.state & Mod1Mask))
1363         {
1364           /*
1365             Convert Alt-Button3 to Button2.
1366           */
1367           event->xbutton.button=Button2;
1368           event->xbutton.state&=(~Mod1Mask);
1369         }
1370       return(MagickTrue);
1371     }
1372     case Expose:
1373     {
1374       if (event->xexpose.window == windows->image.id)
1375         {
1376           XRefreshWindow(display,&windows->image,event);
1377           break;
1378         }
1379       if (event->xexpose.window == windows->magnify.id)
1380         if (event->xexpose.count == 0)
1381           if (windows->magnify.mapped)
1382             {
1383               ExceptionInfo
1384                 *exception;
1385
1386               exception=AcquireExceptionInfo();
1387               XMakeMagnifyImage(display,windows,exception);
1388               exception=DestroyExceptionInfo(exception);
1389               break;
1390             }
1391       if (event->xexpose.window == windows->command.id)
1392         if (event->xexpose.count == 0)
1393           {
1394             (void) XCommandWidget(display,windows,(const char **) NULL,event);
1395             break;
1396           }
1397       break;
1398     }
1399     case FocusOut:
1400     {
1401       /*
1402         Set input focus for backdrop window.
1403       */
1404       if (event->xfocus.window == windows->image.id)
1405         (void) XSetInputFocus(display,windows->image.id,RevertToNone,
1406           CurrentTime);
1407       return(MagickTrue);
1408     }
1409     case ButtonRelease:
1410     case KeyPress:
1411     case KeyRelease:
1412     case MotionNotify:
1413     case SelectionNotify:
1414       return(MagickTrue);
1415     default:
1416       break;
1417   }
1418   return(MagickFalse);
1419 }
1420
1421 #if defined(__cplusplus) || defined(c_plusplus)
1422 }
1423 #endif
1424 \f
1425 /*
1426 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1427 %                                                                             %
1428 %                                                                             %
1429 %                                                                             %
1430 +   X S e t B e v e l C o l o r                                               %
1431 %                                                                             %
1432 %                                                                             %
1433 %                                                                             %
1434 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1435 %
1436 %  XSetBevelColor() sets the graphic context for drawing a beveled border.
1437 %
1438 %  The format of the XSetBevelColor function is:
1439 %
1440 %      XSetBevelColor(display,window_info,raised)
1441 %
1442 %  A description of each parameter follows:
1443 %
1444 %    o display: Specifies a pointer to the Display structure;  returned from
1445 %      XOpenDisplay.
1446 %
1447 %    o window_info: Specifies a pointer to a X11 XWindowInfo structure.
1448 %
1449 %    o raised: A value other than zero indicates the color show be a
1450 %      "highlight" color, otherwise the "shadow" color is set.
1451 %
1452 */
1453 static void XSetBevelColor(Display *display,const XWindowInfo *window_info,
1454   const MagickStatusType raised)
1455 {
1456   if (window_info->depth == 1)
1457     {
1458       Pixmap
1459         stipple;
1460
1461       /*
1462         Monochrome window.
1463       */
1464       (void) XSetBackground(display,window_info->widget_context,
1465         XBlackPixel(display,window_info->screen));
1466       (void) XSetForeground(display,window_info->widget_context,
1467         XWhitePixel(display,window_info->screen));
1468       (void) XSetFillStyle(display,window_info->widget_context,
1469         FillOpaqueStippled);
1470       stipple=window_info->highlight_stipple;
1471       if (raised == MagickFalse)
1472         stipple=window_info->shadow_stipple;
1473       (void) XSetStipple(display,window_info->widget_context,stipple);
1474     }
1475   else
1476     if (raised)
1477       (void) XSetForeground(display,window_info->widget_context,
1478         window_info->pixel_info->highlight_color.pixel);
1479     else
1480       (void) XSetForeground(display,window_info->widget_context,
1481         window_info->pixel_info->shadow_color.pixel);
1482 }
1483 \f
1484 /*
1485 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1486 %                                                                             %
1487 %                                                                             %
1488 %                                                                             %
1489 +   X S e t M a t t e C o l o r                                               %
1490 %                                                                             %
1491 %                                                                             %
1492 %                                                                             %
1493 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1494 %
1495 %  XSetMatteColor() sets the graphic context for drawing the matte.
1496 %
1497 %  The format of the XSetMatteColor function is:
1498 %
1499 %      XSetMatteColor(display,window_info,raised)
1500 %
1501 %  A description of each parameter follows:
1502 %
1503 %    o display: Specifies a pointer to the Display structure;  returned from
1504 %      XOpenDisplay.
1505 %
1506 %    o window_info: Specifies a pointer to a X11 XWindowInfo structure.
1507 %
1508 %    o raised: A value other than zero indicates the matte is active.
1509 %
1510 */
1511 static void XSetMatteColor(Display *display,const XWindowInfo *window_info,
1512   const MagickStatusType raised)
1513 {
1514   if (window_info->depth == 1)
1515     {
1516       /*
1517         Monochrome window.
1518       */
1519       if (raised)
1520         (void) XSetForeground(display,window_info->widget_context,
1521           XWhitePixel(display,window_info->screen));
1522       else
1523         (void) XSetForeground(display,window_info->widget_context,
1524           XBlackPixel(display,window_info->screen));
1525     }
1526   else
1527     if (raised)
1528       (void) XSetForeground(display,window_info->widget_context,
1529         window_info->pixel_info->matte_color.pixel);
1530     else
1531       (void) XSetForeground(display,window_info->widget_context,
1532         window_info->pixel_info->depth_color.pixel);
1533 }
1534 \f
1535 /*
1536 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1537 %                                                                             %
1538 %                                                                             %
1539 %                                                                             %
1540 +   X S e t T e x t C o l o r                                                 %
1541 %                                                                             %
1542 %                                                                             %
1543 %                                                                             %
1544 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1545 %
1546 %  XSetTextColor() sets the graphic context for drawing text on a matte.
1547 %
1548 %  The format of the XSetTextColor function is:
1549 %
1550 %      XSetTextColor(display,window_info,raised)
1551 %
1552 %  A description of each parameter follows:
1553 %
1554 %    o display: Specifies a pointer to the Display structure;  returned from
1555 %      XOpenDisplay.
1556 %
1557 %    o window_info: Specifies a pointer to a X11 XWindowInfo structure.
1558 %
1559 %    o raised: A value other than zero indicates the color show be a
1560 %      "highlight" color, otherwise the "shadow" color is set.
1561 %
1562 */
1563 static void XSetTextColor(Display *display,const XWindowInfo *window_info,
1564   const MagickStatusType raised)
1565 {
1566   ssize_t
1567     foreground,
1568     matte;
1569
1570   if (window_info->depth == 1)
1571     {
1572       /*
1573         Monochrome window.
1574       */
1575       if (raised)
1576         (void) XSetForeground(display,window_info->widget_context,
1577           XBlackPixel(display,window_info->screen));
1578       else
1579         (void) XSetForeground(display,window_info->widget_context,
1580           XWhitePixel(display,window_info->screen));
1581       return;
1582     }
1583   foreground=(ssize_t) XPixelIntensity(
1584     &window_info->pixel_info->foreground_color);
1585   matte=(ssize_t) XPixelIntensity(&window_info->pixel_info->matte_color);
1586   if (MagickAbsoluteValue((int) (foreground-matte)) > (65535L >> 3))
1587     (void) XSetForeground(display,window_info->widget_context,
1588       window_info->pixel_info->foreground_color.pixel);
1589   else
1590     (void) XSetForeground(display,window_info->widget_context,
1591       window_info->pixel_info->background_color.pixel);
1592 }
1593 \f
1594 /*
1595 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1596 %                                                                             %
1597 %                                                                             %
1598 %                                                                             %
1599 %   X C o l o r B r o w s e r W i d g e t                                     %
1600 %                                                                             %
1601 %                                                                             %
1602 %                                                                             %
1603 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1604 %
1605 %  XColorBrowserWidget() displays a Color Browser widget with a color query
1606 %  to the user.  The user keys a reply and presses the Action or Cancel button
1607 %  to exit.  The typed text is returned as the reply function parameter.
1608 %
1609 %  The format of the XColorBrowserWidget method is:
1610 %
1611 %      void XColorBrowserWidget(Display *display,XWindows *windows,
1612 %        const char *action,char *reply)
1613 %
1614 %  A description of each parameter follows:
1615 %
1616 %    o display: Specifies a connection to an X server;  returned from
1617 %      XOpenDisplay.
1618 %
1619 %    o window: Specifies a pointer to a XWindows structure.
1620 %
1621 %    o action: Specifies a pointer to the action of this widget.
1622 %
1623 %    o reply: the response from the user is returned in this parameter.
1624 %
1625 */
1626 MagickPrivate void XColorBrowserWidget(Display *display,XWindows *windows,
1627   const char *action,char *reply)
1628 {
1629 #define CancelButtonText  "Cancel"
1630 #define ColornameText  "Name:"
1631 #define ColorPatternText  "Pattern:"
1632 #define GrabButtonText  "Grab"
1633 #define ResetButtonText  "Reset"
1634
1635   char
1636     **colorlist,
1637     primary_selection[MaxTextExtent],
1638     reset_pattern[MaxTextExtent],
1639     text[MaxTextExtent];
1640
1641   ExceptionInfo
1642     *exception;
1643
1644   int
1645     x,
1646     y;
1647
1648   register int
1649     i;
1650
1651   static char
1652     glob_pattern[MaxTextExtent] = "*";
1653
1654   static MagickStatusType
1655     mask = (MagickStatusType) (CWWidth | CWHeight | CWX | CWY);
1656
1657   Status
1658     status;
1659
1660   unsigned int
1661     height,
1662     text_width,
1663     visible_colors,
1664     width;
1665
1666   size_t
1667     colors,
1668     delay,
1669     state;
1670
1671   XColor
1672     color;
1673
1674   XEvent
1675     event;
1676
1677   XFontStruct
1678     *font_info;
1679
1680   XTextProperty
1681     window_name;
1682
1683   XWidgetInfo
1684     action_info,
1685     cancel_info,
1686     expose_info,
1687     grab_info,
1688     list_info,
1689     mode_info,
1690     north_info,
1691     reply_info,
1692     reset_info,
1693     scroll_info,
1694     selection_info,
1695     slider_info,
1696     south_info,
1697     text_info;
1698
1699   XWindowChanges
1700     window_changes;
1701
1702   /*
1703     Get color list and sort in ascending order.
1704   */
1705   assert(display != (Display *) NULL);
1706   assert(windows != (XWindows *) NULL);
1707   assert(action != (char *) NULL);
1708   assert(reply != (char *) NULL);
1709   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",action);
1710   XSetCursorState(display,windows,MagickTrue);
1711   XCheckRefreshWindows(display,windows);
1712   (void) CopyMagickString(reset_pattern,"*",MaxTextExtent);
1713   exception=AcquireExceptionInfo();
1714   colorlist=GetColorList(glob_pattern,&colors,exception);
1715   if (colorlist == (char **) NULL)
1716     {
1717       /*
1718         Pattern failed, obtain all the colors.
1719       */
1720       (void) CopyMagickString(glob_pattern,"*",MaxTextExtent);
1721       colorlist=GetColorList(glob_pattern,&colors,exception);
1722       if (colorlist == (char **) NULL)
1723         {
1724           XNoticeWidget(display,windows,"Unable to obtain colors names:",
1725             glob_pattern);
1726           (void) XDialogWidget(display,windows,action,"Enter color name:",
1727             reply);
1728           return;
1729         }
1730     }
1731   /*
1732     Determine Color Browser widget attributes.
1733   */
1734   font_info=windows->widget.font_info;
1735   text_width=0;
1736   for (i=0; i < (int) colors; i++)
1737     if (WidgetTextWidth(font_info,colorlist[i]) > text_width)
1738       text_width=WidgetTextWidth(font_info,colorlist[i]);
1739   width=WidgetTextWidth(font_info,(char *) action);
1740   if (WidgetTextWidth(font_info,CancelButtonText) > width)
1741     width=WidgetTextWidth(font_info,CancelButtonText);
1742   if (WidgetTextWidth(font_info,ResetButtonText) > width)
1743     width=WidgetTextWidth(font_info,ResetButtonText);
1744   if (WidgetTextWidth(font_info,GrabButtonText) > width)
1745     width=WidgetTextWidth(font_info,GrabButtonText);
1746   width+=QuantumMargin;
1747   if (WidgetTextWidth(font_info,ColorPatternText) > width)
1748     width=WidgetTextWidth(font_info,ColorPatternText);
1749   if (WidgetTextWidth(font_info,ColornameText) > width)
1750     width=WidgetTextWidth(font_info,ColornameText);
1751   height=(unsigned int) (font_info->ascent+font_info->descent);
1752   /*
1753     Position Color Browser widget.
1754   */
1755   windows->widget.width=(unsigned int)
1756     (width+MagickMin((int) text_width,(int) MaxTextWidth)+6*QuantumMargin);
1757   windows->widget.min_width=(unsigned int)
1758     (width+MinTextWidth+4*QuantumMargin);
1759   if (windows->widget.width < windows->widget.min_width)
1760     windows->widget.width=windows->widget.min_width;
1761   windows->widget.height=(unsigned int)
1762     ((81*height) >> 2)+((13*QuantumMargin) >> 1)+4;
1763   windows->widget.min_height=(unsigned int)
1764     (((23*height) >> 1)+((13*QuantumMargin) >> 1)+4);
1765   if (windows->widget.height < windows->widget.min_height)
1766     windows->widget.height=windows->widget.min_height;
1767   XConstrainWindowPosition(display,&windows->widget);
1768   /*
1769     Map Color Browser widget.
1770   */
1771   (void) CopyMagickString(windows->widget.name,"Browse and Select a Color",
1772     MaxTextExtent);
1773   status=XStringListToTextProperty(&windows->widget.name,1,&window_name);
1774   if (status != False)
1775     {
1776       XSetWMName(display,windows->widget.id,&window_name);
1777       XSetWMIconName(display,windows->widget.id,&window_name);
1778       (void) XFree((void *) window_name.value);
1779     }
1780   window_changes.width=(int) windows->widget.width;
1781   window_changes.height=(int) windows->widget.height;
1782   window_changes.x=windows->widget.x;
1783   window_changes.y=windows->widget.y;
1784   (void) XReconfigureWMWindow(display,windows->widget.id,windows->widget.screen,
1785     mask,&window_changes);
1786   (void) XMapRaised(display,windows->widget.id);
1787   windows->widget.mapped=MagickFalse;
1788   /*
1789     Respond to X events.
1790   */
1791   XGetWidgetInfo((char *) NULL,&slider_info);
1792   XGetWidgetInfo((char *) NULL,&north_info);
1793   XGetWidgetInfo((char *) NULL,&south_info);
1794   XGetWidgetInfo((char *) NULL,&expose_info);
1795   visible_colors=0;
1796   delay=SuspendTime << 2;
1797   state=UpdateConfigurationState;
1798   do
1799   {
1800     if (state & UpdateConfigurationState)
1801       {
1802         int
1803           id;
1804
1805         /*
1806           Initialize button information.
1807         */
1808         XGetWidgetInfo(CancelButtonText,&cancel_info);
1809         cancel_info.width=width;
1810         cancel_info.height=(unsigned int) ((3*height) >> 1);
1811         cancel_info.x=(int)
1812           (windows->widget.width-cancel_info.width-QuantumMargin-2);
1813         cancel_info.y=(int)
1814           (windows->widget.height-cancel_info.height-QuantumMargin);
1815         XGetWidgetInfo(action,&action_info);
1816         action_info.width=width;
1817         action_info.height=(unsigned int) ((3*height) >> 1);
1818         action_info.x=cancel_info.x-(cancel_info.width+(QuantumMargin >> 1)+
1819           (action_info.bevel_width << 1));
1820         action_info.y=cancel_info.y;
1821         XGetWidgetInfo(GrabButtonText,&grab_info);
1822         grab_info.width=width;
1823         grab_info.height=(unsigned int) ((3*height) >> 1);
1824         grab_info.x=QuantumMargin;
1825         grab_info.y=((5*QuantumMargin) >> 1)+height;
1826         XGetWidgetInfo(ResetButtonText,&reset_info);
1827         reset_info.width=width;
1828         reset_info.height=(unsigned int) ((3*height) >> 1);
1829         reset_info.x=QuantumMargin;
1830         reset_info.y=grab_info.y+grab_info.height+QuantumMargin;
1831         /*
1832           Initialize reply information.
1833         */
1834         XGetWidgetInfo(reply,&reply_info);
1835         reply_info.raised=MagickFalse;
1836         reply_info.bevel_width--;
1837         reply_info.width=windows->widget.width-width-((6*QuantumMargin) >> 1);
1838         reply_info.height=height << 1;
1839         reply_info.x=(int) (width+(QuantumMargin << 1));
1840         reply_info.y=action_info.y-reply_info.height-QuantumMargin;
1841         /*
1842           Initialize mode information.
1843         */
1844         XGetWidgetInfo((char *) NULL,&mode_info);
1845         mode_info.active=MagickTrue;
1846         mode_info.bevel_width=0;
1847         mode_info.width=(unsigned int) (action_info.x-(QuantumMargin << 1));
1848         mode_info.height=action_info.height;
1849         mode_info.x=QuantumMargin;
1850         mode_info.y=action_info.y;
1851         /*
1852           Initialize scroll information.
1853         */
1854         XGetWidgetInfo((char *) NULL,&scroll_info);
1855         scroll_info.bevel_width--;
1856         scroll_info.width=height;
1857         scroll_info.height=(unsigned int) (reply_info.y-grab_info.y-
1858           (QuantumMargin >> 1));
1859         scroll_info.x=reply_info.x+(reply_info.width-scroll_info.width);
1860         scroll_info.y=grab_info.y-reply_info.bevel_width;
1861         scroll_info.raised=MagickFalse;
1862         scroll_info.trough=MagickTrue;
1863         north_info=scroll_info;
1864         north_info.raised=MagickTrue;
1865         north_info.width-=(north_info.bevel_width << 1);
1866         north_info.height=north_info.width-1;
1867         north_info.x+=north_info.bevel_width;
1868         north_info.y+=north_info.bevel_width;
1869         south_info=north_info;
1870         south_info.y=scroll_info.y+scroll_info.height-scroll_info.bevel_width-
1871           south_info.height;
1872         id=slider_info.id;
1873         slider_info=north_info;
1874         slider_info.id=id;
1875         slider_info.width-=2;
1876         slider_info.min_y=north_info.y+north_info.height+north_info.bevel_width+
1877           slider_info.bevel_width+2;
1878         slider_info.height=scroll_info.height-((slider_info.min_y-
1879           scroll_info.y+1) << 1)+4;
1880         visible_colors=scroll_info.height/(height+(height >> 3));
1881         if (colors > visible_colors)
1882           slider_info.height=(unsigned int)
1883             ((visible_colors*slider_info.height)/colors);
1884         slider_info.max_y=south_info.y-south_info.bevel_width-
1885           slider_info.bevel_width-2;
1886         slider_info.x=scroll_info.x+slider_info.bevel_width+1;
1887         slider_info.y=slider_info.min_y;
1888         expose_info=scroll_info;
1889         expose_info.y=slider_info.y;
1890         /*
1891           Initialize list information.
1892         */
1893         XGetWidgetInfo((char *) NULL,&list_info);
1894         list_info.raised=MagickFalse;
1895         list_info.bevel_width--;
1896         list_info.width=(unsigned int)
1897           (scroll_info.x-reply_info.x-(QuantumMargin >> 1));
1898         list_info.height=scroll_info.height;
1899         list_info.x=reply_info.x;
1900         list_info.y=scroll_info.y;
1901         if (windows->widget.mapped == MagickFalse)
1902           state|=JumpListState;
1903         /*
1904           Initialize text information.
1905         */
1906         *text='\0';
1907         XGetWidgetInfo(text,&text_info);
1908         text_info.center=MagickFalse;
1909         text_info.width=reply_info.width;
1910         text_info.height=height;
1911         text_info.x=list_info.x-(QuantumMargin >> 1);
1912         text_info.y=QuantumMargin;
1913         /*
1914           Initialize selection information.
1915         */
1916         XGetWidgetInfo((char *) NULL,&selection_info);
1917         selection_info.center=MagickFalse;
1918         selection_info.width=list_info.width;
1919         selection_info.height=(unsigned int) ((9*height) >> 3);
1920         selection_info.x=list_info.x;
1921         state&=(~UpdateConfigurationState);
1922       }
1923     if (state & RedrawWidgetState)
1924       {
1925         /*
1926           Redraw Color Browser window.
1927         */
1928         x=QuantumMargin;
1929         y=text_info.y+((text_info.height-height) >> 1)+font_info->ascent;
1930         (void) XDrawString(display,windows->widget.id,
1931           windows->widget.annotate_context,x,y,ColorPatternText,
1932           Extent(ColorPatternText));
1933         (void) CopyMagickString(text_info.text,glob_pattern,MaxTextExtent);
1934         XDrawWidgetText(display,&windows->widget,&text_info);
1935         XDrawBeveledButton(display,&windows->widget,&grab_info);
1936         XDrawBeveledButton(display,&windows->widget,&reset_info);
1937         XDrawBeveledMatte(display,&windows->widget,&list_info);
1938         XDrawBeveledMatte(display,&windows->widget,&scroll_info);
1939         XDrawTriangleNorth(display,&windows->widget,&north_info);
1940         XDrawBeveledButton(display,&windows->widget,&slider_info);
1941         XDrawTriangleSouth(display,&windows->widget,&south_info);
1942         x=QuantumMargin;
1943         y=reply_info.y+((reply_info.height-height) >> 1)+font_info->ascent;
1944         (void) XDrawString(display,windows->widget.id,
1945           windows->widget.annotate_context,x,y,ColornameText,
1946           Extent(ColornameText));
1947         XDrawBeveledMatte(display,&windows->widget,&reply_info);
1948         XDrawMatteText(display,&windows->widget,&reply_info);
1949         XDrawBeveledButton(display,&windows->widget,&action_info);
1950         XDrawBeveledButton(display,&windows->widget,&cancel_info);
1951         XHighlightWidget(display,&windows->widget,BorderOffset,BorderOffset);
1952         selection_info.id=(~0);
1953         state|=RedrawActionState;
1954         state|=RedrawListState;
1955         state&=(~RedrawWidgetState);
1956       }
1957     if (state & UpdateListState)
1958       {
1959         char
1960           **checklist;
1961
1962         size_t
1963           number_colors;
1964
1965         status=XParseColor(display,windows->widget.map_info->colormap,
1966           glob_pattern,&color);
1967         if ((status != False) || (strchr(glob_pattern,'-') != (char *) NULL))
1968           {
1969             /*
1970               Reply is a single color name-- exit.
1971             */
1972             (void) CopyMagickString(reply,glob_pattern,MaxTextExtent);
1973             (void) CopyMagickString(glob_pattern,reset_pattern,MaxTextExtent);
1974             action_info.raised=MagickFalse;
1975             XDrawBeveledButton(display,&windows->widget,&action_info);
1976             break;
1977           }
1978         /*
1979           Update color list.
1980         */
1981         checklist=GetColorList(glob_pattern,&number_colors,exception);
1982         if (number_colors == 0)
1983           {
1984             (void) CopyMagickString(glob_pattern,reset_pattern,MaxTextExtent);
1985             (void) XBell(display,0);
1986           }
1987         else
1988           {
1989             for (i=0; i < (int) colors; i++)
1990               colorlist[i]=DestroyString(colorlist[i]);
1991             if (colorlist != (char **) NULL)
1992               colorlist=(char **) RelinquishMagickMemory(colorlist);
1993             colorlist=checklist;
1994             colors=number_colors;
1995           }
1996         /*
1997           Sort color list in ascending order.
1998         */
1999         slider_info.height=
2000           scroll_info.height-((slider_info.min_y-scroll_info.y+1) << 1)+1;
2001         if (colors > visible_colors)
2002           slider_info.height=(unsigned int)
2003             ((visible_colors*slider_info.height)/colors);
2004         slider_info.max_y=south_info.y-south_info.bevel_width-
2005           slider_info.bevel_width-2;
2006         slider_info.id=0;
2007         slider_info.y=slider_info.min_y;
2008         expose_info.y=slider_info.y;
2009         selection_info.id=(~0);
2010         list_info.id=(~0);
2011         state|=RedrawListState;
2012         /*
2013           Redraw color name & reply.
2014         */
2015         *reply_info.text='\0';
2016         reply_info.cursor=reply_info.text;
2017         (void) CopyMagickString(text_info.text,glob_pattern,MaxTextExtent);
2018         XDrawWidgetText(display,&windows->widget,&text_info);
2019         XDrawMatteText(display,&windows->widget,&reply_info);
2020         XDrawBeveledMatte(display,&windows->widget,&scroll_info);
2021         XDrawTriangleNorth(display,&windows->widget,&north_info);
2022         XDrawBeveledButton(display,&windows->widget,&slider_info);
2023         XDrawTriangleSouth(display,&windows->widget,&south_info);
2024         XHighlightWidget(display,&windows->widget,BorderOffset,BorderOffset);
2025         state&=(~UpdateListState);
2026       }
2027     if (state & JumpListState)
2028       {
2029         /*
2030           Jump scroll to match user color.
2031         */
2032         list_info.id=(~0);
2033         for (i=0; i < (int) colors; i++)
2034           if (LocaleCompare(colorlist[i],reply) >= 0)
2035             {
2036               list_info.id=LocaleCompare(colorlist[i],reply) == 0 ? i : ~0;
2037               break;
2038             }
2039         if ((i < slider_info.id) ||
2040             (i >= (int) (slider_info.id+visible_colors)))
2041           slider_info.id=i-(visible_colors >> 1);
2042         selection_info.id=(~0);
2043         state|=RedrawListState;
2044         state&=(~JumpListState);
2045       }
2046     if (state & RedrawListState)
2047       {
2048         /*
2049           Determine slider id and position.
2050         */
2051         if (slider_info.id >= (int) (colors-visible_colors))
2052           slider_info.id=(int) (colors-visible_colors);
2053         if ((slider_info.id < 0) || (colors <= visible_colors))
2054           slider_info.id=0;
2055         slider_info.y=slider_info.min_y;
2056         if (colors != 0)
2057           slider_info.y+=(int) (slider_info.id*(slider_info.max_y-
2058             slider_info.min_y+1)/colors);
2059         if (slider_info.id != selection_info.id)
2060           {
2061             /*
2062               Redraw scroll bar and file names.
2063             */
2064             selection_info.id=slider_info.id;
2065             selection_info.y=list_info.y+(height >> 3)+2;
2066             for (i=0; i < (int) visible_colors; i++)
2067             {
2068               selection_info.raised=(slider_info.id+i) != list_info.id ?
2069                 MagickTrue : MagickFalse;
2070               selection_info.text=(char *) NULL;
2071               if ((slider_info.id+i) < (int) colors)
2072                 selection_info.text=colorlist[slider_info.id+i];
2073               XDrawWidgetText(display,&windows->widget,&selection_info);
2074               selection_info.y+=(int) selection_info.height;
2075             }
2076             /*
2077               Update slider.
2078             */
2079             if (slider_info.y > expose_info.y)
2080               {
2081                 expose_info.height=(unsigned int) slider_info.y-expose_info.y;
2082                 expose_info.y=slider_info.y-expose_info.height-
2083                   slider_info.bevel_width-1;
2084               }
2085             else
2086               {
2087                 expose_info.height=(unsigned int) expose_info.y-slider_info.y;
2088                 expose_info.y=slider_info.y+slider_info.height+
2089                   slider_info.bevel_width+1;
2090               }
2091             XDrawTriangleNorth(display,&windows->widget,&north_info);
2092             XDrawMatte(display,&windows->widget,&expose_info);
2093             XDrawBeveledButton(display,&windows->widget,&slider_info);
2094             XDrawTriangleSouth(display,&windows->widget,&south_info);
2095             expose_info.y=slider_info.y;
2096           }
2097         state&=(~RedrawListState);
2098       }
2099     if (state & RedrawActionState)
2100       {
2101         static char
2102           colorname[MaxTextExtent];
2103
2104         /*
2105           Display the selected color in a drawing area.
2106         */
2107         color=windows->widget.pixel_info->matte_color;
2108         (void) XParseColor(display,windows->widget.map_info->colormap,
2109           reply_info.text,&windows->widget.pixel_info->matte_color);
2110         XBestPixel(display,windows->widget.map_info->colormap,(XColor *) NULL,
2111           (unsigned int) windows->widget.visual_info->colormap_size,
2112           &windows->widget.pixel_info->matte_color);
2113         mode_info.text=colorname;
2114         (void) FormatLocaleString(mode_info.text,MaxTextExtent,"#%02x%02x%02x",
2115           windows->widget.pixel_info->matte_color.red,
2116           windows->widget.pixel_info->matte_color.green,
2117           windows->widget.pixel_info->matte_color.blue);
2118         XDrawBeveledButton(display,&windows->widget,&mode_info);
2119         windows->widget.pixel_info->matte_color=color;
2120         state&=(~RedrawActionState);
2121       }
2122     /*
2123       Wait for next event.
2124     */
2125     if (north_info.raised && south_info.raised)
2126       (void) XIfEvent(display,&event,XScreenEvent,(char *) windows);
2127     else
2128       {
2129         /*
2130           Brief delay before advancing scroll bar.
2131         */
2132         XDelay(display,delay);
2133         delay=SuspendTime;
2134         (void) XCheckIfEvent(display,&event,XScreenEvent,(char *) windows);
2135         if (north_info.raised == MagickFalse)
2136           if (slider_info.id > 0)
2137             {
2138               /*
2139                 Move slider up.
2140               */
2141               slider_info.id--;
2142               state|=RedrawListState;
2143             }
2144         if (south_info.raised == MagickFalse)
2145           if (slider_info.id < (int) colors)
2146             {
2147               /*
2148                 Move slider down.
2149               */
2150               slider_info.id++;
2151               state|=RedrawListState;
2152             }
2153         if (event.type != ButtonRelease)
2154           continue;
2155       }
2156     switch (event.type)
2157     {
2158       case ButtonPress:
2159       {
2160         if (MatteIsActive(slider_info,event.xbutton))
2161           {
2162             /*
2163               Track slider.
2164             */
2165             slider_info.active=MagickTrue;
2166             break;
2167           }
2168         if (MatteIsActive(north_info,event.xbutton))
2169           if (slider_info.id > 0)
2170             {
2171               /*
2172                 Move slider up.
2173               */
2174               north_info.raised=MagickFalse;
2175               slider_info.id--;
2176               state|=RedrawListState;
2177               break;
2178             }
2179         if (MatteIsActive(south_info,event.xbutton))
2180           if (slider_info.id < (int) colors)
2181             {
2182               /*
2183                 Move slider down.
2184               */
2185               south_info.raised=MagickFalse;
2186               slider_info.id++;
2187               state|=RedrawListState;
2188               break;
2189             }
2190         if (MatteIsActive(scroll_info,event.xbutton))
2191           {
2192             /*
2193               Move slider.
2194             */
2195             if (event.xbutton.y < slider_info.y)
2196               slider_info.id-=(visible_colors-1);
2197             else
2198               slider_info.id+=(visible_colors-1);
2199             state|=RedrawListState;
2200             break;
2201           }
2202         if (MatteIsActive(list_info,event.xbutton))
2203           {
2204             int
2205               id;
2206
2207             /*
2208               User pressed list matte.
2209             */
2210             id=slider_info.id+(event.xbutton.y-(list_info.y+(height >> 1))+1)/
2211               selection_info.height;
2212             if (id >= (int) colors)
2213               break;
2214             (void) CopyMagickString(reply_info.text,colorlist[id],
2215               MaxTextExtent);
2216             reply_info.highlight=MagickFalse;
2217             reply_info.marker=reply_info.text;
2218             reply_info.cursor=reply_info.text+Extent(reply_info.text);
2219             XDrawMatteText(display,&windows->widget,&reply_info);
2220             state|=RedrawActionState;
2221             if (id == list_info.id)
2222               {
2223                 (void) CopyMagickString(glob_pattern,reply_info.text,
2224                   MaxTextExtent);
2225                 state|=UpdateListState;
2226               }
2227             selection_info.id=(~0);
2228             list_info.id=id;
2229             state|=RedrawListState;
2230             break;
2231           }
2232         if (MatteIsActive(grab_info,event.xbutton))
2233           {
2234             /*
2235               User pressed Grab button.
2236             */
2237             grab_info.raised=MagickFalse;
2238             XDrawBeveledButton(display,&windows->widget,&grab_info);
2239             break;
2240           }
2241         if (MatteIsActive(reset_info,event.xbutton))
2242           {
2243             /*
2244               User pressed Reset button.
2245             */
2246             reset_info.raised=MagickFalse;
2247             XDrawBeveledButton(display,&windows->widget,&reset_info);
2248             break;
2249           }
2250         if (MatteIsActive(mode_info,event.xbutton))
2251           {
2252             /*
2253               User pressed mode button.
2254             */
2255             (void) CopyMagickString(reply_info.text,mode_info.text,
2256               MaxTextExtent);
2257             (void) CopyMagickString(primary_selection,reply_info.text,
2258               MaxTextExtent);
2259             (void) XSetSelectionOwner(display,XA_PRIMARY,windows->widget.id,
2260               event.xbutton.time);
2261             reply_info.highlight=XGetSelectionOwner(display,XA_PRIMARY) ==
2262               windows->widget.id ? MagickTrue : MagickFalse;
2263             reply_info.marker=reply_info.text;
2264             reply_info.cursor=reply_info.text+Extent(reply_info.text);
2265             XDrawMatteText(display,&windows->widget,&reply_info);
2266             break;
2267           }
2268         if (MatteIsActive(action_info,event.xbutton))
2269           {
2270             /*
2271               User pressed action button.
2272             */
2273             action_info.raised=MagickFalse;
2274             XDrawBeveledButton(display,&windows->widget,&action_info);
2275             break;
2276           }
2277         if (MatteIsActive(cancel_info,event.xbutton))
2278           {
2279             /*
2280               User pressed Cancel button.
2281             */
2282             cancel_info.raised=MagickFalse;
2283             XDrawBeveledButton(display,&windows->widget,&cancel_info);
2284             break;
2285           }
2286         if (MatteIsActive(reply_info,event.xbutton) == MagickFalse)
2287           break;
2288         if (event.xbutton.button != Button2)
2289           {
2290             static Time
2291               click_time;
2292
2293             /*
2294               Move text cursor to position of button press.
2295             */
2296             x=event.xbutton.x-reply_info.x-(QuantumMargin >> 2);
2297             for (i=1; i <= Extent(reply_info.marker); i++)
2298               if (XTextWidth(font_info,reply_info.marker,i) > x)
2299                 break;
2300             reply_info.cursor=reply_info.marker+i-1;
2301             if (event.xbutton.time > (click_time+DoubleClick))
2302               reply_info.highlight=MagickFalse;
2303             else
2304               {
2305                 /*
2306                   Become the XA_PRIMARY selection owner.
2307                 */
2308                 (void) CopyMagickString(primary_selection,reply_info.text,
2309                   MaxTextExtent);
2310                 (void) XSetSelectionOwner(display,XA_PRIMARY,windows->widget.id,
2311                   event.xbutton.time);
2312                 reply_info.highlight=XGetSelectionOwner(display,XA_PRIMARY) ==
2313                   windows->widget.id ? MagickTrue : MagickFalse;
2314               }
2315             XDrawMatteText(display,&windows->widget,&reply_info);
2316             click_time=event.xbutton.time;
2317             break;
2318           }
2319         /*
2320           Request primary selection.
2321         */
2322         (void) XConvertSelection(display,XA_PRIMARY,XA_STRING,XA_STRING,
2323           windows->widget.id,event.xbutton.time);
2324         break;
2325       }
2326       case ButtonRelease:
2327       {
2328         if (windows->widget.mapped == MagickFalse)
2329           break;
2330         if (north_info.raised == MagickFalse)
2331           {
2332             /*
2333               User released up button.
2334             */
2335             delay=SuspendTime << 2;
2336             north_info.raised=MagickTrue;
2337             XDrawTriangleNorth(display,&windows->widget,&north_info);
2338           }
2339         if (south_info.raised == MagickFalse)
2340           {
2341             /*
2342               User released down button.
2343             */
2344             delay=SuspendTime << 2;
2345             south_info.raised=MagickTrue;
2346             XDrawTriangleSouth(display,&windows->widget,&south_info);
2347           }
2348         if (slider_info.active)
2349           {
2350             /*
2351               Stop tracking slider.
2352             */
2353             slider_info.active=MagickFalse;
2354             break;
2355           }
2356         if (grab_info.raised == MagickFalse)
2357           {
2358             if (event.xbutton.window == windows->widget.id)
2359               if (MatteIsActive(grab_info,event.xbutton))
2360                 {
2361                   /*
2362                     Select a fill color from the X server.
2363                   */
2364                   (void) XGetWindowColor(display,windows,reply_info.text,
2365                     exception);
2366                   reply_info.marker=reply_info.text;
2367                   reply_info.cursor=reply_info.text+Extent(reply_info.text);
2368                   XDrawMatteText(display,&windows->widget,&reply_info);
2369                   state|=RedrawActionState;
2370                 }
2371             grab_info.raised=MagickTrue;
2372             XDrawBeveledButton(display,&windows->widget,&grab_info);
2373           }
2374         if (reset_info.raised == MagickFalse)
2375           {
2376             if (event.xbutton.window == windows->widget.id)
2377               if (MatteIsActive(reset_info,event.xbutton))
2378                 {
2379                   (void) CopyMagickString(glob_pattern,reset_pattern,
2380                     MaxTextExtent);
2381                   state|=UpdateListState;
2382                 }
2383             reset_info.raised=MagickTrue;
2384             XDrawBeveledButton(display,&windows->widget,&reset_info);
2385           }
2386         if (action_info.raised == MagickFalse)
2387           {
2388             if (event.xbutton.window == windows->widget.id)
2389               {
2390                 if (MatteIsActive(action_info,event.xbutton))
2391                   {
2392                     if (*reply_info.text == '\0')
2393                       (void) XBell(display,0);
2394                     else
2395                       state|=ExitState;
2396                   }
2397               }
2398             action_info.raised=MagickTrue;
2399             XDrawBeveledButton(display,&windows->widget,&action_info);
2400           }
2401         if (cancel_info.raised == MagickFalse)
2402           {
2403             if (event.xbutton.window == windows->widget.id)
2404               if (MatteIsActive(cancel_info,event.xbutton))
2405                 {
2406                   *reply_info.text='\0';
2407                   state|=ExitState;
2408                 }
2409             cancel_info.raised=MagickTrue;
2410             XDrawBeveledButton(display,&windows->widget,&cancel_info);
2411           }
2412         if (MatteIsActive(reply_info,event.xbutton) == MagickFalse)
2413           break;
2414         break;
2415       }
2416       case ClientMessage:
2417       {
2418         /*
2419           If client window delete message, exit.
2420         */
2421         if (event.xclient.message_type != windows->wm_protocols)
2422           break;
2423         if (*event.xclient.data.l == (int) windows->wm_take_focus)
2424           {
2425             (void) XSetInputFocus(display,event.xclient.window,RevertToParent,
2426               (Time) event.xclient.data.l[1]);
2427             break;
2428           }
2429         if (*event.xclient.data.l != (int) windows->wm_delete_window)
2430           break;
2431         if (event.xclient.window == windows->widget.id)
2432           {
2433             *reply_info.text='\0';
2434             state|=ExitState;
2435             break;
2436           }
2437         break;
2438       }
2439       case ConfigureNotify:
2440       {
2441         /*
2442           Update widget configuration.
2443         */
2444         if (event.xconfigure.window != windows->widget.id)
2445           break;
2446         if ((event.xconfigure.width == (int) windows->widget.width) &&
2447             (event.xconfigure.height == (int) windows->widget.height))
2448           break;
2449         windows->widget.width=(unsigned int)
2450           MagickMax(event.xconfigure.width,(int) windows->widget.min_width);
2451         windows->widget.height=(unsigned int)
2452           MagickMax(event.xconfigure.height,(int) windows->widget.min_height);
2453         state|=UpdateConfigurationState;
2454         break;
2455       }
2456       case EnterNotify:
2457       {
2458         if (event.xcrossing.window != windows->widget.id)
2459           break;
2460         state&=(~InactiveWidgetState);
2461         break;
2462       }
2463       case Expose:
2464       {
2465         if (event.xexpose.window != windows->widget.id)
2466           break;
2467         if (event.xexpose.count != 0)
2468           break;
2469         state|=RedrawWidgetState;
2470         break;
2471       }
2472       case KeyPress:
2473       {
2474         static char
2475           command[MaxTextExtent];
2476
2477         static int
2478           length;
2479
2480         static KeySym
2481           key_symbol;
2482
2483         /*
2484           Respond to a user key press.
2485         */
2486         if (event.xkey.window != windows->widget.id)
2487           break;
2488         length=XLookupString((XKeyEvent *) &event.xkey,command,
2489           (int) sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2490         *(command+length)='\0';
2491         if (AreaIsActive(scroll_info,event.xkey))
2492           {
2493             /*
2494               Move slider.
2495             */
2496             switch ((int) key_symbol)
2497             {
2498               case XK_Home:
2499               case XK_KP_Home:
2500               {
2501                 slider_info.id=0;
2502                 break;
2503               }
2504               case XK_Up:
2505               case XK_KP_Up:
2506               {
2507                 slider_info.id--;
2508                 break;
2509               }
2510               case XK_Down:
2511               case XK_KP_Down:
2512               {
2513                 slider_info.id++;
2514                 break;
2515               }
2516               case XK_Prior:
2517               case XK_KP_Prior:
2518               {
2519                 slider_info.id-=visible_colors;
2520                 break;
2521               }
2522               case XK_Next:
2523               case XK_KP_Next:
2524               {
2525                 slider_info.id+=visible_colors;
2526                 break;
2527               }
2528               case XK_End:
2529               case XK_KP_End:
2530               {
2531                 slider_info.id=(int) colors;
2532                 break;
2533               }
2534             }
2535             state|=RedrawListState;
2536             break;
2537           }
2538         if ((key_symbol == XK_Return) || (key_symbol == XK_KP_Enter))
2539           {
2540             /*
2541               Read new color or glob patterm.
2542             */
2543             if (*reply_info.text == '\0')
2544               break;
2545             (void) CopyMagickString(glob_pattern,reply_info.text,MaxTextExtent);
2546             state|=UpdateListState;
2547             break;
2548           }
2549         if (key_symbol == XK_Control_L)
2550           {
2551             state|=ControlState;
2552             break;
2553           }
2554         if (state & ControlState)
2555           switch ((int) key_symbol)
2556           {
2557             case XK_u:
2558             case XK_U:
2559             {
2560               /*
2561                 Erase the entire line of text.
2562               */
2563               *reply_info.text='\0';
2564               reply_info.cursor=reply_info.text;
2565               reply_info.marker=reply_info.text;
2566               reply_info.highlight=MagickFalse;
2567               break;
2568             }
2569             default:
2570               break;
2571           }
2572         XEditText(display,&reply_info,key_symbol,command,state);
2573         XDrawMatteText(display,&windows->widget,&reply_info);
2574         state|=JumpListState;
2575         status=XParseColor(display,windows->widget.map_info->colormap,
2576           reply_info.text,&color);
2577         if (status != False)
2578           state|=RedrawActionState;
2579         break;
2580       }
2581       case KeyRelease:
2582       {
2583         static char
2584           command[MaxTextExtent];
2585
2586         static KeySym
2587           key_symbol;
2588
2589         /*
2590           Respond to a user key release.
2591         */
2592         if (event.xkey.window != windows->widget.id)
2593           break;
2594         (void) XLookupString((XKeyEvent *) &event.xkey,command,
2595           (int) sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2596         if (key_symbol == XK_Control_L)
2597           state&=(~ControlState);
2598         break;
2599       }
2600       case LeaveNotify:
2601       {
2602         if (event.xcrossing.window != windows->widget.id)
2603           break;
2604         state|=InactiveWidgetState;
2605         break;
2606       }
2607       case MapNotify:
2608       {
2609         mask&=(~CWX);
2610         mask&=(~CWY);
2611         break;
2612       }
2613       case MotionNotify:
2614       {
2615         /*
2616           Discard pending button motion events.
2617         */
2618         while (XCheckMaskEvent(display,ButtonMotionMask,&event)) ;
2619         if (slider_info.active)
2620           {
2621             /*
2622               Move slider matte.
2623             */
2624             slider_info.y=event.xmotion.y-
2625               ((slider_info.height+slider_info.bevel_width) >> 1)+1;
2626             if (slider_info.y < slider_info.min_y)
2627               slider_info.y=slider_info.min_y;
2628             if (slider_info.y > slider_info.max_y)
2629               slider_info.y=slider_info.max_y;
2630             slider_info.id=0;
2631             if (slider_info.y != slider_info.min_y)
2632               slider_info.id=(int) ((colors*(slider_info.y-
2633                 slider_info.min_y+1))/(slider_info.max_y-slider_info.min_y+1));
2634             state|=RedrawListState;
2635             break;
2636           }
2637         if (state & InactiveWidgetState)
2638           break;
2639         if (grab_info.raised == MatteIsActive(grab_info,event.xmotion))
2640           {
2641             /*
2642               Grab button status changed.
2643             */
2644             grab_info.raised=!grab_info.raised;
2645             XDrawBeveledButton(display,&windows->widget,&grab_info);
2646             break;
2647           }
2648         if (reset_info.raised == MatteIsActive(reset_info,event.xmotion))
2649           {
2650             /*
2651               Reset button status changed.
2652             */
2653             reset_info.raised=!reset_info.raised;
2654             XDrawBeveledButton(display,&windows->widget,&reset_info);
2655             break;
2656           }
2657         if (action_info.raised == MatteIsActive(action_info,event.xmotion))
2658           {
2659             /*
2660               Action button status changed.
2661             */
2662             action_info.raised=action_info.raised == MagickFalse ?
2663               MagickTrue : MagickFalse;
2664             XDrawBeveledButton(display,&windows->widget,&action_info);
2665             break;
2666           }
2667         if (cancel_info.raised == MatteIsActive(cancel_info,event.xmotion))
2668           {
2669             /*
2670               Cancel button status changed.
2671             */
2672             cancel_info.raised=cancel_info.raised == MagickFalse ?
2673               MagickTrue : MagickFalse;
2674             XDrawBeveledButton(display,&windows->widget,&cancel_info);
2675             break;
2676           }
2677         break;
2678       }
2679       case SelectionClear:
2680       {
2681         reply_info.highlight=MagickFalse;
2682         XDrawMatteText(display,&windows->widget,&reply_info);
2683         break;
2684       }
2685       case SelectionNotify:
2686       {
2687         Atom
2688           type;
2689
2690         int
2691           format;
2692
2693         unsigned char
2694           *data;
2695
2696         unsigned long
2697           after,
2698           length;
2699
2700         /*
2701           Obtain response from primary selection.
2702         */
2703         if (event.xselection.property == (Atom) None)
2704           break;
2705         status=XGetWindowProperty(display,event.xselection.requestor,
2706           event.xselection.property,0L,2047L,MagickTrue,XA_STRING,&type,
2707           &format,&length,&after,&data);
2708         if ((status != Success) || (type != XA_STRING) || (format == 32) ||
2709             (length == 0))
2710           break;
2711         if ((Extent(reply_info.text)+length) >= (MaxTextExtent-1))
2712           (void) XBell(display,0);
2713         else
2714           {
2715             /*
2716               Insert primary selection in reply text.
2717             */
2718             *(data+length)='\0';
2719             XEditText(display,&reply_info,(KeySym) XK_Insert,(char *) data,
2720               state);
2721             XDrawMatteText(display,&windows->widget,&reply_info);
2722             state|=JumpListState;
2723             state|=RedrawActionState;
2724           }
2725         (void) XFree((void *) data);
2726         break;
2727       }
2728       case SelectionRequest:
2729       {
2730         XSelectionEvent
2731           notify;
2732
2733         XSelectionRequestEvent
2734           *request;
2735
2736         if (reply_info.highlight == MagickFalse)
2737           break;
2738         /*
2739           Set primary selection.
2740         */
2741         request=(&(event.xselectionrequest));
2742         (void) XChangeProperty(request->display,request->requestor,
2743           request->property,request->target,8,PropModeReplace,
2744           (unsigned char *) primary_selection,Extent(primary_selection));
2745         notify.type=SelectionNotify;
2746         notify.send_event=MagickTrue;
2747         notify.display=request->display;
2748         notify.requestor=request->requestor;
2749         notify.selection=request->selection;
2750         notify.target=request->target;
2751         notify.time=request->time;
2752         if (request->property == None)
2753           notify.property=request->target;
2754         else
2755           notify.property=request->property;
2756         (void) XSendEvent(request->display,request->requestor,False,
2757           NoEventMask,(XEvent *) &notify);
2758       }
2759       default:
2760         break;
2761     }
2762   } while ((state & ExitState) == 0);
2763   XSetCursorState(display,windows,MagickFalse);
2764   (void) XWithdrawWindow(display,windows->widget.id,windows->widget.screen);
2765   XCheckRefreshWindows(display,windows);
2766   /*
2767     Free color list.
2768   */
2769   for (i=0; i < (int) colors; i++)
2770     colorlist[i]=DestroyString(colorlist[i]);
2771   if (colorlist != (char **) NULL)
2772     colorlist=(char **) RelinquishMagickMemory(colorlist);
2773   exception=DestroyExceptionInfo(exception);
2774   if ((*reply == '\0') || (strchr(reply,'-') != (char *) NULL))
2775     return;
2776   status=XParseColor(display,windows->widget.map_info->colormap,reply,&color);
2777   if (status != False)
2778     return;
2779   XNoticeWidget(display,windows,"Color is unknown to X server:",reply);
2780   (void) CopyMagickString(reply,"gray",MaxTextExtent);
2781 }
2782 \f
2783 /*
2784 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2785 %                                                                             %
2786 %                                                                             %
2787 %                                                                             %
2788 %   X C o m m a n d W i d g e t                                               %
2789 %                                                                             %
2790 %                                                                             %
2791 %                                                                             %
2792 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2793 %
2794 %  XCommandWidget() maps a menu and returns the command pointed to by the user
2795 %  when the button is released.
2796 %
2797 %  The format of the XCommandWidget method is:
2798 %
2799 %      int XCommandWidget(Display *display,XWindows *windows,
2800 %        const char **selections,XEvent *event)
2801 %
2802 %  A description of each parameter follows:
2803 %
2804 %    o selection_number: Specifies the number of the selection that the
2805 %      user choose.
2806 %
2807 %    o display: Specifies a connection to an X server;  returned from
2808 %      XOpenDisplay.
2809 %
2810 %    o window: Specifies a pointer to a XWindows structure.
2811 %
2812 %    o selections: Specifies a pointer to one or more strings that comprise
2813 %      the choices in the menu.
2814 %
2815 %    o event: Specifies a pointer to a X11 XEvent structure.
2816 %
2817 */
2818 MagickPrivate int XCommandWidget(Display *display,XWindows *windows,
2819   const char **selections,XEvent *event)
2820 {
2821 #define tile_width 112
2822 #define tile_height 70
2823
2824   static const unsigned char
2825     tile_bits[]=
2826     {
2827       0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2828       0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2829       0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2830       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00,
2831       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00,
2832       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00,
2833       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2834       0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2835       0x00, 0x00, 0x1e, 0x38, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2836       0x00, 0x00, 0x00, 0x00, 0x1e, 0xbc, 0x9f, 0x03, 0x00, 0x3e, 0x00, 0xc0,
2837       0x1f, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x1e, 0xfc, 0xff, 0x0f, 0x80, 0x3f,
2838       0x00, 0xf0, 0x1f, 0xc0, 0x0f, 0x00, 0x00, 0x00, 0x1e, 0xfc, 0xff, 0x1f,
2839       0xe0, 0x3f, 0x00, 0xfc, 0x1f, 0xf0, 0x0f, 0x00, 0x00, 0x00, 0x1e, 0xfc,
2840       0xff, 0x1f, 0xf0, 0x3f, 0x00, 0xfe, 0x1f, 0xf8, 0x0f, 0x00, 0x00, 0x00,
2841       0x1e, 0xfc, 0xfc, 0x3f, 0xf8, 0x3f, 0x00, 0xff, 0x1e, 0xfc, 0x0f, 0x00,
2842       0x00, 0x00, 0x1e, 0x7c, 0xfc, 0x3e, 0xf8, 0x3c, 0x80, 0x1f, 0x1e, 0x7c,
2843       0x0f, 0x00, 0x00, 0x00, 0x1e, 0x78, 0x78, 0x3c, 0x7c, 0x3c, 0xc0, 0x0f,
2844       0x1e, 0x3e, 0x0f, 0x00, 0x00, 0x00, 0x1e, 0x78, 0x78, 0x3c, 0x7c, 0x3c,
2845       0xc0, 0x07, 0x1e, 0x3e, 0x0f, 0x00, 0x00, 0x00, 0x1e, 0x78, 0x78, 0x3c,
2846       0x7c, 0x7c, 0xc0, 0x0f, 0x1e, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x78,
2847       0x78, 0x3c, 0xfc, 0x7c, 0x80, 0x7f, 0x1e, 0x7c, 0x00, 0x00, 0x00, 0x00,
2848       0x1e, 0xf8, 0x78, 0x7c, 0xf8, 0xff, 0x00, 0xff, 0x1f, 0xf8, 0xff, 0x00,
2849       0x00, 0x00, 0x1e, 0xf8, 0x78, 0x7c, 0xf0, 0xff, 0x07, 0xfe, 0x1f, 0xf8,
2850       0xff, 0x00, 0x00, 0x00, 0x1e, 0xf8, 0x78, 0x7c, 0xf0, 0xff, 0x07, 0xf8,
2851       0x1f, 0xf0, 0xff, 0x01, 0x00, 0x00, 0x1e, 0xf8, 0x78, 0x7c, 0xc0, 0xef,
2852       0x07, 0xe0, 0x1f, 0xc0, 0xff, 0x01, 0x00, 0x00, 0x1e, 0x70, 0x40, 0x78,
2853       0x00, 0xc7, 0x07, 0x00, 0x1e, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x1e, 0x00,
2854       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00,
2855       0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00,
2856       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00,
2857       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
2858       0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2859       0x00, 0xc0, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2860       0x00, 0x00, 0x00, 0xe0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2861       0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
2862       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x01, 0x00, 0x00, 0x00,
2863       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x00,
2864       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78,
2865       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2866       0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x02, 0x00,
2867       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x07,
2868       0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2869       0xc0, 0x0f, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2870       0x60, 0x00, 0xc0, 0x0f, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2871       0x00, 0x00, 0x78, 0x00, 0xc0, 0x8f, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00,
2872       0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0xc0, 0x8f, 0x3f, 0x00, 0x00, 0x00,
2873       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0xe0, 0x9f, 0x7f, 0x00,
2874       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0xe0, 0xdf,
2875       0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x78, 0x00,
2876       0xe0, 0xdf, 0x7b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x0c,
2877       0x78, 0x30, 0xf0, 0xff, 0x7b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e,
2878       0x00, 0x0f, 0xf8, 0x70, 0xf0, 0xff, 0x7b, 0x00, 0x00, 0x1f, 0x00, 0xe0,
2879       0x0f, 0x1e, 0x80, 0x0f, 0xf8, 0x78, 0xf0, 0xfd, 0xf9, 0x00, 0xc0, 0x1f,
2880       0x00, 0xf8, 0x0f, 0x00, 0xe0, 0x1f, 0xf8, 0x7c, 0xf0, 0xfc, 0xf9, 0x00,
2881       0xf0, 0x1f, 0x00, 0xfe, 0x0f, 0x00, 0xf0, 0x07, 0xf8, 0x3e, 0xf8, 0xfc,
2882       0xf0, 0x01, 0xf8, 0x1f, 0x00, 0xff, 0x0f, 0x1e, 0xf0, 0x03, 0xf8, 0x3f,
2883       0xf8, 0xf8, 0xf0, 0x01, 0xfc, 0x1f, 0x80, 0x7f, 0x0f, 0x1e, 0xf8, 0x00,
2884       0xf8, 0x1f, 0x78, 0x18, 0xf0, 0x01, 0x7c, 0x1e, 0xc0, 0x0f, 0x0f, 0x1e,
2885       0x7c, 0x00, 0xf0, 0x0f, 0x78, 0x00, 0xf0, 0x01, 0x3e, 0x1e, 0xe0, 0x07,
2886       0x0f, 0x1e, 0x7c, 0x00, 0xf0, 0x07, 0x7c, 0x00, 0xe0, 0x01, 0x3e, 0x1e,
2887       0xe0, 0x03, 0x0f, 0x1e, 0x3e, 0x00, 0xf0, 0x0f, 0x7c, 0x00, 0xe0, 0x03,
2888       0x3e, 0x3e, 0xe0, 0x07, 0x0f, 0x1e, 0x1e, 0x00, 0xf0, 0x1f, 0x3c, 0x00,
2889       0xe0, 0x03, 0x7e, 0x3e, 0xc0, 0x3f, 0x0f, 0x1e, 0x3e, 0x00, 0xf0, 0x1f,
2890       0x3e, 0x00, 0xe0, 0x03, 0xfc, 0x7f, 0x80, 0xff, 0x0f, 0x1e, 0xfc, 0x00,
2891       0xf0, 0x3e, 0x3e, 0x00, 0xc0, 0x03, 0xf8, 0xff, 0x03, 0xff, 0x0f, 0x1e,
2892       0xfc, 0x07, 0xf0, 0x7c, 0x1e, 0x00, 0xc0, 0x03, 0xf8, 0xff, 0x03, 0xfc,
2893       0x0f, 0x1e, 0xf8, 0x1f, 0xf0, 0xf8, 0x1e, 0x00, 0xc0, 0x03, 0xe0, 0xf7,
2894       0x03, 0xf0, 0x0f, 0x1e, 0xe0, 0x3f, 0xf0, 0x78, 0x1c, 0x00, 0x80, 0x03,
2895       0x80, 0xe3, 0x03, 0x00, 0x0f, 0x1e, 0xc0, 0x3f, 0xf0, 0x30, 0x00, 0x00,
2896       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0e, 0x00, 0x3e, 0x00, 0x00,
2897       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x0f, 0x00, 0x00, 0x10,
2898       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x0f, 0x00,
2899       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0,
2900       0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2901       0x00, 0xe0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2902       0x00, 0x00, 0x00, 0xf0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2903       0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
2904       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, 0x00,
2905       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00,
2906       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c,
2907       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2908       0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
2909     };
2910
2911   int
2912     id,
2913     y;
2914
2915   register int
2916     i;
2917
2918   static unsigned int
2919     number_selections;
2920
2921   unsigned int
2922     height;
2923
2924   size_t
2925     state;
2926
2927   XFontStruct
2928     *font_info;
2929
2930   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2931   assert(display != (Display *) NULL);
2932   assert(windows != (XWindows *) NULL);
2933   font_info=windows->command.font_info;
2934   height=(unsigned int) (font_info->ascent+font_info->descent);
2935   id=(~0);
2936   state=DefaultState;
2937   if (event == (XEvent *) NULL)
2938     {
2939       unsigned int
2940         width;
2941
2942       XTextProperty
2943         window_name;
2944
2945       XWindowChanges
2946         window_changes;
2947
2948       /*
2949         Determine command window attributes.
2950       */
2951       assert(selections != (const char **) NULL);
2952       windows->command.width=0;
2953       for (i=0; selections[i] != (char *) NULL; i++)
2954       {
2955         width=WidgetTextWidth(font_info,(char *) selections[i]);
2956         if (width > windows->command.width)
2957           windows->command.width=width;
2958       }
2959       number_selections=(unsigned int) i;
2960       windows->command.width+=3*QuantumMargin+10;
2961       if ((int) windows->command.width < (tile_width+QuantumMargin+10))
2962         windows->command.width=(unsigned  int) (tile_width+QuantumMargin+10);
2963       windows->command.height=(unsigned  int) (number_selections*
2964         (((3*height) >> 1)+10)+tile_height+20);
2965       windows->command.min_width=windows->command.width;
2966       windows->command.min_height=windows->command.height;
2967       XConstrainWindowPosition(display,&windows->command);
2968       if (windows->command.id != (Window) NULL)
2969         {
2970           Status
2971             status;
2972
2973           /*
2974             Reconfigure command window.
2975           */
2976           status=XStringListToTextProperty(&windows->command.name,1,
2977             &window_name);
2978           if (status != False)
2979             {
2980               XSetWMName(display,windows->command.id,&window_name);
2981               XSetWMIconName(display,windows->command.id,&window_name);
2982               (void) XFree((void *) window_name.value);
2983             }
2984           window_changes.width=(int) windows->command.width;
2985           window_changes.height=(int) windows->command.height;
2986           (void) XReconfigureWMWindow(display,windows->command.id,
2987             windows->command.screen,(unsigned int) (CWWidth | CWHeight),
2988             &window_changes);
2989         }
2990       /*
2991         Allocate selection info memory.
2992       */
2993       if (selection_info != (XWidgetInfo *) NULL)
2994         selection_info=(XWidgetInfo *) RelinquishMagickMemory(selection_info);
2995       selection_info=(XWidgetInfo *) AcquireQuantumMemory(number_selections,
2996         sizeof(*selection_info));
2997       if (selection_info == (XWidgetInfo *) NULL)
2998         {
2999           ThrowXWindowFatalException(ResourceLimitError,
3000             "MemoryAllocationFailed","...");
3001           return(id);
3002         }
3003       state|=UpdateConfigurationState | RedrawWidgetState;
3004     }
3005   /*
3006     Wait for next event.
3007   */
3008   if (event != (XEvent *) NULL)
3009     switch (event->type)
3010     {
3011       case ButtonPress:
3012       {
3013         for (i=0; i < (int) number_selections; i++)
3014         {
3015           if (MatteIsActive(selection_info[i],event->xbutton) == MagickFalse)
3016             continue;
3017           if (i >= (int) windows->command.data)
3018             {
3019               selection_info[i].raised=MagickFalse;
3020               XDrawBeveledButton(display,&windows->command,&selection_info[i]);
3021               break;
3022             }
3023           submenu_info=selection_info[i];
3024           submenu_info.active=MagickTrue;
3025           toggle_info.y=
3026             submenu_info.y+(submenu_info.height >> 1)-(toggle_info.height >> 1);
3027           id=i;
3028           (void) XCheckWindowEvent(display,windows->widget.id,LeaveWindowMask,
3029             event);
3030           break;
3031         }
3032         break;
3033       }
3034       case ButtonRelease:
3035       {
3036         for (i=0; i < (int) number_selections; i++)
3037         {
3038           if (MatteIsActive(selection_info[i],event->xbutton) == MagickFalse)
3039             continue;
3040           id=i;
3041           if (id >= (int) windows->command.data)
3042             {
3043               selection_info[id].raised=MagickTrue;
3044               XDrawBeveledButton(display,&windows->command,&selection_info[id]);
3045               break;
3046             }
3047           break;
3048         }
3049         break;
3050       }
3051       case ClientMessage:
3052       {
3053         /*
3054           If client window delete message, withdraw command widget.
3055         */
3056         if (event->xclient.message_type != windows->wm_protocols)
3057           break;
3058         if (*event->xclient.data.l != (int) windows->wm_delete_window)
3059           break;
3060         (void) XWithdrawWindow(display,windows->command.id,
3061           windows->command.screen);
3062         break;
3063       }
3064       case ConfigureNotify:
3065       {
3066         /*
3067           Update widget configuration.
3068         */
3069         if (event->xconfigure.window != windows->command.id)
3070           break;
3071         if (event->xconfigure.send_event != 0)
3072           {
3073             windows->command.x=event->xconfigure.x;
3074             windows->command.y=event->xconfigure.y;
3075           }
3076         if ((event->xconfigure.width == (int) windows->command.width) &&
3077             (event->xconfigure.height == (int) windows->command.height))
3078           break;
3079         windows->command.width=(unsigned int)
3080           MagickMax(event->xconfigure.width,(int) windows->command.min_width);
3081         windows->command.height=(unsigned int)
3082           MagickMax(event->xconfigure.height,(int) windows->command.min_height);
3083         state|=UpdateConfigurationState;
3084         break;
3085       }
3086       case Expose:
3087       {
3088         if (event->xexpose.window != windows->command.id)
3089           break;
3090         if (event->xexpose.count != 0)
3091           break;
3092         state|=RedrawWidgetState;
3093         break;
3094       }
3095       case MotionNotify:
3096       {
3097         /*
3098           Return the ID of the highlighted menu entry.
3099         */
3100         for ( ; ; )
3101         {
3102           for (i=0; i < (int) number_selections; i++)
3103           {
3104             if (i >= (int) windows->command.data)
3105               {
3106                 if (selection_info[i].raised ==
3107                     MatteIsActive(selection_info[i],event->xmotion))
3108                   {
3109                     /*
3110                       Button status changed.
3111                     */
3112                     selection_info[i].raised=!selection_info[i].raised;
3113                     XDrawBeveledButton(display,&windows->command,
3114                       &selection_info[i]);
3115                   }
3116                 continue;
3117               }
3118             if (MatteIsActive(selection_info[i],event->xmotion) == MagickFalse)
3119               continue;
3120             submenu_info=selection_info[i];
3121             submenu_info.active=MagickTrue;
3122             toggle_info.raised=MagickTrue;
3123             toggle_info.y=submenu_info.y+(submenu_info.height >> 1)-
3124               (toggle_info.height >> 1);
3125             XDrawTriangleEast(display,&windows->command,&toggle_info);
3126             id=i;
3127           }
3128           XDelay(display,SuspendTime);
3129           if (XCheckMaskEvent(display,ButtonMotionMask,event) == MagickFalse)
3130             break;
3131           while (XCheckMaskEvent(display,ButtonMotionMask,event)) ;
3132           toggle_info.raised=MagickFalse;
3133           if (windows->command.data != 0)
3134             XDrawTriangleEast(display,&windows->command,&toggle_info);
3135         }
3136         break;
3137       }
3138       case MapNotify:
3139       {
3140         windows->command.mapped=MagickTrue;
3141         break;
3142       }
3143       case UnmapNotify:
3144       {
3145         windows->command.mapped=MagickFalse;
3146         break;
3147       }
3148       default:
3149         break;
3150     }
3151   if (state & UpdateConfigurationState)
3152     {
3153       /*
3154         Initialize button information.
3155       */
3156       assert(selections != (const char **) NULL);
3157       y=tile_height+20;
3158       for (i=0; i < (int) number_selections; i++)
3159       {
3160         XGetWidgetInfo(selections[i],&selection_info[i]);
3161         selection_info[i].center=MagickFalse;
3162         selection_info[i].bevel_width--;
3163         selection_info[i].height=(unsigned int) ((3*height) >> 1);
3164         selection_info[i].x=(QuantumMargin >> 1)+4;
3165         selection_info[i].width=(unsigned int)
3166           (windows->command.width-(selection_info[i].x << 1));
3167         selection_info[i].y=y;
3168         y+=selection_info[i].height+(selection_info[i].bevel_width << 1)+6;
3169       }
3170       XGetWidgetInfo((char *) NULL,&toggle_info);
3171       toggle_info.bevel_width--;
3172       toggle_info.width=(unsigned int)
3173         (((5*height) >> 3)-(toggle_info.bevel_width << 1));
3174       toggle_info.height=toggle_info.width;
3175       toggle_info.x=selection_info[0].x+selection_info[0].width-
3176         toggle_info.width-(QuantumMargin >> 1);
3177       if (windows->command.mapped)
3178         (void) XClearWindow(display,windows->command.id);
3179     }
3180   if (state & RedrawWidgetState)
3181     {
3182       Pixmap
3183         tile_pixmap;
3184
3185       /*
3186         Draw command buttons.
3187       */
3188       tile_pixmap=XCreatePixmapFromBitmapData(display,windows->command.id,
3189         (char *) tile_bits,tile_width,tile_height,1L,0L,1);
3190       if (tile_pixmap != (Pixmap) NULL)
3191         {
3192           (void) XCopyPlane(display,tile_pixmap,windows->command.id,
3193             windows->command.annotate_context,0,0,tile_width,tile_height,
3194             (int) ((windows->command.width-tile_width) >> 1),10,1L);
3195           (void) XFreePixmap(display,tile_pixmap);
3196         }
3197       for (i=0; i < (int) number_selections; i++)
3198       {
3199         XDrawBeveledButton(display,&windows->command,&selection_info[i]);
3200         if (i >= (int) windows->command.data)
3201           continue;
3202         toggle_info.raised=i == id ? MagickTrue : MagickFalse;
3203         toggle_info.y=selection_info[i].y+
3204           (selection_info[i].height >> 1)-(toggle_info.height >> 1);
3205         XDrawTriangleEast(display,&windows->command,&toggle_info);
3206       }
3207       XHighlightWidget(display,&windows->command,BorderOffset,BorderOffset);
3208     }
3209   return(id);
3210 }
3211 \f
3212 /*
3213 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3214 %                                                                             %
3215 %                                                                             %
3216 %                                                                             %
3217 %   X C o n f i r m W i d g e t                                               %
3218 %                                                                             %
3219 %                                                                             %
3220 %                                                                             %
3221 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3222 %
3223 %  XConfirmWidget() displays a Confirm widget with a notice to the user. The
3224 %  function returns -1 if Dismiss is pressed, 0 for Cancel, and 1 for Yes.
3225 %
3226 %  The format of the XConfirmWidget method is:
3227 %
3228 %      int XConfirmWidget(Display *display,XWindows *windows,
3229 %        const char *reason,const char *description)
3230 %
3231 %  A description of each parameter follows:
3232 %
3233 %    o display: Specifies a connection to an X server;  returned from
3234 %      XOpenDisplay.
3235 %
3236 %    o window: Specifies a pointer to a XWindows structure.
3237 %
3238 %    o reason: Specifies the message to display before terminating the
3239 %      program.
3240 %
3241 %    o description: Specifies any description to the message.
3242 %
3243 */
3244 MagickPrivate int XConfirmWidget(Display *display,XWindows *windows,
3245   const char *reason,const char *description)
3246 {
3247 #define CancelButtonText  "Cancel"
3248 #define DismissButtonText  "Dismiss"
3249 #define YesButtonText  "Yes"
3250
3251   int
3252     confirm,
3253     x,
3254     y;
3255
3256   Status
3257     status;
3258
3259   unsigned int
3260     height,
3261     width;
3262
3263   size_t
3264     state;
3265
3266   XEvent
3267     event;
3268
3269   XFontStruct
3270     *font_info;
3271
3272   XTextProperty
3273     window_name;
3274
3275   XWidgetInfo
3276     cancel_info,
3277     dismiss_info,
3278     yes_info;
3279
3280   XWindowChanges
3281     window_changes;
3282
3283   /*
3284     Determine Confirm widget attributes.
3285   */
3286   assert(display != (Display *) NULL);
3287   assert(windows != (XWindows *) NULL);
3288   assert(reason != (char *) NULL);
3289   assert(description != (char *) NULL);
3290   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",reason);
3291   XCheckRefreshWindows(display,windows);
3292   font_info=windows->widget.font_info;
3293   width=WidgetTextWidth(font_info,CancelButtonText);
3294   if (WidgetTextWidth(font_info,DismissButtonText) > width)
3295     width=WidgetTextWidth(font_info,DismissButtonText);
3296   if (WidgetTextWidth(font_info,YesButtonText) > width)
3297     width=WidgetTextWidth(font_info,YesButtonText);
3298   width<<=1;
3299   if (description != (char *) NULL)
3300     if (WidgetTextWidth(font_info,(char *) description) > width)
3301       width=WidgetTextWidth(font_info,(char *) description);
3302   height=(unsigned int) (font_info->ascent+font_info->descent);
3303   /*
3304     Position Confirm widget.
3305   */
3306   windows->widget.width=(unsigned int) (width+9*QuantumMargin);
3307   windows->widget.min_width=(unsigned int) (9*QuantumMargin+
3308     WidgetTextWidth(font_info,CancelButtonText)+
3309     WidgetTextWidth(font_info,DismissButtonText)+
3310     WidgetTextWidth(font_info,YesButtonText));
3311   if (windows->widget.width < windows->widget.min_width)
3312     windows->widget.width=windows->widget.min_width;
3313   windows->widget.height=(unsigned int) (12*height);
3314   windows->widget.min_height=(unsigned int) (7*height);
3315   if (windows->widget.height < windows->widget.min_height)
3316     windows->widget.height=windows->widget.min_height;
3317   XConstrainWindowPosition(display,&windows->widget);
3318   /*
3319     Map Confirm widget.
3320   */
3321   (void) CopyMagickString(windows->widget.name,"Confirm",MaxTextExtent);
3322   status=XStringListToTextProperty(&windows->widget.name,1,&window_name);
3323   if (status != False)
3324     {
3325       XSetWMName(display,windows->widget.id,&window_name);
3326       XSetWMIconName(display,windows->widget.id,&window_name);
3327       (void) XFree((void *) window_name.value);
3328     }
3329   window_changes.width=(int) windows->widget.width;
3330   window_changes.height=(int) windows->widget.height;
3331   window_changes.x=windows->widget.x;
3332   window_changes.y=windows->widget.y;
3333   (void) XReconfigureWMWindow(display,windows->widget.id,windows->widget.screen,
3334     (unsigned int) (CWWidth | CWHeight | CWX | CWY),&window_changes);
3335   (void) XMapRaised(display,windows->widget.id);
3336   windows->widget.mapped=MagickFalse;
3337   /*
3338     Respond to X events.
3339   */
3340   confirm=0;
3341   state=UpdateConfigurationState;
3342   XSetCursorState(display,windows,MagickTrue);
3343   do
3344   {
3345     if (state & UpdateConfigurationState)
3346       {
3347         /*
3348           Initialize button information.
3349         */
3350         XGetWidgetInfo(CancelButtonText,&cancel_info);
3351         cancel_info.width=(unsigned int) QuantumMargin+
3352           WidgetTextWidth(font_info,CancelButtonText);
3353         cancel_info.height=(unsigned int) ((3*height) >> 1);
3354         cancel_info.x=(int) (windows->widget.width-cancel_info.width-
3355           QuantumMargin);
3356         cancel_info.y=(int) (windows->widget.height-(cancel_info.height << 1));
3357         dismiss_info=cancel_info;
3358         dismiss_info.text=(char *) DismissButtonText;
3359         if (LocaleCompare(description,"Do you want to save it") == 0)
3360           dismiss_info.text=(char *) "Don't Save";
3361         dismiss_info.width=(unsigned int) QuantumMargin+
3362           WidgetTextWidth(font_info,dismiss_info.text);
3363         dismiss_info.x=(int)
3364           ((windows->widget.width >> 1)-(dismiss_info.width >> 1));
3365         yes_info=cancel_info;
3366         yes_info.text=(char *) YesButtonText;
3367         if (LocaleCompare(description,"Do you want to save it") == 0)
3368           yes_info.text=(char *) "Save";
3369         yes_info.width=(unsigned int) QuantumMargin+
3370           WidgetTextWidth(font_info,yes_info.text);
3371         if (yes_info.width < cancel_info.width)
3372           yes_info.width=cancel_info.width;
3373         yes_info.x=QuantumMargin;
3374         state&=(~UpdateConfigurationState);
3375       }
3376     if (state & RedrawWidgetState)
3377       {
3378         /*
3379           Redraw Confirm widget.
3380         */
3381         width=WidgetTextWidth(font_info,(char *) reason);
3382         x=(int) ((windows->widget.width >> 1)-(width >> 1));
3383         y=(int) ((windows->widget.height >> 1)-(height << 1));
3384         (void) XDrawString(display,windows->widget.id,
3385           windows->widget.annotate_context,x,y,(char *) reason,Extent(reason));
3386         if (description != (char *) NULL)
3387           {
3388             char
3389               question[MaxTextExtent];
3390
3391             (void) CopyMagickString(question,description,MaxTextExtent);
3392             (void) ConcatenateMagickString(question,"?",MaxTextExtent);
3393             width=WidgetTextWidth(font_info,question);
3394             x=(int) ((windows->widget.width >> 1)-(width >> 1));
3395             y+=height;
3396             (void) XDrawString(display,windows->widget.id,
3397               windows->widget.annotate_context,x,y,question,Extent(question));
3398           }
3399         XDrawBeveledButton(display,&windows->widget,&cancel_info);
3400         XDrawBeveledButton(display,&windows->widget,&dismiss_info);
3401         XDrawBeveledButton(display,&windows->widget,&yes_info);
3402         XHighlightWidget(display,&windows->widget,BorderOffset,BorderOffset);
3403         state&=(~RedrawWidgetState);
3404       }
3405     /*
3406       Wait for next event.
3407     */
3408     (void) XIfEvent(display,&event,XScreenEvent,(char *) windows);
3409     switch (event.type)
3410     {
3411       case ButtonPress:
3412       {
3413         if (MatteIsActive(cancel_info,event.xbutton))
3414           {
3415             /*
3416               User pressed No button.
3417             */
3418             cancel_info.raised=MagickFalse;
3419             XDrawBeveledButton(display,&windows->widget,&cancel_info);
3420             break;
3421           }
3422         if (MatteIsActive(dismiss_info,event.xbutton))
3423           {
3424             /*
3425               User pressed Dismiss button.
3426             */
3427             dismiss_info.raised=MagickFalse;
3428             XDrawBeveledButton(display,&windows->widget,&dismiss_info);
3429             break;
3430           }
3431         if (MatteIsActive(yes_info,event.xbutton))
3432           {
3433             /*
3434               User pressed Yes button.
3435             */
3436             yes_info.raised=MagickFalse;
3437             XDrawBeveledButton(display,&windows->widget,&yes_info);
3438             break;
3439           }
3440         break;
3441       }
3442       case ButtonRelease:
3443       {
3444         if (windows->widget.mapped == MagickFalse)
3445           break;
3446         if (cancel_info.raised == MagickFalse)
3447           {
3448             if (event.xbutton.window == windows->widget.id)
3449               if (MatteIsActive(cancel_info,event.xbutton))
3450                 {
3451                   confirm=0;
3452                   state|=ExitState;
3453                 }
3454             cancel_info.raised=MagickTrue;
3455             XDrawBeveledButton(display,&windows->widget,&cancel_info);
3456           }
3457         if (dismiss_info.raised == MagickFalse)
3458           {
3459             if (event.xbutton.window == windows->widget.id)
3460               if (MatteIsActive(dismiss_info,event.xbutton))
3461                 {
3462                   confirm=(-1);
3463                   state|=ExitState;
3464                 }
3465             dismiss_info.raised=MagickTrue;
3466             XDrawBeveledButton(display,&windows->widget,&dismiss_info);
3467           }
3468         if (yes_info.raised == MagickFalse)
3469           {
3470             if (event.xbutton.window == windows->widget.id)
3471               if (MatteIsActive(yes_info,event.xbutton))
3472                 {
3473                   confirm=1;
3474                   state|=ExitState;
3475                 }
3476             yes_info.raised=MagickTrue;
3477             XDrawBeveledButton(display,&windows->widget,&yes_info);
3478           }
3479         break;
3480       }
3481       case ClientMessage:
3482       {
3483         /*
3484           If client window delete message, exit.
3485         */
3486         if (event.xclient.message_type != windows->wm_protocols)
3487           break;
3488         if (*event.xclient.data.l == (int) windows->wm_take_focus)
3489           {
3490             (void) XSetInputFocus(display,event.xclient.window,RevertToParent,
3491               (Time) event.xclient.data.l[1]);
3492             break;
3493           }
3494         if (*event.xclient.data.l != (int) windows->wm_delete_window)
3495           break;
3496         if (event.xclient.window == windows->widget.id)
3497           {
3498             state|=ExitState;
3499             break;
3500           }
3501         break;
3502       }
3503       case ConfigureNotify:
3504       {
3505         /*
3506           Update widget configuration.
3507         */
3508         if (event.xconfigure.window != windows->widget.id)
3509           break;
3510         if ((event.xconfigure.width == (int) windows->widget.width) &&
3511             (event.xconfigure.height == (int) windows->widget.height))
3512           break;
3513         windows->widget.width=(unsigned int)
3514           MagickMax(event.xconfigure.width,(int) windows->widget.min_width);
3515         windows->widget.height=(unsigned int)
3516           MagickMax(event.xconfigure.height,(int) windows->widget.min_height);
3517         state|=UpdateConfigurationState;
3518         break;
3519       }
3520       case EnterNotify:
3521       {
3522         if (event.xcrossing.window != windows->widget.id)
3523           break;
3524         state&=(~InactiveWidgetState);
3525         break;
3526       }
3527       case Expose:
3528       {
3529         if (event.xexpose.window != windows->widget.id)
3530           break;
3531         if (event.xexpose.count != 0)
3532           break;
3533         state|=RedrawWidgetState;
3534         break;
3535       }
3536       case KeyPress:
3537       {
3538         static char
3539           command[MaxTextExtent];
3540
3541         static KeySym
3542           key_symbol;
3543
3544         /*
3545           Respond to a user key press.
3546         */
3547         if (event.xkey.window != windows->widget.id)
3548           break;
3549         (void) XLookupString((XKeyEvent *) &event.xkey,command,
3550           (int) sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3551         if ((key_symbol == XK_Return) || (key_symbol == XK_KP_Enter))
3552           {
3553             yes_info.raised=MagickFalse;
3554             XDrawBeveledButton(display,&windows->widget,&yes_info);
3555             confirm=1;
3556             state|=ExitState;
3557             break;
3558           }
3559         break;
3560       }
3561       case LeaveNotify:
3562       {
3563         if (event.xcrossing.window != windows->widget.id)
3564           break;
3565         state|=InactiveWidgetState;
3566         break;
3567       }
3568       case MotionNotify:
3569       {
3570         /*
3571           Discard pending button motion events.
3572         */
3573         while (XCheckMaskEvent(display,ButtonMotionMask,&event)) ;
3574         if (state & InactiveWidgetState)
3575           break;
3576         if (cancel_info.raised == MatteIsActive(cancel_info,event.xmotion))
3577           {
3578             /*
3579               Cancel button status changed.
3580             */
3581             cancel_info.raised=cancel_info.raised == MagickFalse ?
3582               MagickTrue : MagickFalse;
3583             XDrawBeveledButton(display,&windows->widget,&cancel_info);
3584             break;
3585           }
3586         if (dismiss_info.raised == MatteIsActive(dismiss_info,event.xmotion))
3587           {
3588             /*
3589               Dismiss button status changed.
3590             */
3591             dismiss_info.raised=cancel_info.raised == MagickFalse ?
3592               MagickTrue : MagickFalse;
3593             XDrawBeveledButton(display,&windows->widget,&dismiss_info);
3594             break;
3595           }
3596         if (yes_info.raised == MatteIsActive(yes_info,event.xmotion))
3597           {
3598             /*
3599               Yes button status changed.
3600             */
3601             yes_info.raised=yes_info.raised == MagickFalse ?
3602               MagickTrue : MagickFalse;
3603             XDrawBeveledButton(display,&windows->widget,&yes_info);
3604             break;
3605           }
3606         break;
3607       }
3608       default:
3609         break;
3610     }
3611   } while ((state & ExitState) == 0);
3612   XSetCursorState(display,windows,MagickFalse);
3613   (void) XWithdrawWindow(display,windows->widget.id,windows->widget.screen);
3614   XCheckRefreshWindows(display,windows);
3615   return(confirm);
3616 }
3617 \f
3618 /*
3619 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3620 %                                                                             %
3621 %                                                                             %
3622 %                                                                             %
3623 %   X D i a l o g W i d g e t                                                 %
3624 %                                                                             %
3625 %                                                                             %
3626 %                                                                             %
3627 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3628 %
3629 %  XDialogWidget() displays a Dialog widget with a query to the user.  The user
3630 %  keys a reply and presses the Ok or Cancel button to exit.  The typed text is
3631 %  returned as the reply function parameter.
3632 %
3633 %  The format of the XDialogWidget method is:
3634 %
3635 %      int XDialogWidget(Display *display,XWindows *windows,const char *action,
3636 %        const char *query,char *reply)
3637 %
3638 %  A description of each parameter follows:
3639 %
3640 %    o display: Specifies a connection to an X server;  returned from
3641 %      XOpenDisplay.
3642 %
3643 %    o window: Specifies a pointer to a XWindows structure.
3644 %
3645 %    o action: Specifies a pointer to the action of this widget.
3646 %
3647 %    o query: Specifies a pointer to the query to present to the user.
3648 %
3649 %    o reply: the response from the user is returned in this parameter.
3650 %
3651 */
3652 MagickPrivate int XDialogWidget(Display *display,XWindows *windows,
3653   const char *action,const char *query,char *reply)
3654 {
3655 #define CancelButtonText  "Cancel"
3656
3657   char
3658     primary_selection[MaxTextExtent];
3659
3660   int
3661     x;
3662
3663   register int
3664     i;
3665
3666   static MagickBooleanType
3667     raised = MagickFalse;
3668
3669   Status
3670     status;
3671
3672   unsigned int
3673     anomaly,
3674     height,
3675     width;
3676
3677   size_t
3678     state;
3679
3680   XEvent
3681     event;
3682
3683   XFontStruct
3684     *font_info;
3685
3686   XTextProperty
3687     window_name;
3688
3689   XWidgetInfo
3690     action_info,
3691     cancel_info,
3692     reply_info,
3693     special_info,
3694     text_info;
3695
3696   XWindowChanges
3697     window_changes;
3698
3699   /*
3700     Determine Dialog widget attributes.
3701   */
3702   assert(display != (Display *) NULL);
3703   assert(windows != (XWindows *) NULL);
3704   assert(action != (char *) NULL);
3705   assert(query != (char *) NULL);
3706   assert(reply != (char *) NULL);
3707   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",action);
3708   XCheckRefreshWindows(display,windows);
3709   font_info=windows->widget.font_info;
3710   width=WidgetTextWidth(font_info,(char *) action);
3711   if (WidgetTextWidth(font_info,CancelButtonText) > width)
3712     width=WidgetTextWidth(font_info,CancelButtonText);
3713   width+=(3*QuantumMargin) >> 1;
3714   height=(unsigned int) (font_info->ascent+font_info->descent);
3715   /*
3716     Position Dialog widget.
3717   */
3718   windows->widget.width=(unsigned int) MagickMax((int) (2*width),(int)
3719     WidgetTextWidth(font_info,(char *) query));
3720   if (windows->widget.width < WidgetTextWidth(font_info,reply))
3721     windows->widget.width=WidgetTextWidth(font_info,reply);
3722   windows->widget.width+=6*QuantumMargin;
3723   windows->widget.min_width=(unsigned int)
3724     (width+28*XTextWidth(font_info,"#",1)+4*QuantumMargin);
3725   if (windows->widget.width < windows->widget.min_width)
3726     windows->widget.width=windows->widget.min_width;
3727   windows->widget.height=(unsigned int) (7*height+(QuantumMargin << 1));
3728   windows->widget.min_height=windows->widget.height;
3729   if (windows->widget.height < windows->widget.min_height)
3730     windows->widget.height=windows->widget.min_height;
3731   XConstrainWindowPosition(display,&windows->widget);
3732   /*
3733     Map Dialog widget.
3734   */
3735   (void) CopyMagickString(windows->widget.name,"Dialog",MaxTextExtent);
3736   status=XStringListToTextProperty(&windows->widget.name,1,&window_name);
3737   if (status != False)
3738     {
3739       XSetWMName(display,windows->widget.id,&window_name);
3740       XSetWMIconName(display,windows->widget.id,&window_name);
3741       (void) XFree((void *) window_name.value);
3742     }
3743   window_changes.width=(int) windows->widget.width;
3744   window_changes.height=(int) windows->widget.height;
3745   window_changes.x=windows->widget.x;
3746   window_changes.y=windows->widget.y;
3747   (void) XReconfigureWMWindow(display,windows->widget.id,windows->widget.screen,
3748     (unsigned int) (CWWidth | CWHeight | CWX | CWY),&window_changes);
3749   (void) XMapRaised(display,windows->widget.id);
3750   windows->widget.mapped=MagickFalse;
3751   /*
3752     Respond to X events.
3753   */
3754   anomaly=(LocaleCompare(action,"Background") == 0) ||
3755     (LocaleCompare(action,"New") == 0) ||
3756     (LocaleCompare(action,"Quantize") == 0) ||
3757     (LocaleCompare(action,"Resize") == 0) ||
3758     (LocaleCompare(action,"Save") == 0) ||
3759     (LocaleCompare(action,"Shade") == 0);
3760   state=UpdateConfigurationState;
3761   XSetCursorState(display,windows,MagickTrue);
3762   do
3763   {
3764     if (state & UpdateConfigurationState)
3765       {
3766         /*
3767           Initialize button information.
3768         */
3769         XGetWidgetInfo(CancelButtonText,&cancel_info);
3770         cancel_info.width=width;
3771         cancel_info.height=(unsigned int) ((3*height) >> 1);
3772         cancel_info.x=(int)
3773           (windows->widget.width-cancel_info.width-((3*QuantumMargin) >> 1));
3774         cancel_info.y=(int)
3775           (windows->widget.height-cancel_info.height-((3*QuantumMargin) >> 1));
3776         XGetWidgetInfo(action,&action_info);
3777         action_info.width=width;
3778         action_info.height=(unsigned int) ((3*height) >> 1);
3779         action_info.x=cancel_info.x-(cancel_info.width+QuantumMargin+
3780           (action_info.bevel_width << 1));
3781         action_info.y=cancel_info.y;
3782         /*
3783           Initialize reply information.
3784         */
3785         XGetWidgetInfo(reply,&reply_info);
3786         reply_info.raised=MagickFalse;
3787         reply_info.bevel_width--;
3788         reply_info.width=windows->widget.width-(3*QuantumMargin);
3789         reply_info.height=height << 1;
3790         reply_info.x=(3*QuantumMargin) >> 1;
3791         reply_info.y=action_info.y-reply_info.height-QuantumMargin;
3792         /*
3793           Initialize option information.
3794         */
3795         XGetWidgetInfo("Dither",&special_info);
3796         special_info.raised=raised;
3797         special_info.bevel_width--;
3798         special_info.width=(unsigned int) QuantumMargin >> 1;
3799         special_info.height=(unsigned int) QuantumMargin >> 1;
3800         special_info.x=reply_info.x;
3801         special_info.y=action_info.y+action_info.height-special_info.height;
3802         if (LocaleCompare(action,"Background") == 0)
3803           special_info.text=(char *) "Backdrop";
3804         if (LocaleCompare(action,"New") == 0)
3805           special_info.text=(char *) "Gradation";
3806         if (LocaleCompare(action,"Resize") == 0)
3807           special_info.text=(char *) "Constrain ratio";
3808         if (LocaleCompare(action,"Save") == 0)
3809           special_info.text=(char *) "Non-progressive";
3810         if (LocaleCompare(action,"Shade") == 0)
3811           special_info.text=(char *) "Color shading";
3812         /*
3813           Initialize text information.
3814         */
3815         XGetWidgetInfo(query,&text_info);
3816         text_info.width=reply_info.width;
3817         text_info.height=height;
3818         text_info.x=reply_info.x-(QuantumMargin >> 1);
3819         text_info.y=QuantumMargin;
3820         text_info.center=MagickFalse;
3821         state&=(~UpdateConfigurationState);
3822       }
3823     if (state & RedrawWidgetState)
3824       {
3825         /*
3826           Redraw Dialog widget.
3827         */
3828         XDrawWidgetText(display,&windows->widget,&text_info);
3829         XDrawBeveledMatte(display,&windows->widget,&reply_info);
3830         XDrawMatteText(display,&windows->widget,&reply_info);
3831         if (anomaly)
3832           XDrawBeveledButton(display,&windows->widget,&special_info);
3833         XDrawBeveledButton(display,&windows->widget,&action_info);
3834         XDrawBeveledButton(display,&windows->widget,&cancel_info);
3835         XHighlightWidget(display,&windows->widget,BorderOffset,BorderOffset);
3836         state&=(~RedrawWidgetState);
3837       }
3838     /*
3839       Wait for next event.
3840     */
3841     (void) XIfEvent(display,&event,XScreenEvent,(char *) windows);
3842     switch (event.type)
3843     {
3844       case ButtonPress:
3845       {
3846         if (anomaly)
3847           if (MatteIsActive(special_info,event.xbutton))
3848             {
3849               /*
3850                 Option button status changed.
3851               */
3852               special_info.raised=!special_info.raised;
3853               XDrawBeveledButton(display,&windows->widget,&special_info);
3854               break;
3855             }
3856         if (MatteIsActive(action_info,event.xbutton))
3857           {
3858             /*
3859               User pressed Action button.
3860             */
3861             action_info.raised=MagickFalse;
3862             XDrawBeveledButton(display,&windows->widget,&action_info);
3863             break;
3864           }
3865         if (MatteIsActive(cancel_info,event.xbutton))
3866           {
3867             /*
3868               User pressed Cancel button.
3869             */
3870             cancel_info.raised=MagickFalse;
3871             XDrawBeveledButton(display,&windows->widget,&cancel_info);
3872             break;
3873           }
3874         if (MatteIsActive(reply_info,event.xbutton) == MagickFalse)
3875           break;
3876         if (event.xbutton.button != Button2)
3877           {
3878             static Time
3879               click_time;
3880
3881             /*
3882               Move text cursor to position of button press.
3883             */
3884             x=event.xbutton.x-reply_info.x-(QuantumMargin >> 2);
3885             for (i=1; i <= Extent(reply_info.marker); i++)
3886               if (XTextWidth(font_info,reply_info.marker,i) > x)
3887                 break;
3888             reply_info.cursor=reply_info.marker+i-1;
3889             if (event.xbutton.time > (click_time+DoubleClick))
3890               reply_info.highlight=MagickFalse;
3891             else
3892               {
3893                 /*
3894                   Become the XA_PRIMARY selection owner.
3895                 */
3896                 (void) CopyMagickString(primary_selection,reply_info.text,
3897                   MaxTextExtent);
3898                 (void) XSetSelectionOwner(display,XA_PRIMARY,windows->widget.id,
3899                   event.xbutton.time);
3900                 reply_info.highlight=XGetSelectionOwner(display,XA_PRIMARY) ==
3901                   windows->widget.id ? MagickTrue : MagickFalse;
3902               }
3903             XDrawMatteText(display,&windows->widget,&reply_info);
3904             click_time=event.xbutton.time;
3905             break;
3906           }
3907         /*
3908           Request primary selection.
3909         */
3910         (void) XConvertSelection(display,XA_PRIMARY,XA_STRING,XA_STRING,
3911           windows->widget.id,event.xbutton.time);
3912         break;
3913       }
3914       case ButtonRelease:
3915       {
3916         if (windows->widget.mapped == MagickFalse)
3917           break;
3918         if (action_info.raised == MagickFalse)
3919           {
3920             if (event.xbutton.window == windows->widget.id)
3921               if (MatteIsActive(action_info,event.xbutton))
3922                 state|=ExitState;
3923             action_info.raised=MagickTrue;
3924             XDrawBeveledButton(display,&windows->widget,&action_info);
3925           }
3926         if (cancel_info.raised == MagickFalse)
3927           {
3928             if (event.xbutton.window == windows->widget.id)
3929               if (MatteIsActive(cancel_info,event.xbutton))
3930                 {
3931                   *reply_info.text='\0';
3932                   state|=ExitState;
3933                 }
3934             cancel_info.raised=MagickTrue;
3935             XDrawBeveledButton(display,&windows->widget,&cancel_info);
3936           }
3937         break;
3938       }
3939       case ClientMessage:
3940       {
3941         /*
3942           If client window delete message, exit.
3943         */
3944         if (event.xclient.message_type != windows->wm_protocols)
3945           break;
3946         if (*event.xclient.data.l == (int) windows->wm_take_focus)
3947           {
3948             (void) XSetInputFocus(display,event.xclient.window,RevertToParent,
3949               (Time) event.xclient.data.l[1]);
3950             break;
3951           }
3952         if (*event.xclient.data.l != (int) windows->wm_delete_window)
3953           break;
3954         if (event.xclient.window == windows->widget.id)
3955           {
3956             *reply_info.text='\0';
3957             state|=ExitState;
3958             break;
3959           }
3960         break;
3961       }
3962       case ConfigureNotify:
3963       {
3964         /*
3965           Update widget configuration.
3966         */
3967         if (event.xconfigure.window != windows->widget.id)
3968           break;
3969         if ((event.xconfigure.width == (int) windows->widget.width) &&
3970             (event.xconfigure.height == (int) windows->widget.height))
3971           break;
3972         windows->widget.width=(unsigned int)
3973           MagickMax(event.xconfigure.width,(int) windows->widget.min_width);
3974         windows->widget.height=(unsigned int)
3975           MagickMax(event.xconfigure.height,(int) windows->widget.min_height);
3976         state|=UpdateConfigurationState;
3977         break;
3978       }
3979       case EnterNotify:
3980       {
3981         if (event.xcrossing.window != windows->widget.id)
3982           break;
3983         state&=(~InactiveWidgetState);
3984         break;
3985       }
3986       case Expose:
3987       {
3988         if (event.xexpose.window != windows->widget.id)
3989           break;
3990         if (event.xexpose.count != 0)
3991           break;
3992         state|=RedrawWidgetState;
3993         break;
3994       }
3995       case KeyPress:
3996       {
3997         static char
3998           command[MaxTextExtent];
3999
4000         static int
4001           length;
4002
4003         static KeySym
4004           key_symbol;
4005
4006         /*
4007           Respond to a user key press.
4008         */
4009         if (event.xkey.window != windows->widget.id)
4010           break;
4011         length=XLookupString((XKeyEvent *) &event.xkey,command,
4012           (int) sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4013         *(command+length)='\0';
4014         if ((key_symbol == XK_Return) || (key_symbol == XK_KP_Enter))
4015           {
4016             action_info.raised=MagickFalse;
4017             XDrawBeveledButton(display,&windows->widget,&action_info);
4018             state|=ExitState;
4019             break;
4020           }
4021         if (key_symbol == XK_Control_L)
4022           {
4023             state|=ControlState;
4024             break;
4025           }
4026         if (state & ControlState)
4027           switch ((int) key_symbol)
4028           {
4029             case XK_u:
4030             case XK_U:
4031             {
4032               /*
4033                 Erase the entire line of text.
4034               */
4035               *reply_info.text='\0';
4036               reply_info.cursor=reply_info.text;
4037               reply_info.marker=reply_info.text;
4038               reply_info.highlight=MagickFalse;
4039               break;
4040             }
4041             default:
4042               break;
4043           }
4044         XEditText(display,&reply_info,key_symbol,command,state);
4045         XDrawMatteText(display,&windows->widget,&reply_info);
4046         break;
4047       }
4048       case KeyRelease:
4049       {
4050         static char
4051           command[MaxTextExtent];
4052
4053         static KeySym
4054           key_symbol;
4055
4056         /*
4057           Respond to a user key release.
4058         */
4059         if (event.xkey.window != windows->widget.id)
4060           break;
4061         (void) XLookupString((XKeyEvent *) &event.xkey,command,
4062           (int) sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4063         if (key_symbol == XK_Control_L)
4064           state&=(~ControlState);
4065         break;
4066       }
4067       case LeaveNotify:
4068       {
4069         if (event.xcrossing.window != windows->widget.id)
4070           break;
4071         state|=InactiveWidgetState;
4072         break;
4073       }
4074       case MotionNotify:
4075       {
4076         /*
4077           Discard pending button motion events.
4078         */
4079         while (XCheckMaskEvent(display,ButtonMotionMask,&event)) ;
4080         if (state & InactiveWidgetState)
4081           break;
4082         if (action_info.raised == MatteIsActive(action_info,event.xmotion))
4083           {
4084             /*
4085               Action button status changed.
4086             */
4087             action_info.raised=action_info.raised == MagickFalse ?
4088               MagickTrue : MagickFalse;
4089             XDrawBeveledButton(display,&windows->widget,&action_info);
4090             break;
4091           }
4092         if (cancel_info.raised == MatteIsActive(cancel_info,event.xmotion))
4093           {
4094             /*
4095               Cancel button status changed.
4096             */
4097             cancel_info.raised=cancel_info.raised == MagickFalse ?
4098               MagickTrue : MagickFalse;
4099             XDrawBeveledButton(display,&windows->widget,&cancel_info);
4100             break;
4101           }
4102         break;
4103       }
4104       case SelectionClear:
4105       {
4106         reply_info.highlight=MagickFalse;
4107         XDrawMatteText(display,&windows->widget,&reply_info);
4108         break;
4109       }
4110       case SelectionNotify:
4111       {
4112         Atom
4113           type;
4114
4115         int
4116           format;
4117
4118         unsigned char
4119           *data;
4120
4121         unsigned long
4122           after,
4123           length;
4124
4125         /*
4126           Obtain response from primary selection.
4127         */
4128         if (event.xselection.property == (Atom) None)
4129           break;
4130         status=XGetWindowProperty(display,event.xselection.requestor,
4131           event.xselection.property,0L,2047L,MagickTrue,XA_STRING,&type,
4132           &format,&length,&after,&data);
4133         if ((status != Success) || (type != XA_STRING) || (format == 32) ||
4134             (length == 0))
4135           break;
4136         if ((Extent(reply_info.text)+length) >= (MaxTextExtent-1))
4137           (void) XBell(display,0);
4138         else
4139           {
4140             /*
4141               Insert primary selection in reply text.
4142             */
4143             *(data+length)='\0';
4144             XEditText(display,&reply_info,(KeySym) XK_Insert,(char *) data,
4145               state);
4146             XDrawMatteText(display,&windows->widget,&reply_info);
4147           }
4148         (void) XFree((void *) data);
4149         break;
4150       }
4151       case SelectionRequest:
4152       {
4153         XSelectionEvent
4154           notify;
4155
4156         XSelectionRequestEvent
4157           *request;
4158
4159         if (reply_info.highlight == MagickFalse)
4160           break;
4161         /*
4162           Set primary selection.
4163         */
4164         request=(&(event.xselectionrequest));
4165         (void) XChangeProperty(request->display,request->requestor,
4166           request->property,request->target,8,PropModeReplace,
4167           (unsigned char *) primary_selection,Extent(primary_selection));
4168         notify.type=SelectionNotify;
4169         notify.display=request->display;
4170         notify.requestor=request->requestor;
4171         notify.selection=request->selection;
4172         notify.target=request->target;
4173         notify.time=request->time;
4174         if (request->property == None)
4175           notify.property=request->target;
4176         else
4177           notify.property=request->property;
4178         (void) XSendEvent(request->display,request->requestor,False,0,
4179           (XEvent *) &notify);
4180       }
4181       default:
4182         break;
4183     }
4184   } while ((state & ExitState) == 0);
4185   XSetCursorState(display,windows,MagickFalse);
4186   (void) XWithdrawWindow(display,windows->widget.id,windows->widget.screen);
4187   XCheckRefreshWindows(display,windows);
4188   if (anomaly)
4189     if (special_info.raised)
4190       if (*reply != '\0')
4191         raised=MagickTrue;
4192   return(raised == MagickFalse);
4193 }
4194 \f
4195 /*
4196 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4197 %                                                                             %
4198 %                                                                             %
4199 %                                                                             %
4200 %   X F i l e B r o w s e r W i d g e t                                       %
4201 %                                                                             %
4202 %                                                                             %
4203 %                                                                             %
4204 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4205 %
4206 %  XFileBrowserWidget() displays a File Browser widget with a file query to the
4207 %  user.  The user keys a reply and presses the Action or Cancel button to
4208 %  exit.  The typed text is returned as the reply function parameter.
4209 %
4210 %  The format of the XFileBrowserWidget method is:
4211 %
4212 %      void XFileBrowserWidget(Display *display,XWindows *windows,
4213 %        const char *action,char *reply)
4214 %
4215 %  A description of each parameter follows:
4216 %
4217 %    o display: Specifies a connection to an X server;  returned from
4218 %      XOpenDisplay.
4219 %
4220 %    o window: Specifies a pointer to a XWindows structure.
4221 %
4222 %    o action: Specifies a pointer to the action of this widget.
4223 %
4224 %    o reply: the response from the user is returned in this parameter.
4225 %
4226 */
4227 MagickPrivate void XFileBrowserWidget(Display *display,XWindows *windows,
4228   const char *action,char *reply)
4229 {
4230 #define CancelButtonText  "Cancel"
4231 #define DirectoryText  "Directory:"
4232 #define FilenameText  "File name:"
4233 #define GrabButtonText  "Grab"
4234 #define FormatButtonText  "Format"
4235 #define HomeButtonText  "Home"
4236 #define UpButtonText  "Up"
4237
4238   char
4239     *directory,
4240     **filelist,
4241     home_directory[MaxTextExtent],
4242     primary_selection[MaxTextExtent],
4243     text[MaxTextExtent],
4244     working_path[MaxTextExtent];
4245
4246   int
4247     x,
4248     y;
4249
4250   register ssize_t
4251     i;
4252
4253   static char
4254     glob_pattern[MaxTextExtent] = "*",
4255     format[MaxTextExtent] = "miff";
4256
4257   static MagickStatusType
4258     mask = (MagickStatusType) (CWWidth | CWHeight | CWX | CWY);
4259
4260   Status
4261     status;
4262
4263   unsigned int
4264     anomaly,
4265     height,
4266     text_width,
4267     visible_files,
4268     width;
4269
4270   size_t
4271     delay,
4272     files,
4273     state;
4274
4275   XEvent
4276     event;
4277
4278   XFontStruct
4279     *font_info;
4280
4281   XTextProperty
4282     window_name;
4283
4284   XWidgetInfo
4285     action_info,
4286     cancel_info,
4287     expose_info,
4288     special_info,
4289     list_info,
4290     home_info,
4291     north_info,
4292     reply_info,
4293     scroll_info,
4294     selection_info,
4295     slider_info,
4296     south_info,
4297     text_info,
4298     up_info;
4299
4300   XWindowChanges
4301     window_changes;
4302
4303   /*
4304     Read filelist from current directory.
4305   */
4306   assert(display != (Display *) NULL);
4307   assert(windows != (XWindows *) NULL);
4308   assert(action != (char *) NULL);
4309   assert(reply != (char *) NULL);
4310   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",action);
4311   XSetCursorState(display,windows,MagickTrue);
4312   XCheckRefreshWindows(display,windows);
4313   directory=getcwd(home_directory,MaxTextExtent);
4314   (void) directory;
4315   (void) CopyMagickString(working_path,home_directory,MaxTextExtent);
4316   filelist=ListFiles(working_path,glob_pattern,&files);
4317   if (filelist == (char **) NULL)
4318     {
4319       /*
4320         Directory read failed.
4321       */
4322       XNoticeWidget(display,windows,"Unable to read directory:",working_path);
4323       (void) XDialogWidget(display,windows,action,"Enter filename:",reply);
4324       return;
4325     }
4326   /*
4327     Determine File Browser widget attributes.
4328   */
4329   font_info=windows->widget.font_info;
4330   text_width=0;
4331   for (i=0; i < (ssize_t) files; i++)
4332     if (WidgetTextWidth(font_info,filelist[i]) > text_width)
4333       text_width=WidgetTextWidth(font_info,filelist[i]);
4334   width=WidgetTextWidth(font_info,(char *) action);
4335   if (WidgetTextWidth(font_info,GrabButtonText) > width)
4336     width=WidgetTextWidth(font_info,GrabButtonText);
4337   if (WidgetTextWidth(font_info,FormatButtonText) > width)
4338     width=WidgetTextWidth(font_info,FormatButtonText);
4339   if (WidgetTextWidth(font_info,CancelButtonText) > width)
4340     width=WidgetTextWidth(font_info,CancelButtonText);
4341   if (WidgetTextWidth(font_info,HomeButtonText) > width)
4342     width=WidgetTextWidth(font_info,HomeButtonText);
4343   if (WidgetTextWidth(font_info,UpButtonText) > width)
4344     width=WidgetTextWidth(font_info,UpButtonText);
4345   width+=QuantumMargin;
4346   if (WidgetTextWidth(font_info,DirectoryText) > width)
4347     width=WidgetTextWidth(font_info,DirectoryText);
4348   if (WidgetTextWidth(font_info,FilenameText) > width)
4349     width=WidgetTextWidth(font_info,FilenameText);
4350   height=(unsigned int) (font_info->ascent+font_info->descent);
4351   /*
4352     Position File Browser widget.
4353   */
4354   windows->widget.width=width+MagickMin((int) text_width,(int) MaxTextWidth)+
4355     6*QuantumMargin;
4356   windows->widget.min_width=width+MinTextWidth+4*QuantumMargin;
4357   if (windows->widget.width < windows->widget.min_width)
4358     windows->widget.width=windows->widget.min_width;
4359   windows->widget.height=(unsigned int)
4360     (((81*height) >> 2)+((13*QuantumMargin) >> 1)+4);
4361   windows->widget.min_height=(unsigned int)
4362     (((23*height) >> 1)+((13*QuantumMargin) >> 1)+4);
4363   if (windows->widget.height < windows->widget.min_height)
4364     windows->widget.height=windows->widget.min_height;
4365   XConstrainWindowPosition(display,&windows->widget);
4366   /*
4367     Map File Browser widget.
4368   */
4369   (void) CopyMagickString(windows->widget.name,"Browse and Select a File",
4370     MaxTextExtent);
4371   status=XStringListToTextProperty(&windows->widget.name,1,&window_name);
4372   if (status != False)
4373     {
4374       XSetWMName(display,windows->widget.id,&window_name);
4375       XSetWMIconName(display,windows->widget.id,&window_name);
4376       (void) XFree((void *) window_name.value);
4377     }
4378   window_changes.width=(int) windows->widget.width;
4379   window_changes.height=(int) windows->widget.height;
4380   window_changes.x=windows->widget.x;
4381   window_changes.y=windows->widget.y;
4382   (void) XReconfigureWMWindow(display,windows->widget.id,
4383     windows->widget.screen,mask,&window_changes);
4384   (void) XMapRaised(display,windows->widget.id);
4385   windows->widget.mapped=MagickFalse;
4386   /*
4387     Respond to X events.
4388   */
4389   XGetWidgetInfo((char *) NULL,&slider_info);
4390   XGetWidgetInfo((char *) NULL,&north_info);
4391   XGetWidgetInfo((char *) NULL,&south_info);
4392   XGetWidgetInfo((char *) NULL,&expose_info);
4393   visible_files=0;
4394   anomaly=(LocaleCompare(action,"Composite") == 0) ||
4395     (LocaleCompare(action,"Open") == 0) || (LocaleCompare(action,"Map") == 0);
4396   *reply='\0';
4397   delay=SuspendTime << 2;
4398   state=UpdateConfigurationState;
4399   do
4400   {
4401     if (state & UpdateConfigurationState)
4402       {
4403         int
4404           id;
4405
4406         /*
4407           Initialize button information.
4408         */
4409         XGetWidgetInfo(CancelButtonText,&cancel_info);
4410         cancel_info.width=width;
4411         cancel_info.height=(unsigned int) ((3*height) >> 1);
4412         cancel_info.x=(int)
4413           (windows->widget.width-cancel_info.width-QuantumMargin-2);
4414         cancel_info.y=(int)
4415           (windows->widget.height-cancel_info.height-QuantumMargin);
4416         XGetWidgetInfo(action,&action_info);
4417         action_info.width=width;
4418         action_info.height=(unsigned int) ((3*height) >> 1);
4419         action_info.x=cancel_info.x-(cancel_info.width+(QuantumMargin >> 1)+
4420           (action_info.bevel_width << 1));
4421         action_info.y=cancel_info.y;
4422         XGetWidgetInfo(GrabButtonText,&special_info);
4423         special_info.width=width;
4424         special_info.height=(unsigned int) ((3*height) >> 1);
4425         special_info.x=action_info.x-(action_info.width+(QuantumMargin >> 1)+
4426           (special_info.bevel_width << 1));
4427         special_info.y=action_info.y;
4428         if (anomaly == MagickFalse)
4429           {
4430             register char
4431               *p;
4432
4433             special_info.text=(char *) FormatButtonText;
4434             p=reply+Extent(reply)-1;
4435             while ((p > (reply+1)) && (*(p-1) != '.'))
4436               p--;
4437             if ((p > (reply+1)) && (*(p-1) == '.'))
4438               (void) CopyMagickString(format,p,MaxTextExtent);
4439           }
4440         XGetWidgetInfo(UpButtonText,&up_info);
4441         up_info.width=width;
4442         up_info.height=(unsigned int) ((3*height) >> 1);
4443         up_info.x=QuantumMargin;
4444         up_info.y=((5*QuantumMargin) >> 1)+height;
4445         XGetWidgetInfo(HomeButtonText,&home_info);
4446         home_info.width=width;
4447         home_info.height=(unsigned int) ((3*height) >> 1);
4448         home_info.x=QuantumMargin;
4449         home_info.y=up_info.y+up_info.height+QuantumMargin;
4450         /*
4451           Initialize reply information.
4452         */
4453         XGetWidgetInfo(reply,&reply_info);
4454         reply_info.raised=MagickFalse;
4455         reply_info.bevel_width--;
4456         reply_info.width=windows->widget.width-width-((6*QuantumMargin) >> 1);
4457         reply_info.height=height << 1;
4458         reply_info.x=(int) (width+(QuantumMargin << 1));
4459         reply_info.y=action_info.y-reply_info.height-QuantumMargin;
4460         /*
4461           Initialize scroll information.
4462         */
4463         XGetWidgetInfo((char *) NULL,&scroll_info);
4464         scroll_info.bevel_width--;
4465         scroll_info.width=height;
4466         scroll_info.height=(unsigned int)
4467           (reply_info.y-up_info.y-(QuantumMargin >> 1));
4468         scroll_info.x=reply_info.x+(reply_info.width-scroll_info.width);
4469         scroll_info.y=up_info.y-reply_info.bevel_width;
4470         scroll_info.raised=MagickFalse;
4471         scroll_info.trough=MagickTrue;
4472         north_info=scroll_info;
4473         north_info.raised=MagickTrue;
4474         north_info.width-=(north_info.bevel_width << 1);
4475         north_info.height=north_info.width-1;
4476         north_info.x+=north_info.bevel_width;
4477         north_info.y+=north_info.bevel_width;
4478         south_info=north_info;
4479         south_info.y=scroll_info.y+scroll_info.height-scroll_info.bevel_width-
4480           south_info.height;
4481         id=slider_info.id;
4482         slider_info=north_info;
4483         slider_info.id=id;
4484         slider_info.width-=2;
4485         slider_info.min_y=north_info.y+north_info.height+north_info.bevel_width+
4486           slider_info.bevel_width+2;
4487         slider_info.height=scroll_info.height-((slider_info.min_y-
4488           scroll_info.y+1) << 1)+4;
4489         visible_files=scroll_info.height/(height+(height >> 3));
4490         if (files > visible_files)
4491           slider_info.height=(unsigned int)
4492             ((visible_files*slider_info.height)/files);
4493         slider_info.max_y=south_info.y-south_info.bevel_width-
4494           slider_info.bevel_width-2;
4495         slider_info.x=scroll_info.x+slider_info.bevel_width+1;
4496         slider_info.y=slider_info.min_y;
4497         expose_info=scroll_info;
4498         expose_info.y=slider_info.y;
4499         /*
4500           Initialize list information.
4501         */
4502         XGetWidgetInfo((char *) NULL,&list_info);
4503         list_info.raised=MagickFalse;
4504         list_info.bevel_width--;
4505         list_info.width=(unsigned int)
4506           (scroll_info.x-reply_info.x-(QuantumMargin >> 1));
4507         list_info.height=scroll_info.height;
4508         list_info.x=reply_info.x;
4509         list_info.y=scroll_info.y;
4510         if (windows->widget.mapped == MagickFalse)
4511           state|=JumpListState;
4512         /*
4513           Initialize text information.
4514         */
4515         *text='\0';
4516         XGetWidgetInfo(text,&text_info);
4517         text_info.center=MagickFalse;
4518         text_info.width=reply_info.width;
4519         text_info.height=height;
4520         text_info.x=list_info.x-(QuantumMargin >> 1);
4521         text_info.y=QuantumMargin;
4522         /*
4523           Initialize selection information.
4524         */
4525         XGetWidgetInfo((char *) NULL,&selection_info);
4526         selection_info.center=MagickFalse;
4527         selection_info.width=list_info.width;
4528         selection_info.height=(unsigned int) ((9*height) >> 3);
4529         selection_info.x=list_info.x;
4530         state&=(~UpdateConfigurationState);
4531       }
4532     if (state & RedrawWidgetState)
4533       {
4534         /*
4535           Redraw File Browser window.
4536         */
4537         x=QuantumMargin;
4538         y=text_info.y+((text_info.height-height) >> 1)+font_info->ascent;
4539         (void) XDrawString(display,windows->widget.id,
4540           windows->widget.annotate_context,x,y,DirectoryText,
4541           Extent(DirectoryText));
4542         (void) CopyMagickString(text_info.text,working_path,MaxTextExtent);
4543         (void) ConcatenateMagickString(text_info.text,DirectorySeparator,
4544           MaxTextExtent);
4545         (void) ConcatenateMagickString(text_info.text,glob_pattern,
4546           MaxTextExtent);
4547         XDrawWidgetText(display,&windows->widget,&text_info);
4548         XDrawBeveledButton(display,&windows->widget,&up_info);
4549         XDrawBeveledButton(display,&windows->widget,&home_info);
4550         XDrawBeveledMatte(display,&windows->widget,&list_info);
4551         XDrawBeveledMatte(display,&windows->widget,&scroll_info);
4552         XDrawTriangleNorth(display,&windows->widget,&north_info);
4553         XDrawBeveledButton(display,&windows->widget,&slider_info);
4554         XDrawTriangleSouth(display,&windows->widget,&south_info);
4555         x=QuantumMargin;
4556         y=reply_info.y+((reply_info.height-height) >> 1)+font_info->ascent;
4557         (void) XDrawString(display,windows->widget.id,
4558           windows->widget.annotate_context,x,y,FilenameText,
4559           Extent(FilenameText));
4560         XDrawBeveledMatte(display,&windows->widget,&reply_info);
4561         XDrawMatteText(display,&windows->widget,&reply_info);
4562         XDrawBeveledButton(display,&windows->widget,&special_info);
4563         XDrawBeveledButton(display,&windows->widget,&action_info);
4564         XDrawBeveledButton(display,&windows->widget,&cancel_info);
4565         XHighlightWidget(display,&windows->widget,BorderOffset,BorderOffset);
4566         selection_info.id=(~0);
4567         state|=RedrawListState;
4568         state&=(~RedrawWidgetState);
4569       }
4570     if (state & UpdateListState)
4571       {
4572         char
4573           **checklist;
4574
4575         size_t
4576           number_files;
4577
4578         /*
4579           Update file list.
4580         */
4581         checklist=ListFiles(working_path,glob_pattern,&number_files);
4582         if (checklist == (char **) NULL)
4583           {
4584             /*
4585               Reply is a filename, exit.
4586             */
4587             action_info.raised=MagickFalse;
4588             XDrawBeveledButton(display,&windows->widget,&action_info);
4589             break;
4590           }
4591         for (i=0; i < (ssize_t) files; i++)
4592           filelist[i]=DestroyString(filelist[i]);
4593         if (filelist != (char **) NULL)
4594           filelist=(char **) RelinquishMagickMemory(filelist);
4595         filelist=checklist;
4596         files=number_files;
4597         /*
4598           Update file list.
4599         */
4600         slider_info.height=
4601           scroll_info.height-((slider_info.min_y-scroll_info.y+1) << 1)+1;
4602         if (files > visible_files)
4603           slider_info.height=(unsigned int)
4604             ((visible_files*slider_info.height)/files);
4605         slider_info.max_y=south_info.y-south_info.bevel_width-
4606           slider_info.bevel_width-2;
4607         slider_info.id=0;
4608         slider_info.y=slider_info.min_y;
4609         expose_info.y=slider_info.y;
4610         selection_info.id=(~0);
4611         list_info.id=(~0);
4612         state|=RedrawListState;
4613         /*
4614           Redraw directory name & reply.
4615         */
4616         if (IsGlob(reply_info.text) == MagickFalse)
4617           {
4618             *reply_info.text='\0';
4619             reply_info.cursor=reply_info.text;
4620           }
4621         (void) CopyMagickString(text_info.text,working_path,MaxTextExtent);
4622         (void) ConcatenateMagickString(text_info.text,DirectorySeparator,
4623           MaxTextExtent);
4624         (void) ConcatenateMagickString(text_info.text,glob_pattern,
4625           MaxTextExtent);
4626         XDrawWidgetText(display,&windows->widget,&text_info);
4627         XDrawMatteText(display,&windows->widget,&reply_info);
4628         XDrawBeveledMatte(display,&windows->widget,&scroll_info);
4629         XDrawTriangleNorth(display,&windows->widget,&north_info);
4630         XDrawBeveledButton(display,&windows->widget,&slider_info);
4631         XDrawTriangleSouth(display,&windows->widget,&south_info);
4632         XHighlightWidget(display,&windows->widget,BorderOffset,BorderOffset);
4633         state&=(~UpdateListState);
4634       }
4635     if (state & JumpListState)
4636       {
4637         /*
4638           Jump scroll to match user filename.
4639         */
4640         list_info.id=(~0);
4641         for (i=0; i < (ssize_t) files; i++)
4642           if (LocaleCompare(filelist[i],reply) >= 0)
4643             {
4644               list_info.id=(int)
4645                 (LocaleCompare(filelist[i],reply) == 0 ? i : ~0);
4646               break;
4647             }
4648         if ((i < (ssize_t) slider_info.id) ||
4649             (i >= (ssize_t) (slider_info.id+visible_files)))
4650           slider_info.id=(int) i-(visible_files >> 1);
4651         selection_info.id=(~0);
4652         state|=RedrawListState;
4653         state&=(~JumpListState);
4654       }
4655     if (state & RedrawListState)
4656       {
4657         /*
4658           Determine slider id and position.
4659         */
4660         if (slider_info.id >= (int) (files-visible_files))
4661           slider_info.id=(int) (files-visible_files);
4662         if ((slider_info.id < 0) || (files <= visible_files))
4663           slider_info.id=0;
4664         slider_info.y=slider_info.min_y;
4665         if (files > 0)
4666           slider_info.y+=(int) (slider_info.id*(slider_info.max_y-
4667             slider_info.min_y+1)/files);
4668         if (slider_info.id != selection_info.id)
4669           {
4670             /*
4671               Redraw scroll bar and file names.
4672             */
4673             selection_info.id=slider_info.id;
4674             selection_info.y=list_info.y+(height >> 3)+2;
4675             for (i=0; i < (ssize_t) visible_files; i++)
4676             {
4677               selection_info.raised=(int) (slider_info.id+i) != list_info.id ?
4678                 MagickTrue : MagickFalse;
4679               selection_info.text=(char *) NULL;
4680               if ((slider_info.id+i) < (ssize_t) files)
4681                 selection_info.text=filelist[slider_info.id+i];
4682               XDrawWidgetText(display,&windows->widget,&selection_info);
4683               selection_info.y+=(int) selection_info.height;
4684             }
4685             /*
4686               Update slider.
4687             */
4688             if (slider_info.y > expose_info.y)
4689               {
4690                 expose_info.height=(unsigned int) slider_info.y-expose_info.y;
4691                 expose_info.y=slider_info.y-expose_info.height-
4692                   slider_info.bevel_width-1;
4693               }
4694             else
4695               {
4696                 expose_info.height=(unsigned int) expose_info.y-slider_info.y;
4697                 expose_info.y=slider_info.y+slider_info.height+
4698                   slider_info.bevel_width+1;
4699               }
4700             XDrawTriangleNorth(display,&windows->widget,&north_info);
4701             XDrawMatte(display,&windows->widget,&expose_info);
4702             XDrawBeveledButton(display,&windows->widget,&slider_info);
4703             XDrawTriangleSouth(display,&windows->widget,&south_info);
4704             expose_info.y=slider_info.y;
4705           }
4706         state&=(~RedrawListState);
4707       }
4708     /*
4709       Wait for next event.
4710     */
4711     if (north_info.raised && south_info.raised)
4712       (void) XIfEvent(display,&event,XScreenEvent,(char *) windows);
4713     else
4714       {
4715         /*
4716           Brief delay before advancing scroll bar.
4717         */
4718         XDelay(display,delay);
4719         delay=SuspendTime;
4720         (void) XCheckIfEvent(display,&event,XScreenEvent,(char *) windows);
4721         if (north_info.raised == MagickFalse)
4722           if (slider_info.id > 0)
4723             {
4724               /*
4725                 Move slider up.
4726               */
4727               slider_info.id--;
4728               state|=RedrawListState;
4729             }
4730         if (south_info.raised == MagickFalse)
4731           if (slider_info.id < (int) files)
4732             {
4733               /*
4734                 Move slider down.
4735               */
4736               slider_info.id++;
4737               state|=RedrawListState;
4738             }
4739         if (event.type != ButtonRelease)
4740           continue;
4741       }
4742     switch (event.type)
4743     {
4744       case ButtonPress:
4745       {
4746         if (MatteIsActive(slider_info,event.xbutton))
4747           {
4748             /*
4749               Track slider.
4750             */
4751             slider_info.active=MagickTrue;
4752             break;
4753           }
4754         if (MatteIsActive(north_info,event.xbutton))
4755           if (slider_info.id > 0)
4756             {
4757               /*
4758                 Move slider up.
4759               */
4760               north_info.raised=MagickFalse;
4761               slider_info.id--;
4762               state|=RedrawListState;
4763               break;
4764             }
4765         if (MatteIsActive(south_info,event.xbutton))
4766           if (slider_info.id < (int) files)
4767             {
4768               /*
4769                 Move slider down.
4770               */
4771               south_info.raised=MagickFalse;
4772               slider_info.id++;
4773               state|=RedrawListState;
4774               break;
4775             }
4776         if (MatteIsActive(scroll_info,event.xbutton))
4777           {
4778             /*
4779               Move slider.
4780             */
4781             if (event.xbutton.y < slider_info.y)
4782               slider_info.id-=(visible_files-1);
4783             else
4784               slider_info.id+=(visible_files-1);
4785             state|=RedrawListState;
4786             break;
4787           }
4788         if (MatteIsActive(list_info,event.xbutton))
4789           {
4790             int
4791               id;
4792
4793             /*
4794               User pressed file matte.
4795             */
4796             id=slider_info.id+(event.xbutton.y-(list_info.y+(height >> 1))+1)/
4797               selection_info.height;
4798             if (id >= (int) files)
4799               break;
4800             (void) CopyMagickString(reply_info.text,filelist[id],MaxTextExtent);
4801             reply_info.highlight=MagickFalse;
4802             reply_info.marker=reply_info.text;
4803             reply_info.cursor=reply_info.text+Extent(reply_info.text);
4804             XDrawMatteText(display,&windows->widget,&reply_info);
4805             if (id == list_info.id)
4806               {
4807                 register char
4808                   *p;
4809
4810                 p=reply_info.text+strlen(reply_info.text)-1;
4811                 if (*p == *DirectorySeparator)
4812                   ChopPathComponents(reply_info.text,1);
4813                 (void) ConcatenateMagickString(working_path,DirectorySeparator,
4814                   MaxTextExtent);
4815                 (void) ConcatenateMagickString(working_path,reply_info.text,
4816                   MaxTextExtent);
4817                 *reply='\0';
4818                 state|=UpdateListState;
4819               }
4820             selection_info.id=(~0);
4821             list_info.id=id;
4822             state|=RedrawListState;
4823             break;
4824           }
4825         if (MatteIsActive(up_info,event.xbutton))
4826           {
4827             /*
4828               User pressed Up button.
4829             */
4830             up_info.raised=MagickFalse;
4831             XDrawBeveledButton(display,&windows->widget,&up_info);
4832             break;
4833           }
4834         if (MatteIsActive(home_info,event.xbutton))
4835           {
4836             /*
4837               User pressed Home button.
4838             */
4839             home_info.raised=MagickFalse;
4840             XDrawBeveledButton(display,&windows->widget,&home_info);
4841             break;
4842           }
4843         if (MatteIsActive(special_info,event.xbutton))
4844           {
4845             /*
4846               User pressed Special button.
4847             */
4848             special_info.raised=MagickFalse;
4849             XDrawBeveledButton(display,&windows->widget,&special_info);
4850             break;
4851           }
4852         if (MatteIsActive(action_info,event.xbutton))
4853           {
4854             /*
4855               User pressed action button.
4856             */
4857             action_info.raised=MagickFalse;
4858             XDrawBeveledButton(display,&windows->widget,&action_info);
4859             break;
4860           }
4861         if (MatteIsActive(cancel_info,event.xbutton))
4862           {
4863             /*
4864               User pressed Cancel button.
4865             */
4866             cancel_info.raised=MagickFalse;
4867             XDrawBeveledButton(display,&windows->widget,&cancel_info);
4868             break;
4869           }
4870         if (MatteIsActive(reply_info,event.xbutton) == MagickFalse)
4871           break;
4872         if (event.xbutton.button != Button2)
4873           {
4874             static Time
4875               click_time;
4876
4877             /*
4878               Move text cursor to position of button press.
4879             */
4880             x=event.xbutton.x-reply_info.x-(QuantumMargin >> 2);
4881             for (i=1; i <= (ssize_t) Extent(reply_info.marker); i++)
4882               if (XTextWidth(font_info,reply_info.marker,(int) i) > x)
4883                 break;
4884             reply_info.cursor=reply_info.marker+i-1;
4885             if (event.xbutton.time > (click_time+DoubleClick))
4886               reply_info.highlight=MagickFalse;
4887             else
4888               {
4889                 /*
4890                   Become the XA_PRIMARY selection owner.
4891                 */
4892                 (void) CopyMagickString(primary_selection,reply_info.text,
4893                   MaxTextExtent);
4894                 (void) XSetSelectionOwner(display,XA_PRIMARY,windows->widget.id,
4895                   event.xbutton.time);
4896                 reply_info.highlight=XGetSelectionOwner(display,XA_PRIMARY) ==
4897                   windows->widget.id ? MagickTrue : MagickFalse;
4898               }
4899             XDrawMatteText(display,&windows->widget,&reply_info);
4900             click_time=event.xbutton.time;
4901             break;
4902           }
4903         /*
4904           Request primary selection.
4905         */
4906         (void) XConvertSelection(display,XA_PRIMARY,XA_STRING,XA_STRING,
4907           windows->widget.id,event.xbutton.time);
4908         break;
4909       }
4910       case ButtonRelease:
4911       {
4912         if (windows->widget.mapped == MagickFalse)
4913           break;
4914         if (north_info.raised == MagickFalse)
4915           {
4916             /*
4917               User released up button.
4918             */
4919             delay=SuspendTime << 2;
4920             north_info.raised=MagickTrue;
4921             XDrawTriangleNorth(display,&windows->widget,&north_info);
4922           }
4923         if (south_info.raised == MagickFalse)
4924           {
4925             /*
4926               User released down button.
4927             */
4928             delay=SuspendTime << 2;
4929             south_info.raised=MagickTrue;
4930             XDrawTriangleSouth(display,&windows->widget,&south_info);
4931           }
4932         if (slider_info.active)
4933           {
4934             /*
4935               Stop tracking slider.
4936             */
4937             slider_info.active=MagickFalse;
4938             break;
4939           }
4940         if (up_info.raised == MagickFalse)
4941           {
4942             if (event.xbutton.window == windows->widget.id)
4943               if (MatteIsActive(up_info,event.xbutton))
4944                 {
4945                   ChopPathComponents(working_path,1);
4946                   if (*working_path == '\0')
4947                     (void) CopyMagickString(working_path,DirectorySeparator,
4948                       MaxTextExtent);
4949                   state|=UpdateListState;
4950                 }
4951             up_info.raised=MagickTrue;
4952             XDrawBeveledButton(display,&windows->widget,&up_info);
4953           }
4954         if (home_info.raised == MagickFalse)
4955           {
4956             if (event.xbutton.window == windows->widget.id)
4957               if (MatteIsActive(home_info,event.xbutton))
4958                 {
4959                   (void) CopyMagickString(working_path,home_directory,
4960                     MaxTextExtent);
4961                   state|=UpdateListState;
4962                 }
4963             home_info.raised=MagickTrue;
4964             XDrawBeveledButton(display,&windows->widget,&home_info);
4965           }
4966         if (special_info.raised == MagickFalse)
4967           {
4968             if (anomaly == MagickFalse)
4969               {
4970                 char
4971                   **formats;
4972
4973                 ExceptionInfo
4974                   *exception;
4975
4976                 size_t
4977                   number_formats;
4978
4979                 /*
4980                   Let user select image format.
4981                 */
4982                 exception=AcquireExceptionInfo();
4983                 formats=GetMagickList("*",&number_formats,exception);
4984                 exception=DestroyExceptionInfo(exception);
4985                 (void) XCheckDefineCursor(display,windows->widget.id,
4986                   windows->widget.busy_cursor);
4987                 windows->popup.x=windows->widget.x+60;
4988                 windows->popup.y=windows->widget.y+60;
4989                 XListBrowserWidget(display,windows,&windows->popup,
4990                   (const char **) formats,"Select","Select image format type:",
4991                   format);
4992                 XSetCursorState(display,windows,MagickTrue);
4993                 (void) XCheckDefineCursor(display,windows->widget.id,
4994                   windows->widget.cursor);
4995                 LocaleLower(format);
4996                 AppendImageFormat(format,reply_info.text);
4997                 reply_info.cursor=reply_info.text+Extent(reply_info.text);
4998                 XDrawMatteText(display,&windows->widget,&reply_info);
4999                 special_info.raised=MagickTrue;
5000                 XDrawBeveledButton(display,&windows->widget,&special_info);
5001                 for (i=0; i < (ssize_t) number_formats; i++)
5002                   formats[i]=DestroyString(formats[i]);
5003                 formats=(char **) RelinquishMagickMemory(formats);
5004                 break;
5005               }
5006             if (event.xbutton.window == windows->widget.id)
5007               if (MatteIsActive(special_info,event.xbutton))
5008                 {
5009                   (void) CopyMagickString(working_path,"x:",MaxTextExtent);
5010                   state|=ExitState;
5011                 }
5012             special_info.raised=MagickTrue;
5013             XDrawBeveledButton(display,&windows->widget,&special_info);
5014           }
5015         if (action_info.raised == MagickFalse)
5016           {
5017             if (event.xbutton.window == windows->widget.id)
5018               {
5019                 if (MatteIsActive(action_info,event.xbutton))
5020                   {
5021                     if (*reply_info.text == '\0')
5022                       (void) XBell(display,0);
5023                     else
5024                       state|=ExitState;
5025                   }
5026               }
5027             action_info.raised=MagickTrue;
5028             XDrawBeveledButton(display,&windows->widget,&action_info);
5029           }
5030         if (cancel_info.raised == MagickFalse)
5031           {
5032             if (event.xbutton.window == windows->widget.id)
5033               if (MatteIsActive(cancel_info,event.xbutton))
5034                 {
5035                   *reply_info.text='\0';
5036                   *reply='\0';
5037                   state|=ExitState;
5038                 }
5039             cancel_info.raised=MagickTrue;
5040             XDrawBeveledButton(display,&windows->widget,&cancel_info);
5041           }
5042         break;
5043       }
5044       case ClientMessage:
5045       {
5046         /*
5047           If client window delete message, exit.
5048         */
5049         if (event.xclient.message_type != windows->wm_protocols)
5050           break;
5051         if (*event.xclient.data.l == (int) windows->wm_take_focus)
5052           {
5053             (void) XSetInputFocus(display,event.xclient.window,RevertToParent,
5054               (Time) event.xclient.data.l[1]);
5055             break;
5056           }
5057         if (*event.xclient.data.l != (int) windows->wm_delete_window)
5058           break;
5059         if (event.xclient.window == windows->widget.id)
5060           {
5061             *reply_info.text='\0';
5062             state|=ExitState;
5063             break;
5064           }
5065         break;
5066       }
5067       case ConfigureNotify:
5068       {
5069         /*
5070           Update widget configuration.
5071         */
5072         if (event.xconfigure.window != windows->widget.id)
5073           break;
5074         if ((event.xconfigure.width == (int) windows->widget.width) &&
5075             (event.xconfigure.height == (int) windows->widget.height))
5076           break;
5077         windows->widget.width=(unsigned int)
5078           MagickMax(event.xconfigure.width,(int) windows->widget.min_width);
5079         windows->widget.height=(unsigned int)
5080           MagickMax(event.xconfigure.height,(int) windows->widget.min_height);
5081         state|=UpdateConfigurationState;
5082         break;
5083       }
5084       case EnterNotify:
5085       {
5086         if (event.xcrossing.window != windows->widget.id)
5087           break;
5088         state&=(~InactiveWidgetState);
5089         break;
5090       }
5091       case Expose:
5092       {
5093         if (event.xexpose.window != windows->widget.id)
5094           break;
5095         if (event.xexpose.count != 0)
5096           break;
5097         state|=RedrawWidgetState;
5098         break;
5099       }
5100       case KeyPress:
5101       {
5102         static char
5103           command[MaxTextExtent];
5104
5105         static int
5106           length;
5107
5108         static KeySym
5109           key_symbol;
5110
5111         /*
5112           Respond to a user key press.
5113         */
5114         if (event.xkey.window != windows->widget.id)
5115           break;
5116         length=XLookupString((XKeyEvent *) &event.xkey,command,
5117           (int) sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5118         *(command+length)='\0';
5119         if (AreaIsActive(scroll_info,event.xkey))
5120           {
5121             /*
5122               Move slider.
5123             */
5124             switch ((int) key_symbol)
5125             {
5126               case XK_Home:
5127               case XK_KP_Home:
5128               {
5129                 slider_info.id=0;
5130                 break;
5131               }
5132               case XK_Up:
5133               case XK_KP_Up:
5134               {
5135                 slider_info.id--;
5136                 break;
5137               }
5138               case XK_Down:
5139               case XK_KP_Down:
5140               {
5141                 slider_info.id++;
5142                 break;
5143               }
5144               case XK_Prior:
5145               case XK_KP_Prior:
5146               {
5147                 slider_info.id-=visible_files;
5148                 break;
5149               }
5150               case XK_Next:
5151               case XK_KP_Next:
5152               {
5153                 slider_info.id+=visible_files;
5154                 break;
5155               }
5156               case XK_End:
5157               case XK_KP_End:
5158               {
5159                 slider_info.id=(int) files;
5160                 break;
5161               }
5162             }
5163             state|=RedrawListState;
5164             break;
5165           }
5166         if ((key_symbol == XK_Return) || (key_symbol == XK_KP_Enter))
5167           {
5168             /*
5169               Read new directory or glob patterm.
5170             */
5171             if (*reply_info.text == '\0')
5172               break;
5173             if (IsGlob(reply_info.text))
5174               (void) CopyMagickString(glob_pattern,reply_info.text,
5175                 MaxTextExtent);
5176             else
5177               {
5178                 (void) ConcatenateMagickString(working_path,DirectorySeparator,
5179                   MaxTextExtent);
5180                 (void) ConcatenateMagickString(working_path,reply_info.text,
5181                   MaxTextExtent);
5182                 if (*working_path == '~')
5183                   ExpandFilename(working_path);
5184                 *reply='\0';
5185               }
5186             state|=UpdateListState;
5187             break;
5188           }
5189         if (key_symbol == XK_Control_L)
5190           {
5191             state|=ControlState;
5192             break;
5193           }
5194         if (state & ControlState)
5195           switch ((int) key_symbol)
5196           {
5197             case XK_u:
5198             case XK_U:
5199             {
5200               /*
5201                 Erase the entire line of text.
5202               */
5203               *reply_info.text='\0';
5204               reply_info.cursor=reply_info.text;
5205               reply_info.marker=reply_info.text;
5206               reply_info.highlight=MagickFalse;
5207               break;
5208             }
5209             default:
5210               break;
5211           }
5212         XEditText(display,&reply_info,key_symbol,command,state);
5213         XDrawMatteText(display,&windows->widget,&reply_info);
5214         state|=JumpListState;
5215         break;
5216       }
5217       case KeyRelease:
5218       {
5219         static char
5220           command[MaxTextExtent];
5221
5222         static KeySym
5223           key_symbol;
5224
5225         /*
5226           Respond to a user key release.
5227         */
5228         if (event.xkey.window != windows->widget.id)
5229           break;
5230         (void) XLookupString((XKeyEvent *) &event.xkey,command,
5231           (int) sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5232         if (key_symbol == XK_Control_L)
5233           state&=(~ControlState);
5234         break;
5235       }
5236       case LeaveNotify:
5237       {
5238         if (event.xcrossing.window != windows->widget.id)
5239           break;
5240         state|=InactiveWidgetState;
5241         break;
5242       }
5243       case MapNotify:
5244       {
5245         mask&=(~CWX);
5246         mask&=(~CWY);
5247         break;
5248       }
5249       case MotionNotify:
5250       {
5251         /*
5252           Discard pending button motion events.
5253         */
5254         while (XCheckMaskEvent(display,ButtonMotionMask,&event)) ;
5255         if (slider_info.active)
5256           {
5257             /*
5258               Move slider matte.
5259             */
5260             slider_info.y=event.xmotion.y-
5261               ((slider_info.height+slider_info.bevel_width) >> 1)+1;
5262             if (slider_info.y < slider_info.min_y)
5263               slider_info.y=slider_info.min_y;
5264             if (slider_info.y > slider_info.max_y)
5265               slider_info.y=slider_info.max_y;
5266             slider_info.id=0;
5267             if (slider_info.y != slider_info.min_y)
5268               slider_info.id=(int) ((files*(slider_info.y-slider_info.min_y+1))/
5269                 (slider_info.max_y-slider_info.min_y+1));
5270             state|=RedrawListState;
5271             break;
5272           }
5273         if (state & InactiveWidgetState)
5274           break;
5275         if (up_info.raised == MatteIsActive(up_info,event.xmotion))
5276           {
5277             /*
5278               Up button status changed.
5279             */
5280             up_info.raised=!up_info.raised;
5281             XDrawBeveledButton(display,&windows->widget,&up_info);
5282             break;
5283           }
5284         if (home_info.raised == MatteIsActive(home_info,event.xmotion))
5285           {
5286             /*
5287               Home button status changed.
5288             */
5289             home_info.raised=!home_info.raised;
5290             XDrawBeveledButton(display,&windows->widget,&home_info);
5291             break;
5292           }
5293         if (special_info.raised == MatteIsActive(special_info,event.xmotion))
5294           {
5295             /*
5296               Grab button status changed.
5297             */
5298             special_info.raised=!special_info.raised;
5299             XDrawBeveledButton(display,&windows->widget,&special_info);
5300             break;
5301           }
5302         if (action_info.raised == MatteIsActive(action_info,event.xmotion))
5303           {
5304             /*
5305               Action button status changed.
5306             */
5307             action_info.raised=action_info.raised == MagickFalse ?
5308               MagickTrue : MagickFalse;
5309             XDrawBeveledButton(display,&windows->widget,&action_info);
5310             break;
5311           }
5312         if (cancel_info.raised == MatteIsActive(cancel_info,event.xmotion))
5313           {
5314             /*
5315               Cancel button status changed.
5316             */
5317             cancel_info.raised=cancel_info.raised == MagickFalse ?
5318               MagickTrue : MagickFalse;
5319             XDrawBeveledButton(display,&windows->widget,&cancel_info);
5320             break;
5321           }
5322         break;
5323       }
5324       case SelectionClear:
5325       {
5326         reply_info.highlight=MagickFalse;
5327         XDrawMatteText(display,&windows->widget,&reply_info);
5328         break;
5329       }
5330       case SelectionNotify:
5331       {
5332         Atom
5333           type;
5334
5335         int
5336           format;
5337
5338         unsigned char
5339           *data;
5340
5341         unsigned long
5342           after,
5343           length;
5344
5345         /*
5346           Obtain response from primary selection.
5347         */
5348         if (event.xselection.property == (Atom) None)
5349           break;
5350         status=XGetWindowProperty(display,event.xselection.requestor,
5351           event.xselection.property,0L,2047L,MagickTrue,XA_STRING,&type,
5352           &format,&length,&after,&data);
5353         if ((status != Success) || (type != XA_STRING) || (format == 32) ||
5354             (length == 0))
5355           break;
5356         if ((Extent(reply_info.text)+length) >= (MaxTextExtent-1))
5357           (void) XBell(display,0);
5358         else
5359           {
5360             /*
5361               Insert primary selection in reply text.
5362             */
5363             *(data+length)='\0';
5364             XEditText(display,&reply_info,(KeySym) XK_Insert,(char *) data,
5365               state);
5366             XDrawMatteText(display,&windows->widget,&reply_info);
5367             state|=JumpListState;
5368             state|=RedrawActionState;
5369           }
5370         (void) XFree((void *) data);
5371         break;
5372       }
5373       case SelectionRequest:
5374       {
5375         XSelectionEvent
5376           notify;
5377
5378         XSelectionRequestEvent
5379           *request;
5380
5381         if (reply_info.highlight == MagickFalse)
5382           break;
5383         /*
5384           Set primary selection.
5385         */
5386         request=(&(event.xselectionrequest));
5387         (void) XChangeProperty(request->display,request->requestor,
5388           request->property,request->target,8,PropModeReplace,
5389           (unsigned char *) primary_selection,Extent(primary_selection));
5390         notify.type=SelectionNotify;
5391         notify.display=request->display;
5392         notify.requestor=request->requestor;
5393         notify.selection=request->selection;
5394         notify.target=request->target;
5395         notify.time=request->time;
5396         if (request->property == None)
5397           notify.property=request->target;
5398         else
5399           notify.property=request->property;
5400         (void) XSendEvent(request->display,request->requestor,False,0,
5401           (XEvent *) &notify);
5402       }
5403       default:
5404         break;
5405     }
5406   } while ((state & ExitState) == 0);
5407   XSetCursorState(display,windows,MagickFalse);
5408   (void) XWithdrawWindow(display,windows->widget.id,windows->widget.screen);
5409   XCheckRefreshWindows(display,windows);
5410   /*
5411     Free file list.
5412   */
5413   for (i=0; i < (ssize_t) files; i++)
5414     filelist[i]=DestroyString(filelist[i]);
5415   if (filelist != (char **) NULL)
5416     filelist=(char **) RelinquishMagickMemory(filelist);
5417   if (*reply != '\0')
5418     {
5419       (void) ConcatenateMagickString(working_path,DirectorySeparator,
5420         MaxTextExtent);
5421       (void) ConcatenateMagickString(working_path,reply,MaxTextExtent);
5422     }
5423   (void) CopyMagickString(reply,working_path,MaxTextExtent);
5424   if (*reply == '~')
5425     ExpandFilename(reply);
5426 }
5427 \f
5428 /*
5429 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5430 %                                                                             %
5431 %                                                                             %
5432 %                                                                             %
5433 %   X F o n t B r o w s e r W i d g e t                                       %
5434 %                                                                             %
5435 %                                                                             %
5436 %                                                                             %
5437 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5438 %
5439 %  XFontBrowserWidget() displays a Font Browser widget with a font query to the
5440 %  user.  The user keys a reply and presses the Action or Cancel button to
5441 %  exit.  The typed text is returned as the reply function parameter.
5442 %
5443 %  The format of the XFontBrowserWidget method is:
5444 %
5445 %      void XFontBrowserWidget(Display *display,XWindows *windows,
5446 %        const char *action,char *reply)
5447 %
5448 %  A description of each parameter follows:
5449 %
5450 %    o display: Specifies a connection to an X server;  returned from
5451 %      XOpenDisplay.
5452 %
5453 %    o window: Specifies a pointer to a XWindows structure.
5454 %
5455 %    o action: Specifies a pointer to the action of this widget.
5456 %
5457 %    o reply: the response from the user is returned in this parameter.
5458 %
5459 %
5460 */
5461
5462 #if defined(__cplusplus) || defined(c_plusplus)
5463 extern "C" {
5464 #endif
5465
5466 static int FontCompare(const void *x,const void *y)
5467 {
5468   register char
5469     *p,
5470     *q;
5471
5472   p=(char *) *((char **) x);
5473   q=(char *) *((char **) y);
5474   while ((*p != '\0') && (*q != '\0') && (*p == *q))
5475   {
5476     p++;
5477     q++;
5478   }
5479   return(*p-(*q));
5480 }
5481
5482 #if defined(__cplusplus) || defined(c_plusplus)
5483 }
5484 #endif
5485
5486 MagickPrivate void XFontBrowserWidget(Display *display,XWindows *windows,
5487   const char *action,char *reply)
5488 {
5489 #define BackButtonText  "Back"
5490 #define CancelButtonText  "Cancel"
5491 #define FontnameText  "Name:"
5492 #define FontPatternText  "Pattern:"
5493 #define ResetButtonText  "Reset"
5494
5495   char
5496     back_pattern[MaxTextExtent],
5497     **fontlist,
5498     **listhead,
5499     primary_selection[MaxTextExtent],
5500     reset_pattern[MaxTextExtent],
5501     text[MaxTextExtent];
5502
5503   int
5504     fonts,
5505     x,
5506     y;
5507
5508   register int
5509     i;
5510
5511   static char
5512     glob_pattern[MaxTextExtent] = "*";
5513
5514   static MagickStatusType
5515     mask = (MagickStatusType) (CWWidth | CWHeight | CWX | CWY);
5516
5517   Status
5518     status;
5519
5520   unsigned int
5521     height,
5522     text_width,
5523     visible_fonts,
5524     width;
5525
5526   size_t
5527     delay,
5528     state;
5529
5530   XEvent
5531     event;
5532
5533   XFontStruct
5534     *font_info;
5535
5536   XTextProperty
5537     window_name;
5538
5539   XWidgetInfo
5540     action_info,
5541     back_info,
5542     cancel_info,
5543     expose_info,
5544     list_info,
5545     mode_info,
5546     north_info,
5547     reply_info,
5548     reset_info,
5549     scroll_info,
5550     selection_info,
5551     slider_info,
5552     south_info,
5553     text_info;
5554
5555   XWindowChanges
5556     window_changes;
5557
5558   /*
5559     Get font list and sort in ascending order.
5560   */
5561   assert(display != (Display *) NULL);
5562   assert(windows != (XWindows *) NULL);
5563   assert(action != (char *) NULL);
5564   assert(reply != (char *) NULL);
5565   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",action);
5566   XSetCursorState(display,windows,MagickTrue);
5567   XCheckRefreshWindows(display,windows);
5568   (void) CopyMagickString(back_pattern,glob_pattern,MaxTextExtent);
5569   (void) CopyMagickString(reset_pattern,"*",MaxTextExtent);
5570   fontlist=XListFonts(display,glob_pattern,32767,&fonts);
5571   if (fonts == 0)
5572     {
5573       /*
5574         Pattern failed, obtain all the fonts.
5575       */
5576       XNoticeWidget(display,windows,"Unable to obtain fonts names:",
5577         glob_pattern);
5578       (void) CopyMagickString(glob_pattern,"*",MaxTextExtent);
5579       fontlist=XListFonts(display,glob_pattern,32767,&fonts);
5580       if (fontlist == (char **) NULL)
5581         {
5582           XNoticeWidget(display,windows,"Unable to obtain fonts names:",
5583             glob_pattern);
5584           return;
5585         }
5586     }
5587   /*
5588     Sort font list in ascending order.
5589   */
5590   listhead=fontlist;
5591   fontlist=(char **) AcquireQuantumMemory((size_t) fonts,sizeof(*fontlist));
5592   if (fontlist == (char **) NULL)
5593     {
5594       XNoticeWidget(display,windows,"MemoryAllocationFailed",
5595         "UnableToViewFonts");
5596       return;
5597     }
5598   for (i=0; i < fonts; i++)
5599     fontlist[i]=listhead[i];
5600   qsort((void *) fontlist,(size_t) fonts,sizeof(*fontlist),FontCompare);
5601   /*
5602     Determine Font Browser widget attributes.
5603   */
5604   font_info=windows->widget.font_info;
5605   text_width=0;
5606   for (i=0; i < fonts; i++)
5607     if (WidgetTextWidth(font_info,fontlist[i]) > text_width)
5608       text_width=WidgetTextWidth(font_info,fontlist[i]);
5609   width=WidgetTextWidth(font_info,(char *) action);
5610   if (WidgetTextWidth(font_info,CancelButtonText) > width)
5611     width=WidgetTextWidth(font_info,CancelButtonText);
5612   if (WidgetTextWidth(font_info,ResetButtonText) > width)
5613     width=WidgetTextWidth(font_info,ResetButtonText);
5614   if (WidgetTextWidth(font_info,BackButtonText) > width)
5615     width=WidgetTextWidth(font_info,BackButtonText);
5616   width+=QuantumMargin;
5617   if (WidgetTextWidth(font_info,FontPatternText) > width)
5618     width=WidgetTextWidth(font_info,FontPatternText);
5619   if (WidgetTextWidth(font_info,FontnameText) > width)
5620     width=WidgetTextWidth(font_info,FontnameText);
5621   height=(unsigned int) (font_info->ascent+font_info->descent);
5622   /*
5623     Position Font Browser widget.
5624   */
5625   windows->widget.width=width+MagickMin((int) text_width,(int) MaxTextWidth)+
5626     6*QuantumMargin;
5627   windows->widget.min_width=width+MinTextWidth+4*QuantumMargin;
5628   if (windows->widget.width < windows->widget.min_width)
5629     windows->widget.width=windows->widget.min_width;
5630   windows->widget.height=(unsigned int)
5631     (((85*height) >> 2)+((13*QuantumMargin) >> 1)+4);
5632   windows->widget.min_height=(unsigned int)
5633     (((27*height) >> 1)+((13*QuantumMargin) >> 1)+4);
5634   if (windows->widget.height < windows->widget.min_height)
5635     windows->widget.height=windows->widget.min_height;
5636   XConstrainWindowPosition(display,&windows->widget);
5637   /*
5638     Map Font Browser widget.
5639   */
5640   (void) CopyMagickString(windows->widget.name,"Browse and Select a Font",
5641     MaxTextExtent);
5642   status=XStringListToTextProperty(&windows->widget.name,1,&window_name);
5643   if (status != False)
5644     {
5645       XSetWMName(display,windows->widget.id,&window_name);
5646       XSetWMIconName(display,windows->widget.id,&window_name);
5647       (void) XFree((void *) window_name.value);
5648     }
5649   window_changes.width=(int) windows->widget.width;
5650   window_changes.height=(int) windows->widget.height;
5651   window_changes.x=windows->widget.x;
5652   window_changes.y=windows->widget.y;
5653   (void) XReconfigureWMWindow(display,windows->widget.id,
5654     windows->widget.screen,mask,&window_changes);
5655   (void) XMapRaised(display,windows->widget.id);
5656   windows->widget.mapped=MagickFalse;
5657   /*
5658     Respond to X events.
5659   */
5660   XGetWidgetInfo((char *) NULL,&slider_info);
5661   XGetWidgetInfo((char *) NULL,&north_info);
5662   XGetWidgetInfo((char *) NULL,&south_info);
5663   XGetWidgetInfo((char *) NULL,&expose_info);
5664   visible_fonts=0;
5665   delay=SuspendTime << 2;
5666   state=UpdateConfigurationState;
5667   do
5668   {
5669     if (state & UpdateConfigurationState)
5670       {
5671         int
5672           id;
5673
5674         /*
5675           Initialize button information.
5676         */
5677         XGetWidgetInfo(CancelButtonText,&cancel_info);
5678         cancel_info.width=width;
5679         cancel_info.height=(unsigned int) ((3*height) >> 1);
5680         cancel_info.x=(int)
5681           (windows->widget.width-cancel_info.width-QuantumMargin-2);
5682         cancel_info.y=(int)
5683           (windows->widget.height-cancel_info.height-QuantumMargin);
5684         XGetWidgetInfo(action,&action_info);
5685         action_info.width=width;
5686         action_info.height=(unsigned int) ((3*height) >> 1);
5687         action_info.x=cancel_info.x-(cancel_info.width+(QuantumMargin >> 1)+
5688           (action_info.bevel_width << 1));
5689         action_info.y=cancel_info.y;
5690         XGetWidgetInfo(BackButtonText,&back_info);
5691         back_info.width=width;
5692         back_info.height=(unsigned int) ((3*height) >> 1);
5693         back_info.x=QuantumMargin;
5694         back_info.y=((5*QuantumMargin) >> 1)+height;
5695         XGetWidgetInfo(ResetButtonText,&reset_info);
5696         reset_info.width=width;
5697         reset_info.height=(unsigned int) ((3*height) >> 1);
5698         reset_info.x=QuantumMargin;
5699         reset_info.y=back_info.y+back_info.height+QuantumMargin;
5700         /*
5701           Initialize reply information.
5702         */
5703         XGetWidgetInfo(reply,&reply_info);
5704         reply_info.raised=MagickFalse;
5705         reply_info.bevel_width--;
5706         reply_info.width=windows->widget.width-width-((6*QuantumMargin) >> 1);
5707         reply_info.height=height << 1;
5708         reply_info.x=(int) (width+(QuantumMargin << 1));
5709         reply_info.y=action_info.y-(action_info.height << 1)-QuantumMargin;
5710         /*
5711           Initialize mode information.
5712         */
5713         XGetWidgetInfo(reply,&mode_info);
5714         mode_info.bevel_width=0;
5715         mode_info.width=(unsigned int)
5716           (action_info.x-reply_info.x-QuantumMargin);
5717         mode_info.height=action_info.height << 1;
5718         mode_info.x=reply_info.x;
5719         mode_info.y=action_info.y-action_info.height+action_info.bevel_width;
5720         /*
5721           Initialize scroll information.
5722         */
5723         XGetWidgetInfo((char *) NULL,&scroll_info);
5724         scroll_info.bevel_width--;
5725         scroll_info.width=height;
5726         scroll_info.height=(unsigned int)
5727           (reply_info.y-back_info.y-(QuantumMargin >> 1));
5728         scroll_info.x=reply_info.x+(reply_info.width-scroll_info.width);
5729         scroll_info.y=back_info.y-reply_info.bevel_width;
5730         scroll_info.raised=MagickFalse;
5731         scroll_info.trough=MagickTrue;
5732         north_info=scroll_info;
5733         north_info.raised=MagickTrue;
5734         north_info.width-=(north_info.bevel_width << 1);
5735         north_info.height=north_info.width-1;
5736         north_info.x+=north_info.bevel_width;
5737         north_info.y+=north_info.bevel_width;
5738         south_info=north_info;
5739         south_info.y=scroll_info.y+scroll_info.height-scroll_info.bevel_width-
5740           south_info.height;
5741         id=slider_info.id;
5742         slider_info=north_info;
5743         slider_info.id=id;
5744         slider_info.width-=2;
5745         slider_info.min_y=north_info.y+north_info.height+north_info.bevel_width+
5746           slider_info.bevel_width+2;
5747         slider_info.height=scroll_info.height-((slider_info.min_y-
5748           scroll_info.y+1) << 1)+4;
5749         visible_fonts=scroll_info.height/(height+(height >> 3));
5750         if (fonts > (int) visible_fonts)
5751           slider_info.height=(visible_fonts*slider_info.height)/fonts;
5752         slider_info.max_y=south_info.y-south_info.bevel_width-
5753           slider_info.bevel_width-2;
5754         slider_info.x=scroll_info.x+slider_info.bevel_width+1;
5755         slider_info.y=slider_info.min_y;
5756         expose_info=scroll_info;
5757         expose_info.y=slider_info.y;
5758         /*
5759           Initialize list information.
5760         */
5761         XGetWidgetInfo((char *) NULL,&list_info);
5762         list_info.raised=MagickFalse;
5763         list_info.bevel_width--;
5764         list_info.width=(unsigned int)
5765           (scroll_info.x-reply_info.x-(QuantumMargin >> 1));
5766         list_info.height=scroll_info.height;
5767         list_info.x=reply_info.x;
5768         list_info.y=scroll_info.y;
5769         if (windows->widget.mapped == MagickFalse)
5770           state|=JumpListState;
5771         /*
5772           Initialize text information.
5773         */
5774         *text='\0';
5775         XGetWidgetInfo(text,&text_info);
5776         text_info.center=MagickFalse;
5777         text_info.width=reply_info.width;
5778         text_info.height=height;
5779         text_info.x=list_info.x-(QuantumMargin >> 1);
5780         text_info.y=QuantumMargin;
5781         /*
5782           Initialize selection information.
5783         */
5784         XGetWidgetInfo((char *) NULL,&selection_info);
5785         selection_info.center=MagickFalse;
5786         selection_info.width=list_info.width;
5787         selection_info.height=(unsigned int) ((9*height) >> 3);
5788         selection_info.x=list_info.x;
5789         state&=(~UpdateConfigurationState);
5790       }
5791     if (state & RedrawWidgetState)
5792       {
5793         /*
5794           Redraw Font Browser window.
5795         */
5796         x=QuantumMargin;
5797         y=text_info.y+((text_info.height-height) >> 1)+font_info->ascent;
5798         (void) XDrawString(display,windows->widget.id,
5799           windows->widget.annotate_context,x,y,FontPatternText,
5800           Extent(FontPatternText));
5801         (void) CopyMagickString(text_info.text,glob_pattern,MaxTextExtent);
5802         XDrawWidgetText(display,&windows->widget,&text_info);
5803         XDrawBeveledButton(display,&windows->widget,&back_info);
5804         XDrawBeveledButton(display,&windows->widget,&reset_info);
5805         XDrawBeveledMatte(display,&windows->widget,&list_info);
5806         XDrawBeveledMatte(display,&windows->widget,&scroll_info);
5807         XDrawTriangleNorth(display,&windows->widget,&north_info);
5808         XDrawBeveledButton(display,&windows->widget,&slider_info);
5809         XDrawTriangleSouth(display,&windows->widget,&south_info);
5810         x=QuantumMargin;
5811         y=reply_info.y+((reply_info.height-height) >> 1)+font_info->ascent;
5812         (void) XDrawString(display,windows->widget.id,
5813           windows->widget.annotate_context,x,y,FontnameText,
5814           Extent(FontnameText));
5815         XDrawBeveledMatte(display,&windows->widget,&reply_info);
5816         XDrawMatteText(display,&windows->widget,&reply_info);
5817         XDrawBeveledButton(display,&windows->widget,&action_info);
5818         XDrawBeveledButton(display,&windows->widget,&cancel_info);
5819         XHighlightWidget(display,&windows->widget,BorderOffset,BorderOffset);
5820         selection_info.id=(~0);
5821         state|=RedrawActionState;
5822         state|=RedrawListState;
5823         state&=(~RedrawWidgetState);
5824       }
5825     if (state & UpdateListState)
5826       {
5827         char
5828           **checklist;
5829
5830         int
5831           number_fonts;
5832
5833         /*
5834           Update font list.
5835         */
5836         checklist=XListFonts(display,glob_pattern,32767,&number_fonts);
5837         if (checklist == (char **) NULL)
5838           {
5839             if ((strchr(glob_pattern,'*') == (char *) NULL) &&
5840                 (strchr(glob_pattern,'?') == (char *) NULL))
5841               {
5842                 /*
5843                   Might be a scaleable font-- exit.
5844                 */
5845                 (void) CopyMagickString(reply,glob_pattern,MaxTextExtent);
5846                 (void) CopyMagickString(glob_pattern,back_pattern,MaxTextExtent);
5847                 action_info.raised=MagickFalse;
5848                 XDrawBeveledButton(display,&windows->widget,&action_info);
5849                 break;
5850               }
5851             (void) CopyMagickString(glob_pattern,back_pattern,MaxTextExtent);
5852             (void) XBell(display,0);
5853           }
5854         else
5855           if (number_fonts == 1)
5856             {
5857               /*
5858                 Reply is a single font name-- exit.
5859               */
5860               (void) CopyMagickString(reply,checklist[0],MaxTextExtent);
5861               (void) CopyMagickString(glob_pattern,back_pattern,MaxTextExtent);
5862               (void) XFreeFontNames(checklist);
5863               action_info.raised=MagickFalse;
5864               XDrawBeveledButton(display,&windows->widget,&action_info);
5865               break;
5866             }
5867           else
5868             {
5869               (void) XFreeFontNames(listhead);
5870               fontlist=(char **) RelinquishMagickMemory(fontlist);
5871               fontlist=checklist;
5872               fonts=number_fonts;
5873             }
5874         /*
5875           Sort font list in ascending order.
5876         */
5877         listhead=fontlist;
5878         fontlist=(char **) AcquireQuantumMemory((size_t) fonts,
5879           sizeof(*fontlist));
5880         if (fontlist == (char **) NULL)
5881           {
5882             XNoticeWidget(display,windows,"MemoryAllocationFailed",
5883               "UnableToViewFonts");
5884             return;
5885           }
5886         for (i=0; i < fonts; i++)
5887           fontlist[i]=listhead[i];
5888         qsort((void *) fontlist,(size_t) fonts,sizeof(*fontlist),FontCompare);
5889         slider_info.height=
5890           scroll_info.height-((slider_info.min_y-scroll_info.y+1) << 1)+1;
5891         if (fonts > (int) visible_fonts)
5892           slider_info.height=(visible_fonts*slider_info.height)/fonts;
5893         slider_info.max_y=south_info.y-south_info.bevel_width-
5894           slider_info.bevel_width-2;
5895         slider_info.id=0;
5896         slider_info.y=slider_info.min_y;
5897         expose_info.y=slider_info.y;
5898         selection_info.id=(~0);
5899         list_info.id=(~0);
5900         state|=RedrawListState;
5901         /*
5902           Redraw font name & reply.
5903         */
5904         *reply_info.text='\0';
5905         reply_info.cursor=reply_info.text;
5906         (void) CopyMagickString(text_info.text,glob_pattern,MaxTextExtent);
5907         XDrawWidgetText(display,&windows->widget,&text_info);
5908         XDrawMatteText(display,&windows->widget,&reply_info);
5909         XDrawBeveledMatte(display,&windows->widget,&scroll_info);
5910         XDrawTriangleNorth(display,&windows->widget,&north_info);
5911         XDrawBeveledButton(display,&windows->widget,&slider_info);
5912         XDrawTriangleSouth(display,&windows->widget,&south_info);
5913         XHighlightWidget(display,&windows->widget,BorderOffset,BorderOffset);
5914         state&=(~UpdateListState);
5915       }
5916     if (state & JumpListState)
5917       {
5918         /*
5919           Jump scroll to match user font.
5920         */
5921         list_info.id=(~0);
5922         for (i=0; i < fonts; i++)
5923           if (LocaleCompare(fontlist[i],reply) >= 0)
5924             {
5925               list_info.id=LocaleCompare(fontlist[i],reply) == 0 ? i : ~0;
5926               break;
5927             }
5928         if ((i < slider_info.id) || (i >= (int) (slider_info.id+visible_fonts)))
5929           slider_info.id=i-(visible_fonts >> 1);
5930         selection_info.id=(~0);
5931         state|=RedrawListState;
5932         state&=(~JumpListState);
5933       }
5934     if (state & RedrawListState)
5935       {
5936         /*
5937           Determine slider id and position.
5938         */
5939         if (slider_info.id >= (int) (fonts-visible_fonts))
5940           slider_info.id=fonts-visible_fonts;
5941         if ((slider_info.id < 0) || (fonts <= (int) visible_fonts))
5942           slider_info.id=0;
5943         slider_info.y=slider_info.min_y;
5944         if (fonts > 0)
5945           slider_info.y+=
5946             slider_info.id*(slider_info.max_y-slider_info.min_y+1)/fonts;
5947         if (slider_info.id != selection_info.id)
5948           {
5949             /*
5950               Redraw scroll bar and file names.
5951             */
5952             selection_info.id=slider_info.id;
5953             selection_info.y=list_info.y+(height >> 3)+2;
5954             for (i=0; i < (int) visible_fonts; i++)
5955             {
5956               selection_info.raised=(slider_info.id+i) != list_info.id ?
5957                 MagickTrue : MagickFalse;
5958               selection_info.text=(char *) NULL;
5959               if ((slider_info.id+i) < fonts)
5960                 selection_info.text=fontlist[slider_info.id+i];
5961               XDrawWidgetText(display,&windows->widget,&selection_info);
5962               selection_info.y+=(int) selection_info.height;
5963             }
5964             /*
5965               Update slider.
5966             */
5967             if (slider_info.y > expose_info.y)
5968               {
5969                 expose_info.height=(unsigned int) slider_info.y-expose_info.y;
5970                 expose_info.y=slider_info.y-expose_info.height-
5971                   slider_info.bevel_width-1;
5972               }
5973             else
5974               {
5975                 expose_info.height=(unsigned int) expose_info.y-slider_info.y;
5976                 expose_info.y=slider_info.y+slider_info.height+
5977                   slider_info.bevel_width+1;
5978               }
5979             XDrawTriangleNorth(display,&windows->widget,&north_info);
5980             XDrawMatte(display,&windows->widget,&expose_info);
5981             XDrawBeveledButton(display,&windows->widget,&slider_info);
5982             XDrawTriangleSouth(display,&windows->widget,&south_info);
5983             expose_info.y=slider_info.y;
5984           }
5985         state&=(~RedrawListState);
5986       }
5987     if (state & RedrawActionState)
5988       {
5989         XFontStruct
5990           *save_info;
5991
5992         /*
5993           Display the selected font in a drawing area.
5994         */
5995         save_info=windows->widget.font_info;
5996         font_info=XLoadQueryFont(display,reply_info.text);
5997         if (font_info != (XFontStruct *) NULL)
5998           {
5999             windows->widget.font_info=font_info;
6000             (void) XSetFont(display,windows->widget.widget_context,
6001               font_info->fid);
6002           }
6003         XDrawBeveledButton(display,&windows->widget,&mode_info);
6004         windows->widget.font_info=save_info;
6005         if (font_info != (XFontStruct *) NULL)
6006           {
6007             (void) XSetFont(display,windows->widget.widget_context,
6008               windows->widget.font_info->fid);
6009             (void) XFreeFont(display,font_info);
6010           }
6011         XHighlightWidget(display,&windows->widget,BorderOffset,BorderOffset);
6012         XDrawMatteText(display,&windows->widget,&reply_info);
6013         state&=(~RedrawActionState);
6014       }
6015     /*
6016       Wait for next event.
6017     */
6018     if (north_info.raised && south_info.raised)
6019       (void) XIfEvent(display,&event,XScreenEvent,(char *) windows);
6020     else
6021       {
6022         /*
6023           Brief delay before advancing scroll bar.
6024         */
6025         XDelay(display,delay);
6026         delay=SuspendTime;
6027         (void) XCheckIfEvent(display,&event,XScreenEvent,(char *) windows);
6028         if (north_info.raised == MagickFalse)
6029           if (slider_info.id > 0)
6030             {
6031               /*
6032                 Move slider up.
6033               */
6034               slider_info.id--;
6035               state|=RedrawListState;
6036             }
6037         if (south_info.raised == MagickFalse)
6038           if (slider_info.id < fonts)
6039             {
6040               /*
6041                 Move slider down.
6042               */
6043               slider_info.id++;
6044               state|=RedrawListState;
6045             }
6046         if (event.type != ButtonRelease)
6047           continue;
6048       }
6049     switch (event.type)
6050     {
6051       case ButtonPress:
6052       {
6053         if (MatteIsActive(slider_info,event.xbutton))
6054           {
6055             /*
6056               Track slider.
6057             */
6058             slider_info.active=MagickTrue;
6059             break;
6060           }
6061         if (MatteIsActive(north_info,event.xbutton))
6062           if (slider_info.id > 0)
6063             {
6064               /*
6065                 Move slider up.
6066               */
6067               north_info.raised=MagickFalse;
6068               slider_info.id--;
6069               state|=RedrawListState;
6070               break;
6071             }
6072         if (MatteIsActive(south_info,event.xbutton))
6073           if (slider_info.id < fonts)
6074             {
6075               /*
6076                 Move slider down.
6077               */
6078               south_info.raised=MagickFalse;
6079               slider_info.id++;
6080               state|=RedrawListState;
6081               break;
6082             }
6083         if (MatteIsActive(scroll_info,event.xbutton))
6084           {
6085             /*
6086               Move slider.
6087             */
6088             if (event.xbutton.y < slider_info.y)
6089               slider_info.id-=(visible_fonts-1);
6090             else
6091               slider_info.id+=(visible_fonts-1);
6092             state|=RedrawListState;
6093             break;
6094           }
6095         if (MatteIsActive(list_info,event.xbutton))
6096           {
6097             int
6098               id;
6099
6100             /*
6101               User pressed list matte.
6102             */
6103             id=slider_info.id+(event.xbutton.y-(list_info.y+(height >> 1))+1)/
6104               selection_info.height;
6105             if (id >= (int) fonts)
6106               break;
6107             (void) CopyMagickString(reply_info.text,fontlist[id],MaxTextExtent);
6108             reply_info.highlight=MagickFalse;
6109             reply_info.marker=reply_info.text;
6110             reply_info.cursor=reply_info.text+Extent(reply_info.text);
6111             XDrawMatteText(display,&windows->widget,&reply_info);
6112             state|=RedrawActionState;
6113             if (id == list_info.id)
6114               {
6115                 (void) CopyMagickString(glob_pattern,reply_info.text,
6116                   MaxTextExtent);
6117                 state|=UpdateListState;
6118               }
6119             selection_info.id=(~0);
6120             list_info.id=id;
6121             state|=RedrawListState;
6122             break;
6123           }
6124         if (MatteIsActive(back_info,event.xbutton))
6125           {
6126             /*
6127               User pressed Back button.
6128             */
6129             back_info.raised=MagickFalse;
6130             XDrawBeveledButton(display,&windows->widget,&back_info);
6131             break;
6132           }
6133         if (MatteIsActive(reset_info,event.xbutton))
6134           {
6135             /*
6136               User pressed Reset button.
6137             */
6138             reset_info.raised=MagickFalse;
6139             XDrawBeveledButton(display,&windows->widget,&reset_info);
6140             break;
6141           }
6142         if (MatteIsActive(action_info,event.xbutton))
6143           {
6144             /*
6145               User pressed action button.
6146             */
6147             action_info.raised=MagickFalse;
6148             XDrawBeveledButton(display,&windows->widget,&action_info);
6149             break;
6150           }
6151         if (MatteIsActive(cancel_info,event.xbutton))
6152           {
6153             /*
6154               User pressed Cancel button.
6155             */
6156             cancel_info.raised=MagickFalse;
6157             XDrawBeveledButton(display,&windows->widget,&cancel_info);
6158             break;
6159           }
6160         if (MatteIsActive(reply_info,event.xbutton) == MagickFalse)
6161           break;
6162         if (event.xbutton.button != Button2)
6163           {
6164             static Time
6165               click_time;
6166
6167             /*
6168               Move text cursor to position of button press.
6169             */
6170             x=event.xbutton.x-reply_info.x-(QuantumMargin >> 2);
6171             for (i=1; i <= Extent(reply_info.marker); i++)
6172               if (XTextWidth(font_info,reply_info.marker,i) > x)
6173                 break;
6174             reply_info.cursor=reply_info.marker+i-1;
6175             if (event.xbutton.time > (click_time+DoubleClick))
6176               reply_info.highlight=MagickFalse;
6177             else
6178               {
6179                 /*
6180                   Become the XA_PRIMARY selection owner.
6181                 */
6182                 (void) CopyMagickString(primary_selection,reply_info.text,
6183                   MaxTextExtent);
6184                 (void) XSetSelectionOwner(display,XA_PRIMARY,windows->widget.id,
6185                   event.xbutton.time);
6186                 reply_info.highlight=XGetSelectionOwner(display,XA_PRIMARY) ==
6187                   windows->widget.id ? MagickTrue : MagickFalse;
6188               }
6189             XDrawMatteText(display,&windows->widget,&reply_info);
6190             click_time=event.xbutton.time;
6191             break;
6192           }
6193         /*
6194           Request primary selection.
6195         */
6196         (void) XConvertSelection(display,XA_PRIMARY,XA_STRING,XA_STRING,
6197           windows->widget.id,event.xbutton.time);
6198         break;
6199       }
6200       case ButtonRelease:
6201       {
6202         if (windows->widget.mapped == MagickFalse)
6203           break;
6204         if (north_info.raised == MagickFalse)
6205           {
6206             /*
6207               User released up button.
6208             */
6209             delay=SuspendTime << 2;
6210             north_info.raised=MagickTrue;
6211             XDrawTriangleNorth(display,&windows->widget,&north_info);
6212           }
6213         if (south_info.raised == MagickFalse)
6214           {
6215             /*
6216               User released down button.
6217             */
6218             delay=SuspendTime << 2;
6219             south_info.raised=MagickTrue;
6220             XDrawTriangleSouth(display,&windows->widget,&south_info);
6221           }
6222         if (slider_info.active)
6223           {
6224             /*
6225               Stop tracking slider.
6226             */
6227             slider_info.active=MagickFalse;
6228             break;
6229           }
6230         if (back_info.raised == MagickFalse)
6231           {
6232             if (event.xbutton.window == windows->widget.id)
6233               if (MatteIsActive(back_info,event.xbutton))
6234                 {
6235                   (void) CopyMagickString(glob_pattern,back_pattern,
6236                     MaxTextExtent);
6237                   state|=UpdateListState;
6238                 }
6239             back_info.raised=MagickTrue;
6240             XDrawBeveledButton(display,&windows->widget,&back_info);
6241           }
6242         if (reset_info.raised == MagickFalse)
6243           {
6244             if (event.xbutton.window == windows->widget.id)
6245               if (MatteIsActive(reset_info,event.xbutton))
6246                 {
6247                   (void) CopyMagickString(back_pattern,glob_pattern,MaxTextExtent);
6248                   (void) CopyMagickString(glob_pattern,reset_pattern,MaxTextExtent);
6249                   state|=UpdateListState;
6250                 }
6251             reset_info.raised=MagickTrue;
6252             XDrawBeveledButton(display,&windows->widget,&reset_info);
6253           }
6254         if (action_info.raised == MagickFalse)
6255           {
6256             if (event.xbutton.window == windows->widget.id)
6257               {
6258                 if (MatteIsActive(action_info,event.xbutton))
6259                   {
6260                     if (*reply_info.text == '\0')
6261                       (void) XBell(display,0);
6262                     else
6263                       state|=ExitState;
6264                   }
6265               }
6266             action_info.raised=MagickTrue;
6267             XDrawBeveledButton(display,&windows->widget,&action_info);
6268           }
6269         if (cancel_info.raised == MagickFalse)
6270           {
6271             if (event.xbutton.window == windows->widget.id)
6272               if (MatteIsActive(cancel_info,event.xbutton))
6273                 {
6274                   *reply_info.text='\0';
6275                   state|=ExitState;
6276                 }
6277             cancel_info.raised=MagickTrue;
6278             XDrawBeveledButton(display,&windows->widget,&cancel_info);
6279           }
6280         break;
6281       }
6282       case ClientMessage:
6283       {
6284         /*
6285           If client window delete message, exit.
6286         */
6287         if (event.xclient.message_type != windows->wm_protocols)
6288           break;
6289         if (*event.xclient.data.l == (int) windows->wm_take_focus)
6290           {
6291             (void) XSetInputFocus(display,event.xclient.window,RevertToParent,
6292               (Time) event.xclient.data.l[1]);
6293             break;
6294           }
6295         if (*event.xclient.data.l != (int) windows->wm_delete_window)
6296           break;
6297         if (event.xclient.window == windows->widget.id)
6298           {
6299             *reply_info.text='\0';
6300             state|=ExitState;
6301             break;
6302           }
6303         break;
6304       }
6305       case ConfigureNotify:
6306       {
6307         /*
6308           Update widget configuration.
6309         */
6310         if (event.xconfigure.window != windows->widget.id)
6311           break;
6312         if ((event.xconfigure.width == (int) windows->widget.width) &&
6313             (event.xconfigure.height == (int) windows->widget.height))
6314           break;
6315         windows->widget.width=(unsigned int)
6316           MagickMax(event.xconfigure.width,(int) windows->widget.min_width);
6317         windows->widget.height=(unsigned int)
6318           MagickMax(event.xconfigure.height,(int) windows->widget.min_height);
6319         state|=UpdateConfigurationState;
6320         break;
6321       }
6322       case EnterNotify:
6323       {
6324         if (event.xcrossing.window != windows->widget.id)
6325           break;
6326         state&=(~InactiveWidgetState);
6327         break;
6328       }
6329       case Expose:
6330       {
6331         if (event.xexpose.window != windows->widget.id)
6332           break;
6333         if (event.xexpose.count != 0)
6334           break;
6335         state|=RedrawWidgetState;
6336         break;
6337       }
6338       case KeyPress:
6339       {
6340         static char
6341           command[MaxTextExtent];
6342
6343         static int
6344           length;
6345
6346         static KeySym
6347           key_symbol;
6348
6349         /*
6350           Respond to a user key press.
6351         */
6352         if (event.xkey.window != windows->widget.id)
6353           break;
6354         length=XLookupString((XKeyEvent *) &event.xkey,command,
6355           (int) sizeof(command),&key_symbol,(XComposeStatus *) NULL);
6356         *(command+length)='\0';
6357         if (AreaIsActive(scroll_info,event.xkey))
6358           {
6359             /*
6360               Move slider.
6361             */
6362             switch ((int) key_symbol)
6363             {
6364               case XK_Home:
6365               case XK_KP_Home:
6366               {
6367                 slider_info.id=0;
6368                 break;
6369               }
6370               case XK_Up:
6371               case XK_KP_Up:
6372               {
6373                 slider_info.id--;
6374                 break;
6375               }
6376               case XK_Down:
6377               case XK_KP_Down:
6378               {
6379                 slider_info.id++;
6380                 break;
6381               }
6382               case XK_Prior:
6383               case XK_KP_Prior:
6384               {
6385                 slider_info.id-=visible_fonts;
6386                 break;
6387               }
6388               case XK_Next:
6389               case XK_KP_Next:
6390               {
6391                 slider_info.id+=visible_fonts;
6392                 break;
6393               }
6394               case XK_End:
6395               case XK_KP_End:
6396               {
6397                 slider_info.id=fonts;
6398                 break;
6399               }
6400             }
6401             state|=RedrawListState;
6402             break;
6403           }
6404         if ((key_symbol == XK_Return) || (key_symbol == XK_KP_Enter))
6405           {
6406             /*
6407               Read new font or glob patterm.
6408             */
6409             if (*reply_info.text == '\0')
6410               break;
6411             (void) CopyMagickString(back_pattern,glob_pattern,MaxTextExtent);
6412             (void) CopyMagickString(glob_pattern,reply_info.text,MaxTextExtent);
6413             state|=UpdateListState;
6414             break;
6415           }
6416         if (key_symbol == XK_Control_L)
6417           {
6418             state|=ControlState;
6419             break;
6420           }
6421         if (state & ControlState)
6422           switch ((int) key_symbol)
6423           {
6424             case XK_u:
6425             case XK_U:
6426             {
6427               /*
6428                 Erase the entire line of text.
6429               */
6430               *reply_info.text='\0';
6431               reply_info.cursor=reply_info.text;
6432               reply_info.marker=reply_info.text;
6433               reply_info.highlight=MagickFalse;
6434               break;
6435             }
6436             default:
6437               break;
6438           }
6439         XEditText(display,&reply_info,key_symbol,command,state);
6440         XDrawMatteText(display,&windows->widget,&reply_info);
6441         state|=JumpListState;
6442         break;
6443       }
6444       case KeyRelease:
6445       {
6446         static char
6447           command[MaxTextExtent];
6448
6449         static KeySym
6450           key_symbol;
6451
6452         /*
6453           Respond to a user key release.
6454         */
6455         if (event.xkey.window != windows->widget.id)
6456           break;
6457         (void) XLookupString((XKeyEvent *) &event.xkey,command,
6458           (int) sizeof(command),&key_symbol,(XComposeStatus *) NULL);
6459         if (key_symbol == XK_Control_L)
6460           state&=(~ControlState);
6461         break;
6462       }
6463       case LeaveNotify:
6464       {
6465         if (event.xcrossing.window != windows->widget.id)
6466           break;
6467         state|=InactiveWidgetState;
6468         break;
6469       }
6470       case MapNotify:
6471       {
6472         mask&=(~CWX);
6473         mask&=(~CWY);
6474         break;
6475       }
6476       case MotionNotify:
6477       {
6478         /*
6479           Discard pending button motion events.
6480         */
6481         while (XCheckMaskEvent(display,ButtonMotionMask,&event)) ;
6482         if (slider_info.active)
6483           {
6484             /*
6485               Move slider matte.
6486             */
6487             slider_info.y=event.xmotion.y-
6488               ((slider_info.height+slider_info.bevel_width) >> 1)+1;
6489             if (slider_info.y < slider_info.min_y)
6490               slider_info.y=slider_info.min_y;
6491             if (slider_info.y > slider_info.max_y)
6492               slider_info.y=slider_info.max_y;
6493             slider_info.id=0;
6494             if (slider_info.y != slider_info.min_y)
6495               slider_info.id=(fonts*(slider_info.y-slider_info.min_y+1))/
6496                 (slider_info.max_y-slider_info.min_y+1);
6497             state|=RedrawListState;
6498             break;
6499           }
6500         if (state & InactiveWidgetState)
6501           break;
6502         if (back_info.raised == MatteIsActive(back_info,event.xmotion))
6503           {
6504             /*
6505               Back button status changed.
6506             */
6507             back_info.raised=!back_info.raised;
6508             XDrawBeveledButton(display,&windows->widget,&back_info);
6509             break;
6510           }
6511         if (reset_info.raised == MatteIsActive(reset_info,event.xmotion))
6512           {
6513             /*
6514               Reset button status changed.
6515             */
6516             reset_info.raised=!reset_info.raised;
6517             XDrawBeveledButton(display,&windows->widget,&reset_info);
6518             break;
6519           }
6520         if (action_info.raised == MatteIsActive(action_info,event.xmotion))
6521           {
6522             /*
6523               Action button status changed.
6524             */
6525             action_info.raised=action_info.raised == MagickFalse ?
6526               MagickTrue : MagickFalse;
6527             XDrawBeveledButton(display,&windows->widget,&action_info);
6528             break;
6529           }
6530         if (cancel_info.raised == MatteIsActive(cancel_info,event.xmotion))
6531           {
6532             /*
6533               Cancel button status changed.
6534             */
6535             cancel_info.raised=cancel_info.raised == MagickFalse ?
6536               MagickTrue : MagickFalse;
6537             XDrawBeveledButton(display,&windows->widget,&cancel_info);
6538             break;
6539           }
6540         break;
6541       }
6542       case SelectionClear:
6543       {
6544         reply_info.highlight=MagickFalse;
6545         XDrawMatteText(display,&windows->widget,&reply_info);
6546         break;
6547       }
6548       case SelectionNotify:
6549       {
6550         Atom
6551           type;
6552
6553         int
6554           format;
6555
6556         unsigned char
6557           *data;
6558
6559         unsigned long
6560           after,
6561           length;
6562
6563         /*
6564           Obtain response from primary selection.
6565         */
6566         if (event.xselection.property == (Atom) None)
6567           break;
6568         status=XGetWindowProperty(display,event.xselection.requestor,
6569           event.xselection.property,0L,2047L,MagickTrue,XA_STRING,&type,
6570           &format,&length,&after,&data);
6571         if ((status != Success) || (type != XA_STRING) || (format == 32) ||
6572             (length == 0))
6573           break;
6574         if ((Extent(reply_info.text)+length) >= (MaxTextExtent-1))
6575           (void) XBell(display,0);
6576         else
6577           {
6578             /*
6579               Insert primary selection in reply text.
6580             */
6581             *(data+length)='\0';
6582             XEditText(display,&reply_info,(KeySym) XK_Insert,(char *) data,
6583               state);
6584             XDrawMatteText(display,&windows->widget,&reply_info);
6585             state|=JumpListState;
6586             state|=RedrawActionState;
6587           }
6588         (void) XFree((void *) data);
6589         break;
6590       }
6591       case SelectionRequest:
6592       {
6593         XSelectionEvent
6594           notify;
6595
6596         XSelectionRequestEvent
6597           *request;
6598
6599         /*
6600           Set XA_PRIMARY selection.
6601         */
6602         request=(&(event.xselectionrequest));
6603         (void) XChangeProperty(request->display,request->requestor,
6604           request->property,request->target,8,PropModeReplace,
6605           (unsigned char *) primary_selection,Extent(primary_selection));
6606         notify.type=SelectionNotify;
6607         notify.display=request->display;
6608         notify.requestor=request->requestor;
6609         notify.selection=request->selection;
6610         notify.target=request->target;
6611         notify.time=request->time;
6612         if (request->property == None)
6613           notify.property=request->target;
6614         else
6615           notify.property=request->property;
6616         (void) XSendEvent(request->display,request->requestor,False,0,
6617           (XEvent *) &notify);
6618       }
6619       default:
6620         break;
6621     }
6622   } while ((state & ExitState) == 0);
6623   XSetCursorState(display,windows,MagickFalse);
6624   (void) XWithdrawWindow(display,windows->widget.id,windows->widget.screen);
6625   XCheckRefreshWindows(display,windows);
6626   /*
6627     Free font list.
6628   */
6629   (void) XFreeFontNames(listhead);
6630   fontlist=(char **) RelinquishMagickMemory(fontlist);
6631 }
6632 \f
6633 /*
6634 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6635 %                                                                             %
6636 %                                                                             %
6637 %                                                                             %
6638 %   X I n f o W i d g e t                                                     %
6639 %                                                                             %
6640 %                                                                             %
6641 %                                                                             %
6642 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6643 %
6644 %  XInfoWidget() displays text in the Info widget.  The purpose is to inform
6645 %  the user that what activity is currently being performed (e.g. reading
6646 %  an image, rotating an image, etc.).
6647 %
6648 %  The format of the XInfoWidget method is:
6649 %
6650 %      void XInfoWidget(Display *display,XWindows *windows,const char *activity)
6651 %
6652 %  A description of each parameter follows:
6653 %
6654 %    o display: Specifies a connection to an X server;  returned from
6655 %      XOpenDisplay.
6656 %
6657 %    o window: Specifies a pointer to a XWindows structure.
6658 %
6659 %    o activity: This character string reflects the current activity and is
6660 %      displayed in the Info widget.
6661 %
6662 */
6663 MagickPrivate void XInfoWidget(Display *display,XWindows *windows,
6664   const char *activity)
6665 {
6666   unsigned int
6667     height,
6668     margin,
6669     width;
6670
6671   XFontStruct
6672     *font_info;
6673
6674   XWindowChanges
6675     window_changes;
6676
6677   /*
6678     Map Info widget.
6679   */
6680   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
6681   assert(display != (Display *) NULL);
6682   assert(windows != (XWindows *) NULL);
6683   assert(activity != (char *) NULL);
6684   font_info=windows->info.font_info;
6685   width=WidgetTextWidth(font_info,(char *) activity)+((3*QuantumMargin) >> 1)+4;
6686   height=(unsigned int) (((6*(font_info->ascent+font_info->descent)) >> 2)+4);
6687   if ((windows->info.width != width) || (windows->info.height != height))
6688     {
6689       /*
6690         Size Info widget to accommodate the activity text.
6691       */
6692       windows->info.width=width;
6693       windows->info.height=height;
6694       window_changes.width=(int) width;
6695       window_changes.height=(int) height;
6696       (void) XReconfigureWMWindow(display,windows->info.id,windows->info.screen,
6697         (unsigned int) (CWWidth | CWHeight),&window_changes);
6698     }
6699   if (windows->info.mapped == MagickFalse)
6700     {
6701       (void) XMapRaised(display,windows->info.id);
6702       windows->info.mapped=MagickTrue;
6703     }
6704   /*
6705     Initialize Info matte information.
6706   */
6707   height=(unsigned int) (font_info->ascent+font_info->descent);
6708   XGetWidgetInfo(activity,&monitor_info);
6709   monitor_info.bevel_width--;
6710   margin=monitor_info.bevel_width+((windows->info.height-height) >> 1)-2;
6711   monitor_info.center=MagickFalse;
6712   monitor_info.x=(int) margin;
6713   monitor_info.y=(int) margin;
6714   monitor_info.width=windows->info.width-(margin << 1);
6715   monitor_info.height=windows->info.height-(margin << 1)+1;
6716   /*
6717     Draw Info widget.
6718   */
6719   monitor_info.raised=MagickFalse;
6720   XDrawBeveledMatte(display,&windows->info,&monitor_info);
6721   monitor_info.raised=MagickTrue;
6722   XDrawWidgetText(display,&windows->info,&monitor_info);
6723 }
6724 \f
6725 /*
6726 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6727 %                                                                             %
6728 %                                                                             %
6729 %                                                                             %
6730 %   X L i s t B r o w s e r W i d g e t                                       %
6731 %                                                                             %
6732 %                                                                             %
6733 %                                                                             %
6734 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6735 %
6736 %  XListBrowserWidget() displays a List Browser widget with a query to the
6737 %  user.  The user keys a reply or select a reply from the list.  Finally, the
6738 %  user presses the Action or Cancel button to exit.  The typed text is
6739 %  returned as the reply function parameter.
6740 %
6741 %  The format of the XListBrowserWidget method is:
6742 %
6743 %      void XListBrowserWidget(Display *display,XWindows *windows,
6744 %        XWindowInfo *window_info,const char **list,const char *action,
6745 %        const char *query,char *reply)
6746 %
6747 %  A description of each parameter follows:
6748 %
6749 %    o display: Specifies a connection to an X server;  returned from
6750 %      XOpenDisplay.
6751 %
6752 %    o window: Specifies a pointer to a XWindows structure.
6753 %
6754 %    o list: Specifies a pointer to an array of strings.  The user can
6755 %      select from these strings as a possible reply value.
6756 %
6757 %    o action: Specifies a pointer to the action of this widget.
6758 %
6759 %    o query: Specifies a pointer to the query to present to the user.
6760 %
6761 %    o reply: the response from the user is returned in this parameter.
6762 %
6763 */
6764 MagickPrivate void XListBrowserWidget(Display *display,XWindows *windows,
6765   XWindowInfo *window_info,const char **list,const char *action,
6766   const char *query,char *reply)
6767 {
6768 #define CancelButtonText  "Cancel"
6769
6770   char
6771     primary_selection[MaxTextExtent];
6772
6773   int
6774     x;
6775
6776   register int
6777     i;
6778
6779   static MagickStatusType
6780     mask = (MagickStatusType) (CWWidth | CWHeight | CWX | CWY);
6781
6782   Status
6783     status;
6784
6785   unsigned int
6786     entries,
6787     height,
6788     text_width,
6789     visible_entries,
6790     width;
6791
6792   size_t
6793     delay,
6794     state;
6795
6796   XEvent
6797     event;
6798
6799   XFontStruct
6800     *font_info;
6801
6802   XTextProperty
6803     window_name;
6804
6805   XWidgetInfo
6806     action_info,
6807     cancel_info,
6808     expose_info,
6809     list_info,
6810     north_info,
6811     reply_info,
6812     scroll_info,
6813     selection_info,
6814     slider_info,
6815     south_info,
6816     text_info;
6817
6818   XWindowChanges
6819     window_changes;
6820
6821   /*
6822     Count the number of entries in the list.
6823   */
6824   assert(display != (Display *) NULL);
6825   assert(windows != (XWindows *) NULL);
6826   assert(window_info != (XWindowInfo *) NULL);
6827   assert(list != (const char **) NULL);
6828   assert(action != (char *) NULL);
6829   assert(query != (char *) NULL);
6830   assert(reply != (char *) NULL);
6831   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",action);
6832   XSetCursorState(display,windows,MagickTrue);
6833   XCheckRefreshWindows(display,windows);
6834   if (list == (const char **) NULL)
6835     {
6836       XNoticeWidget(display,windows,"No text to browse:",(char *) NULL);
6837       return;
6838     }
6839   for (entries=0; ; entries++)
6840     if (list[entries] == (char *) NULL)
6841       break;
6842   /*
6843     Determine Font Browser widget attributes.
6844   */
6845   font_info=window_info->font_info;
6846   text_width=WidgetTextWidth(font_info,(char *) query);
6847   for (i=0; i < (int) entries; i++)
6848     if (WidgetTextWidth(font_info,(char *) list[i]) > text_width)
6849       text_width=WidgetTextWidth(font_info,(char *) list[i]);
6850   width=WidgetTextWidth(font_info,(char *) action);
6851   if (WidgetTextWidth(font_info,CancelButtonText) > width)
6852     width=WidgetTextWidth(font_info,CancelButtonText);
6853   width+=QuantumMargin;
6854   height=(unsigned int) (font_info->ascent+font_info->descent);
6855   /*
6856     Position List Browser widget.
6857   */
6858   window_info->width=(unsigned int) MagickMin((int) text_width,(int)
6859     MaxTextWidth)+((9*QuantumMargin) >> 1);
6860   window_info->min_width=(unsigned int) (MinTextWidth+4*QuantumMargin);
6861   if (window_info->width < window_info->min_width)
6862     window_info->width=window_info->min_width;
6863   window_info->height=(unsigned int)
6864     (((81*height) >> 2)+((13*QuantumMargin) >> 1)+4);
6865   window_info->min_height=(unsigned int)
6866     (((23*height) >> 1)+((13*QuantumMargin) >> 1)+4);
6867   if (window_info->height < window_info->min_height)
6868     window_info->height=window_info->min_height;
6869   XConstrainWindowPosition(display,window_info);
6870   /*
6871     Map List Browser widget.
6872   */
6873   (void) CopyMagickString(window_info->name,"Browse",MaxTextExtent);
6874   status=XStringListToTextProperty(&window_info->name,1,&window_name);
6875   if (status != False)
6876     {
6877       XSetWMName(display,window_info->id,&window_name);
6878       XSetWMIconName(display,windows->widget.id,&window_name);
6879       (void) XFree((void *) window_name.value);
6880     }
6881   window_changes.width=(int) window_info->width;
6882   window_changes.height=(int) window_info->height;
6883   window_changes.x=window_info->x;
6884   window_changes.y=window_info->y;
6885   (void) XReconfigureWMWindow(display,window_info->id,window_info->screen,mask,
6886     &window_changes);
6887   (void) XMapRaised(display,window_info->id);
6888   window_info->mapped=MagickFalse;
6889   /*
6890     Respond to X events.
6891   */
6892   XGetWidgetInfo((char *) NULL,&slider_info);
6893   XGetWidgetInfo((char *) NULL,&north_info);
6894   XGetWidgetInfo((char *) NULL,&south_info);
6895   XGetWidgetInfo((char *) NULL,&expose_info);
6896   visible_entries=0;
6897   delay=SuspendTime << 2;
6898   state=UpdateConfigurationState;
6899   do
6900   {
6901     if (state & UpdateConfigurationState)
6902       {
6903         int
6904           id;
6905
6906         /*
6907           Initialize button information.
6908         */
6909         XGetWidgetInfo(CancelButtonText,&cancel_info);
6910         cancel_info.width=width;
6911         cancel_info.height=(unsigned int) ((3*height) >> 1);
6912         cancel_info.x=(int)
6913           (window_info->width-cancel_info.width-QuantumMargin-2);
6914         cancel_info.y=(int)
6915           (window_info->height-cancel_info.height-QuantumMargin);
6916         XGetWidgetInfo(action,&action_info);
6917         action_info.width=width;
6918         action_info.height=(unsigned int) ((3*height) >> 1);
6919         action_info.x=cancel_info.x-(cancel_info.width+(QuantumMargin >> 1)+
6920           (action_info.bevel_width << 1));
6921         action_info.y=cancel_info.y;
6922         /*
6923           Initialize reply information.
6924         */
6925         XGetWidgetInfo(reply,&reply_info);
6926         reply_info.raised=MagickFalse;
6927         reply_info.bevel_width--;
6928         reply_info.width=window_info->width-((4*QuantumMargin) >> 1);
6929         reply_info.height=height << 1;
6930         reply_info.x=QuantumMargin;
6931         reply_info.y=action_info.y-reply_info.height-QuantumMargin;
6932         /*
6933           Initialize scroll information.
6934         */
6935         XGetWidgetInfo((char *) NULL,&scroll_info);
6936         scroll_info.bevel_width--;
6937         scroll_info.width=height;
6938         scroll_info.height=(unsigned int)
6939           (reply_info.y-((6*QuantumMargin) >> 1)-height);
6940         scroll_info.x=reply_info.x+(reply_info.width-scroll_info.width);
6941         scroll_info.y=((5*QuantumMargin) >> 1)+height-reply_info.bevel_width;
6942         scroll_info.raised=MagickFalse;
6943         scroll_info.trough=MagickTrue;
6944         north_info=scroll_info;
6945         north_info.raised=MagickTrue;
6946         north_info.width-=(north_info.bevel_width << 1);
6947         north_info.height=north_info.width-1;
6948         north_info.x+=north_info.bevel_width;
6949         north_info.y+=north_info.bevel_width;
6950         south_info=north_info;
6951         south_info.y=scroll_info.y+scroll_info.height-scroll_info.bevel_width-
6952           south_info.height;
6953         id=slider_info.id;
6954         slider_info=north_info;
6955         slider_info.id=id;
6956         slider_info.width-=2;
6957         slider_info.min_y=north_info.y+north_info.height+north_info.bevel_width+
6958           slider_info.bevel_width+2;
6959         slider_info.height=scroll_info.height-((slider_info.min_y-
6960           scroll_info.y+1) << 1)+4;
6961         visible_entries=scroll_info.height/(height+(height >> 3));
6962         if (entries > visible_entries)
6963           slider_info.height=(visible_entries*slider_info.height)/entries;
6964         slider_info.max_y=south_info.y-south_info.bevel_width-
6965           slider_info.bevel_width-2;
6966         slider_info.x=scroll_info.x+slider_info.bevel_width+1;
6967         slider_info.y=slider_info.min_y;
6968         expose_info=scroll_info;
6969         expose_info.y=slider_info.y;
6970         /*
6971           Initialize list information.
6972         */
6973         XGetWidgetInfo((char *) NULL,&list_info);
6974         list_info.raised=MagickFalse;
6975         list_info.bevel_width--;
6976         list_info.width=(unsigned int)
6977           (scroll_info.x-reply_info.x-(QuantumMargin >> 1));
6978         list_info.height=scroll_info.height;
6979         list_info.x=reply_info.x;
6980         list_info.y=scroll_info.y;
6981         if (window_info->mapped == MagickFalse)
6982           for (i=0; i < (int) entries; i++)
6983             if (LocaleCompare(list[i],reply) == 0)
6984               {
6985                 list_info.id=i;
6986                 slider_info.id=i-(visible_entries >> 1);
6987                 if (slider_info.id < 0)
6988                   slider_info.id=0;
6989               }
6990         /*
6991           Initialize text information.
6992         */
6993         XGetWidgetInfo(query,&text_info);
6994         text_info.width=reply_info.width;
6995         text_info.height=height;
6996         text_info.x=list_info.x-(QuantumMargin >> 1);
6997         text_info.y=QuantumMargin;
6998         /*
6999           Initialize selection information.
7000         */
7001         XGetWidgetInfo((char *) NULL,&selection_info);
7002         selection_info.center=MagickFalse;
7003         selection_info.width=list_info.width;
7004         selection_info.height=(unsigned int) ((9*height) >> 3);
7005         selection_info.x=list_info.x;
7006         state&=(~UpdateConfigurationState);
7007       }
7008     if (state & RedrawWidgetState)
7009       {
7010         /*
7011           Redraw List Browser window.
7012         */
7013         XDrawWidgetText(display,window_info,&text_info);
7014         XDrawBeveledMatte(display,window_info,&list_info);
7015         XDrawBeveledMatte(display,window_info,&scroll_info);
7016         XDrawTriangleNorth(display,window_info,&north_info);
7017         XDrawBeveledButton(display,window_info,&slider_info);
7018         XDrawTriangleSouth(display,window_info,&south_info);
7019         XDrawBeveledMatte(display,window_info,&reply_info);
7020         XDrawMatteText(display,window_info,&reply_info);
7021         XDrawBeveledButton(display,window_info,&action_info);
7022         XDrawBeveledButton(display,window_info,&cancel_info);
7023         XHighlightWidget(display,window_info,BorderOffset,BorderOffset);
7024         selection_info.id=(~0);
7025         state|=RedrawActionState;
7026         state|=RedrawListState;
7027         state&=(~RedrawWidgetState);
7028       }
7029     if (state & RedrawListState)
7030       {
7031         /*
7032           Determine slider id and position.
7033         */
7034         if (slider_info.id >= (int) (entries-visible_entries))
7035           slider_info.id=(int) (entries-visible_entries);
7036         if ((slider_info.id < 0) || (entries <= visible_entries))
7037           slider_info.id=0;
7038         slider_info.y=slider_info.min_y;
7039         if (entries > 0)
7040           slider_info.y+=
7041             slider_info.id*(slider_info.max_y-slider_info.min_y+1)/entries;
7042         if (slider_info.id != selection_info.id)
7043           {
7044             /*
7045               Redraw scroll bar and file names.
7046             */
7047             selection_info.id=slider_info.id;
7048             selection_info.y=list_info.y+(height >> 3)+2;
7049             for (i=0; i < (int) visible_entries; i++)
7050             {
7051               selection_info.raised=(slider_info.id+i) != list_info.id ?
7052                 MagickTrue : MagickFalse;
7053               selection_info.text=(char *) NULL;
7054               if ((slider_info.id+i) < (int) entries)
7055                 selection_info.text=(char *) list[slider_info.id+i];
7056               XDrawWidgetText(display,window_info,&selection_info);
7057               selection_info.y+=(int) selection_info.height;
7058             }
7059             /*
7060               Update slider.
7061             */
7062             if (slider_info.y > expose_info.y)
7063               {
7064                 expose_info.height=(unsigned int) slider_info.y-expose_info.y;
7065                 expose_info.y=slider_info.y-expose_info.height-
7066                   slider_info.bevel_width-1;
7067               }
7068             else
7069               {
7070                 expose_info.height=(unsigned int) expose_info.y-slider_info.y;
7071                 expose_info.y=slider_info.y+slider_info.height+
7072                   slider_info.bevel_width+1;
7073               }
7074             XDrawTriangleNorth(display,window_info,&north_info);
7075             XDrawMatte(display,window_info,&expose_info);
7076             XDrawBeveledButton(display,window_info,&slider_info);
7077             XDrawTriangleSouth(display,window_info,&south_info);
7078             expose_info.y=slider_info.y;
7079           }
7080         state&=(~RedrawListState);
7081       }
7082     /*
7083       Wait for next event.
7084     */
7085     if (north_info.raised && south_info.raised)
7086       (void) XIfEvent(display,&event,XScreenEvent,(char *) windows);
7087     else
7088       {
7089         /*
7090           Brief delay before advancing scroll bar.
7091         */
7092         XDelay(display,delay);
7093         delay=SuspendTime;
7094         (void) XCheckIfEvent(display,&event,XScreenEvent,(char *) windows);
7095         if (north_info.raised == MagickFalse)
7096           if (slider_info.id > 0)
7097             {
7098               /*
7099                 Move slider up.
7100               */
7101               slider_info.id--;
7102               state|=RedrawListState;
7103             }
7104         if (south_info.raised == MagickFalse)
7105           if (slider_info.id < (int) entries)
7106             {
7107               /*
7108                 Move slider down.
7109               */
7110               slider_info.id++;
7111               state|=RedrawListState;
7112             }
7113         if (event.type != ButtonRelease)
7114           continue;
7115       }
7116     switch (event.type)
7117     {
7118       case ButtonPress:
7119       {
7120         if (MatteIsActive(slider_info,event.xbutton))
7121           {
7122             /*
7123               Track slider.
7124             */
7125             slider_info.active=MagickTrue;
7126             break;
7127           }
7128         if (MatteIsActive(north_info,event.xbutton))
7129           if (slider_info.id > 0)
7130             {
7131               /*
7132                 Move slider up.
7133               */
7134               north_info.raised=MagickFalse;
7135               slider_info.id--;
7136               state|=RedrawListState;
7137               break;
7138             }
7139         if (MatteIsActive(south_info,event.xbutton))
7140           if (slider_info.id < (int) entries)
7141             {
7142               /*
7143                 Move slider down.
7144               */
7145               south_info.raised=MagickFalse;
7146               slider_info.id++;
7147               state|=RedrawListState;
7148               break;
7149             }
7150         if (MatteIsActive(scroll_info,event.xbutton))
7151           {
7152             /*
7153               Move slider.
7154             */
7155             if (event.xbutton.y < slider_info.y)
7156               slider_info.id-=(visible_entries-1);
7157             else
7158               slider_info.id+=(visible_entries-1);
7159             state|=RedrawListState;
7160             break;
7161           }
7162         if (MatteIsActive(list_info,event.xbutton))
7163           {
7164             int
7165               id;
7166
7167             /*
7168               User pressed list matte.
7169             */
7170             id=slider_info.id+(event.xbutton.y-(list_info.y+(height >> 1))+1)/
7171               selection_info.height;
7172             if (id >= (int) entries)
7173               break;
7174             (void) CopyMagickString(reply_info.text,list[id],MaxTextExtent);
7175             reply_info.highlight=MagickFalse;
7176             reply_info.marker=reply_info.text;
7177             reply_info.cursor=reply_info.text+Extent(reply_info.text);
7178             XDrawMatteText(display,window_info,&reply_info);
7179             selection_info.id=(~0);
7180             if (id == list_info.id)
7181               {
7182                 action_info.raised=MagickFalse;
7183                 XDrawBeveledButton(display,window_info,&action_info);
7184                 state|=ExitState;
7185               }
7186             list_info.id=id;
7187             state|=RedrawListState;
7188             break;
7189           }
7190         if (MatteIsActive(action_info,event.xbutton))
7191           {
7192             /*
7193               User pressed action button.
7194             */
7195             action_info.raised=MagickFalse;
7196             XDrawBeveledButton(display,window_info,&action_info);
7197             break;
7198           }
7199         if (MatteIsActive(cancel_info,event.xbutton))
7200           {
7201             /*
7202               User pressed Cancel button.
7203             */
7204             cancel_info.raised=MagickFalse;
7205             XDrawBeveledButton(display,window_info,&cancel_info);
7206             break;
7207           }
7208         if (MatteIsActive(reply_info,event.xbutton) == MagickFalse)
7209           break;
7210         if (event.xbutton.button != Button2)
7211           {
7212             static Time
7213               click_time;
7214
7215             /*
7216               Move text cursor to position of button press.
7217             */
7218             x=event.xbutton.x-reply_info.x-(QuantumMargin >> 2);
7219             for (i=1; i <= Extent(reply_info.marker); i++)
7220               if (XTextWidth(font_info,reply_info.marker,i) > x)
7221                 break;
7222             reply_info.cursor=reply_info.marker+i-1;
7223             if (event.xbutton.time > (click_time+DoubleClick))
7224               reply_info.highlight=MagickFalse;
7225             else
7226               {
7227                 /*
7228                   Become the XA_PRIMARY selection owner.
7229                 */
7230                 (void) CopyMagickString(primary_selection,reply_info.text,
7231                   MaxTextExtent);
7232                 (void) XSetSelectionOwner(display,XA_PRIMARY,window_info->id,
7233                   event.xbutton.time);
7234                 reply_info.highlight=XGetSelectionOwner(display,XA_PRIMARY) ==
7235                   window_info->id ? MagickTrue : MagickFalse;
7236               }
7237             XDrawMatteText(display,window_info,&reply_info);
7238             click_time=event.xbutton.time;
7239             break;
7240           }
7241         /*
7242           Request primary selection.
7243         */
7244         (void) XConvertSelection(display,XA_PRIMARY,XA_STRING,XA_STRING,
7245           window_info->id,event.xbutton.time);
7246         break;
7247       }
7248       case ButtonRelease:
7249       {
7250         if (window_info->mapped == MagickFalse)
7251           break;
7252         if (north_info.raised == MagickFalse)
7253           {
7254             /*
7255               User released up button.
7256             */
7257             delay=SuspendTime << 2;
7258             north_info.raised=MagickTrue;
7259             XDrawTriangleNorth(display,window_info,&north_info);
7260           }
7261         if (south_info.raised == MagickFalse)
7262           {
7263             /*
7264               User released down button.
7265             */
7266             delay=SuspendTime << 2;
7267             south_info.raised=MagickTrue;
7268             XDrawTriangleSouth(display,window_info,&south_info);
7269           }
7270         if (slider_info.active)
7271           {
7272             /*
7273               Stop tracking slider.
7274             */
7275             slider_info.active=MagickFalse;
7276             break;
7277           }
7278         if (action_info.raised == MagickFalse)
7279           {
7280             if (event.xbutton.window == window_info->id)
7281               {
7282                 if (MatteIsActive(action_info,event.xbutton))
7283                   {
7284                     if (*reply_info.text == '\0')
7285                       (void) XBell(display,0);
7286                     else
7287                       state|=ExitState;
7288                   }
7289               }
7290             action_info.raised=MagickTrue;
7291             XDrawBeveledButton(display,window_info,&action_info);
7292           }
7293         if (cancel_info.raised == MagickFalse)
7294           {
7295             if (event.xbutton.window == window_info->id)
7296               if (MatteIsActive(cancel_info,event.xbutton))
7297                 {
7298                   *reply_info.text='\0';
7299                   state|=ExitState;
7300                 }
7301             cancel_info.raised=MagickTrue;
7302             XDrawBeveledButton(display,window_info,&cancel_info);
7303           }
7304         if (MatteIsActive(reply_info,event.xbutton) == MagickFalse)
7305           break;
7306         break;
7307       }
7308       case ClientMessage:
7309       {
7310         /*
7311           If client window delete message, exit.
7312         */
7313         if (event.xclient.message_type != windows->wm_protocols)
7314           break;
7315         if (*event.xclient.data.l == (int) windows->wm_take_focus)
7316           {
7317             (void) XSetInputFocus(display,event.xclient.window,RevertToParent,
7318               (Time) event.xclient.data.l[1]);
7319             break;
7320           }
7321         if (*event.xclient.data.l != (int) windows->wm_delete_window)
7322           break;
7323         if (event.xclient.window == window_info->id)
7324           {
7325             *reply_info.text='\0';
7326             state|=ExitState;
7327             break;
7328           }
7329         break;
7330       }
7331       case ConfigureNotify:
7332       {
7333         /*
7334           Update widget configuration.
7335         */
7336         if (event.xconfigure.window != window_info->id)
7337           break;
7338         if ((event.xconfigure.width == (int) window_info->width) &&
7339             (event.xconfigure.height == (int) window_info->height))
7340           break;
7341         window_info->width=(unsigned int)
7342           MagickMax(event.xconfigure.width,(int) window_info->min_width);
7343         window_info->height=(unsigned int)
7344           MagickMax(event.xconfigure.height,(int) window_info->min_height);
7345         state|=UpdateConfigurationState;
7346         break;
7347       }
7348       case EnterNotify:
7349       {
7350         if (event.xcrossing.window != window_info->id)
7351           break;
7352         state&=(~InactiveWidgetState);
7353         break;
7354       }
7355       case Expose:
7356       {
7357         if (event.xexpose.window != window_info->id)
7358           break;
7359         if (event.xexpose.count != 0)
7360           break;
7361         state|=RedrawWidgetState;
7362         break;
7363       }
7364       case KeyPress:
7365       {
7366         static char
7367           command[MaxTextExtent];
7368
7369         static int
7370           length;
7371
7372         static KeySym
7373           key_symbol;
7374
7375         /*
7376           Respond to a user key press.
7377         */
7378         if (event.xkey.window != window_info->id)
7379           break;
7380         length=XLookupString((XKeyEvent *) &event.xkey,command,
7381           (int) sizeof(command),&key_symbol,(XComposeStatus *) NULL);
7382         *(command+length)='\0';
7383         if (AreaIsActive(scroll_info,event.xkey))
7384           {
7385             /*
7386               Move slider.
7387             */
7388             switch ((int) key_symbol)
7389             {
7390               case XK_Home:
7391               case XK_KP_Home:
7392               {
7393                 slider_info.id=0;
7394                 break;
7395               }
7396               case XK_Up:
7397               case XK_KP_Up:
7398               {
7399                 slider_info.id--;
7400                 break;
7401               }
7402               case XK_Down:
7403               case XK_KP_Down:
7404               {
7405                 slider_info.id++;
7406                 break;
7407               }
7408               case XK_Prior:
7409               case XK_KP_Prior:
7410               {
7411                 slider_info.id-=visible_entries;
7412                 break;
7413               }
7414               case XK_Next:
7415               case XK_KP_Next:
7416               {
7417                 slider_info.id+=visible_entries;
7418                 break;
7419               }
7420               case XK_End:
7421               case XK_KP_End:
7422               {
7423                 slider_info.id=(int) entries;
7424                 break;
7425               }
7426             }
7427             state|=RedrawListState;
7428             break;
7429           }
7430         if ((key_symbol == XK_Return) || (key_symbol == XK_KP_Enter))
7431           {
7432             /*
7433               Read new entry.
7434             */
7435             if (*reply_info.text == '\0')
7436               break;
7437             action_info.raised=MagickFalse;
7438             XDrawBeveledButton(display,window_info,&action_info);
7439             state|=ExitState;
7440             break;
7441           }
7442         if (key_symbol == XK_Control_L)
7443           {
7444             state|=ControlState;
7445             break;
7446           }
7447         if (state & ControlState)
7448           switch ((int) key_symbol)
7449           {
7450             case XK_u:
7451             case XK_U:
7452             {
7453               /*
7454                 Erase the entire line of text.
7455               */
7456               *reply_info.text='\0';
7457               reply_info.cursor=reply_info.text;
7458               reply_info.marker=reply_info.text;
7459               reply_info.highlight=MagickFalse;
7460               break;
7461             }
7462             default:
7463               break;
7464           }
7465         XEditText(display,&reply_info,key_symbol,command,state);
7466         XDrawMatteText(display,window_info,&reply_info);
7467         break;
7468       }
7469       case KeyRelease:
7470       {
7471         static char
7472           command[MaxTextExtent];
7473
7474         static KeySym
7475           key_symbol;
7476
7477         /*
7478           Respond to a user key release.
7479         */
7480         if (event.xkey.window != window_info->id)
7481           break;
7482         (void) XLookupString((XKeyEvent *) &event.xkey,command,
7483           (int) sizeof(command),&key_symbol,(XComposeStatus *) NULL);
7484         if (key_symbol == XK_Control_L)
7485           state&=(~ControlState);
7486         break;
7487       }
7488       case LeaveNotify:
7489       {
7490         if (event.xcrossing.window != window_info->id)
7491           break;
7492         state|=InactiveWidgetState;
7493         break;
7494       }
7495       case MapNotify:
7496       {
7497         mask&=(~CWX);
7498         mask&=(~CWY);
7499         break;
7500       }
7501       case MotionNotify:
7502       {
7503         /*
7504           Discard pending button motion events.
7505         */
7506         while (XCheckMaskEvent(display,ButtonMotionMask,&event)) ;
7507         if (slider_info.active)
7508           {
7509             /*
7510               Move slider matte.
7511             */
7512             slider_info.y=event.xmotion.y-
7513               ((slider_info.height+slider_info.bevel_width) >> 1)+1;
7514             if (slider_info.y < slider_info.min_y)
7515               slider_info.y=slider_info.min_y;
7516             if (slider_info.y > slider_info.max_y)
7517               slider_info.y=slider_info.max_y;
7518             slider_info.id=0;
7519             if (slider_info.y != slider_info.min_y)
7520               slider_info.id=(int) ((entries*(slider_info.y-
7521                 slider_info.min_y+1))/(slider_info.max_y-slider_info.min_y+1));
7522             state|=RedrawListState;
7523             break;
7524           }
7525         if (state & InactiveWidgetState)
7526           break;
7527         if (action_info.raised == MatteIsActive(action_info,event.xmotion))
7528           {
7529             /*
7530               Action button status changed.
7531             */
7532             action_info.raised=action_info.raised == MagickFalse ?
7533               MagickTrue : MagickFalse;
7534             XDrawBeveledButton(display,window_info,&action_info);
7535             break;
7536           }
7537         if (cancel_info.raised == MatteIsActive(cancel_info,event.xmotion))
7538           {
7539             /*
7540               Cancel button status changed.
7541             */
7542             cancel_info.raised=cancel_info.raised == MagickFalse ?
7543               MagickTrue : MagickFalse;
7544             XDrawBeveledButton(display,window_info,&cancel_info);
7545             break;
7546           }
7547         break;
7548       }
7549       case SelectionClear:
7550       {
7551         reply_info.highlight=MagickFalse;
7552         XDrawMatteText(display,window_info,&reply_info);
7553         break;
7554       }
7555       case SelectionNotify:
7556       {
7557         Atom
7558           type;
7559
7560         int
7561           format;
7562
7563         unsigned char
7564           *data;
7565
7566         unsigned long
7567           after,
7568           length;
7569
7570         /*
7571           Obtain response from primary selection.
7572         */
7573         if (event.xselection.property == (Atom) None)
7574           break;
7575         status=XGetWindowProperty(display,
7576           event.xselection.requestor,event.xselection.property,0L,2047L,
7577           MagickTrue,XA_STRING,&type,&format,&length,&after,&data);
7578         if ((status != Success) || (type != XA_STRING) || (format == 32) ||
7579             (length == 0))
7580           break;
7581         if ((Extent(reply_info.text)+length) >= (MaxTextExtent-1))
7582           (void) XBell(display,0);
7583         else
7584           {
7585             /*
7586               Insert primary selection in reply text.
7587             */
7588             *(data+length)='\0';
7589             XEditText(display,&reply_info,(KeySym) XK_Insert,(char *) data,
7590               state);
7591             XDrawMatteText(display,window_info,&reply_info);
7592             state|=RedrawActionState;
7593           }
7594         (void) XFree((void *) data);
7595         break;
7596       }
7597       case SelectionRequest:
7598       {
7599         XSelectionEvent
7600           notify;
7601
7602         XSelectionRequestEvent
7603           *request;
7604
7605         if (reply_info.highlight == MagickFalse)
7606           break;
7607         /*
7608           Set primary selection.
7609         */
7610         request=(&(event.xselectionrequest));
7611         (void) XChangeProperty(request->display,request->requestor,
7612           request->property,request->target,8,PropModeReplace,
7613           (unsigned char *) primary_selection,Extent(primary_selection));
7614         notify.type=SelectionNotify;
7615         notify.send_event=MagickTrue;
7616         notify.display=request->display;
7617         notify.requestor=request->requestor;
7618         notify.selection=request->selection;
7619         notify.target=request->target;
7620         notify.time=request->time;
7621         if (request->property == None)
7622           notify.property=request->target;
7623         else
7624           notify.property=request->property;
7625         (void) XSendEvent(request->display,request->requestor,False,NoEventMask,
7626           (XEvent *) &notify);
7627       }
7628       default:
7629         break;
7630     }
7631   } while ((state & ExitState) == 0);
7632   XSetCursorState(display,windows,MagickFalse);
7633   (void) XWithdrawWindow(display,window_info->id,window_info->screen);
7634   XCheckRefreshWindows(display,windows);
7635 }
7636 \f
7637 /*
7638 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7639 %                                                                             %
7640 %                                                                             %
7641 %                                                                             %
7642 %   X M e n u W i d g e t                                                     %
7643 %                                                                             %
7644 %                                                                             %
7645 %                                                                             %
7646 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7647 %
7648 %  XMenuWidget() maps a menu and returns the command pointed to by the user
7649 %  when the button is released.
7650 %
7651 %  The format of the XMenuWidget method is:
7652 %
7653 %      int XMenuWidget(Display *display,XWindows *windows,const char *title,
7654 %        const char **selections,char *item)
7655 %
7656 %  A description of each parameter follows:
7657 %
7658 %    o selection_number: Specifies the number of the selection that the
7659 %      user choose.
7660 %
7661 %    o display: Specifies a connection to an X server;  returned from
7662 %      XOpenDisplay.
7663 %
7664 %    o window: Specifies a pointer to a XWindows structure.
7665 %
7666 %    o title: Specifies a character string that describes the menu selections.
7667 %
7668 %    o selections: Specifies a pointer to one or more strings that comprise
7669 %      the choices in the menu.
7670 %
7671 %    o item: Specifies a character array.  The item selected from the menu
7672 %      is returned here.
7673 %
7674 */
7675 MagickPrivate int XMenuWidget(Display *display,XWindows *windows,
7676   const char *title,const char **selections,char *item)
7677 {
7678   Cursor
7679     cursor;
7680
7681   int
7682     id,
7683     x,
7684     y;
7685
7686   unsigned int
7687     height,
7688     number_selections,
7689     title_height,
7690     top_offset,
7691     width;
7692
7693   size_t
7694     state;
7695
7696   XEvent
7697     event;
7698
7699   XFontStruct
7700     *font_info;
7701
7702   XSetWindowAttributes
7703     window_attributes;
7704
7705   XWidgetInfo
7706     highlight_info,
7707     menu_info,
7708     selection_info;
7709
7710   XWindowChanges
7711     window_changes;
7712
7713   /*
7714     Determine Menu widget attributes.
7715   */
7716   assert(display != (Display *) NULL);
7717   assert(windows != (XWindows *) NULL);
7718   assert(title != (char *) NULL);
7719   assert(selections != (const char **) NULL);
7720   assert(item != (char *) NULL);
7721   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",title);
7722   font_info=windows->widget.font_info;
7723   windows->widget.width=submenu_info.active == 0 ?
7724     WidgetTextWidth(font_info,(char *) title) : 0;
7725   for (id=0; selections[id] != (char *) NULL; id++)
7726   {
7727     width=WidgetTextWidth(font_info,(char *) selections[id]);
7728     if (width > windows->widget.width)
7729       windows->widget.width=width;
7730   }
7731   number_selections=(unsigned int) id;
7732   XGetWidgetInfo((char *) NULL,&menu_info);
7733   title_height=(unsigned int) (submenu_info.active == 0 ?
7734     (3*(font_info->descent+font_info->ascent) >> 1)+5 : 2);
7735   width=WidgetTextWidth(font_info,(char *) title);
7736   height=(unsigned int) ((3*(font_info->ascent+font_info->descent)) >> 1);
7737   /*
7738     Position Menu widget.
7739   */
7740   windows->widget.width+=QuantumMargin+(menu_info.bevel_width << 1);
7741   top_offset=title_height+menu_info.bevel_width-1;
7742   windows->widget.height=top_offset+number_selections*height+4;
7743   windows->widget.min_width=windows->widget.width;
7744   windows->widget.min_height=windows->widget.height;
7745   XQueryPosition(display,windows->widget.root,&x,&y);
7746   windows->widget.x=x-(QuantumMargin >> 1);
7747   if (submenu_info.active != 0)
7748     {
7749       windows->widget.x=
7750         windows->command.x+windows->command.width-QuantumMargin;
7751       toggle_info.raised=MagickTrue;
7752       XDrawTriangleEast(display,&windows->command,&toggle_info);
7753     }
7754   windows->widget.y=submenu_info.active == 0 ? y-(int)
7755     ((3*title_height) >> 2) : y;
7756   if (submenu_info.active != 0)
7757     windows->widget.y=windows->command.y+submenu_info.y;
7758   XConstrainWindowPosition(display,&windows->widget);
7759   /*
7760     Map Menu widget.
7761   */
7762   window_attributes.override_redirect=MagickTrue;
7763   (void) XChangeWindowAttributes(display,windows->widget.id,
7764     (size_t) CWOverrideRedirect,&window_attributes);
7765   window_changes.width=(int) windows->widget.width;
7766   window_changes.height=(int) windows->widget.height;
7767   window_changes.x=windows->widget.x;
7768   window_changes.y=windows->widget.y;
7769   (void) XReconfigureWMWindow(display,windows->widget.id,windows->widget.screen,
7770     (unsigned int) (CWWidth | CWHeight | CWX | CWY),&window_changes);
7771   (void) XMapRaised(display,windows->widget.id);
7772   windows->widget.mapped=MagickFalse;
7773   /*
7774     Respond to X events.
7775   */
7776   selection_info.height=height;
7777   cursor=XCreateFontCursor(display,XC_right_ptr);
7778   (void) XCheckDefineCursor(display,windows->image.id,cursor);
7779   (void) XCheckDefineCursor(display,windows->command.id,cursor);
7780   (void) XCheckDefineCursor(display,windows->widget.id,cursor);
7781   state=UpdateConfigurationState;
7782   do
7783   {
7784     if (state & UpdateConfigurationState)
7785       {
7786         /*
7787           Initialize selection information.
7788         */
7789         XGetWidgetInfo((char *) NULL,&menu_info);
7790         menu_info.bevel_width--;
7791         menu_info.width=windows->widget.width-((menu_info.bevel_width) << 1);
7792         menu_info.height=windows->widget.height-((menu_info.bevel_width) << 1);
7793         menu_info.x=(int) menu_info.bevel_width;
7794         menu_info.y=(int) menu_info.bevel_width;
7795         XGetWidgetInfo((char *) NULL,&selection_info);
7796         selection_info.center=MagickFalse;
7797         selection_info.width=menu_info.width;
7798         selection_info.height=height;
7799         selection_info.x=menu_info.x;
7800         highlight_info=selection_info;
7801         highlight_info.bevel_width--;
7802         highlight_info.width-=(highlight_info.bevel_width << 1);
7803         highlight_info.height-=(highlight_info.bevel_width << 1);
7804         highlight_info.x+=highlight_info.bevel_width;
7805         state&=(~UpdateConfigurationState);
7806       }
7807     if (state & RedrawWidgetState)
7808       {
7809         /*
7810           Redraw Menu widget.
7811         */
7812         if (submenu_info.active == 0)
7813           {
7814             y=(int) title_height;
7815             XSetBevelColor(display,&windows->widget,MagickFalse);
7816             (void) XDrawLine(display,windows->widget.id,
7817               windows->widget.widget_context,selection_info.x,y-1,
7818               (int) selection_info.width,y-1);
7819             XSetBevelColor(display,&windows->widget,MagickTrue);
7820             (void) XDrawLine(display,windows->widget.id,
7821               windows->widget.widget_context,selection_info.x,y,
7822               (int) selection_info.width,y);
7823             (void) XSetFillStyle(display,windows->widget.widget_context,
7824               FillSolid);
7825           }
7826         /*
7827           Draw menu selections.
7828         */
7829         selection_info.center=MagickTrue;
7830         selection_info.y=(int) menu_info.bevel_width;
7831         selection_info.text=(char *) title;
7832         if (submenu_info.active == 0)
7833           XDrawWidgetText(display,&windows->widget,&selection_info);
7834         selection_info.center=MagickFalse;
7835         selection_info.y=(int) top_offset;
7836         for (id=0; id < (int) number_selections; id++)
7837         {
7838           selection_info.text=(char *) selections[id];
7839           XDrawWidgetText(display,&windows->widget,&selection_info);
7840           highlight_info.y=selection_info.y+highlight_info.bevel_width;
7841           if (id == selection_info.id)
7842             XDrawBevel(display,&windows->widget,&highlight_info);
7843           selection_info.y+=(int) selection_info.height;
7844         }
7845         XDrawBevel(display,&windows->widget,&menu_info);
7846         state&=(~RedrawWidgetState);
7847       }
7848     if (number_selections > 2)
7849       {
7850         /*
7851           Redraw Menu line.
7852         */
7853         y=(int) (top_offset+selection_info.height*(number_selections-1));
7854         XSetBevelColor(display,&windows->widget,MagickFalse);
7855         (void) XDrawLine(display,windows->widget.id,
7856           windows->widget.widget_context,selection_info.x,y-1,
7857           (int) selection_info.width,y-1);
7858         XSetBevelColor(display,&windows->widget,MagickTrue);
7859         (void) XDrawLine(display,windows->widget.id,
7860           windows->widget.widget_context,selection_info.x,y,
7861           (int) selection_info.width,y);
7862         (void) XSetFillStyle(display,windows->widget.widget_context,FillSolid);
7863       }
7864     /*
7865       Wait for next event.
7866     */
7867     (void) XIfEvent(display,&event,XScreenEvent,(char *) windows);
7868     switch (event.type)
7869     {
7870       case ButtonPress:
7871       {
7872         if (event.xbutton.window != windows->widget.id)
7873           {
7874             /*
7875               exit menu.
7876             */
7877             if (event.xbutton.window == windows->command.id)
7878               (void) XPutBackEvent(display,&event);
7879             selection_info.id=(~0);
7880             *item='\0';
7881             state|=ExitState;
7882             break;
7883           }
7884         state&=(~InactiveWidgetState);
7885         id=(event.xbutton.y-top_offset)/(int) selection_info.height;
7886         selection_info.id=id;
7887         if ((id < 0) || (id >= (int) number_selections))
7888           break;
7889         /*
7890           Highlight this selection.
7891         */
7892         selection_info.y=(int) (top_offset+id*selection_info.height);
7893         selection_info.text=(char *) selections[id];
7894         XDrawWidgetText(display,&windows->widget,&selection_info);
7895         highlight_info.y=selection_info.y+highlight_info.bevel_width;
7896         XDrawBevel(display,&windows->widget,&highlight_info);
7897         break;
7898       }
7899       case ButtonRelease:
7900       {
7901         if (windows->widget.mapped == MagickFalse)
7902           break;
7903         if (event.xbutton.window == windows->command.id)
7904           if ((state & InactiveWidgetState) == 0)
7905             break;
7906         /*
7907           exit menu.
7908         */
7909         XSetCursorState(display,windows,MagickFalse);
7910         *item='\0';
7911         state|=ExitState;
7912         break;
7913       }
7914       case ConfigureNotify:
7915       {
7916         /*
7917           Update widget configuration.
7918         */
7919         if (event.xconfigure.window != windows->widget.id)
7920           break;
7921         if ((event.xconfigure.width == (int) windows->widget.width) &&
7922             (event.xconfigure.height == (int) windows->widget.height))
7923           break;
7924         windows->widget.width=(unsigned int)
7925           MagickMax(event.xconfigure.width,(int) windows->widget.min_width);
7926         windows->widget.height=(unsigned int)
7927           MagickMax(event.xconfigure.height,(int) windows->widget.min_height);
7928         state|=UpdateConfigurationState;
7929         break;
7930       }
7931       case EnterNotify:
7932       {
7933         if (event.xcrossing.window != windows->widget.id)
7934           break;
7935         if (event.xcrossing.state == 0)
7936           break;
7937         state&=(~InactiveWidgetState);
7938         id=((event.xcrossing.y-top_offset)/(int) selection_info.height);
7939         if ((selection_info.id >= 0) &&
7940             (selection_info.id < (int) number_selections))
7941           {
7942             /*
7943               Unhighlight last selection.
7944             */
7945             if (id == selection_info.id)
7946               break;
7947             selection_info.y=(int)
7948               (top_offset+selection_info.id*selection_info.height);
7949             selection_info.text=(char *) selections[selection_info.id];
7950             XDrawWidgetText(display,&windows->widget,&selection_info);
7951           }
7952         if ((id < 0) || (id >= (int) number_selections))
7953           break;
7954         /*
7955           Highlight this selection.
7956         */
7957         selection_info.id=id;
7958         selection_info.y=(int)
7959           (top_offset+selection_info.id*selection_info.height);
7960         selection_info.text=(char *) selections[selection_info.id];
7961         XDrawWidgetText(display,&windows->widget,&selection_info);
7962         highlight_info.y=selection_info.y+highlight_info.bevel_width;
7963         XDrawBevel(display,&windows->widget,&highlight_info);
7964         break;
7965       }
7966       case Expose:
7967       {
7968         if (event.xexpose.window != windows->widget.id)
7969           break;
7970         if (event.xexpose.count != 0)
7971           break;
7972         state|=RedrawWidgetState;
7973         break;
7974       }
7975       case LeaveNotify:
7976       {
7977         if (event.xcrossing.window != windows->widget.id)
7978           break;
7979         state|=InactiveWidgetState;
7980         id=selection_info.id;
7981         if ((id < 0) || (id >= (int) number_selections))
7982           break;
7983         /*
7984           Unhighlight last selection.
7985         */
7986         selection_info.y=(int) (top_offset+id*selection_info.height);
7987         selection_info.id=(~0);
7988         selection_info.text=(char *) selections[id];
7989         XDrawWidgetText(display,&windows->widget,&selection_info);
7990         break;
7991       }
7992       case MotionNotify:
7993       {
7994         /*
7995           Discard pending button motion events.
7996         */
7997         while (XCheckMaskEvent(display,ButtonMotionMask,&event)) ;
7998         if (submenu_info.active != 0)
7999           if (event.xmotion.window == windows->command.id)
8000             {
8001               if ((state & InactiveWidgetState) == 0)
8002                 {
8003                   if (MatteIsActive(submenu_info,event.xmotion) == MagickFalse)
8004                     {
8005                       selection_info.id=(~0);
8006                         *item='\0';
8007                       state|=ExitState;
8008                       break;
8009                     }
8010                 }
8011               else
8012                 if (WindowIsActive(windows->command,event.xmotion))
8013                   {
8014                     selection_info.id=(~0);
8015                     *item='\0';
8016                     state|=ExitState;
8017                     break;
8018                   }
8019             }
8020         if (event.xmotion.window != windows->widget.id)
8021           break;
8022         if (state & InactiveWidgetState)
8023           break;
8024         id=(event.xmotion.y-top_offset)/(int) selection_info.height;
8025         if ((selection_info.id >= 0) &&
8026             (selection_info.id < (int) number_selections))
8027           {
8028             /*
8029               Unhighlight last selection.
8030             */
8031             if (id == selection_info.id)
8032               break;
8033             selection_info.y=(int)
8034               (top_offset+selection_info.id*selection_info.height);
8035             selection_info.text=(char *) selections[selection_info.id];
8036             XDrawWidgetText(display,&windows->widget,&selection_info);
8037           }
8038         selection_info.id=id;
8039         if ((id < 0) || (id >= (int) number_selections))
8040           break;
8041         /*
8042           Highlight this selection.
8043         */
8044         selection_info.y=(int) (top_offset+id*selection_info.height);
8045         selection_info.text=(char *) selections[id];
8046         XDrawWidgetText(display,&windows->widget,&selection_info);
8047         highlight_info.y=selection_info.y+highlight_info.bevel_width;
8048         XDrawBevel(display,&windows->widget,&highlight_info);
8049         break;
8050       }
8051       default:
8052         break;
8053     }
8054   } while ((state & ExitState) == 0);
8055   (void) XFreeCursor(display,cursor);
8056   window_attributes.override_redirect=MagickFalse;
8057   (void) XChangeWindowAttributes(display,windows->widget.id,
8058     (size_t) CWOverrideRedirect,&window_attributes);
8059   (void) XWithdrawWindow(display,windows->widget.id,windows->widget.screen);
8060   XCheckRefreshWindows(display,windows);
8061   if (submenu_info.active != 0)
8062     {
8063       submenu_info.active=MagickFalse;
8064       toggle_info.raised=MagickFalse;
8065       XDrawTriangleEast(display,&windows->command,&toggle_info);
8066     }
8067   if ((selection_info.id < 0) || (selection_info.id >= (int) number_selections))
8068     return(~0);
8069   (void) CopyMagickString(item,selections[selection_info.id],MaxTextExtent);
8070   return(selection_info.id);
8071 }
8072 \f
8073 /*
8074 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8075 %                                                                             %
8076 %                                                                             %
8077 %                                                                             %
8078 %   X N o t i c e W i d g e t                                                 %
8079 %                                                                             %
8080 %                                                                             %
8081 %                                                                             %
8082 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8083 %
8084 %  XNoticeWidget() displays a Notice widget with a notice to the user.  The
8085 %  function returns when the user presses the "Dismiss" button.
8086 %
8087 %  The format of the XNoticeWidget method is:
8088 %
8089 %      void XNoticeWidget(Display *display,XWindows *windows,
8090 %        const char *reason,const char *description)
8091 %
8092 %  A description of each parameter follows:
8093 %
8094 %    o display: Specifies a connection to an X server;  returned from
8095 %      XOpenDisplay.
8096 %
8097 %    o window: Specifies a pointer to a XWindows structure.
8098 %
8099 %    o reason: Specifies the message to display before terminating the
8100 %      program.
8101 %
8102 %    o description: Specifies any description to the message.
8103 %
8104 */
8105 MagickPrivate void XNoticeWidget(Display *display,XWindows *windows,
8106   const char *reason,const char *description)
8107 {
8108 #define DismissButtonText  "Dismiss"
8109 #define Timeout  8
8110
8111   const char
8112     *text;
8113
8114   int
8115     x,
8116     y;
8117
8118   Status
8119     status;
8120
8121   time_t
8122     timer;
8123
8124   unsigned int
8125     height,
8126     width;
8127
8128   size_t
8129     state;
8130
8131   XEvent
8132     event;
8133
8134   XFontStruct
8135     *font_info;
8136
8137   XTextProperty
8138     window_name;
8139
8140   XWidgetInfo
8141     dismiss_info;
8142
8143   XWindowChanges
8144     window_changes;
8145
8146   /*
8147     Determine Notice widget attributes.
8148   */
8149   assert(display != (Display *) NULL);
8150   assert(windows != (XWindows *) NULL);
8151   assert(reason != (char *) NULL);
8152   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",reason);
8153   XDelay(display,SuspendTime << 3);  /* avoid surpise with delay */
8154   XSetCursorState(display,windows,MagickTrue);
8155   XCheckRefreshWindows(display,windows);
8156   font_info=windows->widget.font_info;
8157   width=WidgetTextWidth(font_info,DismissButtonText);
8158   text=GetLocaleExceptionMessage(XServerError,reason);
8159   if (text != (char *) NULL)
8160     if (WidgetTextWidth(font_info,(char *) text) > width)
8161       width=WidgetTextWidth(font_info,(char *) text);
8162   if (description != (char *) NULL)
8163     {
8164       text=GetLocaleExceptionMessage(XServerError,description);
8165       if (text != (char *) NULL)
8166         if (WidgetTextWidth(font_info,(char *) text) > width)
8167           width=WidgetTextWidth(font_info,(char *) text);
8168     }
8169   height=(unsigned int) (font_info->ascent+font_info->descent);
8170   /*
8171     Position Notice widget.
8172   */
8173   windows->widget.width=width+4*QuantumMargin;
8174   windows->widget.min_width=width+QuantumMargin;
8175   if (windows->widget.width < windows->widget.min_width)
8176     windows->widget.width=windows->widget.min_width;
8177   windows->widget.height=(unsigned int) (12*height);
8178   windows->widget.min_height=(unsigned int) (7*height);
8179   if (windows->widget.height < windows->widget.min_height)
8180     windows->widget.height=windows->widget.min_height;
8181   XConstrainWindowPosition(display,&windows->widget);
8182   /*
8183     Map Notice widget.
8184   */
8185   (void) CopyMagickString(windows->widget.name,"Notice",MaxTextExtent);
8186   status=XStringListToTextProperty(&windows->widget.name,1,&window_name);
8187   if (status != False)
8188     {
8189       XSetWMName(display,windows->widget.id,&window_name);
8190       XSetWMIconName(display,windows->widget.id,&window_name);
8191       (void) XFree((void *) window_name.value);
8192     }
8193   window_changes.width=(int) windows->widget.width;
8194   window_changes.height=(int) windows->widget.height;
8195   window_changes.x=windows->widget.x;
8196   window_changes.y=windows->widget.y;
8197   (void) XReconfigureWMWindow(display,windows->widget.id,windows->widget.screen,
8198     (unsigned int) (CWWidth | CWHeight | CWX | CWY),&window_changes);
8199   (void) XMapRaised(display,windows->widget.id);
8200   windows->widget.mapped=MagickFalse;
8201   (void) XBell(display,0);
8202   /*
8203     Respond to X events.
8204   */
8205   timer=time((time_t *) NULL)+Timeout;
8206   state=UpdateConfigurationState;
8207   do
8208   {
8209     if (time((time_t *) NULL) > timer)
8210       break;
8211     if (state & UpdateConfigurationState)
8212       {
8213         /*
8214           Initialize Dismiss button information.
8215         */
8216         XGetWidgetInfo(DismissButtonText,&dismiss_info);
8217         dismiss_info.width=(unsigned int) QuantumMargin+
8218           WidgetTextWidth(font_info,DismissButtonText);
8219         dismiss_info.height=(unsigned int) ((3*height) >> 1);
8220         dismiss_info.x=(int)
8221           ((windows->widget.width >> 1)-(dismiss_info.width >> 1));
8222         dismiss_info.y=(int)
8223           (windows->widget.height-(dismiss_info.height << 1));
8224         state&=(~UpdateConfigurationState);
8225       }
8226     if (state & RedrawWidgetState)
8227       {
8228         /*
8229           Redraw Notice widget.
8230         */
8231         width=WidgetTextWidth(font_info,(char *) reason);
8232         x=(int) ((windows->widget.width >> 1)-(width >> 1));
8233         y=(int) ((windows->widget.height >> 1)-(height << 1));
8234         (void) XDrawString(display,windows->widget.id,
8235           windows->widget.annotate_context,x,y,(char *) reason,Extent(reason));
8236         if (description != (char *) NULL)
8237           {
8238             width=WidgetTextWidth(font_info,(char *) description);
8239             x=(int) ((windows->widget.width >> 1)-(width >> 1));
8240             y+=height;
8241             (void) XDrawString(display,windows->widget.id,
8242               windows->widget.annotate_context,x,y,(char *) description,
8243               Extent(description));
8244           }
8245         XDrawBeveledButton(display,&windows->widget,&dismiss_info);
8246         XHighlightWidget(display,&windows->widget,BorderOffset,BorderOffset);
8247         state&=(~RedrawWidgetState);
8248       }
8249     /*
8250       Wait for next event.
8251     */
8252     if (XCheckIfEvent(display,&event,XScreenEvent,(char *) windows) == MagickFalse)
8253       {
8254         /*
8255           Do not block if delay > 0.
8256         */
8257         XDelay(display,SuspendTime << 2);
8258         continue;
8259       }
8260     switch (event.type)
8261     {
8262       case ButtonPress:
8263       {
8264         if (MatteIsActive(dismiss_info,event.xbutton))
8265           {
8266             /*
8267               User pressed Dismiss button.
8268             */
8269             dismiss_info.raised=MagickFalse;
8270             XDrawBeveledButton(display,&windows->widget,&dismiss_info);
8271             break;
8272           }
8273         break;
8274       }
8275       case ButtonRelease:
8276       {
8277         if (windows->widget.mapped == MagickFalse)
8278           break;
8279         if (dismiss_info.raised == MagickFalse)
8280           {
8281             if (event.xbutton.window == windows->widget.id)
8282               if (MatteIsActive(dismiss_info,event.xbutton))
8283                 state|=ExitState;
8284             dismiss_info.raised=MagickTrue;
8285             XDrawBeveledButton(display,&windows->widget,&dismiss_info);
8286           }
8287         break;
8288       }
8289       case ClientMessage:
8290       {
8291         /*
8292           If client window delete message, exit.
8293         */
8294         if (event.xclient.message_type != windows->wm_protocols)
8295           break;
8296         if (*event.xclient.data.l == (int) windows->wm_take_focus)
8297           {
8298             (void) XSetInputFocus(display,event.xclient.window,RevertToParent,
8299               (Time) event.xclient.data.l[1]);
8300             break;
8301           }
8302         if (*event.xclient.data.l != (int) windows->wm_delete_window)
8303           break;
8304         if (event.xclient.window == windows->widget.id)
8305           {
8306             state|=ExitState;
8307             break;
8308           }
8309         break;
8310       }
8311       case ConfigureNotify:
8312       {
8313         /*
8314           Update widget configuration.
8315         */
8316         if (event.xconfigure.window != windows->widget.id)
8317           break;
8318         if ((event.xconfigure.width == (int) windows->widget.width) &&
8319             (event.xconfigure.height == (int) windows->widget.height))
8320           break;
8321         windows->widget.width=(unsigned int)
8322           MagickMax(event.xconfigure.width,(int) windows->widget.min_width);
8323         windows->widget.height=(unsigned int)
8324           MagickMax(event.xconfigure.height,(int) windows->widget.min_height);
8325         state|=UpdateConfigurationState;
8326         break;
8327       }
8328       case EnterNotify:
8329       {
8330         if (event.xcrossing.window != windows->widget.id)
8331           break;
8332         state&=(~InactiveWidgetState);
8333         break;
8334       }
8335       case Expose:
8336       {
8337         if (event.xexpose.window != windows->widget.id)
8338           break;
8339         if (event.xexpose.count != 0)
8340           break;
8341         state|=RedrawWidgetState;
8342         break;
8343       }
8344       case KeyPress:
8345       {
8346         static char
8347           command[MaxTextExtent];
8348
8349         static KeySym
8350           key_symbol;
8351
8352         /*
8353           Respond to a user key press.
8354         */
8355         if (event.xkey.window != windows->widget.id)
8356           break;
8357         (void) XLookupString((XKeyEvent *) &event.xkey,command,
8358           (int) sizeof(command),&key_symbol,(XComposeStatus *) NULL);
8359         if ((key_symbol == XK_Return) || (key_symbol == XK_KP_Enter))
8360           {
8361             dismiss_info.raised=MagickFalse;
8362             XDrawBeveledButton(display,&windows->widget,&dismiss_info);
8363             state|=ExitState;
8364             break;
8365           }
8366         break;
8367       }
8368       case LeaveNotify:
8369       {
8370         if (event.xcrossing.window != windows->widget.id)
8371           break;
8372         state|=InactiveWidgetState;
8373         break;
8374       }
8375       case MotionNotify:
8376       {
8377         /*
8378           Discard pending button motion events.
8379         */
8380         while (XCheckMaskEvent(display,ButtonMotionMask,&event)) ;
8381         if (state & InactiveWidgetState)
8382           break;
8383         if (dismiss_info.raised == MatteIsActive(dismiss_info,event.xmotion))
8384           {
8385             /*
8386               Dismiss button status changed.
8387             */
8388             dismiss_info.raised=
8389               dismiss_info.raised == MagickFalse ? MagickTrue : MagickFalse;
8390             XDrawBeveledButton(display,&windows->widget,&dismiss_info);
8391             break;
8392           }
8393         break;
8394       }
8395       default:
8396         break;
8397     }
8398   } while ((state & ExitState) == 0);
8399   XSetCursorState(display,windows,MagickFalse);
8400   (void) XWithdrawWindow(display,windows->widget.id,windows->widget.screen);
8401   XCheckRefreshWindows(display,windows);
8402 }
8403 \f
8404 /*
8405 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8406 %                                                                             %
8407 %                                                                             %
8408 %                                                                             %
8409 %   X P r e f e r e n c e s W i d g e t                                       %
8410 %                                                                             %
8411 %                                                                             %
8412 %                                                                             %
8413 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8414 %
8415 %  XPreferencesWidget() displays a Preferences widget with program preferences.
8416 %  If the user presses the Apply button, the preferences are stored in a
8417 %  configuration file in the users' home directory.
8418 %
8419 %  The format of the XPreferencesWidget method is:
8420 %
8421 %      MagickBooleanType XPreferencesWidget(Display *display,
8422 %        XResourceInfo *resource_info,XWindows *windows)
8423 %
8424 %  A description of each parameter follows:
8425 %
8426 %    o display: Specifies a connection to an X server;  returned from
8427 %      XOpenDisplay.
8428 %
8429 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
8430 %
8431 %    o window: Specifies a pointer to a XWindows structure.
8432 %
8433 */
8434 MagickPrivate MagickBooleanType XPreferencesWidget(Display *display,
8435   XResourceInfo *resource_info,XWindows *windows)
8436 {
8437 #define ApplyButtonText  "Apply"
8438 #define CacheButtonText  "%lu mega-bytes of memory in the undo edit cache   "
8439 #define CancelButtonText  "Cancel"
8440 #define NumberPreferences  8
8441
8442   static const char
8443     *Preferences[] =
8444     {
8445       "display image centered on a backdrop",
8446       "confirm on program exit",
8447       "confirm on image edits",
8448       "correct image for display gamma",
8449       "display warning messages",
8450       "apply Floyd/Steinberg error diffusion to image",
8451       "use a shared colormap for colormapped X visuals",
8452       "display images as an X server pixmap"
8453     };
8454
8455   char
8456     cache[MaxTextExtent];
8457
8458   int
8459     x,
8460     y;
8461
8462   register int
8463     i;
8464
8465   Status
8466     status;
8467
8468   unsigned int
8469     height,
8470     text_width,
8471     width;
8472
8473   size_t
8474     state;
8475
8476   XEvent
8477     event;
8478
8479   XFontStruct
8480     *font_info;
8481
8482   XTextProperty
8483     window_name;
8484
8485   XWidgetInfo
8486     apply_info,
8487     cache_info,
8488     cancel_info,
8489     preferences_info[NumberPreferences];
8490
8491   XWindowChanges
8492     window_changes;
8493
8494   /*
8495     Determine Preferences widget attributes.
8496   */
8497   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
8498   assert(display != (Display *) NULL);
8499   assert(resource_info != (XResourceInfo *) NULL);
8500   assert(windows != (XWindows *) NULL);
8501   XCheckRefreshWindows(display,windows);
8502   font_info=windows->widget.font_info;
8503   text_width=WidgetTextWidth(font_info,CacheButtonText);
8504   for (i=0; i < NumberPreferences; i++)
8505     if (WidgetTextWidth(font_info,(char *) Preferences[i]) > text_width)
8506       text_width=WidgetTextWidth(font_info,(char *) Preferences[i]);
8507   width=WidgetTextWidth(font_info,ApplyButtonText);
8508   if (WidgetTextWidth(font_info,CancelButtonText) > width)
8509     width=WidgetTextWidth(font_info,CancelButtonText);
8510   width+=(unsigned int) QuantumMargin;
8511   height=(unsigned int) (font_info->ascent+font_info->descent);
8512   /*
8513     Position Preferences widget.
8514   */
8515   windows->widget.width=(unsigned int) (MagickMax((int) (width << 1),
8516     (int) text_width)+6*QuantumMargin);
8517   windows->widget.min_width=(width << 1)+QuantumMargin;
8518   if (windows->widget.width < windows->widget.min_width)
8519     windows->widget.width=windows->widget.min_width;
8520   windows->widget.height=(unsigned int)
8521     (7*height+NumberPreferences*(height+(QuantumMargin >> 1)));
8522   windows->widget.min_height=(unsigned int)
8523     (7*height+NumberPreferences*(height+(QuantumMargin >> 1)));
8524   if (windows->widget.height < windows->widget.min_height)
8525     windows->widget.height=windows->widget.min_height;
8526   XConstrainWindowPosition(display,&windows->widget);
8527   /*
8528     Map Preferences widget.
8529   */
8530   (void) CopyMagickString(windows->widget.name,"Preferences",MaxTextExtent);
8531   status=XStringListToTextProperty(&windows->widget.name,1,&window_name);
8532   if (status != False)
8533     {
8534       XSetWMName(display,windows->widget.id,&window_name);
8535       XSetWMIconName(display,windows->widget.id,&window_name);
8536       (void) XFree((void *) window_name.value);
8537     }
8538   window_changes.width=(int) windows->widget.width;
8539   window_changes.height=(int) windows->widget.height;
8540   window_changes.x=windows->widget.x;
8541   window_changes.y=windows->widget.y;
8542   (void) XReconfigureWMWindow(display,windows->widget.id,windows->widget.screen,
8543     (unsigned int) (CWWidth | CWHeight | CWX | CWY),&window_changes);
8544   (void) XMapRaised(display,windows->widget.id);
8545   windows->widget.mapped=MagickFalse;
8546   /*
8547     Respond to X events.
8548   */
8549   state=UpdateConfigurationState;
8550   XSetCursorState(display,windows,MagickTrue);
8551   do
8552   {
8553     if (state & UpdateConfigurationState)
8554       {
8555         /*
8556           Initialize button information.
8557         */
8558         XGetWidgetInfo(CancelButtonText,&cancel_info);
8559         cancel_info.width=width;
8560         cancel_info.height=(unsigned int) (3*height) >> 1;
8561         cancel_info.x=(int) windows->widget.width-cancel_info.width-
8562           (QuantumMargin << 1);
8563         cancel_info.y=(int) windows->widget.height-
8564           cancel_info.height-QuantumMargin;
8565         XGetWidgetInfo(ApplyButtonText,&apply_info);
8566         apply_info.width=width;
8567         apply_info.height=(unsigned int) (3*height) >> 1;
8568         apply_info.x=QuantumMargin << 1;
8569         apply_info.y=cancel_info.y;
8570         y=(int) (height << 1);
8571         for (i=0; i < NumberPreferences; i++)
8572         {
8573           XGetWidgetInfo(Preferences[i],&preferences_info[i]);
8574           preferences_info[i].bevel_width--;
8575           preferences_info[i].width=(unsigned int) QuantumMargin >> 1;
8576           preferences_info[i].height=(unsigned int) QuantumMargin >> 1;
8577           preferences_info[i].x=QuantumMargin << 1;
8578           preferences_info[i].y=y;
8579           y+=height+(QuantumMargin >> 1);
8580         }
8581         preferences_info[0].raised=resource_info->backdrop ==
8582           MagickFalse ? MagickTrue : MagickFalse;
8583         preferences_info[1].raised=resource_info->confirm_exit ==
8584           MagickFalse ? MagickTrue : MagickFalse;
8585         preferences_info[2].raised=resource_info->confirm_edit ==
8586           MagickFalse ? MagickTrue : MagickFalse;
8587         preferences_info[3].raised=resource_info->gamma_correct ==
8588           MagickFalse ? MagickTrue : MagickFalse;
8589         preferences_info[4].raised=resource_info->display_warnings ==
8590           MagickFalse ? MagickTrue : MagickFalse;
8591         preferences_info[5].raised=
8592           resource_info->quantize_info->dither_method == NoDitherMethod ?
8593           MagickTrue : MagickFalse;
8594         preferences_info[6].raised=resource_info->colormap !=
8595           SharedColormap ? MagickTrue : MagickFalse;
8596         preferences_info[7].raised=resource_info->use_pixmap ==
8597           MagickFalse ? MagickTrue : MagickFalse;
8598         (void) FormatLocaleString(cache,MaxTextExtent,CacheButtonText,
8599           (unsigned long) resource_info->undo_cache);
8600         XGetWidgetInfo(cache,&cache_info);
8601         cache_info.bevel_width--;
8602         cache_info.width=(unsigned int) QuantumMargin >> 1;
8603         cache_info.height=(unsigned int) QuantumMargin >> 1;
8604         cache_info.x=QuantumMargin << 1;
8605         cache_info.y=y;
8606         state&=(~UpdateConfigurationState);
8607       }
8608     if (state & RedrawWidgetState)
8609       {
8610         /*
8611           Redraw Preferences widget.
8612         */
8613         XDrawBeveledButton(display,&windows->widget,&apply_info);
8614         XDrawBeveledButton(display,&windows->widget,&cancel_info);
8615         for (i=0; i < NumberPreferences; i++)
8616           XDrawBeveledButton(display,&windows->widget,&preferences_info[i]);
8617         XDrawTriangleEast(display,&windows->widget,&cache_info);
8618         XHighlightWidget(display,&windows->widget,BorderOffset,BorderOffset);
8619         state&=(~RedrawWidgetState);
8620       }
8621     /*
8622       Wait for next event.
8623     */
8624     (void) XIfEvent(display,&event,XScreenEvent,(char *) windows);
8625     switch (event.type)
8626     {
8627       case ButtonPress:
8628       {
8629         if (MatteIsActive(apply_info,event.xbutton))
8630           {
8631             /*
8632               User pressed Apply button.
8633             */
8634             apply_info.raised=MagickFalse;
8635             XDrawBeveledButton(display,&windows->widget,&apply_info);
8636             break;
8637           }
8638         if (MatteIsActive(cancel_info,event.xbutton))
8639           {
8640             /*
8641               User pressed Cancel button.
8642             */
8643             cancel_info.raised=MagickFalse;
8644             XDrawBeveledButton(display,&windows->widget,&cancel_info);
8645             break;
8646           }
8647         for (i=0; i < NumberPreferences; i++)
8648           if (MatteIsActive(preferences_info[i],event.xbutton))
8649             {
8650               /*
8651                 User pressed a Preferences button.
8652               */
8653               preferences_info[i].raised=preferences_info[i].raised ==
8654                 MagickFalse ? MagickTrue : MagickFalse;
8655               XDrawBeveledButton(display,&windows->widget,&preferences_info[i]);
8656               break;
8657             }
8658         if (MatteIsActive(cache_info,event.xbutton))
8659           {
8660             /*
8661               User pressed Cache button.
8662             */
8663             x=cache_info.x+cache_info.width+cache_info.bevel_width+
8664               (QuantumMargin >> 1);
8665             y=cache_info.y+((cache_info.height-height) >> 1);
8666             width=WidgetTextWidth(font_info,cache);
8667             (void) XClearArea(display,windows->widget.id,x,y,width,height,
8668               False);
8669             resource_info->undo_cache<<=1;
8670             if (resource_info->undo_cache > 256)
8671               resource_info->undo_cache=1;
8672             (void) FormatLocaleString(cache,MaxTextExtent,CacheButtonText,
8673               (unsigned long) resource_info->undo_cache);
8674             cache_info.raised=MagickFalse;
8675             XDrawTriangleEast(display,&windows->widget,&cache_info);
8676             break;
8677           }
8678         break;
8679       }
8680       case ButtonRelease:
8681       {
8682         if (windows->widget.mapped == MagickFalse)
8683           break;
8684         if (apply_info.raised == MagickFalse)
8685           {
8686             if (event.xbutton.window == windows->widget.id)
8687               if (MatteIsActive(apply_info,event.xbutton))
8688                 state|=ExitState;
8689             apply_info.raised=MagickTrue;
8690             XDrawBeveledButton(display,&windows->widget,&apply_info);
8691             apply_info.raised=MagickFalse;
8692           }
8693         if (cancel_info.raised == MagickFalse)
8694           {
8695             if (event.xbutton.window == windows->widget.id)
8696               if (MatteIsActive(cancel_info,event.xbutton))
8697                 state|=ExitState;
8698             cancel_info.raised=MagickTrue;
8699             XDrawBeveledButton(display,&windows->widget,&cancel_info);
8700           }
8701         if (cache_info.raised == MagickFalse)
8702           {
8703             cache_info.raised=MagickTrue;
8704             XDrawTriangleEast(display,&windows->widget,&cache_info);
8705           }
8706         break;
8707       }
8708       case ClientMessage:
8709       {
8710         /*
8711           If client window delete message, exit.
8712         */
8713         if (event.xclient.message_type != windows->wm_protocols)
8714           break;
8715         if (*event.xclient.data.l == (int) windows->wm_take_focus)
8716           {
8717             (void) XSetInputFocus(display,event.xclient.window,RevertToParent,
8718               (Time) event.xclient.data.l[1]);
8719             break;
8720           }
8721         if (*event.xclient.data.l != (int) windows->wm_delete_window)
8722           break;
8723         if (event.xclient.window == windows->widget.id)
8724           {
8725             state|=ExitState;
8726             break;
8727           }
8728         break;
8729       }
8730       case ConfigureNotify:
8731       {
8732         /*
8733           Update widget configuration.
8734         */
8735         if (event.xconfigure.window != windows->widget.id)
8736           break;
8737         if ((event.xconfigure.width == (int) windows->widget.width) &&
8738             (event.xconfigure.height == (int) windows->widget.height))
8739           break;
8740         windows->widget.width=(unsigned int)
8741           MagickMax(event.xconfigure.width,(int) windows->widget.min_width);
8742         windows->widget.height=(unsigned int)
8743           MagickMax(event.xconfigure.height,(int) windows->widget.min_height);
8744         state|=UpdateConfigurationState;
8745         break;
8746       }
8747       case EnterNotify:
8748       {
8749         if (event.xcrossing.window != windows->widget.id)
8750           break;
8751         state&=(~InactiveWidgetState);
8752         break;
8753       }
8754       case Expose:
8755       {
8756         if (event.xexpose.window != windows->widget.id)
8757           break;
8758         if (event.xexpose.count != 0)
8759           break;
8760         state|=RedrawWidgetState;
8761         break;
8762       }
8763       case KeyPress:
8764       {
8765         static char
8766           command[MaxTextExtent];
8767
8768         static KeySym
8769           key_symbol;
8770
8771         /*
8772           Respond to a user key press.
8773         */
8774         if (event.xkey.window != windows->widget.id)
8775           break;
8776         (void) XLookupString((XKeyEvent *) &event.xkey,command,
8777           (int) sizeof(command),&key_symbol,(XComposeStatus *) NULL);
8778         if ((key_symbol == XK_Return) || (key_symbol == XK_KP_Enter))
8779           {
8780             apply_info.raised=MagickFalse;
8781             XDrawBeveledButton(display,&windows->widget,&apply_info);
8782             state|=ExitState;
8783             break;
8784           }
8785         break;
8786       }
8787       case LeaveNotify:
8788       {
8789         if (event.xcrossing.window != windows->widget.id)
8790           break;
8791         state|=InactiveWidgetState;
8792         break;
8793       }
8794       case MotionNotify:
8795       {
8796         /*
8797           Discard pending button motion events.
8798         */
8799         while (XCheckMaskEvent(display,ButtonMotionMask,&event)) ;
8800         if (state & InactiveWidgetState)
8801           break;
8802         if (apply_info.raised == MatteIsActive(apply_info,event.xmotion))
8803           {
8804             /*
8805               Apply button status changed.
8806             */
8807             apply_info.raised=
8808               apply_info.raised == MagickFalse ? MagickTrue : MagickFalse;
8809             XDrawBeveledButton(display,&windows->widget,&apply_info);
8810             break;
8811           }
8812         if (cancel_info.raised == MatteIsActive(cancel_info,event.xmotion))
8813           {
8814             /*
8815               Cancel button status changed.
8816             */
8817             cancel_info.raised=
8818               cancel_info.raised == MagickFalse ? MagickTrue : MagickFalse;
8819             XDrawBeveledButton(display,&windows->widget,&cancel_info);
8820             break;
8821           }
8822         break;
8823       }
8824       default:
8825         break;
8826     }
8827   } while ((state & ExitState) == 0);
8828   XSetCursorState(display,windows,MagickFalse);
8829   (void) XWithdrawWindow(display,windows->widget.id,windows->widget.screen);
8830   XCheckRefreshWindows(display,windows);
8831   if (apply_info.raised)
8832     return(MagickFalse);
8833   /*
8834     Save user preferences to the client configuration file.
8835   */
8836   resource_info->backdrop=
8837     preferences_info[0].raised == MagickFalse ? MagickTrue : MagickFalse;
8838   resource_info->confirm_exit=
8839     preferences_info[1].raised == MagickFalse ? MagickTrue : MagickFalse;
8840   resource_info->confirm_edit=
8841     preferences_info[2].raised == MagickFalse ? MagickTrue : MagickFalse;
8842   resource_info->gamma_correct=
8843     preferences_info[3].raised == MagickFalse ? MagickTrue : MagickFalse;
8844   resource_info->display_warnings=
8845      preferences_info[4].raised == MagickFalse ? MagickTrue : MagickFalse;
8846   resource_info->quantize_info->dither_method=
8847     preferences_info[5].raised == MagickFalse ?
8848     RiemersmaDitherMethod : NoDitherMethod;
8849   resource_info->colormap=SharedColormap;
8850   if (preferences_info[6].raised)
8851     resource_info->colormap=PrivateColormap;
8852   resource_info->use_pixmap=
8853     preferences_info[7].raised == MagickFalse ? MagickTrue : MagickFalse;
8854   XUserPreferences(resource_info);
8855   return(MagickTrue);
8856 }
8857 \f
8858 /*
8859 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8860 %                                                                             %
8861 %                                                                             %
8862 %                                                                             %
8863 %   X P r o g r e s s M o n i t o r W i d g e t                               %
8864 %                                                                             %
8865 %                                                                             %
8866 %                                                                             %
8867 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8868 %
8869 %  XProgressMonitorWidget() displays the progress a task is making in
8870 %  completing a task.  A span of zero toggles the active status.  An inactive
8871 %  state disables the progress monitor.
8872 %
8873 %  The format of the XProgressMonitorWidget method is:
8874 %
8875 %      void XProgressMonitorWidget(Display *display,XWindows *windows,
8876 %        const char *task,const MagickOffsetType offset,
8877 %        const MagickSizeType span)
8878 %
8879 %  A description of each parameter follows:
8880 %
8881 %    o display: Specifies a connection to an X server;  returned from
8882 %      XOpenDisplay.
8883 %
8884 %    o window: Specifies a pointer to a XWindows structure.
8885 %
8886 %    o task: Identifies the task in progress.
8887 %
8888 %    o offset: Specifies the offset position within the span which represents
8889 %      how much progress has been made in completing a task.
8890 %
8891 %    o span: Specifies the span relative to completing a task.
8892 %
8893 */
8894 MagickPrivate void XProgressMonitorWidget(Display *display,XWindows *windows,
8895   const char *task,const MagickOffsetType offset,const MagickSizeType span)
8896 {
8897   unsigned int
8898     width;
8899
8900   XEvent
8901     event;
8902
8903   assert(display != (Display *) NULL);
8904   assert(windows != (XWindows *) NULL);
8905   assert(task != (const char *) NULL);
8906   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",task);
8907   if (span == 0)
8908     return;
8909   /*
8910     Update image windows if there is a pending expose event.
8911   */
8912   while (XCheckTypedWindowEvent(display,windows->command.id,Expose,&event))
8913     (void) XCommandWidget(display,windows,(const char **) NULL,&event);
8914   while (XCheckTypedWindowEvent(display,windows->image.id,Expose,&event))
8915     XRefreshWindow(display,&windows->image,&event);
8916   while (XCheckTypedWindowEvent(display,windows->info.id,Expose,&event))
8917     if (monitor_info.text != (char *) NULL)
8918       XInfoWidget(display,windows,monitor_info.text);
8919   /*
8920     Draw progress monitor bar to represent percent completion of a task.
8921   */
8922   if ((windows->info.mapped == MagickFalse) || (task != monitor_info.text))
8923     XInfoWidget(display,windows,task);
8924   width=(unsigned int) (((offset+1)*(windows->info.width-
8925     (2*monitor_info.x)))/span);
8926   if (width < monitor_info.width)
8927     {
8928       monitor_info.raised=MagickTrue;
8929       XDrawWidgetText(display,&windows->info,&monitor_info);
8930       monitor_info.raised=MagickFalse;
8931     }
8932   monitor_info.width=width;
8933   XDrawWidgetText(display,&windows->info,&monitor_info);
8934   (void) XFlush(display);
8935 }
8936 \f
8937 /*
8938 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8939 %                                                                             %
8940 %                                                                             %
8941 %                                                                             %
8942 %   X T e x t V i e w W i d g e t                                             %
8943 %                                                                             %
8944 %                                                                             %
8945 %                                                                             %
8946 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8947 %
8948 %  XTextViewWidget() displays text in a Text View widget.
8949 %
8950 %  The format of the XTextViewWidget method is:
8951 %
8952 %      void XTextViewWidget(Display *display,const XResourceInfo *resource_info,
8953 %        XWindows *windows,const MagickBooleanType mono,const char *title,
8954 %        const char **textlist)
8955 %
8956 %  A description of each parameter follows:
8957 %
8958 %    o display: Specifies a connection to an X server;  returned from
8959 %      XOpenDisplay.
8960 %
8961 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
8962 %
8963 %    o window: Specifies a pointer to a XWindows structure.
8964 %
8965 %    o mono:  Use mono-spaced font when displaying text.
8966 %
8967 %    o title: This character string is displayed at the top of the widget
8968 %      window.
8969 %
8970 %    o textlist: This string list is displayed within the Text View widget.
8971 %
8972 */
8973 MagickPrivate void XTextViewWidget(Display *display,
8974   const XResourceInfo *resource_info,XWindows *windows,
8975   const MagickBooleanType mono,const char *title,const char **textlist)
8976 {
8977 #define DismissButtonText  "Dismiss"
8978
8979   char
8980     primary_selection[MaxTextExtent];
8981
8982   register int
8983     i;
8984
8985   static MagickStatusType
8986     mask = (MagickStatusType) (CWWidth | CWHeight | CWX | CWY);
8987
8988   Status
8989     status;
8990
8991   unsigned int
8992     height,
8993     lines,
8994     text_width,
8995     visible_lines,
8996     width;
8997
8998   size_t
8999     delay,
9000     state;
9001
9002   XEvent
9003     event;
9004
9005   XFontStruct
9006     *font_info,
9007     *text_info;
9008
9009   XTextProperty
9010     window_name;
9011
9012   XWidgetInfo
9013     dismiss_info,
9014     expose_info,
9015     list_info,
9016     north_info,
9017     scroll_info,
9018     selection_info,
9019     slider_info,
9020     south_info;
9021
9022   XWindowChanges
9023     window_changes;
9024
9025   /*
9026     Convert text string to a text list.
9027   */
9028   assert(display != (Display *) NULL);
9029   assert(resource_info != (XResourceInfo *) NULL);
9030   assert(windows != (XWindows *) NULL);
9031   assert(title != (const char *) NULL);
9032   assert(textlist != (const char **) NULL);
9033   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",title);
9034   XSetCursorState(display,windows,MagickTrue);
9035   XCheckRefreshWindows(display,windows);
9036   if (textlist == (const char **) NULL)
9037     {
9038       XNoticeWidget(display,windows,"No text to view:",(char *) NULL);
9039       return;
9040     }
9041   /*
9042     Determine Text View widget attributes.
9043   */
9044   font_info=windows->widget.font_info;
9045   text_info=(XFontStruct *) NULL;
9046   if (mono != MagickFalse)
9047     text_info=XBestFont(display,resource_info,MagickTrue);
9048   if (text_info == (XFontStruct *) NULL)
9049     text_info=windows->widget.font_info;
9050   text_width=0;
9051   for (i=0; textlist[i] != (char *) NULL; i++)
9052     if (WidgetTextWidth(text_info,(char *) textlist[i]) > text_width)
9053       text_width=(unsigned int) XTextWidth(text_info,(char *) textlist[i],
9054         MagickMin(Extent(textlist[i]),160));
9055   lines=(unsigned int) i;
9056   width=WidgetTextWidth(font_info,DismissButtonText);
9057   width+=QuantumMargin;
9058   height=(unsigned int) (text_info->ascent+text_info->descent);
9059   /*
9060     Position Text View widget.
9061   */
9062   windows->widget.width=(unsigned int) (MagickMin((int) text_width,
9063     (int) MaxTextWidth)+5*QuantumMargin);
9064   windows->widget.min_width=(unsigned int) (MinTextWidth+4*QuantumMargin);
9065   if (windows->widget.width < windows->widget.min_width)
9066     windows->widget.width=windows->widget.min_width;
9067   windows->widget.height=(unsigned int) (MagickMin(MagickMax((int) lines,3),32)*
9068     height+((13*height) >> 1)+((9*QuantumMargin) >> 1));
9069   windows->widget.min_height=(unsigned int) (3*height+((13*height) >> 1)+((9*
9070     QuantumMargin) >> 1));
9071   if (windows->widget.height < windows->widget.min_height)
9072     windows->widget.height=windows->widget.min_height;
9073   XConstrainWindowPosition(display,&windows->widget);
9074   /*
9075     Map Text View widget.
9076   */
9077   (void) CopyMagickString(windows->widget.name,title,MaxTextExtent);
9078   status=XStringListToTextProperty(&windows->widget.name,1,&window_name);
9079   if (status != False)
9080     {
9081       XSetWMName(display,windows->widget.id,&window_name);
9082       XSetWMIconName(display,windows->widget.id,&window_name);
9083       (void) XFree((void *) window_name.value);
9084     }
9085   window_changes.width=(int) windows->widget.width;
9086   window_changes.height=(int) windows->widget.height;
9087   window_changes.x=windows->widget.x;
9088   window_changes.y=windows->widget.y;
9089   (void) XReconfigureWMWindow(display,windows->widget.id,
9090     windows->widget.screen,(unsigned int) mask,&window_changes);
9091   (void) XMapRaised(display,windows->widget.id);
9092   windows->widget.mapped=MagickFalse;
9093   /*
9094     Respond to X events.
9095   */
9096   XGetWidgetInfo((char *) NULL,&slider_info);
9097   XGetWidgetInfo((char *) NULL,&north_info);
9098   XGetWidgetInfo((char *) NULL,&south_info);
9099   XGetWidgetInfo((char *) NULL,&expose_info);
9100   visible_lines=0;
9101   delay=SuspendTime << 2;
9102   height=(unsigned int) (font_info->ascent+font_info->descent);
9103   state=UpdateConfigurationState;
9104   do
9105   {
9106     if (state & UpdateConfigurationState)
9107       {
9108         int
9109           id;
9110
9111         /*
9112           Initialize button information.
9113         */
9114         XGetWidgetInfo(DismissButtonText,&dismiss_info);
9115         dismiss_info.width=width;
9116         dismiss_info.height=(unsigned int) ((3*height) >> 1);
9117         dismiss_info.x=(int) windows->widget.width-dismiss_info.width-
9118           QuantumMargin-2;
9119         dismiss_info.y=(int) windows->widget.height-dismiss_info.height-
9120           QuantumMargin;
9121         /*
9122           Initialize scroll information.
9123         */
9124         XGetWidgetInfo((char *) NULL,&scroll_info);
9125         scroll_info.bevel_width--;
9126         scroll_info.width=height;
9127         scroll_info.height=(unsigned int) (dismiss_info.y-((5*QuantumMargin) >>
9128           1));
9129         scroll_info.x=(int) windows->widget.width-QuantumMargin-
9130           scroll_info.width;
9131         scroll_info.y=(3*QuantumMargin) >> 1;
9132         scroll_info.raised=MagickFalse;
9133         scroll_info.trough=MagickTrue;
9134         north_info=scroll_info;
9135         north_info.raised=MagickTrue;
9136         north_info.width-=(north_info.bevel_width << 1);
9137         north_info.height=north_info.width-1;
9138         north_info.x+=north_info.bevel_width;
9139         north_info.y+=north_info.bevel_width;
9140         south_info=north_info;
9141         south_info.y=scroll_info.y+scroll_info.height-scroll_info.bevel_width-
9142           south_info.height;
9143         id=slider_info.id;
9144         slider_info=north_info;
9145         slider_info.id=id;
9146         slider_info.width-=2;
9147         slider_info.min_y=north_info.y+north_info.height+north_info.bevel_width+
9148           slider_info.bevel_width+2;
9149         slider_info.height=scroll_info.height-((slider_info.min_y-
9150           scroll_info.y+1) << 1)+4;
9151         visible_lines=scroll_info.height/(text_info->ascent+text_info->descent+
9152           ((text_info->ascent+text_info->descent) >> 3));
9153         if (lines > visible_lines)
9154           slider_info.height=(unsigned int) (visible_lines*slider_info.height)/
9155             lines;
9156         slider_info.max_y=south_info.y-south_info.bevel_width-
9157           slider_info.bevel_width-2;
9158         slider_info.x=scroll_info.x+slider_info.bevel_width+1;
9159         slider_info.y=slider_info.min_y;
9160         expose_info=scroll_info;
9161         expose_info.y=slider_info.y;
9162         /*
9163           Initialize list information.
9164         */
9165         XGetWidgetInfo((char *) NULL,&list_info);
9166         list_info.raised=MagickFalse;
9167         list_info.bevel_width--;
9168         list_info.width=(unsigned int) scroll_info.x-((3*QuantumMargin) >> 1);
9169         list_info.height=scroll_info.height;
9170         list_info.x=QuantumMargin;
9171         list_info.y=scroll_info.y;
9172         /*
9173           Initialize selection information.
9174         */
9175         XGetWidgetInfo((char *) NULL,&selection_info);
9176         selection_info.center=MagickFalse;
9177         selection_info.width=list_info.width;
9178         selection_info.height=(unsigned int)
9179           (9*(text_info->ascent+text_info->descent)) >> 3;
9180         selection_info.x=list_info.x;
9181         state&=(~UpdateConfigurationState);
9182       }
9183     if (state & RedrawWidgetState)
9184       {
9185         /*
9186           Redraw Text View window.
9187         */
9188         XDrawBeveledMatte(display,&windows->widget,&list_info);
9189         XDrawBeveledMatte(display,&windows->widget,&scroll_info);
9190         XDrawTriangleNorth(display,&windows->widget,&north_info);
9191         XDrawBeveledButton(display,&windows->widget,&slider_info);
9192         XDrawTriangleSouth(display,&windows->widget,&south_info);
9193         XDrawBeveledButton(display,&windows->widget,&dismiss_info);
9194         XHighlightWidget(display,&windows->widget,BorderOffset,BorderOffset);
9195         selection_info.id=(~0);
9196         state|=RedrawListState;
9197         state&=(~RedrawWidgetState);
9198       }
9199     if (state & RedrawListState)
9200       {
9201         /*
9202           Determine slider id and position.
9203         */
9204         if (slider_info.id >= (int) (lines-visible_lines))
9205           slider_info.id=(int) lines-visible_lines;
9206         if ((slider_info.id < 0) || (lines <= visible_lines))
9207           slider_info.id=0;
9208         slider_info.y=slider_info.min_y;
9209         if (lines != 0)
9210           slider_info.y+=
9211             slider_info.id*(slider_info.max_y-slider_info.min_y+1)/lines;
9212         if (slider_info.id != selection_info.id)
9213           {
9214             /*
9215               Redraw scroll bar and text.
9216             */
9217             windows->widget.font_info=text_info;
9218             (void) XSetFont(display,windows->widget.annotate_context,
9219               text_info->fid);
9220             (void) XSetFont(display,windows->widget.highlight_context,
9221               text_info->fid);
9222             selection_info.id=slider_info.id;
9223             selection_info.y=list_info.y+(height >> 3)+2;
9224             for (i=0; i < (int) visible_lines; i++)
9225             {
9226               selection_info.raised=
9227                 (slider_info.id+i) != list_info.id ? MagickTrue : MagickFalse;
9228               selection_info.text=(char *) NULL;
9229               if ((slider_info.id+i) < (int) lines)
9230                 selection_info.text=(char *) textlist[slider_info.id+i];
9231               XDrawWidgetText(display,&windows->widget,&selection_info);
9232               selection_info.y+=(int) selection_info.height;
9233             }
9234             windows->widget.font_info=font_info;
9235             (void) XSetFont(display,windows->widget.annotate_context,
9236               font_info->fid);
9237             (void) XSetFont(display,windows->widget.highlight_context,
9238               font_info->fid);
9239             /*
9240               Update slider.
9241             */
9242             if (slider_info.y > expose_info.y)
9243               {
9244                 expose_info.height=(unsigned int) slider_info.y-expose_info.y;
9245                 expose_info.y=slider_info.y-expose_info.height-
9246                   slider_info.bevel_width-1;
9247               }
9248             else
9249               {
9250                 expose_info.height=(unsigned int) expose_info.y-slider_info.y;
9251                 expose_info.y=slider_info.y+slider_info.height+
9252                   slider_info.bevel_width+1;
9253               }
9254             XDrawTriangleNorth(display,&windows->widget,&north_info);
9255             XDrawMatte(display,&windows->widget,&expose_info);
9256             XDrawBeveledButton(display,&windows->widget,&slider_info);
9257             XDrawTriangleSouth(display,&windows->widget,&south_info);
9258             expose_info.y=slider_info.y;
9259           }
9260         state&=(~RedrawListState);
9261       }
9262     /*
9263       Wait for next event.
9264     */
9265     if (north_info.raised && south_info.raised)
9266       (void) XIfEvent(display,&event,XScreenEvent,(char *) windows);
9267     else
9268       {
9269         /*
9270           Brief delay before advancing scroll bar.
9271         */
9272         XDelay(display,delay);
9273         delay=SuspendTime;
9274         (void) XCheckIfEvent(display,&event,XScreenEvent,(char *) windows);
9275         if (north_info.raised == MagickFalse)
9276           if (slider_info.id > 0)
9277             {
9278               /*
9279                 Move slider up.
9280               */
9281               slider_info.id--;
9282               state|=RedrawListState;
9283             }
9284         if (south_info.raised == MagickFalse)
9285           if (slider_info.id < (int) lines)
9286             {
9287               /*
9288                 Move slider down.
9289               */
9290               slider_info.id++;
9291               state|=RedrawListState;
9292             }
9293         if (event.type != ButtonRelease)
9294           continue;
9295       }
9296     switch (event.type)
9297     {
9298       case ButtonPress:
9299       {
9300         if (MatteIsActive(slider_info,event.xbutton))
9301           {
9302             /*
9303               Track slider.
9304             */
9305             slider_info.active=MagickTrue;
9306             break;
9307           }
9308         if (MatteIsActive(north_info,event.xbutton))
9309           if (slider_info.id > 0)
9310             {
9311               /*
9312                 Move slider up.
9313               */
9314               north_info.raised=MagickFalse;
9315               slider_info.id--;
9316               state|=RedrawListState;
9317               break;
9318             }
9319         if (MatteIsActive(south_info,event.xbutton))
9320           if (slider_info.id < (int) lines)
9321             {
9322               /*
9323                 Move slider down.
9324               */
9325               south_info.raised=MagickFalse;
9326               slider_info.id++;
9327               state|=RedrawListState;
9328               break;
9329             }
9330         if (MatteIsActive(scroll_info,event.xbutton))
9331           {
9332             /*
9333               Move slider.
9334             */
9335             if (event.xbutton.y < slider_info.y)
9336               slider_info.id-=(visible_lines-1);
9337             else
9338               slider_info.id+=(visible_lines-1);
9339             state|=RedrawListState;
9340             break;
9341           }
9342         if (MatteIsActive(dismiss_info,event.xbutton))
9343           {
9344             /*
9345               User pressed Dismiss button.
9346             */
9347             dismiss_info.raised=MagickFalse;
9348             XDrawBeveledButton(display,&windows->widget,&dismiss_info);
9349             break;
9350           }
9351         if (MatteIsActive(list_info,event.xbutton))
9352           {
9353             int
9354               id;
9355
9356             static Time
9357               click_time;
9358
9359             /*
9360               User pressed list matte.
9361             */
9362             id=slider_info.id+(event.xbutton.y-(list_info.y+(height >> 1))+1)/
9363               selection_info.height;
9364             if (id >= (int) lines)
9365               break;
9366             if (id != list_info.id)
9367               {
9368                 list_info.id=id;
9369                 click_time=event.xbutton.time;
9370                 break;
9371               }
9372             list_info.id=id;
9373             if (event.xbutton.time >= (click_time+DoubleClick))
9374               {
9375                 click_time=event.xbutton.time;
9376                 break;
9377               }
9378             click_time=event.xbutton.time;
9379             /*
9380               Become the XA_PRIMARY selection owner.
9381             */
9382             (void) CopyMagickString(primary_selection,textlist[list_info.id],
9383               MaxTextExtent);
9384             (void) XSetSelectionOwner(display,XA_PRIMARY,windows->widget.id,
9385               event.xbutton.time);
9386             if (XGetSelectionOwner(display,XA_PRIMARY) != windows->widget.id)
9387               break;
9388             selection_info.id=(~0);
9389             list_info.id=id;
9390             state|=RedrawListState;
9391             break;
9392           }
9393         break;
9394       }
9395       case ButtonRelease:
9396       {
9397         if (windows->widget.mapped == MagickFalse)
9398           break;
9399         if (north_info.raised == MagickFalse)
9400           {
9401             /*
9402               User released up button.
9403             */
9404             delay=SuspendTime << 2;
9405             north_info.raised=MagickTrue;
9406             XDrawTriangleNorth(display,&windows->widget,&north_info);
9407           }
9408         if (south_info.raised == MagickFalse)
9409           {
9410             /*
9411               User released down button.
9412             */
9413             delay=SuspendTime << 2;
9414             south_info.raised=MagickTrue;
9415             XDrawTriangleSouth(display,&windows->widget,&south_info);
9416           }
9417         if (slider_info.active)
9418           {
9419             /*
9420               Stop tracking slider.
9421             */
9422             slider_info.active=MagickFalse;
9423             break;
9424           }
9425         if (dismiss_info.raised == MagickFalse)
9426           {
9427             if (event.xbutton.window == windows->widget.id)
9428               if (MatteIsActive(dismiss_info,event.xbutton))
9429                 state|=ExitState;
9430             dismiss_info.raised=MagickTrue;
9431             XDrawBeveledButton(display,&windows->widget,&dismiss_info);
9432           }
9433         break;
9434       }
9435       case ClientMessage:
9436       {
9437         /*
9438           If client window delete message, exit.
9439         */
9440         if (event.xclient.message_type != windows->wm_protocols)
9441           break;
9442         if (*event.xclient.data.l == (int) windows->wm_take_focus)
9443           {
9444             (void) XSetInputFocus(display,event.xclient.window,RevertToParent,
9445               (Time) event.xclient.data.l[1]);
9446             break;
9447           }
9448         if (*event.xclient.data.l != (int) windows->wm_delete_window)
9449           break;
9450         if (event.xclient.window == windows->widget.id)
9451           {
9452             state|=ExitState;
9453             break;
9454           }
9455         break;
9456       }
9457       case ConfigureNotify:
9458       {
9459         /*
9460           Update widget configuration.
9461         */
9462         if (event.xconfigure.window != windows->widget.id)
9463           break;
9464         if ((event.xconfigure.width == (int) windows->widget.width) &&
9465             (event.xconfigure.height == (int) windows->widget.height))
9466           break;
9467         windows->widget.width=(unsigned int)
9468           MagickMax(event.xconfigure.width,(int) windows->widget.min_width);
9469         windows->widget.height=(unsigned int)
9470           MagickMax(event.xconfigure.height,(int) windows->widget.min_height);
9471         state|=UpdateConfigurationState;
9472         break;
9473       }
9474       case EnterNotify:
9475       {
9476         if (event.xcrossing.window != windows->widget.id)
9477           break;
9478         state&=(~InactiveWidgetState);
9479         break;
9480       }
9481       case Expose:
9482       {
9483         if (event.xexpose.window != windows->widget.id)
9484           break;
9485         if (event.xexpose.count != 0)
9486           break;
9487         state|=RedrawWidgetState;
9488         break;
9489       }
9490       case KeyPress:
9491       {
9492         static char
9493           command[MaxTextExtent];
9494
9495         static int
9496           length;
9497
9498         static KeySym
9499           key_symbol;
9500
9501         /*
9502           Respond to a user key press.
9503         */
9504         if (event.xkey.window != windows->widget.id)
9505           break;
9506         length=XLookupString((XKeyEvent *) &event.xkey,command,
9507           (int) sizeof(command),&key_symbol,(XComposeStatus *) NULL);
9508         *(command+length)='\0';
9509         if ((key_symbol == XK_Return) || (key_symbol == XK_KP_Enter))
9510           {
9511             dismiss_info.raised=MagickFalse;
9512             XDrawBeveledButton(display,&windows->widget,&dismiss_info);
9513             state|=ExitState;
9514             break;
9515           }
9516         if (AreaIsActive(scroll_info,event.xkey))
9517           {
9518             /*
9519               Move slider.
9520             */
9521             switch ((int) key_symbol)
9522             {
9523               case XK_Home:
9524               case XK_KP_Home:
9525               {
9526                 slider_info.id=0;
9527                 break;
9528               }
9529               case XK_Up:
9530               case XK_KP_Up:
9531               {
9532                 slider_info.id--;
9533                 break;
9534               }
9535               case XK_Down:
9536               case XK_KP_Down:
9537               {
9538                 slider_info.id++;
9539                 break;
9540               }
9541               case XK_Prior:
9542               case XK_KP_Prior:
9543               {
9544                 slider_info.id-=visible_lines;
9545                 break;
9546               }
9547               case XK_Next:
9548               case XK_KP_Next:
9549               {
9550                 slider_info.id+=visible_lines;
9551                 break;
9552               }
9553               case XK_End:
9554               case XK_KP_End:
9555               {
9556                 slider_info.id=(int) lines;
9557                 break;
9558               }
9559             }
9560             state|=RedrawListState;
9561             break;
9562           }
9563         break;
9564       }
9565       case KeyRelease:
9566         break;
9567       case LeaveNotify:
9568       {
9569         if (event.xcrossing.window != windows->widget.id)
9570           break;
9571         state|=InactiveWidgetState;
9572         break;
9573       }
9574       case MapNotify:
9575       {
9576         mask&=(~CWX);
9577         mask&=(~CWY);
9578         break;
9579       }
9580       case MotionNotify:
9581       {
9582         /*
9583           Discard pending button motion events.
9584         */
9585         while (XCheckMaskEvent(display,ButtonMotionMask,&event)) ;
9586         if (slider_info.active)
9587           {
9588             /*
9589               Move slider matte.
9590             */
9591             slider_info.y=event.xmotion.y-
9592               ((slider_info.height+slider_info.bevel_width) >> 1)+1;
9593             if (slider_info.y < slider_info.min_y)
9594               slider_info.y=slider_info.min_y;
9595             if (slider_info.y > slider_info.max_y)
9596               slider_info.y=slider_info.max_y;
9597             slider_info.id=0;
9598             if (slider_info.y != slider_info.min_y)
9599               slider_info.id=(int) (lines*(slider_info.y-slider_info.min_y+1))/
9600                 (slider_info.max_y-slider_info.min_y+1);
9601             state|=RedrawListState;
9602             break;
9603           }
9604         if (state & InactiveWidgetState)
9605           break;
9606         if (dismiss_info.raised == MatteIsActive(dismiss_info,event.xmotion))
9607           {
9608             /*
9609               Dismiss button status changed.
9610             */
9611             dismiss_info.raised=
9612               dismiss_info.raised == MagickFalse ? MagickTrue : MagickFalse;
9613             XDrawBeveledButton(display,&windows->widget,&dismiss_info);
9614             break;
9615           }
9616         break;
9617       }
9618       case SelectionClear:
9619       {
9620         list_info.id=(~0);
9621         selection_info.id=(~0);
9622         state|=RedrawListState;
9623         break;
9624       }
9625       case SelectionRequest:
9626       {
9627         XSelectionEvent
9628           notify;
9629
9630         XSelectionRequestEvent
9631           *request;
9632
9633         if (list_info.id == (~0))
9634           break;
9635         /*
9636           Set primary selection.
9637         */
9638         request=(&(event.xselectionrequest));
9639         (void) XChangeProperty(request->display,request->requestor,
9640           request->property,request->target,8,PropModeReplace,
9641           (unsigned char *) primary_selection,Extent(primary_selection));
9642         notify.type=SelectionNotify;
9643         notify.send_event=MagickTrue;
9644         notify.display=request->display;
9645         notify.requestor=request->requestor;
9646         notify.selection=request->selection;
9647         notify.target=request->target;
9648         notify.time=request->time;
9649         if (request->property == None)
9650           notify.property=request->target;
9651         else
9652           notify.property=request->property;
9653         (void) XSendEvent(request->display,request->requestor,False,NoEventMask,
9654           (XEvent *) &notify);
9655       }
9656       default:
9657         break;
9658     }
9659   } while ((state & ExitState) == 0);
9660   if (text_info != windows->widget.font_info)
9661     (void) XFreeFont(display,text_info);
9662   XSetCursorState(display,windows,MagickFalse);
9663   (void) XWithdrawWindow(display,windows->widget.id,windows->widget.screen);
9664   XCheckRefreshWindows(display,windows);
9665 }
9666 #endif