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