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