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