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