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