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