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