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