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