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