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