MagickCore  7.0.10
widget.c
Go to the documentation of this file.
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-2020 ImageMagick Studio LLC, a non-profit organization %
22 % dedicated to making software imaging solutions freely available. %
23 % %
24 % You may not use this file except in compliance with the License. You may %
25 % obtain a copy of the License at %
26 % %
27 % https://imagemagick.org/script/license.php %
28 % %
29 % Unless required by applicable law or agreed to in writing, software %
30 % distributed under the License is distributed on an "AS IS" BASIS, %
31 % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
32 % See the License for the specific language governing permissions and %
33 % limitations under the License. %
34 % %
35 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
36 %
37 %
38 */
39 
40 /*
41  Include declarations.
42 */
43 #include "MagickCore/studio.h"
44 #include "MagickCore/color.h"
46 #include "MagickCore/exception.h"
48 #include "MagickCore/image.h"
49 #include "MagickCore/magick.h"
50 #include "MagickCore/memory_.h"
51 #include "MagickCore/string_.h"
53 #include "MagickCore/token.h"
55 #include "MagickCore/utility.h"
58 #include "MagickCore/widget.h"
60 
61 #if defined(MAGICKCORE_X11_DELEGATE)
62 
63 /*
64  Define declarations.
65 */
66 #define AreaIsActive(matte_info,position) ( \
67  ((position.y >= (int) (matte_info.y-matte_info.bevel_width)) && \
68  (position.y < (int) (matte_info.y+matte_info.height+matte_info.bevel_width))) \
69  ? MagickTrue : MagickFalse)
70 #define Extent(s) ((int) strlen(s))
71 #define MatteIsActive(matte_info,position) ( \
72  ((position.x >= (int) (matte_info.x-matte_info.bevel_width)) && \
73  (position.y >= (int) (matte_info.y-matte_info.bevel_width)) && \
74  (position.x < (int) (matte_info.x+matte_info.width+matte_info.bevel_width)) && \
75  (position.y < (int) (matte_info.y+matte_info.height+matte_info.bevel_width))) \
76  ? MagickTrue : MagickFalse)
77 #define MaxTextWidth ((unsigned int) (255*XTextWidth(font_info,"_",1)))
78 #define MinTextWidth (26*XTextWidth(font_info,"_",1))
79 #define QuantumMargin MagickMax(font_info->max_bounds.width,12)
80 #define WidgetTextWidth(font_info,text) \
81  ((unsigned int) XTextWidth(font_info,text,Extent(text)))
82 #define WindowIsActive(window_info,position) ( \
83  ((position.x >= 0) && (position.y >= 0) && \
84  (position.x < (int) window_info.width) && \
85  (position.y < (int) window_info.height)) ? MagickTrue : MagickFalse)
86 
87 /*
88  Enum declarations.
89 */
90 typedef enum
91 {
92  ControlState = 0x0001,
93  InactiveWidgetState = 0x0004,
94  JumpListState = 0x0008,
95  RedrawActionState = 0x0010,
96  RedrawListState = 0x0020,
97  RedrawWidgetState = 0x0040,
98  UpdateListState = 0x0100
99 } WidgetState;
100 
101 /*
102  Typedef declarations.
103 */
104 typedef struct _XWidgetInfo
105 {
106  char
107  *cursor,
108  *text,
109  *marker;
110 
111  int
112  id;
113 
114  unsigned int
115  bevel_width,
116  width,
117  height;
118 
119  int
120  x,
121  y,
122  min_y,
123  max_y;
124 
126  raised,
127  active,
128  center,
129  trough,
130  highlight;
131 } XWidgetInfo;
132 
133 /*
134  Variable declarations.
135 */
136 static XWidgetInfo
137  monitor_info =
138  {
139  (char *) NULL, (char *) NULL, (char *) NULL, 0, 0, 0, 0, 0, 0, 0, 0,
141  },
142  submenu_info =
143  {
144  (char *) NULL, (char *) NULL, (char *) NULL, 0, 0, 0, 0, 0, 0, 0, 0,
146  },
147  *selection_info = (XWidgetInfo *) NULL,
148  toggle_info =
149  {
150  (char *) NULL, (char *) NULL, (char *) NULL, 0, 0, 0, 0, 0, 0, 0, 0,
152  };
153 
154 /*
155  Constant declarations.
156 */
157 static const int
158  BorderOffset = 4,
159  DoubleClick = 250;
160 
161 /*
162  Method prototypes.
163 */
164 static void
165  XDrawMatte(Display *,const XWindowInfo *,const XWidgetInfo *),
166  XSetBevelColor(Display *,const XWindowInfo *,const MagickStatusType),
167  XSetMatteColor(Display *,const XWindowInfo *,const MagickStatusType),
168  XSetTextColor(Display *,const XWindowInfo *,const MagickStatusType);
169 
170 /*
171 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
172 % %
173 % %
174 % %
175 % D e s t r o y X W i d g e t %
176 % %
177 % %
178 % %
179 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
180 %
181 % DestroyXWidget() destroys resources associated with the X widget.
182 %
183 % The format of the DestroyXWidget method is:
184 %
185 % void DestroyXWidget()
186 %
187 % A description of each parameter follows:
188 %
189 */
190 MagickPrivate void DestroyXWidget(void)
191 {
192  if (selection_info != (XWidgetInfo *) NULL)
193  selection_info=(XWidgetInfo *) RelinquishMagickMemory(selection_info);
194 }
195 
196 /*
197 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
198 % %
199 % %
200 % %
201 + X D r a w B e v e l %
202 % %
203 % %
204 % %
205 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
206 %
207 % XDrawBevel() "sets off" an area with a highlighted upper and left bevel and
208 % a shadowed lower and right bevel. The highlighted and shadowed bevels
209 % create a 3-D effect.
210 %
211 % The format of the XDrawBevel function is:
212 %
213 % XDrawBevel(display,window_info,bevel_info)
214 %
215 % A description of each parameter follows:
216 %
217 % o display: Specifies a pointer to the Display structure; returned from
218 % XOpenDisplay.
219 %
220 % o window_info: Specifies a pointer to a X11 XWindowInfo structure.
221 %
222 % o bevel_info: Specifies a pointer to a XWidgetInfo structure. It
223 % contains the extents of the bevel.
224 %
225 */
226 static void XDrawBevel(Display *display,const XWindowInfo *window_info,
227  const XWidgetInfo *bevel_info)
228 {
229  int
230  x1,
231  x2,
232  y1,
233  y2;
234 
235  unsigned int
236  bevel_width;
237 
238  XPoint
239  points[6];
240 
241  /*
242  Draw upper and left beveled border.
243  */
244  x1=bevel_info->x;
245  y1=bevel_info->y+bevel_info->height;
246  x2=bevel_info->x+bevel_info->width;
247  y2=bevel_info->y;
248  bevel_width=bevel_info->bevel_width;
249  points[0].x=x1;
250  points[0].y=y1;
251  points[1].x=x1;
252  points[1].y=y2;
253  points[2].x=x2;
254  points[2].y=y2;
255  points[3].x=x2+bevel_width;
256  points[3].y=y2-bevel_width;
257  points[4].x=x1-bevel_width;
258  points[4].y=y2-bevel_width;
259  points[5].x=x1-bevel_width;
260  points[5].y=y1+bevel_width;
261  XSetBevelColor(display,window_info,bevel_info->raised);
262  (void) XFillPolygon(display,window_info->id,window_info->widget_context,
263  points,6,Complex,CoordModeOrigin);
264  /*
265  Draw lower and right beveled border.
266  */
267  points[0].x=x1;
268  points[0].y=y1;
269  points[1].x=x2;
270  points[1].y=y1;
271  points[2].x=x2;
272  points[2].y=y2;
273  points[3].x=x2+bevel_width;
274  points[3].y=y2-bevel_width;
275  points[4].x=x2+bevel_width;
276  points[4].y=y1+bevel_width;
277  points[5].x=x1-bevel_width;
278  points[5].y=y1+bevel_width;
279  XSetBevelColor(display,window_info,!bevel_info->raised);
280  (void) XFillPolygon(display,window_info->id,window_info->widget_context,
281  points,6,Complex,CoordModeOrigin);
282  (void) XSetFillStyle(display,window_info->widget_context,FillSolid);
283 }
284 
285 /*
286 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
287 % %
288 % %
289 % %
290 + X D r a w B e v e l e d B u t t o n %
291 % %
292 % %
293 % %
294 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
295 %
296 % XDrawBeveledButton() draws a button with a highlighted upper and left bevel
297 % and a shadowed lower and right bevel. The highlighted and shadowed bevels
298 % create a 3-D effect.
299 %
300 % The format of the XDrawBeveledButton function is:
301 %
302 % XDrawBeveledButton(display,window_info,button_info)
303 %
304 % A description of each parameter follows:
305 %
306 % o display: Specifies a pointer to the Display structure; returned from
307 % XOpenDisplay.
308 %
309 % o window_info: Specifies a pointer to a X11 XWindowInfo structure.
310 %
311 % o button_info: Specifies a pointer to a XWidgetInfo structure. It
312 % contains the extents of the button.
313 %
314 */
315 
316 static void XDrawBeveledButton(Display *display,const XWindowInfo *window_info,
317  const XWidgetInfo *button_info)
318 {
319  int
320  x,
321  y;
322 
323  unsigned int
324  width;
325 
326  XFontStruct
327  *font_info;
328 
329  XRectangle
330  crop_info;
331 
332  /*
333  Draw matte.
334  */
335  XDrawBevel(display,window_info,button_info);
336  XSetMatteColor(display,window_info,button_info->raised);
337  (void) XFillRectangle(display,window_info->id,window_info->widget_context,
338  button_info->x,button_info->y,button_info->width,button_info->height);
339  x=button_info->x-button_info->bevel_width-1;
340  y=button_info->y-button_info->bevel_width-1;
341  (void) XSetForeground(display,window_info->widget_context,
342  window_info->pixel_info->trough_color.pixel);
343  if (button_info->raised || (window_info->depth == 1))
344  (void) XDrawRectangle(display,window_info->id,window_info->widget_context,
345  x,y,button_info->width+(button_info->bevel_width << 1)+1,
346  button_info->height+(button_info->bevel_width << 1)+1);
347  if (button_info->text == (char *) NULL)
348  return;
349  /*
350  Set cropping region.
351  */
352  crop_info.width=(unsigned short) button_info->width;
353  crop_info.height=(unsigned short) button_info->height;
354  crop_info.x=button_info->x;
355  crop_info.y=button_info->y;
356  /*
357  Draw text.
358  */
359  font_info=window_info->font_info;
360  width=WidgetTextWidth(font_info,button_info->text);
361  x=button_info->x+(QuantumMargin >> 1);
362  if (button_info->center)
363  x=button_info->x+(button_info->width >> 1)-(width >> 1);
364  y=button_info->y+((button_info->height-
365  (font_info->ascent+font_info->descent)) >> 1)+font_info->ascent;
366  if ((int) button_info->width == (QuantumMargin >> 1))
367  {
368  /*
369  Option button-- write label to right of button.
370  */
371  XSetTextColor(display,window_info,MagickTrue);
372  x=button_info->x+button_info->width+button_info->bevel_width+
373  (QuantumMargin >> 1);
374  (void) XDrawString(display,window_info->id,window_info->widget_context,
375  x,y,button_info->text,Extent(button_info->text));
376  return;
377  }
378  (void) XSetClipRectangles(display,window_info->widget_context,0,0,&crop_info,
379  1,Unsorted);
380  XSetTextColor(display,window_info,button_info->raised);
381  (void) XDrawString(display,window_info->id,window_info->widget_context,x,y,
382  button_info->text,Extent(button_info->text));
383  (void) XSetClipMask(display,window_info->widget_context,None);
384  if (button_info->raised == MagickFalse)
385  XDelay(display,SuspendTime << 2);
386 }
387 
388 /*
389 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
390 % %
391 % %
392 % %
393 + X D r a w B e v e l e d M a t t e %
394 % %
395 % %
396 % %
397 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
398 %
399 % XDrawBeveledMatte() draws a matte with a shadowed upper and left bevel and
400 % a highlighted lower and right bevel. The highlighted and shadowed bevels
401 % create a 3-D effect.
402 %
403 % The format of the XDrawBeveledMatte function is:
404 %
405 % XDrawBeveledMatte(display,window_info,matte_info)
406 %
407 % A description of each parameter follows:
408 %
409 % o display: Specifies a pointer to the Display structure; returned from
410 % XOpenDisplay.
411 %
412 % o window_info: Specifies a pointer to a X11 XWindowInfo structure.
413 %
414 % o matte_info: Specifies a pointer to a XWidgetInfo structure. It
415 % contains the extents of the matte.
416 %
417 */
418 static void XDrawBeveledMatte(Display *display,const XWindowInfo *window_info,
419  const XWidgetInfo *matte_info)
420 {
421  /*
422  Draw matte.
423  */
424  XDrawBevel(display,window_info,matte_info);
425  XDrawMatte(display,window_info,matte_info);
426 }
427 
428 /*
429 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
430 % %
431 % %
432 % %
433 + X D r a w M a t t e %
434 % %
435 % %
436 % %
437 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
438 %
439 % XDrawMatte() fills a rectangular area with the matte color.
440 %
441 % The format of the XDrawMatte function is:
442 %
443 % XDrawMatte(display,window_info,matte_info)
444 %
445 % A description of each parameter follows:
446 %
447 % o display: Specifies a pointer to the Display structure; returned from
448 % XOpenDisplay.
449 %
450 % o window_info: Specifies a pointer to a X11 XWindowInfo structure.
451 %
452 % o matte_info: Specifies a pointer to a XWidgetInfo structure. It
453 % contains the extents of the matte.
454 %
455 */
456 static void XDrawMatte(Display *display,const XWindowInfo *window_info,
457  const XWidgetInfo *matte_info)
458 {
459  /*
460  Draw matte.
461  */
462  if ((matte_info->trough == MagickFalse) || (window_info->depth == 1))
463  (void) XFillRectangle(display,window_info->id,
464  window_info->highlight_context,matte_info->x,matte_info->y,
465  matte_info->width,matte_info->height);
466  else
467  {
468  (void) XSetForeground(display,window_info->widget_context,
469  window_info->pixel_info->trough_color.pixel);
470  (void) XFillRectangle(display,window_info->id,window_info->widget_context,
471  matte_info->x,matte_info->y,matte_info->width,matte_info->height);
472  }
473 }
474 
475 /*
476 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
477 % %
478 % %
479 % %
480 + X D r a w M a t t e T e x t %
481 % %
482 % %
483 % %
484 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
485 %
486 % XDrawMatteText() draws a matte with text. If the text exceeds the extents
487 % of the text, a portion of the text relative to the cursor is displayed.
488 %
489 % The format of the XDrawMatteText function is:
490 %
491 % XDrawMatteText(display,window_info,text_info)
492 %
493 % A description of each parameter follows:
494 %
495 % o display: Specifies a pointer to the Display structure; returned from
496 % XOpenDisplay.
497 %
498 % o window_info: Specifies a pointer to a X11 XWindowInfo structure.
499 %
500 % o text_info: Specifies a pointer to a XWidgetInfo structure. It
501 % contains the extents of the text.
502 %
503 */
504 static void XDrawMatteText(Display *display,const XWindowInfo *window_info,
505  XWidgetInfo *text_info)
506 {
507  const char
508  *text;
509 
510  int
511  n,
512  x,
513  y;
514 
515  register int
516  i;
517 
518  unsigned int
519  height,
520  width;
521 
522  XFontStruct
523  *font_info;
524 
525  XRectangle
526  crop_info;
527 
528  /*
529  Clear the text area.
530  */
531  XSetMatteColor(display,window_info,MagickFalse);
532  (void) XFillRectangle(display,window_info->id,window_info->widget_context,
533  text_info->x,text_info->y,text_info->width,text_info->height);
534  if (text_info->text == (char *) NULL)
535  return;
536  XSetTextColor(display,window_info,text_info->highlight);
537  font_info=window_info->font_info;
538  x=text_info->x+(QuantumMargin >> 2);
539  y=text_info->y+font_info->ascent+(text_info->height >> 2);
540  width=text_info->width-(QuantumMargin >> 1);
541  height=(unsigned int) (font_info->ascent+font_info->descent);
542  if (*text_info->text == '\0')
543  {
544  /*
545  No text-- just draw cursor.
546  */
547  (void) XDrawLine(display,window_info->id,window_info->annotate_context,
548  x,y+3,x,y-height+3);
549  return;
550  }
551  /*
552  Set cropping region.
553  */
554  crop_info.width=(unsigned short) text_info->width;
555  crop_info.height=(unsigned short) text_info->height;
556  crop_info.x=text_info->x;
557  crop_info.y=text_info->y;
558  /*
559  Determine beginning of the visible text.
560  */
561  if (text_info->cursor < text_info->marker)
562  text_info->marker=text_info->cursor;
563  else
564  {
565  text=text_info->marker;
566  if (XTextWidth(font_info,(char *) text,(int) (text_info->cursor-text)) >
567  (int) width)
568  {
569  text=text_info->text;
570  for (i=0; i < Extent(text); i++)
571  {
572  n=XTextWidth(font_info,(char *) text+i,(int)
573  (text_info->cursor-text-i));
574  if (n <= (int) width)
575  break;
576  }
577  text_info->marker=(char *) text+i;
578  }
579  }
580  /*
581  Draw text and cursor.
582  */
583  if (text_info->highlight == MagickFalse)
584  {
585  (void) XSetClipRectangles(display,window_info->widget_context,0,0,
586  &crop_info,1,Unsorted);
587  (void) XDrawString(display,window_info->id,window_info->widget_context,
588  x,y,text_info->marker,Extent(text_info->marker));
589  (void) XSetClipMask(display,window_info->widget_context,None);
590  }
591  else
592  {
593  (void) XSetClipRectangles(display,window_info->annotate_context,0,0,
594  &crop_info,1,Unsorted);
595  width=WidgetTextWidth(font_info,text_info->marker);
596  (void) XFillRectangle(display,window_info->id,
597  window_info->annotate_context,x,y-font_info->ascent,width,height);
598  (void) XSetClipMask(display,window_info->annotate_context,None);
599  (void) XSetClipRectangles(display,window_info->highlight_context,0,0,
600  &crop_info,1,Unsorted);
601  (void) XDrawString(display,window_info->id,
602  window_info->highlight_context,x,y,text_info->marker,
603  Extent(text_info->marker));
604  (void) XSetClipMask(display,window_info->highlight_context,None);
605  }
606  x+=XTextWidth(font_info,text_info->marker,(int)
607  (text_info->cursor-text_info->marker));
608  (void) XDrawLine(display,window_info->id,window_info->annotate_context,x,y+3,
609  x,y-height+3);
610 }
611 
612 /*
613 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
614 % %
615 % %
616 % %
617 + X D r a w T r i a n g l e E a s t %
618 % %
619 % %
620 % %
621 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
622 %
623 % XDrawTriangleEast() draws a triangle with a highlighted left bevel and a
624 % shadowed right and lower bevel. The highlighted and shadowed bevels create
625 % a 3-D effect.
626 %
627 % The format of the XDrawTriangleEast function is:
628 %
629 % XDrawTriangleEast(display,window_info,triangle_info)
630 %
631 % A description of each parameter follows:
632 %
633 % o display: Specifies a pointer to the Display structure; returned from
634 % XOpenDisplay.
635 %
636 % o window_info: Specifies a pointer to a X11 XWindowInfo structure.
637 %
638 % o triangle_info: Specifies a pointer to a XWidgetInfo structure. It
639 % contains the extents of the triangle.
640 %
641 */
642 static void XDrawTriangleEast(Display *display,const XWindowInfo *window_info,
643  const XWidgetInfo *triangle_info)
644 {
645  int
646  x1,
647  x2,
648  x3,
649  y1,
650  y2,
651  y3;
652 
653  unsigned int
654  bevel_width;
655 
656  XFontStruct
657  *font_info;
658 
659  XPoint
660  points[4];
661 
662  /*
663  Draw triangle matte.
664  */
665  x1=triangle_info->x;
666  y1=triangle_info->y;
667  x2=triangle_info->x+triangle_info->width;
668  y2=triangle_info->y+(triangle_info->height >> 1);
669  x3=triangle_info->x;
670  y3=triangle_info->y+triangle_info->height;
671  bevel_width=triangle_info->bevel_width;
672  points[0].x=x1;
673  points[0].y=y1;
674  points[1].x=x2;
675  points[1].y=y2;
676  points[2].x=x3;
677  points[2].y=y3;
678  XSetMatteColor(display,window_info,triangle_info->raised);
679  (void) XFillPolygon(display,window_info->id,window_info->widget_context,
680  points,3,Complex,CoordModeOrigin);
681  /*
682  Draw bottom bevel.
683  */
684  points[0].x=x2;
685  points[0].y=y2;
686  points[1].x=x3;
687  points[1].y=y3;
688  points[2].x=x3-bevel_width;
689  points[2].y=y3+bevel_width;
690  points[3].x=x2+bevel_width;
691  points[3].y=y2;
692  XSetBevelColor(display,window_info,!triangle_info->raised);
693  (void) XFillPolygon(display,window_info->id,window_info->widget_context,
694  points,4,Complex,CoordModeOrigin);
695  /*
696  Draw Left bevel.
697  */
698  points[0].x=x3;
699  points[0].y=y3;
700  points[1].x=x1;
701  points[1].y=y1;
702  points[2].x=x1-bevel_width+1;
703  points[2].y=y1-bevel_width;
704  points[3].x=x3-bevel_width+1;
705  points[3].y=y3+bevel_width;
706  XSetBevelColor(display,window_info,triangle_info->raised);
707  (void) XFillPolygon(display,window_info->id,window_info->widget_context,
708  points,4,Complex,CoordModeOrigin);
709  /*
710  Draw top bevel.
711  */
712  points[0].x=x1;
713  points[0].y=y1;
714  points[1].x=x2;
715  points[1].y=y2;
716  points[2].x=x2+bevel_width;
717  points[2].y=y2;
718  points[3].x=x1-bevel_width;
719  points[3].y=y1-bevel_width;
720  (void) XFillPolygon(display,window_info->id,window_info->widget_context,
721  points,4,Complex,CoordModeOrigin);
722  (void) XSetFillStyle(display,window_info->widget_context,FillSolid);
723  if (triangle_info->text == (char *) NULL)
724  return;
725  /*
726  Write label to right of triangle.
727  */
728  font_info=window_info->font_info;
729  XSetTextColor(display,window_info,MagickTrue);
730  x1=triangle_info->x+triangle_info->width+triangle_info->bevel_width+
731  (QuantumMargin >> 1);
732  y1=triangle_info->y+((triangle_info->height-
733  (font_info->ascent+font_info->descent)) >> 1)+font_info->ascent;
734  (void) XDrawString(display,window_info->id,window_info->widget_context,x1,y1,
735  triangle_info->text,Extent(triangle_info->text));
736 }
737 
738 /*
739 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
740 % %
741 % %
742 % %
743 + X D r a w T r i a n g l e N o r t h %
744 % %
745 % %
746 % %
747 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
748 %
749 % XDrawTriangleNorth() draws a triangle with a highlighted left bevel and a
750 % shadowed right and lower bevel. The highlighted and shadowed bevels create
751 % a 3-D effect.
752 %
753 % The format of the XDrawTriangleNorth function is:
754 %
755 % XDrawTriangleNorth(display,window_info,triangle_info)
756 %
757 % A description of each parameter follows:
758 %
759 % o display: Specifies a pointer to the Display structure; returned from
760 % XOpenDisplay.
761 %
762 % o window_info: Specifies a pointer to a X11 XWindowInfo structure.
763 %
764 % o triangle_info: Specifies a pointer to a XWidgetInfo structure. It
765 % contains the extents of the triangle.
766 %
767 */
768 static void XDrawTriangleNorth(Display *display,const XWindowInfo *window_info,
769  const XWidgetInfo *triangle_info)
770 {
771  int
772  x1,
773  x2,
774  x3,
775  y1,
776  y2,
777  y3;
778 
779  unsigned int
780  bevel_width;
781 
782  XPoint
783  points[4];
784 
785  /*
786  Draw triangle matte.
787  */
788  x1=triangle_info->x;
789  y1=triangle_info->y+triangle_info->height;
790  x2=triangle_info->x+(triangle_info->width >> 1);
791  y2=triangle_info->y;
792  x3=triangle_info->x+triangle_info->width;
793  y3=triangle_info->y+triangle_info->height;
794  bevel_width=triangle_info->bevel_width;
795  points[0].x=x1;
796  points[0].y=y1;
797  points[1].x=x2;
798  points[1].y=y2;
799  points[2].x=x3;
800  points[2].y=y3;
801  XSetMatteColor(display,window_info,triangle_info->raised);
802  (void) XFillPolygon(display,window_info->id,window_info->widget_context,
803  points,3,Complex,CoordModeOrigin);
804  /*
805  Draw left bevel.
806  */
807  points[0].x=x1;
808  points[0].y=y1;
809  points[1].x=x2;
810  points[1].y=y2;
811  points[2].x=x2;
812  points[2].y=y2-bevel_width-2;
813  points[3].x=x1-bevel_width-1;
814  points[3].y=y1+bevel_width;
815  XSetBevelColor(display,window_info,triangle_info->raised);
816  (void) XFillPolygon(display,window_info->id,window_info->widget_context,
817  points,4,Complex,CoordModeOrigin);
818  /*
819  Draw right bevel.
820  */
821  points[0].x=x2;
822  points[0].y=y2;
823  points[1].x=x3;
824  points[1].y=y3;
825  points[2].x=x3+bevel_width;
826  points[2].y=y3+bevel_width;
827  points[3].x=x2;
828  points[3].y=y2-bevel_width;
829  XSetBevelColor(display,window_info,!triangle_info->raised);
830  (void) XFillPolygon(display,window_info->id,window_info->widget_context,
831  points,4,Complex,CoordModeOrigin);
832  /*
833  Draw lower bevel.
834  */
835  points[0].x=x3;
836  points[0].y=y3;
837  points[1].x=x1;
838  points[1].y=y1;
839  points[2].x=x1-bevel_width;
840  points[2].y=y1+bevel_width;
841  points[3].x=x3+bevel_width;
842  points[3].y=y3+bevel_width;
843  (void) XFillPolygon(display,window_info->id,window_info->widget_context,
844  points,4,Complex,CoordModeOrigin);
845  (void) XSetFillStyle(display,window_info->widget_context,FillSolid);
846 }
847 
848 /*
849 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
850 % %
851 % %
852 % %
853 + X D r a w T r i a n g l e S o u t h %
854 % %
855 % %
856 % %
857 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
858 %
859 % XDrawTriangleSouth() draws a border with a highlighted left and right bevel
860 % and a shadowed lower bevel. The highlighted and shadowed bevels create a
861 % 3-D effect.
862 %
863 % The format of the XDrawTriangleSouth function is:
864 %
865 % XDrawTriangleSouth(display,window_info,triangle_info)
866 %
867 % A description of each parameter follows:
868 %
869 % o display: Specifies a pointer to the Display structure; returned from
870 % XOpenDisplay.
871 %
872 % o window_info: Specifies a pointer to a X11 XWindowInfo structure.
873 %
874 % o triangle_info: Specifies a pointer to a XWidgetInfo structure. It
875 % contains the extents of the triangle.
876 %
877 */
878 static void XDrawTriangleSouth(Display *display,const XWindowInfo *window_info,
879  const XWidgetInfo *triangle_info)
880 {
881  int
882  x1,
883  x2,
884  x3,
885  y1,
886  y2,
887  y3;
888 
889  unsigned int
890  bevel_width;
891 
892  XPoint
893  points[4];
894 
895  /*
896  Draw triangle matte.
897  */
898  x1=triangle_info->x;
899  y1=triangle_info->y;
900  x2=triangle_info->x+(triangle_info->width >> 1);
901  y2=triangle_info->y+triangle_info->height;
902  x3=triangle_info->x+triangle_info->width;
903  y3=triangle_info->y;
904  bevel_width=triangle_info->bevel_width;
905  points[0].x=x1;
906  points[0].y=y1;
907  points[1].x=x2;
908  points[1].y=y2;
909  points[2].x=x3;
910  points[2].y=y3;
911  XSetMatteColor(display,window_info,triangle_info->raised);
912  (void) XFillPolygon(display,window_info->id,window_info->widget_context,
913  points,3,Complex,CoordModeOrigin);
914  /*
915  Draw top bevel.
916  */
917  points[0].x=x3;
918  points[0].y=y3;
919  points[1].x=x1;
920  points[1].y=y1;
921  points[2].x=x1-bevel_width;
922  points[2].y=y1-bevel_width;
923  points[3].x=x3+bevel_width;
924  points[3].y=y3-bevel_width;
925  XSetBevelColor(display,window_info,triangle_info->raised);
926  (void) XFillPolygon(display,window_info->id,window_info->widget_context,
927  points,4,Complex,CoordModeOrigin);
928  /*
929  Draw right bevel.
930  */
931  points[0].x=x2;
932  points[0].y=y2;
933  points[1].x=x3+1;
934  points[1].y=y3-bevel_width;
935  points[2].x=x3+bevel_width;
936  points[2].y=y3-bevel_width;
937  points[3].x=x2;
938  points[3].y=y2+bevel_width;
939  XSetBevelColor(display,window_info,!triangle_info->raised);
940  (void) XFillPolygon(display,window_info->id,window_info->widget_context,
941  points,4,Complex,CoordModeOrigin);
942  /*
943  Draw left bevel.
944  */
945  points[0].x=x1;
946  points[0].y=y1;
947  points[1].x=x2;
948  points[1].y=y2;
949  points[2].x=x2;
950  points[2].y=y2+bevel_width;
951  points[3].x=x1-bevel_width;
952  points[3].y=y1-bevel_width;
953  XSetBevelColor(display,window_info,triangle_info->raised);
954  (void) XFillPolygon(display,window_info->id,window_info->widget_context,
955  points,4,Complex,CoordModeOrigin);
956  (void) XSetFillStyle(display,window_info->widget_context,FillSolid);
957 }
958 
959 /*
960 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
961 % %
962 % %
963 % %
964 + X D r a w W i d g e t T e x t %
965 % %
966 % %
967 % %
968 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
969 %
970 % XDrawWidgetText() first clears the widget and draws a text string justifed
971 % left (or center) in the x-direction and centered within the y-direction.
972 %
973 % The format of the XDrawWidgetText function is:
974 %
975 % XDrawWidgetText(display,window_info,text_info)
976 %
977 % A description of each parameter follows:
978 %
979 % o display: Specifies a pointer to the Display structure; returned from
980 % XOpenDisplay.
981 %
982 % o window_info: Specifies a pointer to a XWindowText structure.
983 %
984 % o text_info: Specifies a pointer to XWidgetInfo structure.
985 %
986 */
987 static void XDrawWidgetText(Display *display,const XWindowInfo *window_info,
988  XWidgetInfo *text_info)
989 {
990  GC
991  widget_context;
992 
993  int
994  x,
995  y;
996 
997  unsigned int
998  height,
999  width;
1000 
1001  XFontStruct
1002  *font_info;
1003 
1004  XRectangle
1005  crop_info;
1006 
1007  /*
1008  Clear the text area.
1009  */
1010  widget_context=window_info->annotate_context;
1011  if (text_info->raised)
1012  (void) XClearArea(display,window_info->id,text_info->x,text_info->y,
1013  text_info->width,text_info->height,MagickFalse);
1014  else
1015  {
1016  (void) XFillRectangle(display,window_info->id,widget_context,text_info->x,
1017  text_info->y,text_info->width,text_info->height);
1018  widget_context=window_info->highlight_context;
1019  }
1020  if (text_info->text == (char *) NULL)
1021  return;
1022  if (*text_info->text == '\0')
1023  return;
1024  /*
1025  Set cropping region.
1026  */
1027  font_info=window_info->font_info;
1028  crop_info.width=(unsigned short) text_info->width;
1029  crop_info.height=(unsigned short) text_info->height;
1030  crop_info.x=text_info->x;
1031  crop_info.y=text_info->y;
1032  /*
1033  Draw text.
1034  */
1035  width=WidgetTextWidth(font_info,text_info->text);
1036  x=text_info->x+(QuantumMargin >> 1);
1037  if (text_info->center)
1038  x=text_info->x+(text_info->width >> 1)-(width >> 1);
1039  if (text_info->raised)
1040  if (width > (text_info->width-QuantumMargin))
1041  x+=(text_info->width-QuantumMargin-width);
1042  height=(unsigned int) (font_info->ascent+font_info->descent);
1043  y=text_info->y+((text_info->height-height) >> 1)+font_info->ascent;
1044  (void) XSetClipRectangles(display,widget_context,0,0,&crop_info,1,Unsorted);
1045  (void) XDrawString(display,window_info->id,widget_context,x,y,text_info->text,
1046  Extent(text_info->text));
1047  (void) XSetClipMask(display,widget_context,None);
1048  if (x < text_info->x)
1049  (void) XDrawLine(display,window_info->id,window_info->annotate_context,
1050  text_info->x,text_info->y,text_info->x,text_info->y+text_info->height-1);
1051 }
1052 
1053 /*
1054 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1055 % %
1056 % %
1057 % %
1058 + X E d i t T e x t %
1059 % %
1060 % %
1061 % %
1062 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1063 %
1064 % XEditText() edits a text string as indicated by the key symbol.
1065 %
1066 % The format of the XEditText function is:
1067 %
1068 % XEditText(display,text_info,key_symbol,text,state)
1069 %
1070 % A description of each parameter follows:
1071 %
1072 % o display: Specifies a connection to an X server; returned from
1073 % XOpenDisplay.
1074 %
1075 % o text_info: Specifies a pointer to a XWidgetInfo structure. It
1076 % contains the extents of the text.
1077 %
1078 % o key_symbol: A X11 KeySym that indicates what editing function to
1079 % perform to the text.
1080 %
1081 % o text: A character string to insert into the text.
1082 %
1083 % o state: An size_t that indicates whether the key symbol is a
1084 % control character or not.
1085 %
1086 */
1087 static void XEditText(Display *display,XWidgetInfo *text_info,
1088  const KeySym key_symbol,char *text,const size_t state)
1089 {
1090  switch ((int) key_symbol)
1091  {
1092  case XK_BackSpace:
1093  case XK_Delete:
1094  {
1095  if (text_info->highlight)
1096  {
1097  /*
1098  Erase the entire line of text.
1099  */
1100  *text_info->text='\0';
1101  text_info->cursor=text_info->text;
1102  text_info->marker=text_info->text;
1103  text_info->highlight=MagickFalse;
1104  }
1105  /*
1106  Erase one character.
1107  */
1108  if (text_info->cursor != text_info->text)
1109  {
1110  text_info->cursor--;
1111  (void) memmove(text_info->cursor,text_info->cursor+1,
1112  strlen(text_info->cursor+1)+1);
1113  text_info->highlight=MagickFalse;
1114  break;
1115  }
1116  }
1117  case XK_Left:
1118  case XK_KP_Left:
1119  {
1120  /*
1121  Move cursor one position left.
1122  */
1123  if (text_info->cursor == text_info->text)
1124  break;
1125  text_info->cursor--;
1126  break;
1127  }
1128  case XK_Right:
1129  case XK_KP_Right:
1130  {
1131  /*
1132  Move cursor one position right.
1133  */
1134  if (text_info->cursor == (text_info->text+Extent(text_info->text)))
1135  break;
1136  text_info->cursor++;
1137  break;
1138  }
1139  default:
1140  {
1141  register char
1142  *p,
1143  *q;
1144 
1145  register int
1146  i;
1147 
1148  if (state & ControlState)
1149  break;
1150  if (*text == '\0')
1151  break;
1152  if ((Extent(text_info->text)+1) >= (int) MagickPathExtent)
1153  (void) XBell(display,0);
1154  else
1155  {
1156  if (text_info->highlight)
1157  {
1158  /*
1159  Erase the entire line of text.
1160  */
1161  *text_info->text='\0';
1162  text_info->cursor=text_info->text;
1163  text_info->marker=text_info->text;
1164  text_info->highlight=MagickFalse;
1165  }
1166  /*
1167  Insert a string into the text.
1168  */
1169  q=text_info->text+Extent(text_info->text)+strlen(text);
1170  for (i=0; i <= Extent(text_info->cursor); i++)
1171  {
1172  *q=(*(q-Extent(text)));
1173  q--;
1174  }
1175  p=text;
1176  for (i=0; i < Extent(text); i++)
1177  *text_info->cursor++=(*p++);
1178  }
1179  break;
1180  }
1181  }
1182 }
1183 
1184 /*
1185 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1186 % %
1187 % %
1188 % %
1189 + X G e t W i d g e t I n f o %
1190 % %
1191 % %
1192 % %
1193 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1194 %
1195 % XGetWidgetInfo() initializes the XWidgetInfo structure.
1196 %
1197 % The format of the XGetWidgetInfo function is:
1198 %
1199 % XGetWidgetInfo(text,widget_info)
1200 %
1201 % A description of each parameter follows:
1202 %
1203 % o text: A string of characters associated with the widget.
1204 %
1205 % o widget_info: Specifies a pointer to a X11 XWidgetInfo structure.
1206 %
1207 */
1208 static void XGetWidgetInfo(const char *text,XWidgetInfo *widget_info)
1209 {
1210  /*
1211  Initialize widget info.
1212  */
1213  widget_info->id=(~0);
1214  widget_info->bevel_width=3;
1215  widget_info->width=1;
1216  widget_info->height=1;
1217  widget_info->x=0;
1218  widget_info->y=0;
1219  widget_info->min_y=0;
1220  widget_info->max_y=0;
1221  widget_info->raised=MagickTrue;
1222  widget_info->active=MagickFalse;
1223  widget_info->center=MagickTrue;
1224  widget_info->trough=MagickFalse;
1225  widget_info->highlight=MagickFalse;
1226  widget_info->text=(char *) text;
1227  widget_info->cursor=(char *) text;
1228  if (text != (char *) NULL)
1229  widget_info->cursor+=Extent(text);
1230  widget_info->marker=(char *) text;
1231 }
1232 
1233 /*
1234 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1235 % %
1236 % %
1237 % %
1238 + X H i g h l i g h t W i d g e t %
1239 % %
1240 % %
1241 % %
1242 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1243 %
1244 % XHighlightWidget() draws a highlighted border around a window.
1245 %
1246 % The format of the XHighlightWidget function is:
1247 %
1248 % XHighlightWidget(display,window_info,x,y)
1249 %
1250 % A description of each parameter follows:
1251 %
1252 % o display: Specifies a pointer to the Display structure; returned from
1253 % XOpenDisplay.
1254 %
1255 % o window_info: Specifies a pointer to a X11 XWindowInfo structure.
1256 %
1257 % o x: Specifies an integer representing the rectangle offset in the
1258 % x-direction.
1259 %
1260 % o y: Specifies an integer representing the rectangle offset in the
1261 % y-direction.
1262 %
1263 */
1264 static void XHighlightWidget(Display *display,const XWindowInfo *window_info,
1265  const int x,const int y)
1266 {
1267  /*
1268  Draw the widget highlighting rectangle.
1269  */
1270  XSetBevelColor(display,window_info,MagickTrue);
1271  (void) XDrawRectangle(display,window_info->id,window_info->widget_context,x,y,
1272  window_info->width-(x << 1),window_info->height-(y << 1));
1273  (void) XDrawRectangle(display,window_info->id,window_info->widget_context,
1274  x-1,y-1,window_info->width-(x << 1)+1,window_info->height-(y << 1)+1);
1275  XSetBevelColor(display,window_info,MagickFalse);
1276  (void) XDrawRectangle(display,window_info->id,window_info->widget_context,
1277  x-1,y-1,window_info->width-(x << 1),window_info->height-(y << 1));
1278  (void) XSetFillStyle(display,window_info->widget_context,FillSolid);
1279 }
1280 
1281 /*
1282 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1283 % %
1284 % %
1285 % %
1286 + X S c r e e n E v e n t %
1287 % %
1288 % %
1289 % %
1290 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1291 %
1292 % XScreenEvent() returns MagickTrue if the any event on the X server queue is
1293 % associated with the widget window.
1294 %
1295 % The format of the XScreenEvent function is:
1296 %
1297 % int XScreenEvent(Display *display,XEvent *event,char *data)
1298 %
1299 % A description of each parameter follows:
1300 %
1301 % o display: Specifies a pointer to the Display structure; returned from
1302 % XOpenDisplay.
1303 %
1304 % o event: Specifies a pointer to a X11 XEvent structure.
1305 %
1306 % o data: Specifies a pointer to a XWindows structure.
1307 %
1308 */
1309 
1310 #if defined(__cplusplus) || defined(c_plusplus)
1311 extern "C" {
1312 #endif
1313 
1314 static int XScreenEvent(Display *display,XEvent *event,char *data)
1315 {
1316  XWindows
1317  *windows;
1318 
1319  windows=(XWindows *) data;
1320  if (event->xany.window == windows->popup.id)
1321  {
1322  if (event->type == MapNotify)
1323  windows->popup.mapped=MagickTrue;
1324  if (event->type == UnmapNotify)
1325  windows->popup.mapped=MagickFalse;
1326  return(MagickTrue);
1327  }
1328  if (event->xany.window == windows->widget.id)
1329  {
1330  if (event->type == MapNotify)
1331  windows->widget.mapped=MagickTrue;
1332  if (event->type == UnmapNotify)
1333  windows->widget.mapped=MagickFalse;
1334  return(MagickTrue);
1335  }
1336  switch (event->type)
1337  {
1338  case ButtonPress:
1339  {
1340  if ((event->xbutton.button == Button3) &&
1341  (event->xbutton.state & Mod1Mask))
1342  {
1343  /*
1344  Convert Alt-Button3 to Button2.
1345  */
1346  event->xbutton.button=Button2;
1347  event->xbutton.state&=(~Mod1Mask);
1348  }
1349  return(MagickTrue);
1350  }
1351  case Expose:
1352  {
1353  if (event->xexpose.window == windows->image.id)
1354  {
1355  XRefreshWindow(display,&windows->image,event);
1356  break;
1357  }
1358  if (event->xexpose.window == windows->magnify.id)
1359  if (event->xexpose.count == 0)
1360  if (windows->magnify.mapped)
1361  {
1363  *exception;
1364 
1365  exception=AcquireExceptionInfo();
1366  XMakeMagnifyImage(display,windows,exception);
1367  exception=DestroyExceptionInfo(exception);
1368  break;
1369  }
1370  if (event->xexpose.window == windows->command.id)
1371  if (event->xexpose.count == 0)
1372  {
1373  (void) XCommandWidget(display,windows,(const char *const *) NULL,
1374  event);
1375  break;
1376  }
1377  break;
1378  }
1379  case FocusOut:
1380  {
1381  /*
1382  Set input focus for backdrop window.
1383  */
1384  if (event->xfocus.window == windows->image.id)
1385  (void) XSetInputFocus(display,windows->image.id,RevertToNone,
1386  CurrentTime);
1387  return(MagickTrue);
1388  }
1389  case ButtonRelease:
1390  case KeyPress:
1391  case KeyRelease:
1392  case MotionNotify:
1393  case SelectionNotify:
1394  return(MagickTrue);
1395  default:
1396  break;
1397  }
1398  return(MagickFalse);
1399 }
1400 
1401 #if defined(__cplusplus) || defined(c_plusplus)
1402 }
1403 #endif
1404 
1405 /*
1406 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1407 % %
1408 % %
1409 % %
1410 + X S e t B e v e l C o l o r %
1411 % %
1412 % %
1413 % %
1414 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1415 %
1416 % XSetBevelColor() sets the graphic context for drawing a beveled border.
1417 %
1418 % The format of the XSetBevelColor function is:
1419 %
1420 % XSetBevelColor(display,window_info,raised)
1421 %
1422 % A description of each parameter follows:
1423 %
1424 % o display: Specifies a pointer to the Display structure; returned from
1425 % XOpenDisplay.
1426 %
1427 % o window_info: Specifies a pointer to a X11 XWindowInfo structure.
1428 %
1429 % o raised: A value other than zero indicates the color show be a
1430 % "highlight" color, otherwise the "shadow" color is set.
1431 %
1432 */
1433 static void XSetBevelColor(Display *display,const XWindowInfo *window_info,
1434  const MagickStatusType raised)
1435 {
1436  if (window_info->depth == 1)
1437  {
1438  Pixmap
1439  stipple;
1440 
1441  /*
1442  Monochrome window.
1443  */
1444  (void) XSetBackground(display,window_info->widget_context,
1445  XBlackPixel(display,window_info->screen));
1446  (void) XSetForeground(display,window_info->widget_context,
1447  XWhitePixel(display,window_info->screen));
1448  (void) XSetFillStyle(display,window_info->widget_context,
1449  FillOpaqueStippled);
1450  stipple=window_info->highlight_stipple;
1451  if (raised == MagickFalse)
1452  stipple=window_info->shadow_stipple;
1453  (void) XSetStipple(display,window_info->widget_context,stipple);
1454  }
1455  else
1456  if (raised)
1457  (void) XSetForeground(display,window_info->widget_context,
1458  window_info->pixel_info->highlight_color.pixel);
1459  else
1460  (void) XSetForeground(display,window_info->widget_context,
1461  window_info->pixel_info->shadow_color.pixel);
1462 }
1463 
1464 /*
1465 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1466 % %
1467 % %
1468 % %
1469 + X S e t M a t t e C o l o r %
1470 % %
1471 % %
1472 % %
1473 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1474 %
1475 % XSetMatteColor() sets the graphic context for drawing the matte.
1476 %
1477 % The format of the XSetMatteColor function is:
1478 %
1479 % XSetMatteColor(display,window_info,raised)
1480 %
1481 % A description of each parameter follows:
1482 %
1483 % o display: Specifies a pointer to the Display structure; returned from
1484 % XOpenDisplay.
1485 %
1486 % o window_info: Specifies a pointer to a X11 XWindowInfo structure.
1487 %
1488 % o raised: A value other than zero indicates the matte is active.
1489 %
1490 */
1491 static void XSetMatteColor(Display *display,const XWindowInfo *window_info,
1492  const MagickStatusType raised)
1493 {
1494  if (window_info->depth == 1)
1495  {
1496  /*
1497  Monochrome window.
1498  */
1499  if (raised)
1500  (void) XSetForeground(display,window_info->widget_context,
1501  XWhitePixel(display,window_info->screen));
1502  else
1503  (void) XSetForeground(display,window_info->widget_context,
1504  XBlackPixel(display,window_info->screen));
1505  }
1506  else
1507  if (raised)
1508  (void) XSetForeground(display,window_info->widget_context,
1509  window_info->pixel_info->matte_color.pixel);
1510  else
1511  (void) XSetForeground(display,window_info->widget_context,
1512  window_info->pixel_info->depth_color.pixel);
1513 }
1514 
1515 /*
1516 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1517 % %
1518 % %
1519 % %
1520 + X S e t T e x t C o l o r %
1521 % %
1522 % %
1523 % %
1524 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1525 %
1526 % XSetTextColor() sets the graphic context for drawing text on a matte.
1527 %
1528 % The format of the XSetTextColor function is:
1529 %
1530 % XSetTextColor(display,window_info,raised)
1531 %
1532 % A description of each parameter follows:
1533 %
1534 % o display: Specifies a pointer to the Display structure; returned from
1535 % XOpenDisplay.
1536 %
1537 % o window_info: Specifies a pointer to a X11 XWindowInfo structure.
1538 %
1539 % o raised: A value other than zero indicates the color show be a
1540 % "highlight" color, otherwise the "shadow" color is set.
1541 %
1542 */
1543 static void XSetTextColor(Display *display,const XWindowInfo *window_info,
1544  const MagickStatusType raised)
1545 {
1546  ssize_t
1547  foreground,
1548  matte;
1549 
1550  if (window_info->depth == 1)
1551  {
1552  /*
1553  Monochrome window.
1554  */
1555  if (raised)
1556  (void) XSetForeground(display,window_info->widget_context,
1557  XBlackPixel(display,window_info->screen));
1558  else
1559  (void) XSetForeground(display,window_info->widget_context,
1560  XWhitePixel(display,window_info->screen));
1561  return;
1562  }
1563  foreground=(ssize_t) XPixelIntensity(
1564  &window_info->pixel_info->foreground_color);
1565  matte=(ssize_t) XPixelIntensity(&window_info->pixel_info->matte_color);
1566  if (MagickAbsoluteValue((int) (foreground-matte)) > (65535L >> 3))
1567  (void) XSetForeground(display,window_info->widget_context,
1568  window_info->pixel_info->foreground_color.pixel);
1569  else
1570  (void) XSetForeground(display,window_info->widget_context,
1571  window_info->pixel_info->background_color.pixel);
1572 }
1573 
1574 /*
1575 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1576 % %
1577 % %
1578 % %
1579 % X C o l o r B r o w s e r W i d g e t %
1580 % %
1581 % %
1582 % %
1583 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1584 %
1585 % XColorBrowserWidget() displays a Color Browser widget with a color query
1586 % to the user. The user keys a reply and presses the Action or Cancel button
1587 % to exit. The typed text is returned as the reply function parameter.
1588 %
1589 % The format of the XColorBrowserWidget method is:
1590 %
1591 % void XColorBrowserWidget(Display *display,XWindows *windows,
1592 % const char *action,char *reply)
1593 %
1594 % A description of each parameter follows:
1595 %
1596 % o display: Specifies a connection to an X server; returned from
1597 % XOpenDisplay.
1598 %
1599 % o window: Specifies a pointer to a XWindows structure.
1600 %
1601 % o action: Specifies a pointer to the action of this widget.
1602 %
1603 % o reply: the response from the user is returned in this parameter.
1604 %
1605 */
1606 MagickPrivate void XColorBrowserWidget(Display *display,XWindows *windows,
1607  const char *action,char *reply)
1608 {
1609 #define CancelButtonText "Cancel"
1610 #define ColornameText "Name:"
1611 #define ColorPatternText "Pattern:"
1612 #define GrabButtonText "Grab"
1613 #define ResetButtonText "Reset"
1614 
1615  char
1616  **colorlist,
1617  primary_selection[MagickPathExtent],
1618  reset_pattern[MagickPathExtent],
1619  text[MagickPathExtent];
1620 
1622  *exception;
1623 
1624  int
1625  x,
1626  y;
1627 
1628  register int
1629  i;
1630 
1631  static char
1632  glob_pattern[MagickPathExtent] = "*";
1633 
1634  static MagickStatusType
1635  mask = (MagickStatusType) (CWWidth | CWHeight | CWX | CWY);
1636 
1637  Status
1638  status;
1639 
1640  unsigned int
1641  height,
1642  text_width,
1643  visible_colors,
1644  width;
1645 
1646  size_t
1647  colors,
1648  delay,
1649  state;
1650 
1651  XColor
1652  color;
1653 
1654  XEvent
1655  event;
1656 
1657  XFontStruct
1658  *font_info;
1659 
1660  XTextProperty
1661  window_name;
1662 
1663  XWidgetInfo
1664  action_info,
1665  cancel_info,
1666  expose_info,
1667  grab_info,
1668  list_info,
1669  mode_info,
1670  north_info,
1671  reply_info,
1672  reset_info,
1673  scroll_info,
1674  selection_info,
1675  slider_info,
1676  south_info,
1677  text_info;
1678 
1679  XWindowChanges
1680  window_changes;
1681 
1682  /*
1683  Get color list and sort in ascending order.
1684  */
1685  assert(display != (Display *) NULL);
1686  assert(windows != (XWindows *) NULL);
1687  assert(action != (char *) NULL);
1688  assert(reply != (char *) NULL);
1689  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",action);
1690  XSetCursorState(display,windows,MagickTrue);
1691  XCheckRefreshWindows(display,windows);
1692  (void) CopyMagickString(reset_pattern,"*",MagickPathExtent);
1693  exception=AcquireExceptionInfo();
1694  colorlist=GetColorList(glob_pattern,&colors,exception);
1695  if (colorlist == (char **) NULL)
1696  {
1697  /*
1698  Pattern failed, obtain all the colors.
1699  */
1700  (void) CopyMagickString(glob_pattern,"*",MagickPathExtent);
1701  colorlist=GetColorList(glob_pattern,&colors,exception);
1702  if (colorlist == (char **) NULL)
1703  {
1704  XNoticeWidget(display,windows,"Unable to obtain colors names:",
1705  glob_pattern);
1706  (void) XDialogWidget(display,windows,action,"Enter color name:",
1707  reply);
1708  return;
1709  }
1710  }
1711  /*
1712  Determine Color Browser widget attributes.
1713  */
1714  font_info=windows->widget.font_info;
1715  text_width=0;
1716  for (i=0; i < (int) colors; i++)
1717  if (WidgetTextWidth(font_info,colorlist[i]) > text_width)
1718  text_width=WidgetTextWidth(font_info,colorlist[i]);
1719  width=WidgetTextWidth(font_info,(char *) action);
1720  if (WidgetTextWidth(font_info,CancelButtonText) > width)
1721  width=WidgetTextWidth(font_info,CancelButtonText);
1722  if (WidgetTextWidth(font_info,ResetButtonText) > width)
1723  width=WidgetTextWidth(font_info,ResetButtonText);
1724  if (WidgetTextWidth(font_info,GrabButtonText) > width)
1725  width=WidgetTextWidth(font_info,GrabButtonText);
1726  width+=QuantumMargin;
1727  if (WidgetTextWidth(font_info,ColorPatternText) > width)
1728  width=WidgetTextWidth(font_info,ColorPatternText);
1729  if (WidgetTextWidth(font_info,ColornameText) > width)
1730  width=WidgetTextWidth(font_info,ColornameText);
1731  height=(unsigned int) (font_info->ascent+font_info->descent);
1732  /*
1733  Position Color Browser widget.
1734  */
1735  windows->widget.width=(unsigned int)
1736  (width+MagickMin((int) text_width,(int) MaxTextWidth)+6*QuantumMargin);
1737  windows->widget.min_width=(unsigned int)
1738  (width+MinTextWidth+4*QuantumMargin);
1739  if (windows->widget.width < windows->widget.min_width)
1740  windows->widget.width=windows->widget.min_width;
1741  windows->widget.height=(unsigned int)
1742  ((81*height) >> 2)+((13*QuantumMargin) >> 1)+4;
1743  windows->widget.min_height=(unsigned int)
1744  (((23*height) >> 1)+((13*QuantumMargin) >> 1)+4);
1745  if (windows->widget.height < windows->widget.min_height)
1746  windows->widget.height=windows->widget.min_height;
1747  XConstrainWindowPosition(display,&windows->widget);
1748  /*
1749  Map Color Browser widget.
1750  */
1751  (void) CopyMagickString(windows->widget.name,"Browse and Select a Color",
1753  status=XStringListToTextProperty(&windows->widget.name,1,&window_name);
1754  if (status != False)
1755  {
1756  XSetWMName(display,windows->widget.id,&window_name);
1757  XSetWMIconName(display,windows->widget.id,&window_name);
1758  (void) XFree((void *) window_name.value);
1759  }
1760  window_changes.width=(int) windows->widget.width;
1761  window_changes.height=(int) windows->widget.height;
1762  window_changes.x=windows->widget.x;
1763  window_changes.y=windows->widget.y;
1764  (void) XReconfigureWMWindow(display,windows->widget.id,windows->widget.screen,
1765  mask,&window_changes);
1766  (void) XMapRaised(display,windows->widget.id);
1767  windows->widget.mapped=MagickFalse;
1768  /*
1769  Respond to X events.
1770  */
1771  XGetWidgetInfo((char *) NULL,&mode_info);
1772  XGetWidgetInfo((char *) NULL,&slider_info);
1773  XGetWidgetInfo((char *) NULL,&north_info);
1774  XGetWidgetInfo((char *) NULL,&south_info);
1775  XGetWidgetInfo((char *) NULL,&expose_info);
1776  XGetWidgetInfo((char *) NULL,&selection_info);
1777  visible_colors=0;
1778  delay=SuspendTime << 2;
1779  state=UpdateConfigurationState;
1780  do
1781  {
1782  if (state & UpdateConfigurationState)
1783  {
1784  int
1785  id;
1786 
1787  /*
1788  Initialize button information.
1789  */
1790  XGetWidgetInfo(CancelButtonText,&cancel_info);
1791  cancel_info.width=width;
1792  cancel_info.height=(unsigned int) ((3*height) >> 1);
1793  cancel_info.x=(int)
1794  (windows->widget.width-cancel_info.width-QuantumMargin-2);
1795  cancel_info.y=(int)
1796  (windows->widget.height-cancel_info.height-QuantumMargin);
1797  XGetWidgetInfo(action,&action_info);
1798  action_info.width=width;
1799  action_info.height=(unsigned int) ((3*height) >> 1);
1800  action_info.x=cancel_info.x-(cancel_info.width+(QuantumMargin >> 1)+
1801  (action_info.bevel_width << 1));
1802  action_info.y=cancel_info.y;
1803  XGetWidgetInfo(GrabButtonText,&grab_info);
1804  grab_info.width=width;
1805  grab_info.height=(unsigned int) ((3*height) >> 1);
1806  grab_info.x=QuantumMargin;
1807  grab_info.y=((5*QuantumMargin) >> 1)+height;
1808  XGetWidgetInfo(ResetButtonText,&reset_info);
1809  reset_info.width=width;
1810  reset_info.height=(unsigned int) ((3*height) >> 1);
1811  reset_info.x=QuantumMargin;
1812  reset_info.y=grab_info.y+grab_info.height+QuantumMargin;
1813  /*
1814  Initialize reply information.
1815  */
1816  XGetWidgetInfo(reply,&reply_info);
1817  reply_info.raised=MagickFalse;
1818  reply_info.bevel_width--;
1819  reply_info.width=windows->widget.width-width-((6*QuantumMargin) >> 1);
1820  reply_info.height=height << 1;
1821  reply_info.x=(int) (width+(QuantumMargin << 1));
1822  reply_info.y=action_info.y-reply_info.height-QuantumMargin;
1823  /*
1824  Initialize mode information.
1825  */
1826  XGetWidgetInfo((char *) NULL,&mode_info);
1827  mode_info.active=MagickTrue;
1828  mode_info.bevel_width=0;
1829  mode_info.width=(unsigned int) (action_info.x-(QuantumMargin << 1));
1830  mode_info.height=action_info.height;
1831  mode_info.x=QuantumMargin;
1832  mode_info.y=action_info.y;
1833  /*
1834  Initialize scroll information.
1835  */
1836  XGetWidgetInfo((char *) NULL,&scroll_info);
1837  scroll_info.bevel_width--;
1838  scroll_info.width=height;
1839  scroll_info.height=(unsigned int) (reply_info.y-grab_info.y-
1840  (QuantumMargin >> 1));
1841  scroll_info.x=reply_info.x+(reply_info.width-scroll_info.width);
1842  scroll_info.y=grab_info.y-reply_info.bevel_width;
1843  scroll_info.raised=MagickFalse;
1844  scroll_info.trough=MagickTrue;
1845  north_info=scroll_info;
1846  north_info.raised=MagickTrue;
1847  north_info.width-=(north_info.bevel_width << 1);
1848  north_info.height=north_info.width-1;
1849  north_info.x+=north_info.bevel_width;
1850  north_info.y+=north_info.bevel_width;
1851  south_info=north_info;
1852  south_info.y=scroll_info.y+scroll_info.height-scroll_info.bevel_width-
1853  south_info.height;
1854  id=slider_info.id;
1855  slider_info=north_info;
1856  slider_info.id=id;
1857  slider_info.width-=2;
1858  slider_info.min_y=north_info.y+north_info.height+north_info.bevel_width+
1859  slider_info.bevel_width+2;
1860  slider_info.height=scroll_info.height-((slider_info.min_y-
1861  scroll_info.y+1) << 1)+4;
1862  visible_colors=scroll_info.height/(height+(height >> 3));
1863  if (colors > visible_colors)
1864  slider_info.height=(unsigned int)
1865  ((visible_colors*slider_info.height)/colors);
1866  slider_info.max_y=south_info.y-south_info.bevel_width-
1867  slider_info.bevel_width-2;
1868  slider_info.x=scroll_info.x+slider_info.bevel_width+1;
1869  slider_info.y=slider_info.min_y;
1870  expose_info=scroll_info;
1871  expose_info.y=slider_info.y;
1872  /*
1873  Initialize list information.
1874  */
1875  XGetWidgetInfo((char *) NULL,&list_info);
1876  list_info.raised=MagickFalse;
1877  list_info.bevel_width--;
1878  list_info.width=(unsigned int)
1879  (scroll_info.x-reply_info.x-(QuantumMargin >> 1));
1880  list_info.height=scroll_info.height;
1881  list_info.x=reply_info.x;
1882  list_info.y=scroll_info.y;
1883  if (windows->widget.mapped == MagickFalse)
1884  state|=JumpListState;
1885  /*
1886  Initialize text information.
1887  */
1888  *text='\0';
1889  XGetWidgetInfo(text,&text_info);
1890  text_info.center=MagickFalse;
1891  text_info.width=reply_info.width;
1892  text_info.height=height;
1893  text_info.x=list_info.x-(QuantumMargin >> 1);
1894  text_info.y=QuantumMargin;
1895  /*
1896  Initialize selection information.
1897  */
1898  XGetWidgetInfo((char *) NULL,&selection_info);
1899  selection_info.center=MagickFalse;
1900  selection_info.width=list_info.width;
1901  selection_info.height=(unsigned int) ((9*height) >> 3);
1902  selection_info.x=list_info.x;
1903  state&=(~UpdateConfigurationState);
1904  }
1905  if (state & RedrawWidgetState)
1906  {
1907  /*
1908  Redraw Color Browser window.
1909  */
1910  x=QuantumMargin;
1911  y=text_info.y+((text_info.height-height) >> 1)+font_info->ascent;
1912  (void) XDrawString(display,windows->widget.id,
1913  windows->widget.annotate_context,x,y,ColorPatternText,
1914  Extent(ColorPatternText));
1915  (void) CopyMagickString(text_info.text,glob_pattern,MagickPathExtent);
1916  XDrawWidgetText(display,&windows->widget,&text_info);
1917  XDrawBeveledButton(display,&windows->widget,&grab_info);
1918  XDrawBeveledButton(display,&windows->widget,&reset_info);
1919  XDrawBeveledMatte(display,&windows->widget,&list_info);
1920  XDrawBeveledMatte(display,&windows->widget,&scroll_info);
1921  XDrawTriangleNorth(display,&windows->widget,&north_info);
1922  XDrawBeveledButton(display,&windows->widget,&slider_info);
1923  XDrawTriangleSouth(display,&windows->widget,&south_info);
1924  x=QuantumMargin;
1925  y=reply_info.y+((reply_info.height-height) >> 1)+font_info->ascent;
1926  (void) XDrawString(display,windows->widget.id,
1927  windows->widget.annotate_context,x,y,ColornameText,
1928  Extent(ColornameText));
1929  XDrawBeveledMatte(display,&windows->widget,&reply_info);
1930  XDrawMatteText(display,&windows->widget,&reply_info);
1931  XDrawBeveledButton(display,&windows->widget,&action_info);
1932  XDrawBeveledButton(display,&windows->widget,&cancel_info);
1933  XHighlightWidget(display,&windows->widget,BorderOffset,BorderOffset);
1934  selection_info.id=(~0);
1935  state|=RedrawActionState;
1936  state|=RedrawListState;
1937  state&=(~RedrawWidgetState);
1938  }
1939  if (state & UpdateListState)
1940  {
1941  char
1942  **checklist;
1943 
1944  size_t
1945  number_colors;
1946 
1947  status=XParseColor(display,windows->widget.map_info->colormap,
1948  glob_pattern,&color);
1949  if ((status != False) || (strchr(glob_pattern,'-') != (char *) NULL))
1950  {
1951  /*
1952  Reply is a single color name-- exit.
1953  */
1954  (void) CopyMagickString(reply,glob_pattern,MagickPathExtent);
1955  (void) CopyMagickString(glob_pattern,reset_pattern,MagickPathExtent);
1956  action_info.raised=MagickFalse;
1957  XDrawBeveledButton(display,&windows->widget,&action_info);
1958  break;
1959  }
1960  /*
1961  Update color list.
1962  */
1963  checklist=GetColorList(glob_pattern,&number_colors,exception);
1964  if (number_colors == 0)
1965  {
1966  (void) CopyMagickString(glob_pattern,reset_pattern,MagickPathExtent);
1967  (void) XBell(display,0);
1968  }
1969  else
1970  {
1971  for (i=0; i < (int) colors; i++)
1972  colorlist[i]=DestroyString(colorlist[i]);
1973  if (colorlist != (char **) NULL)
1974  colorlist=(char **) RelinquishMagickMemory(colorlist);
1975  colorlist=checklist;
1976  colors=number_colors;
1977  }
1978  /*
1979  Sort color list in ascending order.
1980  */
1981  slider_info.height=
1982  scroll_info.height-((slider_info.min_y-scroll_info.y+1) << 1)+1;
1983  if (colors > visible_colors)
1984  slider_info.height=(unsigned int)
1985  ((visible_colors*slider_info.height)/colors);
1986  slider_info.max_y=south_info.y-south_info.bevel_width-
1987  slider_info.bevel_width-2;
1988  slider_info.id=0;
1989  slider_info.y=slider_info.min_y;
1990  expose_info.y=slider_info.y;
1991  selection_info.id=(~0);
1992  list_info.id=(~0);
1993  state|=RedrawListState;
1994  /*
1995  Redraw color name & reply.
1996  */
1997  *reply_info.text='\0';
1998  reply_info.cursor=reply_info.text;
1999  (void) CopyMagickString(text_info.text,glob_pattern,MagickPathExtent);
2000  XDrawWidgetText(display,&windows->widget,&text_info);
2001  XDrawMatteText(display,&windows->widget,&reply_info);
2002  XDrawBeveledMatte(display,&windows->widget,&scroll_info);
2003  XDrawTriangleNorth(display,&windows->widget,&north_info);
2004  XDrawBeveledButton(display,&windows->widget,&slider_info);
2005  XDrawTriangleSouth(display,&windows->widget,&south_info);
2006  XHighlightWidget(display,&windows->widget,BorderOffset,BorderOffset);
2007  state&=(~UpdateListState);
2008  }
2009  if (state & JumpListState)
2010  {
2011  /*
2012  Jump scroll to match user color.
2013  */
2014  list_info.id=(~0);
2015  for (i=0; i < (int) colors; i++)
2016  if (LocaleCompare(colorlist[i],reply) >= 0)
2017  {
2018  list_info.id=LocaleCompare(colorlist[i],reply) == 0 ? i : ~0;
2019  break;
2020  }
2021  if ((i < slider_info.id) ||
2022  (i >= (int) (slider_info.id+visible_colors)))
2023  slider_info.id=i-(visible_colors >> 1);
2024  selection_info.id=(~0);
2025  state|=RedrawListState;
2026  state&=(~JumpListState);
2027  }
2028  if (state & RedrawListState)
2029  {
2030  /*
2031  Determine slider id and position.
2032  */
2033  if (slider_info.id >= (int) (colors-visible_colors))
2034  slider_info.id=(int) (colors-visible_colors);
2035  if ((slider_info.id < 0) || (colors <= visible_colors))
2036  slider_info.id=0;
2037  slider_info.y=slider_info.min_y;
2038  if (colors != 0)
2039  slider_info.y+=((ssize_t) slider_info.id*(slider_info.max_y-
2040  slider_info.min_y+1)/colors);
2041  if (slider_info.id != selection_info.id)
2042  {
2043  /*
2044  Redraw scroll bar and file names.
2045  */
2046  selection_info.id=slider_info.id;
2047  selection_info.y=list_info.y+(height >> 3)+2;
2048  for (i=0; i < (int) visible_colors; i++)
2049  {
2050  selection_info.raised=(slider_info.id+i) != list_info.id ?
2052  selection_info.text=(char *) NULL;
2053  if ((slider_info.id+i) < (int) colors)
2054  selection_info.text=colorlist[slider_info.id+i];
2055  XDrawWidgetText(display,&windows->widget,&selection_info);
2056  selection_info.y+=(int) selection_info.height;
2057  }
2058  /*
2059  Update slider.
2060  */
2061  if (slider_info.y > expose_info.y)
2062  {
2063  expose_info.height=(unsigned int) slider_info.y-expose_info.y;
2064  expose_info.y=slider_info.y-expose_info.height-
2065  slider_info.bevel_width-1;
2066  }
2067  else
2068  {
2069  expose_info.height=(unsigned int) expose_info.y-slider_info.y;
2070  expose_info.y=slider_info.y+slider_info.height+
2071  slider_info.bevel_width+1;
2072  }
2073  XDrawTriangleNorth(display,&windows->widget,&north_info);
2074  XDrawMatte(display,&windows->widget,&expose_info);
2075  XDrawBeveledButton(display,&windows->widget,&slider_info);
2076  XDrawTriangleSouth(display,&windows->widget,&south_info);
2077  expose_info.y=slider_info.y;
2078  }
2079  state&=(~RedrawListState);
2080  }
2081  if (state & RedrawActionState)
2082  {
2083  static char
2084  colorname[MagickPathExtent];
2085 
2086  /*
2087  Display the selected color in a drawing area.
2088  */
2089  color=windows->widget.pixel_info->matte_color;
2090  (void) XParseColor(display,windows->widget.map_info->colormap,
2091  reply_info.text,&windows->widget.pixel_info->matte_color);
2092  XBestPixel(display,windows->widget.map_info->colormap,(XColor *) NULL,
2093  (unsigned int) windows->widget.visual_info->colormap_size,
2094  &windows->widget.pixel_info->matte_color);
2095  mode_info.text=colorname;
2096  (void) FormatLocaleString(mode_info.text,MagickPathExtent,
2097  "#%02x%02x%02x",windows->widget.pixel_info->matte_color.red,
2098  windows->widget.pixel_info->matte_color.green,
2099  windows->widget.pixel_info->matte_color.blue);
2100  XDrawBeveledButton(display,&windows->widget,&mode_info);
2101  windows->widget.pixel_info->matte_color=color;
2102  state&=(~RedrawActionState);
2103  }
2104  /*
2105  Wait for next event.
2106  */
2107  if (north_info.raised && south_info.raised)
2108  (void) XIfEvent(display,&event,XScreenEvent,(char *) windows);
2109  else
2110  {
2111  /*
2112  Brief delay before advancing scroll bar.
2113  */
2114  XDelay(display,delay);
2115  delay=SuspendTime;
2116  (void) XCheckIfEvent(display,&event,XScreenEvent,(char *) windows);
2117  if (north_info.raised == MagickFalse)
2118  if (slider_info.id > 0)
2119  {
2120  /*
2121  Move slider up.
2122  */
2123  slider_info.id--;
2124  state|=RedrawListState;
2125  }
2126  if (south_info.raised == MagickFalse)
2127  if (slider_info.id < (int) colors)
2128  {
2129  /*
2130  Move slider down.
2131  */
2132  slider_info.id++;
2133  state|=RedrawListState;
2134  }
2135  if (event.type != ButtonRelease)
2136  continue;
2137  }
2138  switch (event.type)
2139  {
2140  case ButtonPress:
2141  {
2142  if (MatteIsActive(slider_info,event.xbutton))
2143  {
2144  /*
2145  Track slider.
2146  */
2147  slider_info.active=MagickTrue;
2148  break;
2149  }
2150  if (MatteIsActive(north_info,event.xbutton))
2151  if (slider_info.id > 0)
2152  {
2153  /*
2154  Move slider up.
2155  */
2156  north_info.raised=MagickFalse;
2157  slider_info.id--;
2158  state|=RedrawListState;
2159  break;
2160  }
2161  if (MatteIsActive(south_info,event.xbutton))
2162  if (slider_info.id < (int) colors)
2163  {
2164  /*
2165  Move slider down.
2166  */
2167  south_info.raised=MagickFalse;
2168  slider_info.id++;
2169  state|=RedrawListState;
2170  break;
2171  }
2172  if (MatteIsActive(scroll_info,event.xbutton))
2173  {
2174  /*
2175  Move slider.
2176  */
2177  if (event.xbutton.y < slider_info.y)
2178  slider_info.id-=(visible_colors-1);
2179  else
2180  slider_info.id+=(visible_colors-1);
2181  state|=RedrawListState;
2182  break;
2183  }
2184  if (MatteIsActive(list_info,event.xbutton))
2185  {
2186  int
2187  id;
2188 
2189  /*
2190  User pressed list matte.
2191  */
2192  id=slider_info.id+(event.xbutton.y-(list_info.y+(height >> 1))+1)/
2193  selection_info.height;
2194  if (id >= (int) colors)
2195  break;
2196  (void) CopyMagickString(reply_info.text,colorlist[id],
2198  reply_info.highlight=MagickFalse;
2199  reply_info.marker=reply_info.text;
2200  reply_info.cursor=reply_info.text+Extent(reply_info.text);
2201  XDrawMatteText(display,&windows->widget,&reply_info);
2202  state|=RedrawActionState;
2203  if (id == list_info.id)
2204  {
2205  (void) CopyMagickString(glob_pattern,reply_info.text,
2207  state|=UpdateListState;
2208  }
2209  selection_info.id=(~0);
2210  list_info.id=id;
2211  state|=RedrawListState;
2212  break;
2213  }
2214  if (MatteIsActive(grab_info,event.xbutton))
2215  {
2216  /*
2217  User pressed Grab button.
2218  */
2219  grab_info.raised=MagickFalse;
2220  XDrawBeveledButton(display,&windows->widget,&grab_info);
2221  break;
2222  }
2223  if (MatteIsActive(reset_info,event.xbutton))
2224  {
2225  /*
2226  User pressed Reset button.
2227  */
2228  reset_info.raised=MagickFalse;
2229  XDrawBeveledButton(display,&windows->widget,&reset_info);
2230  break;
2231  }
2232  if (MatteIsActive(mode_info,event.xbutton))
2233  {
2234  /*
2235  User pressed mode button.
2236  */
2237  if (mode_info.text != (char *) NULL)
2238  (void) CopyMagickString(reply_info.text,mode_info.text,
2240  (void) CopyMagickString(primary_selection,reply_info.text,
2242  (void) XSetSelectionOwner(display,XA_PRIMARY,windows->widget.id,
2243  event.xbutton.time);
2244  reply_info.highlight=XGetSelectionOwner(display,XA_PRIMARY) ==
2245  windows->widget.id ? MagickTrue : MagickFalse;
2246  reply_info.marker=reply_info.text;
2247  reply_info.cursor=reply_info.text+Extent(reply_info.text);
2248  XDrawMatteText(display,&windows->widget,&reply_info);
2249  break;
2250  }
2251  if (MatteIsActive(action_info,event.xbutton))
2252  {
2253  /*
2254  User pressed action button.
2255  */
2256  action_info.raised=MagickFalse;
2257  XDrawBeveledButton(display,&windows->widget,&action_info);
2258  break;
2259  }
2260  if (MatteIsActive(cancel_info,event.xbutton))
2261  {
2262  /*
2263  User pressed Cancel button.
2264  */
2265  cancel_info.raised=MagickFalse;
2266  XDrawBeveledButton(display,&windows->widget,&cancel_info);
2267  break;
2268  }
2269  if (MatteIsActive(reply_info,event.xbutton) == MagickFalse)
2270  break;
2271  if (event.xbutton.button != Button2)
2272  {
2273  static Time
2274  click_time;
2275 
2276  /*
2277  Move text cursor to position of button press.
2278  */
2279  x=event.xbutton.x-reply_info.x-(QuantumMargin >> 2);
2280  for (i=1; i <= Extent(reply_info.marker); i++)
2281  if (XTextWidth(font_info,reply_info.marker,i) > x)
2282  break;
2283  reply_info.cursor=reply_info.marker+i-1;
2284  if (event.xbutton.time > (click_time+DoubleClick))
2285  reply_info.highlight=MagickFalse;
2286  else
2287  {
2288  /*
2289  Become the XA_PRIMARY selection owner.
2290  */
2291  (void) CopyMagickString(primary_selection,reply_info.text,
2293  (void) XSetSelectionOwner(display,XA_PRIMARY,windows->widget.id,
2294  event.xbutton.time);
2295  reply_info.highlight=XGetSelectionOwner(display,XA_PRIMARY) ==
2296  windows->widget.id ? MagickTrue : MagickFalse;
2297  }
2298  XDrawMatteText(display,&windows->widget,&reply_info);
2299  click_time=event.xbutton.time;
2300  break;
2301  }
2302  /*
2303  Request primary selection.
2304  */
2305  (void) XConvertSelection(display,XA_PRIMARY,XA_STRING,XA_STRING,
2306  windows->widget.id,event.xbutton.time);
2307  break;
2308  }
2309  case ButtonRelease:
2310  {
2311  if (windows->widget.mapped == MagickFalse)
2312  break;
2313  if (north_info.raised == MagickFalse)
2314  {
2315  /*
2316  User released up button.
2317  */
2318  delay=SuspendTime << 2;
2319  north_info.raised=MagickTrue;
2320  XDrawTriangleNorth(display,&windows->widget,&north_info);
2321  }
2322  if (south_info.raised == MagickFalse)
2323  {
2324  /*
2325  User released down button.
2326  */
2327  delay=SuspendTime << 2;
2328  south_info.raised=MagickTrue;
2329  XDrawTriangleSouth(display,&windows->widget,&south_info);
2330  }
2331  if (slider_info.active)
2332  {
2333  /*
2334  Stop tracking slider.
2335  */
2336  slider_info.active=MagickFalse;
2337  break;
2338  }
2339  if (grab_info.raised == MagickFalse)
2340  {
2341  if (event.xbutton.window == windows->widget.id)
2342  if (MatteIsActive(grab_info,event.xbutton))
2343  {
2344  /*
2345  Select a fill color from the X server.
2346  */
2347  (void) XGetWindowColor(display,windows,reply_info.text,
2348  exception);
2349  reply_info.marker=reply_info.text;
2350  reply_info.cursor=reply_info.text+Extent(reply_info.text);
2351  XDrawMatteText(display,&windows->widget,&reply_info);
2352  state|=RedrawActionState;
2353  }
2354  grab_info.raised=MagickTrue;
2355  XDrawBeveledButton(display,&windows->widget,&grab_info);
2356  }
2357  if (reset_info.raised == MagickFalse)
2358  {
2359  if (event.xbutton.window == windows->widget.id)
2360  if (MatteIsActive(reset_info,event.xbutton))
2361  {
2362  (void) CopyMagickString(glob_pattern,reset_pattern,
2364  state|=UpdateListState;
2365  }
2366  reset_info.raised=MagickTrue;
2367  XDrawBeveledButton(display,&windows->widget,&reset_info);
2368  }
2369  if (action_info.raised == MagickFalse)
2370  {
2371  if (event.xbutton.window == windows->widget.id)
2372  {
2373  if (MatteIsActive(action_info,event.xbutton))
2374  {
2375  if (*reply_info.text == '\0')
2376  (void) XBell(display,0);
2377  else
2378  state|=ExitState;
2379  }
2380  }
2381  action_info.raised=MagickTrue;
2382  XDrawBeveledButton(display,&windows->widget,&action_info);
2383  }
2384  if (cancel_info.raised == MagickFalse)
2385  {
2386  if (event.xbutton.window == windows->widget.id)
2387  if (MatteIsActive(cancel_info,event.xbutton))
2388  {
2389  *reply_info.text='\0';
2390  state|=ExitState;
2391  }
2392  cancel_info.raised=MagickTrue;
2393  XDrawBeveledButton(display,&windows->widget,&cancel_info);
2394  }
2395  if (MatteIsActive(reply_info,event.xbutton) == MagickFalse)
2396  break;
2397  break;
2398  }
2399  case ClientMessage:
2400  {
2401  /*
2402  If client window delete message, exit.
2403  */
2404  if (event.xclient.message_type != windows->wm_protocols)
2405  break;
2406  if (*event.xclient.data.l == (int) windows->wm_take_focus)
2407  {
2408  (void) XSetInputFocus(display,event.xclient.window,RevertToParent,
2409  (Time) event.xclient.data.l[1]);
2410  break;
2411  }
2412  if (*event.xclient.data.l != (int) windows->wm_delete_window)
2413  break;
2414  if (event.xclient.window == windows->widget.id)
2415  {
2416  *reply_info.text='\0';
2417  state|=ExitState;
2418  break;
2419  }
2420  break;
2421  }
2422  case ConfigureNotify:
2423  {
2424  /*
2425  Update widget configuration.
2426  */
2427  if (event.xconfigure.window != windows->widget.id)
2428  break;
2429  if ((event.xconfigure.width == (int) windows->widget.width) &&
2430  (event.xconfigure.height == (int) windows->widget.height))
2431  break;
2432  windows->widget.width=(unsigned int)
2433  MagickMax(event.xconfigure.width,(int) windows->widget.min_width);
2434  windows->widget.height=(unsigned int)
2435  MagickMax(event.xconfigure.height,(int) windows->widget.min_height);
2436  state|=UpdateConfigurationState;
2437  break;
2438  }
2439  case EnterNotify:
2440  {
2441  if (event.xcrossing.window != windows->widget.id)
2442  break;
2443  state&=(~InactiveWidgetState);
2444  break;
2445  }
2446  case Expose:
2447  {
2448  if (event.xexpose.window != windows->widget.id)
2449  break;
2450  if (event.xexpose.count != 0)
2451  break;
2452  state|=RedrawWidgetState;
2453  break;
2454  }
2455  case KeyPress:
2456  {
2457  static char
2458  command[MagickPathExtent];
2459 
2460  static int
2461  length;
2462 
2463  static KeySym
2464  key_symbol;
2465 
2466  /*
2467  Respond to a user key press.
2468  */
2469  if (event.xkey.window != windows->widget.id)
2470  break;
2471  length=XLookupString((XKeyEvent *) &event.xkey,command,
2472  (int) sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2473  *(command+length)='\0';
2474  if (AreaIsActive(scroll_info,event.xkey))
2475  {
2476  /*
2477  Move slider.
2478  */
2479  switch ((int) key_symbol)
2480  {
2481  case XK_Home:
2482  case XK_KP_Home:
2483  {
2484  slider_info.id=0;
2485  break;
2486  }
2487  case XK_Up:
2488  case XK_KP_Up:
2489  {
2490  slider_info.id--;
2491  break;
2492  }
2493  case XK_Down:
2494  case XK_KP_Down:
2495  {
2496  slider_info.id++;
2497  break;
2498  }
2499  case XK_Prior:
2500  case XK_KP_Prior:
2501  {
2502  slider_info.id-=visible_colors;
2503  break;
2504  }
2505  case XK_Next:
2506  case XK_KP_Next:
2507  {
2508  slider_info.id+=visible_colors;
2509  break;
2510  }
2511  case XK_End:
2512  case XK_KP_End:
2513  {
2514  slider_info.id=(int) colors;
2515  break;
2516  }
2517  }
2518  state|=RedrawListState;
2519  break;
2520  }
2521  if ((key_symbol == XK_Return) || (key_symbol == XK_KP_Enter))
2522  {
2523  /*
2524  Read new color or glob patterm.
2525  */
2526  if (*reply_info.text == '\0')
2527  break;
2528  (void) CopyMagickString(glob_pattern,reply_info.text,MagickPathExtent);
2529  state|=UpdateListState;
2530  break;
2531  }
2532  if (key_symbol == XK_Control_L)
2533  {
2534  state|=ControlState;
2535  break;
2536  }
2537  if (state & ControlState)
2538  switch ((int) key_symbol)
2539  {
2540  case XK_u:
2541  case XK_U:
2542  {
2543  /*
2544  Erase the entire line of text.
2545  */
2546  *reply_info.text='\0';
2547  reply_info.cursor=reply_info.text;
2548  reply_info.marker=reply_info.text;
2549  reply_info.highlight=MagickFalse;
2550  break;
2551  }
2552  default:
2553  break;
2554  }
2555  XEditText(display,&reply_info,key_symbol,command,state);
2556  XDrawMatteText(display,&windows->widget,&reply_info);
2557  state|=JumpListState;
2558  status=XParseColor(display,windows->widget.map_info->colormap,
2559  reply_info.text,&color);
2560  if (status != False)
2561  state|=RedrawActionState;
2562  break;
2563  }
2564  case KeyRelease:
2565  {
2566  static char
2567  command[MagickPathExtent];
2568 
2569  static KeySym
2570  key_symbol;
2571 
2572  /*
2573  Respond to a user key release.
2574  */
2575  if (event.xkey.window != windows->widget.id)
2576  break;
2577  (void) XLookupString((XKeyEvent *) &event.xkey,command,
2578  (int) sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2579  if (key_symbol == XK_Control_L)
2580  state&=(~ControlState);
2581  break;
2582  }
2583  case LeaveNotify:
2584  {
2585  if (event.xcrossing.window != windows->widget.id)
2586  break;
2587  state|=InactiveWidgetState;
2588  break;
2589  }
2590  case MapNotify:
2591  {
2592  mask&=(~CWX);
2593  mask&=(~CWY);
2594  break;
2595  }
2596  case MotionNotify:
2597  {
2598  /*
2599  Discard pending button motion events.
2600  */
2601  while (XCheckMaskEvent(display,ButtonMotionMask,&event)) ;
2602  if (slider_info.active)
2603  {
2604  /*
2605  Move slider matte.
2606  */
2607  slider_info.y=event.xmotion.y-
2608  ((slider_info.height+slider_info.bevel_width) >> 1)+1;
2609  if (slider_info.y < slider_info.min_y)
2610  slider_info.y=slider_info.min_y;
2611  if (slider_info.y > slider_info.max_y)
2612  slider_info.y=slider_info.max_y;
2613  slider_info.id=0;
2614  if (slider_info.y != slider_info.min_y)
2615  slider_info.id=(int) ((colors*(slider_info.y-
2616  slider_info.min_y+1))/(slider_info.max_y-slider_info.min_y+1));
2617  state|=RedrawListState;
2618  break;
2619  }
2620  if (state & InactiveWidgetState)
2621  break;
2622  if (grab_info.raised == MatteIsActive(grab_info,event.xmotion))
2623  {
2624  /*
2625  Grab button status changed.
2626  */
2627  grab_info.raised=!grab_info.raised;
2628  XDrawBeveledButton(display,&windows->widget,&grab_info);
2629  break;
2630  }
2631  if (reset_info.raised == MatteIsActive(reset_info,event.xmotion))
2632  {
2633  /*
2634  Reset button status changed.
2635  */
2636  reset_info.raised=!reset_info.raised;
2637  XDrawBeveledButton(display,&windows->widget,&reset_info);
2638  break;
2639  }
2640  if (action_info.raised == MatteIsActive(action_info,event.xmotion))
2641  {
2642  /*
2643  Action button status changed.
2644  */
2645  action_info.raised=action_info.raised == MagickFalse ?
2647  XDrawBeveledButton(display,&windows->widget,&action_info);
2648  break;
2649  }
2650  if (cancel_info.raised == MatteIsActive(cancel_info,event.xmotion))
2651  {
2652  /*
2653  Cancel button status changed.
2654  */
2655  cancel_info.raised=cancel_info.raised == MagickFalse ?
2657  XDrawBeveledButton(display,&windows->widget,&cancel_info);
2658  break;
2659  }
2660  break;
2661  }
2662  case SelectionClear:
2663  {
2664  reply_info.highlight=MagickFalse;
2665  XDrawMatteText(display,&windows->widget,&reply_info);
2666  break;
2667  }
2668  case SelectionNotify:
2669  {
2670  Atom
2671  type;
2672 
2673  int
2674  format;
2675 
2676  unsigned char
2677  *data;
2678 
2679  unsigned long
2680  after,
2681  length;
2682 
2683  /*
2684  Obtain response from primary selection.
2685  */
2686  if (event.xselection.property == (Atom) None)
2687  break;
2688  status=XGetWindowProperty(display,event.xselection.requestor,
2689  event.xselection.property,0L,2047L,MagickTrue,XA_STRING,&type,
2690  &format,&length,&after,&data);
2691  if ((status != Success) || (type != XA_STRING) || (format == 32) ||
2692  (length == 0))
2693  break;
2694  if ((Extent(reply_info.text)+length) >= (MagickPathExtent-1))
2695  (void) XBell(display,0);
2696  else
2697  {
2698  /*
2699  Insert primary selection in reply text.
2700  */
2701  *(data+length)='\0';
2702  XEditText(display,&reply_info,(KeySym) XK_Insert,(char *) data,
2703  state);
2704  XDrawMatteText(display,&windows->widget,&reply_info);
2705  state|=JumpListState;
2706  state|=RedrawActionState;
2707  }
2708  (void) XFree((void *) data);
2709  break;
2710  }
2711  case SelectionRequest:
2712  {
2713  XSelectionEvent
2714  notify;
2715 
2716  XSelectionRequestEvent
2717  *request;
2718 
2719  if (reply_info.highlight == MagickFalse)
2720  break;
2721  /*
2722  Set primary selection.
2723  */
2724  request=(&(event.xselectionrequest));
2725  (void) XChangeProperty(request->display,request->requestor,
2726  request->property,request->target,8,PropModeReplace,
2727  (unsigned char *) primary_selection,Extent(primary_selection));
2728  notify.type=SelectionNotify;
2729  notify.send_event=MagickTrue;
2730  notify.display=request->display;
2731  notify.requestor=request->requestor;
2732  notify.selection=request->selection;
2733  notify.target=request->target;
2734  notify.time=request->time;
2735  if (request->property == None)
2736  notify.property=request->target;
2737  else
2738  notify.property=request->property;
2739  (void) XSendEvent(request->display,request->requestor,False,
2740  NoEventMask,(XEvent *) &notify);
2741  }
2742  default:
2743  break;
2744  }
2745  } while ((state & ExitState) == 0);
2746  XSetCursorState(display,windows,MagickFalse);
2747  (void) XWithdrawWindow(display,windows->widget.id,windows->widget.screen);
2748  XCheckRefreshWindows(display,windows);
2749  /*
2750  Free color list.
2751  */
2752  for (i=0; i < (int) colors; i++)
2753  colorlist[i]=DestroyString(colorlist[i]);
2754  if (colorlist != (char **) NULL)
2755  colorlist=(char **) RelinquishMagickMemory(colorlist);
2756  exception=DestroyExceptionInfo(exception);
2757  if ((*reply == '\0') || (strchr(reply,'-') != (char *) NULL))
2758  return;
2759  status=XParseColor(display,windows->widget.map_info->colormap,reply,&color);
2760  if (status != False)
2761  return;
2762  XNoticeWidget(display,windows,"Color is unknown to X server:",reply);
2763  (void) CopyMagickString(reply,"gray",MagickPathExtent);
2764 }
2765 
2766 /*
2767 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2768 % %
2769 % %
2770 % %
2771 % X C o m m a n d W i d g e t %
2772 % %
2773 % %
2774 % %
2775 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2776 %
2777 % XCommandWidget() maps a menu and returns the command pointed to by the user
2778 % when the button is released.
2779 %
2780 % The format of the XCommandWidget method is:
2781 %
2782 % int XCommandWidget(Display *display,XWindows *windows,
2783 % const char *const *selections,XEvent *event)
2784 %
2785 % A description of each parameter follows:
2786 %
2787 % o selection_number: Specifies the number of the selection that the
2788 % user choose.
2789 %
2790 % o display: Specifies a connection to an X server; returned from
2791 % XOpenDisplay.
2792 %
2793 % o window: Specifies a pointer to a XWindows structure.
2794 %
2795 % o selections: Specifies a pointer to one or more strings that comprise
2796 % the choices in the menu.
2797 %
2798 % o event: Specifies a pointer to a X11 XEvent structure.
2799 %
2800 */
2801 MagickPrivate int XCommandWidget(Display *display,XWindows *windows,
2802  const char *const *selections,XEvent *event)
2803 {
2804 #define tile_width 112
2805 #define tile_height 70
2806 
2807  static const unsigned char
2808  tile_bits[]=
2809  {
2810  0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2811  0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2812  0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2813  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00,
2814  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00,
2815  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00,
2816  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2817  0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2818  0x00, 0x00, 0x1e, 0x38, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2819  0x00, 0x00, 0x00, 0x00, 0x1e, 0xbc, 0x9f, 0x03, 0x00, 0x3e, 0x00, 0xc0,
2820  0x1f, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x1e, 0xfc, 0xff, 0x0f, 0x80, 0x3f,
2821  0x00, 0xf0, 0x1f, 0xc0, 0x0f, 0x00, 0x00, 0x00, 0x1e, 0xfc, 0xff, 0x1f,
2822  0xe0, 0x3f, 0x00, 0xfc, 0x1f, 0xf0, 0x0f, 0x00, 0x00, 0x00, 0x1e, 0xfc,
2823  0xff, 0x1f, 0xf0, 0x3f, 0x00, 0xfe, 0x1f, 0xf8, 0x0f, 0x00, 0x00, 0x00,
2824  0x1e, 0xfc, 0xfc, 0x3f, 0xf8, 0x3f, 0x00, 0xff, 0x1e, 0xfc, 0x0f, 0x00,
2825  0x00, 0x00, 0x1e, 0x7c, 0xfc, 0x3e, 0xf8, 0x3c, 0x80, 0x1f, 0x1e, 0x7c,
2826  0x0f, 0x00, 0x00, 0x00, 0x1e, 0x78, 0x78, 0x3c, 0x7c, 0x3c, 0xc0, 0x0f,
2827  0x1e, 0x3e, 0x0f, 0x00, 0x00, 0x00, 0x1e, 0x78, 0x78, 0x3c, 0x7c, 0x3c,
2828  0xc0, 0x07, 0x1e, 0x3e, 0x0f, 0x00, 0x00, 0x00, 0x1e, 0x78, 0x78, 0x3c,
2829  0x7c, 0x7c, 0xc0, 0x0f, 0x1e, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x78,
2830  0x78, 0x3c, 0xfc, 0x7c, 0x80, 0x7f, 0x1e, 0x7c, 0x00, 0x00, 0x00, 0x00,
2831  0x1e, 0xf8, 0x78, 0x7c, 0xf8, 0xff, 0x00, 0xff, 0x1f, 0xf8, 0xff, 0x00,
2832  0x00, 0x00, 0x1e, 0xf8, 0x78, 0x7c, 0xf0, 0xff, 0x07, 0xfe, 0x1f, 0xf8,
2833  0xff, 0x00, 0x00, 0x00, 0x1e, 0xf8, 0x78, 0x7c, 0xf0, 0xff, 0x07, 0xf8,
2834  0x1f, 0xf0, 0xff, 0x01, 0x00, 0x00, 0x1e, 0xf8, 0x78, 0x7c, 0xc0, 0xef,
2835  0x07, 0xe0, 0x1f, 0xc0, 0xff, 0x01, 0x00, 0x00, 0x1e, 0x70, 0x40, 0x78,
2836  0x00, 0xc7, 0x07, 0x00, 0x1e, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x1e, 0x00,
2837  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00,
2838  0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00,
2839  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00,
2840  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
2841  0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2842  0x00, 0xc0, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2843  0x00, 0x00, 0x00, 0xe0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2844  0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
2845  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x01, 0x00, 0x00, 0x00,
2846  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x00,
2847  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78,
2848  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2849  0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x02, 0x00,
2850  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x07,
2851  0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2852  0xc0, 0x0f, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2853  0x60, 0x00, 0xc0, 0x0f, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2854  0x00, 0x00, 0x78, 0x00, 0xc0, 0x8f, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00,
2855  0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0xc0, 0x8f, 0x3f, 0x00, 0x00, 0x00,
2856  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0xe0, 0x9f, 0x7f, 0x00,
2857  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0xe0, 0xdf,
2858  0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x78, 0x00,
2859  0xe0, 0xdf, 0x7b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x0c,
2860  0x78, 0x30, 0xf0, 0xff, 0x7b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e,
2861  0x00, 0x0f, 0xf8, 0x70, 0xf0, 0xff, 0x7b, 0x00, 0x00, 0x1f, 0x00, 0xe0,
2862  0x0f, 0x1e, 0x80, 0x0f, 0xf8, 0x78, 0xf0, 0xfd, 0xf9, 0x00, 0xc0, 0x1f,
2863  0x00, 0xf8, 0x0f, 0x00, 0xe0, 0x1f, 0xf8, 0x7c, 0xf0, 0xfc, 0xf9, 0x00,
2864  0xf0, 0x1f, 0x00, 0xfe, 0x0f, 0x00, 0xf0, 0x07, 0xf8, 0x3e, 0xf8, 0xfc,
2865  0xf0, 0x01, 0xf8, 0x1f, 0x00, 0xff, 0x0f, 0x1e, 0xf0, 0x03, 0xf8, 0x3f,
2866  0xf8, 0xf8, 0xf0, 0x01, 0xfc, 0x1f, 0x80, 0x7f, 0x0f, 0x1e, 0xf8, 0x00,
2867  0xf8, 0x1f, 0x78, 0x18, 0xf0, 0x01, 0x7c, 0x1e, 0xc0, 0x0f, 0x0f, 0x1e,
2868  0x7c, 0x00, 0xf0, 0x0f, 0x78, 0x00, 0xf0, 0x01, 0x3e, 0x1e, 0xe0, 0x07,
2869  0x0f, 0x1e, 0x7c, 0x00, 0xf0, 0x07, 0x7c, 0x00, 0xe0, 0x01, 0x3e, 0x1e,
2870  0xe0, 0x03, 0x0f, 0x1e, 0x3e, 0x00, 0xf0, 0x0f, 0x7c, 0x00, 0xe0, 0x03,
2871  0x3e, 0x3e, 0xe0, 0x07, 0x0f, 0x1e, 0x1e, 0x00, 0xf0, 0x1f, 0x3c, 0x00,
2872  0xe0, 0x03, 0x7e, 0x3e, 0xc0, 0x3f, 0x0f, 0x1e, 0x3e, 0x00, 0xf0, 0x1f,
2873  0x3e, 0x00, 0xe0, 0x03, 0xfc, 0x7f, 0x80, 0xff, 0x0f, 0x1e, 0xfc, 0x00,
2874  0xf0, 0x3e, 0x3e, 0x00, 0xc0, 0x03, 0xf8, 0xff, 0x03, 0xff, 0x0f, 0x1e,
2875  0xfc, 0x07, 0xf0, 0x7c, 0x1e, 0x00, 0xc0, 0x03, 0xf8, 0xff, 0x03, 0xfc,
2876  0x0f, 0x1e, 0xf8, 0x1f, 0xf0, 0xf8, 0x1e, 0x00, 0xc0, 0x03, 0xe0, 0xf7,
2877  0x03, 0xf0, 0x0f, 0x1e, 0xe0, 0x3f, 0xf0, 0x78, 0x1c, 0x00, 0x80, 0x03,
2878  0x80, 0xe3, 0x03, 0x00, 0x0f, 0x1e, 0xc0, 0x3f, 0xf0, 0x30, 0x00, 0x00,
2879  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0e, 0x00, 0x3e, 0x00, 0x00,
2880  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x0f, 0x00, 0x00, 0x10,
2881  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x0f, 0x00,
2882  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0,
2883  0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2884  0x00, 0xe0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2885  0x00, 0x00, 0x00, 0xf0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2886  0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
2887  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, 0x00,
2888  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00,
2889  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c,
2890  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2891  0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
2892  };
2893 
2894  int
2895  id,
2896  y;
2897 
2898  register int
2899  i;
2900 
2901  static unsigned int
2902  number_selections;
2903 
2904  unsigned int
2905  height;
2906 
2907  size_t
2908  state;
2909 
2910  XFontStruct
2911  *font_info;
2912 
2913  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2914  assert(display != (Display *) NULL);
2915  assert(windows != (XWindows *) NULL);
2916  font_info=windows->command.font_info;
2917  height=(unsigned int) (font_info->ascent+font_info->descent);
2918  id=(~0);
2919  state=DefaultState;
2920  if (event == (XEvent *) NULL)
2921  {
2922  unsigned int
2923  width;
2924 
2925  XTextProperty
2926  window_name;
2927 
2928  XWindowChanges
2929  window_changes;
2930 
2931  /*
2932  Determine command window attributes.
2933  */
2934  assert(selections != (const char **) NULL);
2935  windows->command.width=0;
2936  for (i=0; selections[i] != (char *) NULL; i++)
2937  {
2938  width=WidgetTextWidth(font_info,(char *) selections[i]);
2939  if (width > windows->command.width)
2940  windows->command.width=width;
2941  }
2942  number_selections=(unsigned int) i;
2943  windows->command.width+=3*QuantumMargin+10;
2944  if ((int) windows->command.width < (tile_width+QuantumMargin+10))
2945  windows->command.width=(unsigned int) (tile_width+QuantumMargin+10);
2946  windows->command.height=(unsigned int) (number_selections*
2947  (((3*height) >> 1)+10)+tile_height+20);
2948  windows->command.min_width=windows->command.width;
2949  windows->command.min_height=windows->command.height;
2950  XConstrainWindowPosition(display,&windows->command);
2951  if (windows->command.id != (Window) NULL)
2952  {
2953  Status
2954  status;
2955 
2956  /*
2957  Reconfigure command window.
2958  */
2959  status=XStringListToTextProperty(&windows->command.name,1,
2960  &window_name);
2961  if (status != False)
2962  {
2963  XSetWMName(display,windows->command.id,&window_name);
2964  XSetWMIconName(display,windows->command.id,&window_name);
2965  (void) XFree((void *) window_name.value);
2966  }
2967  window_changes.width=(int) windows->command.width;
2968  window_changes.height=(int) windows->command.height;
2969  (void) XReconfigureWMWindow(display,windows->command.id,
2970  windows->command.screen,(unsigned int) (CWWidth | CWHeight),
2971  &window_changes);
2972  }
2973  /*
2974  Allocate selection info memory.
2975  */
2976  if (selection_info != (XWidgetInfo *) NULL)
2977  selection_info=(XWidgetInfo *) RelinquishMagickMemory(selection_info);
2978  selection_info=(XWidgetInfo *) AcquireQuantumMemory(number_selections,
2979  sizeof(*selection_info));
2980  if (selection_info == (XWidgetInfo *) NULL)
2981  {
2982  ThrowXWindowFatalException(ResourceLimitFatalError,
2983  "MemoryAllocationFailed","...");
2984  return(id);
2985  }
2986  state|=UpdateConfigurationState | RedrawWidgetState;
2987  }
2988  /*
2989  Wait for next event.
2990  */
2991  if (event != (XEvent *) NULL)
2992  switch (event->type)
2993  {
2994  case ButtonPress:
2995  {
2996  for (i=0; i < (int) number_selections; i++)
2997  {
2998  if (MatteIsActive(selection_info[i],event->xbutton) == MagickFalse)
2999  continue;
3000  if (i >= (int) windows->command.data)
3001  {
3002  selection_info[i].raised=MagickFalse;
3003  XDrawBeveledButton(display,&windows->command,&selection_info[i]);
3004  break;
3005  }
3006  submenu_info=selection_info[i];
3007  submenu_info.active=MagickTrue;
3008  toggle_info.y=submenu_info.y+(submenu_info.height >> 1)-
3009  (toggle_info.height >> 1);
3010  id=i;
3011  (void) XCheckWindowEvent(display,windows->widget.id,LeaveWindowMask,
3012  event);
3013  break;
3014  }
3015  break;
3016  }
3017  case ButtonRelease:
3018  {
3019  for (i=0; i < (int) number_selections; i++)
3020  {
3021  if (MatteIsActive(selection_info[i],event->xbutton) == MagickFalse)
3022  continue;
3023  id=i;
3024  if (id >= (int) windows->command.data)
3025  {
3026  selection_info[id].raised=MagickTrue;
3027  XDrawBeveledButton(display,&windows->command,&selection_info[id]);
3028  break;
3029  }
3030  break;
3031  }
3032  break;
3033  }
3034  case ClientMessage:
3035  {
3036  /*
3037  If client window delete message, withdraw command widget.
3038  */
3039  if (event->xclient.message_type != windows->wm_protocols)
3040  break;
3041  if (*event->xclient.data.l != (int) windows->wm_delete_window)
3042  break;
3043  (void) XWithdrawWindow(display,windows->command.id,
3044  windows->command.screen);
3045  break;
3046  }
3047  case ConfigureNotify:
3048  {
3049  /*
3050  Update widget configuration.
3051  */
3052  if (event->xconfigure.window != windows->command.id)
3053  break;
3054  if (event->xconfigure.send_event != 0)
3055  {
3056  windows->command.x=event->xconfigure.x;
3057  windows->command.y=event->xconfigure.y;
3058  }
3059  if ((event->xconfigure.width == (int) windows->command.width) &&
3060  (event->xconfigure.height == (int) windows->command.height))
3061  break;
3062  windows->command.width=(unsigned int)
3063  MagickMax(event->xconfigure.width,(int) windows->command.min_width);
3064  windows->command.height=(unsigned int)
3065  MagickMax(event->xconfigure.height,(int) windows->command.min_height);
3066  state|=UpdateConfigurationState;
3067  break;
3068  }
3069  case Expose:
3070  {
3071  if (event->xexpose.window != windows->command.id)
3072  break;
3073  if (event->xexpose.count != 0)
3074  break;
3075  state|=RedrawWidgetState;
3076  break;
3077  }
3078  case MotionNotify:
3079  {
3080  /*
3081  Return the ID of the highlighted menu entry.
3082  */
3083  for ( ; ; )
3084  {
3085  for (i=0; i < (int) number_selections; i++)
3086  {
3087  if (i >= (int) windows->command.data)
3088  {
3089  if (selection_info[i].raised ==
3090  MatteIsActive(selection_info[i],event->xmotion))
3091  {
3092  /*
3093  Button status changed.
3094  */
3095  selection_info[i].raised=!selection_info[i].raised;
3096  XDrawBeveledButton(display,&windows->command,
3097  &selection_info[i]);
3098  }
3099  continue;
3100  }
3101  if (MatteIsActive(selection_info[i],event->xmotion) == MagickFalse)
3102  continue;
3103  submenu_info=selection_info[i];
3104  submenu_info.active=MagickTrue;
3105  toggle_info.raised=MagickTrue;
3106  toggle_info.y=submenu_info.y+(submenu_info.height >> 1)-
3107  (toggle_info.height >> 1);
3108  XDrawTriangleEast(display,&windows->command,&toggle_info);
3109  id=i;
3110  }
3111  XDelay(display,SuspendTime);
3112  if (XCheckMaskEvent(display,ButtonMotionMask,event) == MagickFalse)
3113  break;
3114  while (XCheckMaskEvent(display,ButtonMotionMask,event)) ;
3115  toggle_info.raised=MagickFalse;
3116  if (windows->command.data != 0)
3117  XDrawTriangleEast(display,&windows->command,&toggle_info);
3118  }
3119  break;
3120  }
3121  case MapNotify:
3122  {
3123  windows->command.mapped=MagickTrue;
3124  break;
3125  }
3126  case UnmapNotify:
3127  {
3128  windows->command.mapped=MagickFalse;
3129  break;
3130  }
3131  default:
3132  break;
3133  }
3134  if (state & UpdateConfigurationState)
3135  {
3136  /*
3137  Initialize button information.
3138  */
3139  assert(selections != (const char **) NULL);
3140  y=tile_height+20;
3141  for (i=0; i < (int) number_selections; i++)
3142  {
3143  XGetWidgetInfo(selections[i],&selection_info[i]);
3144  selection_info[i].center=MagickFalse;
3145  selection_info[i].bevel_width--;
3146  selection_info[i].height=(unsigned int) ((3*height) >> 1);
3147  selection_info[i].x=(QuantumMargin >> 1)+4;
3148  selection_info[i].width=(unsigned int) (windows->command.width-
3149  (selection_info[i].x << 1));
3150  selection_info[i].y=y;
3151  y+=selection_info[i].height+(selection_info[i].bevel_width << 1)+6;
3152  }
3153  XGetWidgetInfo((char *) NULL,&toggle_info);
3154  toggle_info.bevel_width--;
3155  toggle_info.width=(unsigned int) (((5*height) >> 3)-
3156  (toggle_info.bevel_width << 1));
3157  toggle_info.height=toggle_info.width;
3158  toggle_info.x=selection_info[0].x+selection_info[0].width-
3159  toggle_info.width-(QuantumMargin >> 1);
3160  if (windows->command.mapped)
3161  (void) XClearWindow(display,windows->command.id);
3162  }
3163  if (state & RedrawWidgetState)
3164  {
3165  Pixmap
3166  tile_pixmap;
3167 
3168  /*
3169  Draw command buttons.
3170  */
3171  tile_pixmap=XCreatePixmapFromBitmapData(display,windows->command.id,
3172  (char *) tile_bits,tile_width,tile_height,1L,0L,1);
3173  if (tile_pixmap != (Pixmap) NULL)
3174  {
3175  (void) XCopyPlane(display,tile_pixmap,windows->command.id,
3176  windows->command.annotate_context,0,0,tile_width,tile_height,
3177  (int) ((windows->command.width-tile_width) >> 1),10,1L);
3178  (void) XFreePixmap(display,tile_pixmap);
3179  }
3180  for (i=0; i < (int) number_selections; i++)
3181  {
3182  XDrawBeveledButton(display,&windows->command,&selection_info[i]);
3183  if (i >= (int) windows->command.data)
3184  continue;
3185  toggle_info.raised=MagickFalse;
3186  toggle_info.y=selection_info[i].y+(selection_info[i].height >> 1)-
3187  (toggle_info.height >> 1);
3188  XDrawTriangleEast(display,&windows->command,&toggle_info);
3189  }
3190  XHighlightWidget(display,&windows->command,BorderOffset,BorderOffset);
3191  }
3192  return(id);
3193 }
3194 
3195 /*
3196 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3197 % %
3198 % %
3199 % %
3200 % X C o n f i r m W i d g e t %
3201 % %
3202 % %
3203 % %
3204 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3205 %
3206 % XConfirmWidget() displays a Confirm widget with a notice to the user. The
3207 % function returns -1 if Dismiss is pressed, 0 for Cancel, and 1 for Yes.
3208 %
3209 % The format of the XConfirmWidget method is:
3210 %
3211 % int XConfirmWidget(Display *display,XWindows *windows,
3212 % const char *reason,const char *description)
3213 %
3214 % A description of each parameter follows:
3215 %
3216 % o display: Specifies a connection to an X server; returned from
3217 % XOpenDisplay.
3218 %
3219 % o window: Specifies a pointer to a XWindows structure.
3220 %
3221 % o reason: Specifies the message to display before terminating the
3222 % program.
3223 %
3224 % o description: Specifies any description to the message.
3225 %
3226 */
3227 MagickPrivate int XConfirmWidget(Display *display,XWindows *windows,
3228  const char *reason,const char *description)
3229 {
3230 #define CancelButtonText "Cancel"
3231 #define DismissButtonText "Dismiss"
3232 #define YesButtonText "Yes"
3233 
3234  int
3235  confirm,
3236  x,
3237  y;
3238 
3239  Status
3240  status;
3241 
3242  unsigned int
3243  height,
3244  width;
3245 
3246  size_t
3247  state;
3248 
3249  XEvent
3250  event;
3251 
3252  XFontStruct
3253  *font_info;
3254 
3255  XTextProperty
3256  window_name;
3257 
3258  XWidgetInfo
3259  cancel_info,
3260  dismiss_info,
3261  yes_info;
3262 
3263  XWindowChanges
3264  window_changes;
3265 
3266  /*
3267  Determine Confirm widget attributes.
3268  */
3269  assert(display != (Display *) NULL);
3270  assert(windows != (XWindows *) NULL);
3271  assert(reason != (char *) NULL);
3272  assert(description != (char *) NULL);
3273  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",reason);
3274  XCheckRefreshWindows(display,windows);
3275  font_info=windows->widget.font_info;
3276  width=WidgetTextWidth(font_info,CancelButtonText);
3277  if (WidgetTextWidth(font_info,DismissButtonText) > width)
3278  width=WidgetTextWidth(font_info,DismissButtonText);
3279  if (WidgetTextWidth(font_info,YesButtonText) > width)
3280  width=WidgetTextWidth(font_info,YesButtonText);
3281  width<<=1;
3282  if (description != (char *) NULL)
3283  if (WidgetTextWidth(font_info,(char *) description) > width)
3284  width=WidgetTextWidth(font_info,(char *) description);
3285  height=(unsigned int) (font_info->ascent+font_info->descent);
3286  /*
3287  Position Confirm widget.
3288  */
3289  windows->widget.width=(unsigned int) (width+9*QuantumMargin);
3290  windows->widget.min_width=(unsigned int) (9*QuantumMargin+
3291  WidgetTextWidth(font_info,CancelButtonText)+
3292  WidgetTextWidth(font_info,DismissButtonText)+
3293  WidgetTextWidth(font_info,YesButtonText));
3294  if (windows->widget.width < windows->widget.min_width)
3295  windows->widget.width=windows->widget.min_width;
3296  windows->widget.height=(unsigned int) (12*height);
3297  windows->widget.min_height=(unsigned int) (7*height);
3298  if (windows->widget.height < windows->widget.min_height)
3299  windows->widget.height=windows->widget.min_height;
3300  XConstrainWindowPosition(display,&windows->widget);
3301  /*
3302  Map Confirm widget.
3303  */
3304  (void) CopyMagickString(windows->widget.name,"Confirm",MagickPathExtent);
3305  status=XStringListToTextProperty(&windows->widget.name,1,&window_name);
3306  if (status != False)
3307  {
3308  XSetWMName(display,windows->widget.id,&window_name);
3309  XSetWMIconName(display,windows->widget.id,&window_name);
3310  (void) XFree((void *) window_name.value);
3311  }
3312  window_changes.width=(int) windows->widget.width;
3313  window_changes.height=(int) windows->widget.height;
3314  window_changes.x=windows->widget.x;
3315  window_changes.y=windows->widget.y;
3316  (void) XReconfigureWMWindow(display,windows->widget.id,windows->widget.screen,
3317  (unsigned int) (CWWidth | CWHeight | CWX | CWY),&window_changes);
3318  (void) XMapRaised(display,windows->widget.id);
3319  windows->widget.mapped=MagickFalse;
3320  /*
3321  Respond to X events.
3322  */
3323  confirm=0;
3324  state=UpdateConfigurationState;
3325  XSetCursorState(display,windows,MagickTrue);
3326  do
3327  {
3328  if (state & UpdateConfigurationState)
3329  {
3330  /*
3331  Initialize button information.
3332  */
3333  XGetWidgetInfo(CancelButtonText,&cancel_info);
3334  cancel_info.width=(unsigned int) QuantumMargin+
3335  WidgetTextWidth(font_info,CancelButtonText);
3336  cancel_info.height=(unsigned int) ((3*height) >> 1);
3337  cancel_info.x=(int) (windows->widget.width-cancel_info.width-
3338  QuantumMargin);
3339  cancel_info.y=(int) (windows->widget.height-(cancel_info.height << 1));
3340  dismiss_info=cancel_info;
3341  dismiss_info.text=(char *) DismissButtonText;
3342  if (LocaleCompare(description,"Do you want to save it") == 0)
3343  dismiss_info.text=(char *) "Don't Save";
3344  dismiss_info.width=(unsigned int) QuantumMargin+
3345  WidgetTextWidth(font_info,dismiss_info.text);
3346  dismiss_info.x=(int)
3347  ((windows->widget.width >> 1)-(dismiss_info.width >> 1));
3348  yes_info=cancel_info;
3349  yes_info.text=(char *) YesButtonText;
3350  if (LocaleCompare(description,"Do you want to save it") == 0)
3351  yes_info.text=(char *) "Save";
3352  yes_info.width=(unsigned int) QuantumMargin+
3353  WidgetTextWidth(font_info,yes_info.text);
3354  if (yes_info.width < cancel_info.width)
3355  yes_info.width=cancel_info.width;
3356  yes_info.x=QuantumMargin;
3357  state&=(~UpdateConfigurationState);
3358  }
3359  if (state & RedrawWidgetState)
3360  {
3361  /*
3362  Redraw Confirm widget.
3363  */
3364  width=WidgetTextWidth(font_info,(char *) reason);
3365  x=(int) ((windows->widget.width >> 1)-(width >> 1));
3366  y=(int) ((windows->widget.height >> 1)-(height << 1));
3367  (void) XDrawString(display,windows->widget.id,
3368  windows->widget.annotate_context,x,y,(char *) reason,Extent(reason));
3369  if (description != (char *) NULL)
3370  {
3371  char
3372  question[MagickPathExtent];
3373 
3374  (void) CopyMagickString(question,description,MagickPathExtent);
3375  (void) ConcatenateMagickString(question,"?",MagickPathExtent);
3376  width=WidgetTextWidth(font_info,question);
3377  x=(int) ((windows->widget.width >> 1)-(width >> 1));
3378  y+=height;
3379  (void) XDrawString(display,windows->widget.id,
3380  windows->widget.annotate_context,x,y,question,Extent(question));
3381  }
3382  XDrawBeveledButton(display,&windows->widget,&cancel_info);
3383  XDrawBeveledButton(display,&windows->widget,&dismiss_info);
3384  XDrawBeveledButton(display,&windows->widget,&yes_info);
3385  XHighlightWidget(display,&windows->widget,BorderOffset,BorderOffset);
3386  state&=(~RedrawWidgetState);
3387  }
3388  /*
3389  Wait for next event.
3390  */
3391  (void) XIfEvent(display,&event,XScreenEvent,(char *) windows);
3392  switch (event.type)
3393  {
3394  case ButtonPress:
3395  {
3396  if (MatteIsActive(cancel_info,event.xbutton))
3397  {
3398  /*
3399  User pressed No button.
3400  */
3401  cancel_info.raised=MagickFalse;
3402  XDrawBeveledButton(display,&windows->widget,&cancel_info);
3403  break;
3404  }
3405  if (MatteIsActive(dismiss_info,event.xbutton))
3406  {
3407  /*
3408  User pressed Dismiss button.
3409  */
3410  dismiss_info.raised=MagickFalse;
3411  XDrawBeveledButton(display,&windows->widget,&dismiss_info);
3412  break;
3413  }
3414  if (MatteIsActive(yes_info,event.xbutton))
3415  {
3416  /*
3417  User pressed Yes button.
3418  */
3419  yes_info.raised=MagickFalse;
3420  XDrawBeveledButton(display,&windows->widget,&yes_info);
3421  break;
3422  }
3423  break;
3424  }
3425  case ButtonRelease:
3426  {
3427  if (windows->widget.mapped == MagickFalse)
3428  break;
3429  if (cancel_info.raised == MagickFalse)
3430  {
3431  if (event.xbutton.window == windows->widget.id)
3432  if (MatteIsActive(cancel_info,event.xbutton))
3433  {
3434  confirm=0;
3435  state|=ExitState;
3436  }
3437  cancel_info.raised=MagickTrue;
3438  XDrawBeveledButton(display,&windows->widget,&cancel_info);
3439  }
3440  if (dismiss_info.raised == MagickFalse)
3441  {
3442  if (event.xbutton.window == windows->widget.id)
3443  if (MatteIsActive(dismiss_info,event.xbutton))
3444  {
3445  confirm=(-1);
3446  state|=ExitState;
3447  }
3448  dismiss_info.raised=MagickTrue;
3449  XDrawBeveledButton(display,&windows->widget,&dismiss_info);
3450  }
3451  if (yes_info.raised == MagickFalse)
3452  {
3453  if (event.xbutton.window == windows->widget.id)
3454  if (MatteIsActive(yes_info,event.xbutton))
3455  {
3456  confirm=1;
3457  state|=ExitState;
3458  }
3459  yes_info.raised=MagickTrue;
3460  XDrawBeveledButton(display,&windows->widget,&yes_info);
3461  }
3462  break;
3463  }
3464  case ClientMessage:
3465  {
3466  /*
3467  If client window delete message, exit.
3468  */
3469  if (event.xclient.message_type != windows->wm_protocols)
3470  break;
3471  if (*event.xclient.data.l == (int) windows->wm_take_focus)
3472  {
3473  (void) XSetInputFocus(display,event.xclient.window,RevertToParent,
3474  (Time) event.xclient.data.l[1]);
3475  break;
3476  }
3477  if (*event.xclient.data.l != (int) windows->wm_delete_window)
3478  break;
3479  if (event.xclient.window == windows->widget.id)
3480  {
3481  state|=ExitState;
3482  break;
3483  }
3484  break;
3485  }
3486  case ConfigureNotify:
3487  {
3488  /*
3489  Update widget configuration.
3490  */
3491  if (event.xconfigure.window != windows->widget.id)
3492  break;
3493  if ((event.xconfigure.width == (int) windows->widget.width) &&
3494  (event.xconfigure.height == (int) windows->widget.height))
3495  break;
3496  windows->widget.width=(unsigned int)
3497  MagickMax(event.xconfigure.width,(int) windows->widget.min_width);
3498  windows->widget.height=(unsigned int)
3499  MagickMax(event.xconfigure.height,(int) windows->widget.min_height);
3500  state|=UpdateConfigurationState;
3501  break;
3502  }
3503  case EnterNotify:
3504  {
3505  if (event.xcrossing.window != windows->widget.id)
3506  break;
3507  state&=(~InactiveWidgetState);
3508  break;
3509  }
3510  case Expose:
3511  {
3512  if (event.xexpose.window != windows->widget.id)
3513  break;
3514  if (event.xexpose.count != 0)
3515  break;
3516  state|=RedrawWidgetState;
3517  break;
3518  }
3519  case KeyPress:
3520  {
3521  static char
3522  command[MagickPathExtent];
3523 
3524  static KeySym
3525  key_symbol;
3526 
3527  /*
3528  Respond to a user key press.
3529  */
3530  if (event.xkey.window != windows->widget.id)
3531  break;
3532  (void) XLookupString((XKeyEvent *) &event.xkey,command,
3533  (int) sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3534  if ((key_symbol == XK_Return) || (key_symbol == XK_KP_Enter))
3535  {
3536  yes_info.raised=MagickFalse;
3537  XDrawBeveledButton(display,&windows->widget,&yes_info);
3538  confirm=1;
3539  state|=ExitState;
3540  break;
3541  }
3542  break;
3543  }
3544  case LeaveNotify:
3545  {
3546  if (event.xcrossing.window != windows->widget.id)
3547  break;
3548  state|=InactiveWidgetState;
3549  break;
3550  }
3551  case MotionNotify:
3552  {
3553  /*
3554  Discard pending button motion events.
3555  */
3556  while (XCheckMaskEvent(display,ButtonMotionMask,&event)) ;
3557  if (state & InactiveWidgetState)
3558  break;
3559  if (cancel_info.raised == MatteIsActive(cancel_info,event.xmotion))
3560  {
3561  /*
3562  Cancel button status changed.
3563  */
3564  cancel_info.raised=cancel_info.raised == MagickFalse ?
3566  XDrawBeveledButton(display,&windows->widget,&cancel_info);
3567  break;
3568  }
3569  if (dismiss_info.raised == MatteIsActive(dismiss_info,event.xmotion))
3570  {
3571  /*
3572  Dismiss button status changed.
3573  */
3574  dismiss_info.raised=dismiss_info.raised == MagickFalse ?
3576  XDrawBeveledButton(display,&windows->widget,&dismiss_info);
3577  break;
3578  }
3579  if (yes_info.raised == MatteIsActive(yes_info,event.xmotion))
3580  {
3581  /*
3582  Yes button status changed.
3583  */
3584  yes_info.raised=yes_info.raised == MagickFalse ?
3586  XDrawBeveledButton(display,&windows->widget,&yes_info);
3587  break;
3588  }
3589  break;
3590  }
3591  default:
3592  break;
3593  }
3594  } while ((state & ExitState) == 0);
3595  XSetCursorState(display,windows,MagickFalse);
3596  (void) XWithdrawWindow(display,windows->widget.id,windows->widget.screen);
3597  XCheckRefreshWindows(display,windows);
3598  return(confirm);
3599 }
3600 
3601 /*
3602 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3603 % %
3604 % %
3605 % %
3606 % X D i a l o g W i d g e t %
3607 % %
3608 % %
3609 % %
3610 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3611 %
3612 % XDialogWidget() displays a Dialog widget with a query to the user. The user
3613 % keys a reply and presses the Ok or Cancel button to exit. The typed text is
3614 % returned as the reply function parameter.
3615 %
3616 % The format of the XDialogWidget method is:
3617 %
3618 % int XDialogWidget(Display *display,XWindows *windows,const char *action,
3619 % const char *query,char *reply)
3620 %
3621 % A description of each parameter follows:
3622 %
3623 % o display: Specifies a connection to an X server; returned from
3624 % XOpenDisplay.
3625 %
3626 % o window: Specifies a pointer to a XWindows structure.
3627 %
3628 % o action: Specifies a pointer to the action of this widget.
3629 %
3630 % o query: Specifies a pointer to the query to present to the user.
3631 %
3632 % o reply: the response from the user is returned in this parameter.
3633 %
3634 */
3635 MagickPrivate int XDialogWidget(Display *display,XWindows *windows,
3636  const char *action,const char *query,char *reply)
3637 {
3638 #define CancelButtonText "Cancel"
3639 
3640  char
3641  primary_selection[MagickPathExtent];
3642 
3643  int
3644  x;
3645 
3646  register int
3647  i;
3648 
3649  static MagickBooleanType
3650  raised = MagickFalse;
3651 
3652  Status
3653  status;
3654 
3655  unsigned int
3656  anomaly,
3657  height,
3658  width;
3659 
3660  size_t
3661  state;
3662 
3663  XEvent
3664  event;
3665 
3666  XFontStruct
3667  *font_info;
3668 
3669  XTextProperty
3670  window_name;
3671 
3672  XWidgetInfo
3673  action_info,
3674  cancel_info,
3675  reply_info,
3676  special_info,
3677  text_info;
3678 
3679  XWindowChanges
3680  window_changes;
3681 
3682  /*
3683  Determine Dialog widget attributes.
3684  */
3685  assert(display != (Display *) NULL);
3686  assert(windows != (XWindows *) NULL);
3687  assert(action != (char *) NULL);
3688  assert(query != (char *) NULL);
3689  assert(reply != (char *) NULL);
3690  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",action);
3691  XCheckRefreshWindows(display,windows);
3692  font_info=windows->widget.font_info;
3693  width=WidgetTextWidth(font_info,(char *) action);
3694  if (WidgetTextWidth(font_info,CancelButtonText) > width)
3695  width=WidgetTextWidth(font_info,CancelButtonText);
3696  width+=(3*QuantumMargin) >> 1;
3697  height=(unsigned int) (font_info->ascent+font_info->descent);
3698  /*
3699  Position Dialog widget.
3700  */
3701  windows->widget.width=(unsigned int) MagickMax((int) (2*width),(int)
3702  WidgetTextWidth(font_info,(char *) query));
3703  if (windows->widget.width < WidgetTextWidth(font_info,reply))
3704  windows->widget.width=WidgetTextWidth(font_info,reply);
3705  windows->widget.width+=6*QuantumMargin;
3706  windows->widget.min_width=(unsigned int)
3707  (width+28*XTextWidth(font_info,"#",1)+4*QuantumMargin);
3708  if (windows->widget.width < windows->widget.min_width)
3709  windows->widget.width=windows->widget.min_width;
3710  windows->widget.height=(unsigned int) (7*height+(QuantumMargin << 1));
3711  windows->widget.min_height=windows->widget.height;
3712  if (windows->widget.height < windows->widget.min_height)
3713  windows->widget.height=windows->widget.min_height;
3714  XConstrainWindowPosition(display,&windows->widget);
3715  /*
3716  Map Dialog widget.
3717  */
3718  (void) CopyMagickString(windows->widget.name,"Dialog",MagickPathExtent);
3719  status=XStringListToTextProperty(&windows->widget.name,1,&window_name);
3720  if (status != False)
3721  {
3722  XSetWMName(display,windows->widget.id,&window_name);
3723  XSetWMIconName(display,windows->widget.id,&window_name);
3724  (void) XFree((void *) window_name.value);
3725  }
3726  window_changes.width=(int) windows->widget.width;
3727  window_changes.height=(int) windows->widget.height;
3728  window_changes.x=windows->widget.x;
3729  window_changes.y=windows->widget.y;
3730  (void) XReconfigureWMWindow(display,windows->widget.id,windows->widget.screen,
3731  (unsigned int) (CWWidth | CWHeight | CWX | CWY),&window_changes);
3732  (void) XMapRaised(display,windows->widget.id);
3733  windows->widget.mapped=MagickFalse;
3734  /*
3735  Respond to X events.
3736  */
3737  anomaly=(LocaleCompare(action,"Background") == 0) ||
3738  (LocaleCompare(action,"New") == 0) ||
3739  (LocaleCompare(action,"Quantize") == 0) ||
3740  (LocaleCompare(action,"Resize") == 0) ||
3741  (LocaleCompare(action,"Save") == 0) ||
3742  (LocaleCompare(action,"Shade") == 0);
3743  state=UpdateConfigurationState;
3744  XSetCursorState(display,windows,MagickTrue);
3745  do
3746  {
3747  if (state & UpdateConfigurationState)
3748  {
3749  /*
3750  Initialize button information.
3751  */
3752  XGetWidgetInfo(CancelButtonText,&cancel_info);
3753  cancel_info.width=width;
3754  cancel_info.height=(unsigned int) ((3*height) >> 1);
3755  cancel_info.x=(int)
3756  (windows->widget.width-cancel_info.width-((3*QuantumMargin) >> 1));
3757  cancel_info.y=(int)
3758  (windows->widget.height-cancel_info.height-((3*QuantumMargin) >> 1));
3759  XGetWidgetInfo(action,&action_info);
3760  action_info.width=width;
3761  action_info.height=(unsigned int) ((3*height) >> 1);
3762  action_info.x=cancel_info.x-(cancel_info.width+QuantumMargin+
3763  (action_info.bevel_width << 1));
3764  action_info.y=cancel_info.y;
3765  /*
3766  Initialize reply information.
3767  */
3768  XGetWidgetInfo(reply,&reply_info);
3769  reply_info.raised=MagickFalse;
3770  reply_info.bevel_width--;
3771  reply_info.width=windows->widget.width-(3*QuantumMargin);
3772  reply_info.height=height << 1;
3773  reply_info.x=(3*QuantumMargin) >> 1;
3774  reply_info.y=action_info.y-reply_info.height-QuantumMargin;
3775  /*
3776  Initialize option information.
3777  */
3778  XGetWidgetInfo("Dither",&special_info);
3779  special_info.raised=raised;
3780  special_info.bevel_width--;
3781  special_info.width=(unsigned int) QuantumMargin >> 1;
3782  special_info.height=(unsigned int) QuantumMargin >> 1;
3783  special_info.x=reply_info.x;
3784  special_info.y=action_info.y+action_info.height-special_info.height;
3785  if (LocaleCompare(action,"Background") == 0)
3786  special_info.text=(char *) "Backdrop";
3787  if (LocaleCompare(action,"New") == 0)
3788  special_info.text=(char *) "Gradation";
3789  if (LocaleCompare(action,"Resize") == 0)
3790  special_info.text=(char *) "Constrain ratio";
3791  if (LocaleCompare(action,"Save") == 0)
3792  special_info.text=(char *) "Non-progressive";
3793  if (LocaleCompare(action,"Shade") == 0)
3794  special_info.text=(char *) "Color shading";
3795  /*
3796  Initialize text information.
3797  */
3798  XGetWidgetInfo(query,&text_info);
3799  text_info.width=reply_info.width;
3800  text_info.height=height;
3801  text_info.x=reply_info.x-(QuantumMargin >> 1);
3802  text_info.y=QuantumMargin;
3803  text_info.center=MagickFalse;
3804  state&=(~UpdateConfigurationState);
3805  }
3806  if (state & RedrawWidgetState)
3807  {
3808  /*
3809  Redraw Dialog widget.
3810  */
3811  XDrawWidgetText(display,&windows->widget,&text_info);
3812  XDrawBeveledMatte(display,&windows->widget,&reply_info);
3813  XDrawMatteText(display,&windows->widget,&reply_info);
3814  if (anomaly)
3815  XDrawBeveledButton(display,&windows->widget,&special_info);
3816  XDrawBeveledButton(display,&windows->widget,&action_info);
3817  XDrawBeveledButton(display,&windows->widget,&cancel_info);
3818  XHighlightWidget(display,&windows->widget,BorderOffset,BorderOffset);
3819  state&=(~RedrawWidgetState);
3820  }
3821  /*
3822  Wait for next event.
3823  */
3824  (void) XIfEvent(display,&event,XScreenEvent,(char *) windows);
3825  switch (event.type)
3826  {
3827  case ButtonPress:
3828  {
3829  if (anomaly)
3830  if (MatteIsActive(special_info,event.xbutton))
3831  {
3832  /*
3833  Option button status changed.
3834  */
3835  special_info.raised=!special_info.raised;
3836  XDrawBeveledButton(display,&windows->widget,&special_info);
3837  break;
3838  }
3839  if (MatteIsActive(action_info,event.xbutton))
3840  {
3841  /*
3842  User pressed Action button.
3843  */
3844  action_info.raised=MagickFalse;
3845  XDrawBeveledButton(display,&windows->widget,&action_info);
3846  break;
3847  }
3848  if (MatteIsActive(cancel_info,event.xbutton))
3849  {
3850  /*
3851  User pressed Cancel button.
3852  */
3853  cancel_info.raised=MagickFalse;
3854  XDrawBeveledButton(display,&windows->widget,&cancel_info);
3855  break;
3856  }
3857  if (MatteIsActive(reply_info,event.xbutton) == MagickFalse)
3858  break;
3859  if (event.xbutton.button != Button2)
3860  {
3861  static Time
3862  click_time;
3863 
3864  /*
3865  Move text cursor to position of button press.
3866  */
3867  x=event.xbutton.x-reply_info.x-(QuantumMargin >> 2);
3868  for (i=1; i <= Extent(reply_info.marker); i++)
3869  if (XTextWidth(font_info,reply_info.marker,i) > x)
3870  break;
3871  reply_info.cursor=reply_info.marker+i-1;
3872  if (event.xbutton.time > (click_time+DoubleClick))
3873  reply_info.highlight=MagickFalse;
3874  else
3875  {
3876  /*
3877  Become the XA_PRIMARY selection owner.
3878  */
3879  (void) CopyMagickString(primary_selection,reply_info.text,
3881  (void) XSetSelectionOwner(display,XA_PRIMARY,windows->widget.id,
3882  event.xbutton.time);
3883  reply_info.highlight=XGetSelectionOwner(display,XA_PRIMARY) ==
3884  windows->widget.id ? MagickTrue : MagickFalse;
3885  }
3886  XDrawMatteText(display,&windows->widget,&reply_info);
3887  click_time=event.xbutton.time;
3888  break;
3889  }
3890  /*
3891  Request primary selection.
3892  */
3893  (void) XConvertSelection(display,XA_PRIMARY,XA_STRING,XA_STRING,
3894  windows->widget.id,event.xbutton.time);
3895  break;
3896  }
3897  case ButtonRelease:
3898  {
3899  if (windows->widget.mapped == MagickFalse)
3900  break;
3901  if (action_info.raised == MagickFalse)
3902  {
3903  if (event.xbutton.window == windows->widget.id)
3904  if (MatteIsActive(action_info,event.xbutton))
3905  state|=ExitState;
3906  action_info.raised=MagickTrue;
3907  XDrawBeveledButton(display,&windows->widget,&action_info);
3908  }
3909  if (cancel_info.raised == MagickFalse)
3910  {
3911  if (event.xbutton.window == windows->widget.id)
3912  if (MatteIsActive(cancel_info,event.xbutton))
3913  {
3914  *reply_info.text='\0';
3915  state|=ExitState;
3916  }
3917  cancel_info.raised=MagickTrue;
3918  XDrawBeveledButton(display,&windows->widget,&cancel_info);
3919  }
3920  break;
3921  }
3922  case ClientMessage:
3923  {
3924  /*
3925  If client window delete message, exit.
3926  */
3927  if (event.xclient.message_type != windows->wm_protocols)
3928  break;
3929  if (*event.xclient.data.l == (int) windows->wm_take_focus)
3930  {
3931  (void) XSetInputFocus(display,event.xclient.window,RevertToParent,
3932  (Time) event.xclient.data.l[1]);
3933  break;
3934  }
3935  if (*event.xclient.data.l != (int) windows->wm_delete_window)
3936  break;
3937  if (event.xclient.window == windows->widget.id)
3938  {
3939  *reply_info.text='\0';
3940  state|=ExitState;
3941  break;
3942  }
3943  break;
3944  }
3945  case ConfigureNotify:
3946  {
3947  /*
3948  Update widget configuration.
3949  */
3950  if (event.xconfigure.window != windows->widget.id)
3951  break;
3952  if ((event.xconfigure.width == (int) windows->widget.width) &&
3953  (event.xconfigure.height == (int) windows->widget.height))
3954  break;
3955  windows->widget.width=(unsigned int)
3956  MagickMax(event.xconfigure.width,(int) windows->widget.min_width);
3957  windows->widget.height=(unsigned int)
3958  MagickMax(event.xconfigure.height,(int) windows->widget.min_height);
3959  state|=UpdateConfigurationState;
3960  break;
3961  }
3962  case EnterNotify:
3963  {
3964  if (event.xcrossing.window != windows->widget.id)
3965  break;
3966  state&=(~InactiveWidgetState);
3967  break;
3968  }
3969  case Expose:
3970  {
3971  if (event.xexpose.window != windows->widget.id)
3972  break;
3973  if (event.xexpose.count != 0)
3974  break;
3975  state|=RedrawWidgetState;
3976  break;
3977  }
3978  case KeyPress:
3979  {
3980  static char
3981  command[MagickPathExtent];
3982 
3983  static int
3984  length;
3985 
3986  static KeySym
3987  key_symbol;
3988 
3989  /*
3990  Respond to a user key press.
3991  */
3992  if (event.xkey.window != windows->widget.id)
3993  break;
3994  length=XLookupString((XKeyEvent *) &event.xkey,command,
3995  (int) sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3996  *(command+length)='\0';
3997  if ((key_symbol == XK_Return) || (key_symbol == XK_KP_Enter))
3998  {
3999  action_info.raised=MagickFalse;
4000  XDrawBeveledButton(display,&windows->widget,&action_info);
4001  state|=ExitState;
4002  break;
4003  }
4004  if (key_symbol == XK_Control_L)
4005  {
4006  state|=ControlState;
4007  break;
4008  }
4009  if (state & ControlState)
4010  switch ((int) key_symbol)
4011  {
4012  case XK_u:
4013  case XK_U:
4014  {
4015  /*
4016  Erase the entire line of text.
4017  */
4018  *reply_info.text='\0';
4019  reply_info.cursor=reply_info.text;
4020  reply_info.marker=reply_info.text;
4021  reply_info.highlight=MagickFalse;
4022  break;
4023  }
4024  default:
4025  break;
4026  }
4027  XEditText(display,&reply_info,key_symbol,command,state);
4028  XDrawMatteText(display,&windows->widget,&reply_info);
4029  break;
4030  }
4031  case KeyRelease:
4032  {
4033  static char
4034  command[MagickPathExtent];
4035 
4036  static KeySym
4037  key_symbol;
4038 
4039  /*
4040  Respond to a user key release.
4041  */
4042  if (event.xkey.window != windows->widget.id)
4043  break;
4044  (void) XLookupString((XKeyEvent *) &event.xkey,command,
4045  (int) sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4046  if (key_symbol == XK_Control_L)
4047  state&=(~ControlState);
4048  break;
4049  }
4050  case LeaveNotify:
4051  {
4052  if (event.xcrossing.window != windows->widget.id)
4053  break;
4054  state|=InactiveWidgetState;
4055  break;
4056  }
4057  case MotionNotify:
4058  {
4059  /*
4060  Discard pending button motion events.
4061  */
4062  while (XCheckMaskEvent(display,ButtonMotionMask,&event)) ;
4063  if (state & InactiveWidgetState)
4064  break;
4065  if (action_info.raised == MatteIsActive(action_info,event.xmotion))
4066  {
4067  /*
4068  Action button status changed.
4069  */
4070  action_info.raised=action_info.raised == MagickFalse ?
4072  XDrawBeveledButton(display,&windows->widget,&action_info);
4073  break;
4074  }
4075  if (cancel_info.raised == MatteIsActive(cancel_info,event.xmotion))
4076  {
4077  /*
4078  Cancel button status changed.
4079  */
4080  cancel_info.raised=cancel_info.raised == MagickFalse ?
4082  XDrawBeveledButton(display,&windows->widget,&cancel_info);
4083  break;
4084  }
4085  break;
4086  }
4087  case SelectionClear:
4088  {
4089  reply_info.highlight=MagickFalse;
4090  XDrawMatteText(display,&windows->widget,&reply_info);
4091  break;
4092  }
4093  case SelectionNotify:
4094  {
4095  Atom
4096  type;
4097 
4098  int
4099  format;
4100 
4101  unsigned char
4102  *data;
4103 
4104  unsigned long
4105  after,
4106  length;
4107 
4108  /*
4109  Obtain response from primary selection.
4110  */
4111  if (event.xselection.property == (Atom) None)
4112  break;
4113  status=XGetWindowProperty(display,event.xselection.requestor,
4114  event.xselection.property,0L,2047L,MagickTrue,XA_STRING,&type,
4115  &format,&length,&after,&data);
4116  if ((status != Success) || (type != XA_STRING) || (format == 32) ||
4117  (length == 0))
4118  break;
4119  if ((Extent(reply_info.text)+length) >= (MagickPathExtent-1))
4120  (void) XBell(display,0);
4121  else
4122  {
4123  /*
4124  Insert primary selection in reply text.
4125  */
4126  *(data+length)='\0';
4127  XEditText(display,&reply_info,(KeySym) XK_Insert,(char *) data,
4128  state);
4129  XDrawMatteText(display,&windows->widget,&reply_info);
4130  }
4131  (void) XFree((void *) data);
4132  break;
4133  }
4134  case SelectionRequest:
4135  {
4136  XSelectionEvent
4137  notify;
4138 
4139  XSelectionRequestEvent
4140  *request;
4141 
4142  if (reply_info.highlight == MagickFalse)
4143  break;
4144  /*
4145  Set primary selection.
4146  */
4147  request=(&(event.xselectionrequest));
4148  (void) XChangeProperty(request->display,request->requestor,
4149  request->property,request->target,8,PropModeReplace,
4150  (unsigned char *) primary_selection,Extent(primary_selection));
4151  notify.type=SelectionNotify;
4152  notify.display=request->display;
4153  notify.requestor=request->requestor;
4154  notify.selection=request->selection;
4155  notify.target=request->target;
4156  notify.time=request->time;
4157  if (request->property == None)
4158  notify.property=request->target;
4159  else
4160  notify.property=request->property;
4161  (void) XSendEvent(request->display,request->requestor,False,0,
4162  (XEvent *) &notify);
4163  }
4164  default:
4165  break;
4166  }
4167  } while ((state & ExitState) == 0);
4168  XSetCursorState(display,windows,MagickFalse);
4169  (void) XWithdrawWindow(display,windows->widget.id,windows->widget.screen);
4170  XCheckRefreshWindows(display,windows);
4171  if (anomaly)
4172  if (special_info.raised)
4173  if (*reply != '\0')
4174  raised=MagickTrue;
4175  return(raised == MagickFalse);
4176 }
4177 
4178 /*
4179 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4180 % %
4181 % %
4182 % %
4183 % X F i l e B r o w s e r W i d g e t %
4184 % %
4185 % %
4186 % %
4187 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4188 %
4189 % XFileBrowserWidget() displays a File Browser widget with a file query to the
4190 % user. The user keys a reply and presses the Action or Cancel button to
4191 % exit. The typed text is returned as the reply function parameter.
4192 %
4193 % The format of the XFileBrowserWidget method is:
4194 %
4195 % void XFileBrowserWidget(Display *display,XWindows *windows,
4196 % const char *action,char *reply)
4197 %
4198 % A description of each parameter follows:
4199 %
4200 % o display: Specifies a connection to an X server; returned from
4201 % XOpenDisplay.
4202 %
4203 % o window: Specifies a pointer to a XWindows structure.
4204 %
4205 % o action: Specifies a pointer to the action of this widget.
4206 %
4207 % o reply: the response from the user is returned in this parameter.
4208 %
4209 */
4210 MagickPrivate void XFileBrowserWidget(Display *display,XWindows *windows,
4211  const char *action,char *reply)
4212 {
4213 #define CancelButtonText "Cancel"
4214 #define DirectoryText "Directory:"
4215 #define FilenameText "File name:"
4216 #define GrabButtonText "Grab"
4217 #define FormatButtonText "Format"
4218 #define HomeButtonText "Home"
4219 #define UpButtonText "Up"
4220 
4221  char
4222  *directory,
4223  **filelist,
4224  home_directory[MagickPathExtent],
4225  primary_selection[MagickPathExtent],
4226  text[MagickPathExtent],
4227  working_path[MagickPathExtent];
4228 
4229  int
4230  x,
4231  y;
4232 
4233  register ssize_t
4234  i;
4235 
4236  static char
4237  glob_pattern[MagickPathExtent] = "*",
4238  format[MagickPathExtent] = "miff";
4239 
4240  static MagickStatusType
4241  mask = (MagickStatusType) (CWWidth | CWHeight | CWX | CWY);
4242 
4243  Status
4244  status;
4245 
4246  unsigned int
4247  anomaly,
4248  height,
4249  text_width,
4250  visible_files,
4251  width;
4252 
4253  size_t
4254  delay,
4255  files,
4256  state;
4257 
4258  XEvent
4259  event;
4260 
4261  XFontStruct
4262  *font_info;
4263 
4264  XTextProperty
4265  window_name;
4266 
4267  XWidgetInfo
4268  action_info,
4269  cancel_info,
4270  expose_info,
4271  special_info,
4272  list_info,
4273  home_info,
4274  north_info,
4275  reply_info,
4276  scroll_info,
4277  selection_info,
4278  slider_info,
4279  south_info,
4280  text_info,
4281  up_info;
4282 
4283  XWindowChanges
4284  window_changes;
4285 
4286  /*
4287  Read filelist from current directory.
4288  */
4289  assert(display != (Display *) NULL);
4290  assert(windows != (XWindows *) NULL);
4291  assert(action != (char *) NULL);
4292  assert(reply != (char *) NULL);
4293  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",action);
4294  XSetCursorState(display,windows,MagickTrue);
4295  XCheckRefreshWindows(display,windows);
4296  directory=getcwd(home_directory,MagickPathExtent);
4297  (void) directory;
4298  (void) CopyMagickString(working_path,home_directory,MagickPathExtent);
4299  filelist=ListFiles(working_path,glob_pattern,&files);
4300  if (filelist == (char **) NULL)
4301  {
4302  /*
4303  Directory read failed.
4304  */
4305  XNoticeWidget(display,windows,"Unable to read directory:",working_path);
4306  (void) XDialogWidget(display,windows,action,"Enter filename:",reply);
4307  return;
4308  }
4309  /*
4310  Determine File Browser widget attributes.
4311  */
4312  font_info=windows->widget.font_info;
4313  text_width=0;
4314  for (i=0; i < (ssize_t) files; i++)
4315  if (WidgetTextWidth(font_info,filelist[i]) > text_width)
4316  text_width=WidgetTextWidth(font_info,filelist[i]);
4317  width=WidgetTextWidth(font_info,(char *) action);
4318  if (WidgetTextWidth(font_info,GrabButtonText) > width)
4319  width=WidgetTextWidth(font_info,GrabButtonText);
4320  if (WidgetTextWidth(font_info,FormatButtonText) > width)
4321  width=WidgetTextWidth(font_info,FormatButtonText);
4322  if (WidgetTextWidth(font_info,CancelButtonText) > width)
4323  width=WidgetTextWidth(font_info,CancelButtonText);
4324  if (WidgetTextWidth(font_info,HomeButtonText) > width)
4325  width=WidgetTextWidth(font_info,HomeButtonText);
4326  if (WidgetTextWidth(font_info,UpButtonText) > width)
4327  width=WidgetTextWidth(font_info,UpButtonText);
4328  width+=QuantumMargin;
4329  if (WidgetTextWidth(font_info,DirectoryText) > width)
4330  width=WidgetTextWidth(font_info,DirectoryText);
4331  if (WidgetTextWidth(font_info,FilenameText) > width)
4332  width=WidgetTextWidth(font_info,FilenameText);
4333  height=(unsigned int) (font_info->ascent+font_info->descent);
4334  /*
4335  Position File Browser widget.
4336  */
4337  windows->widget.width=width+MagickMin((int) text_width,(int) MaxTextWidth)+
4338  6*QuantumMargin;
4339  windows->widget.min_width=width+MinTextWidth+4*QuantumMargin;
4340  if (windows->widget.width < windows->widget.min_width)
4341  windows->widget.width=windows->widget.min_width;
4342  windows->widget.height=(unsigned int)
4343  (((81*height) >> 2)+((13*QuantumMargin) >> 1)+4);
4344  windows->widget.min_height=(unsigned int)
4345  (((23*height) >> 1)+((13*QuantumMargin) >> 1)+4);
4346  if (windows->widget.height < windows->widget.min_height)
4347  windows->widget.height=windows->widget.min_height;
4348  XConstrainWindowPosition(display,&windows->widget);
4349  /*
4350  Map File Browser widget.
4351  */
4352  (void) CopyMagickString(windows->widget.name,"Browse and Select a File",
4354  status=XStringListToTextProperty(&windows->widget.name,1,&window_name);
4355  if (status != False)
4356  {
4357  XSetWMName(display,windows->widget.id,&window_name);
4358  XSetWMIconName(display,windows->widget.id,&window_name);
4359  (void) XFree((void *) window_name.value);
4360  }
4361  window_changes.width=(int) windows->widget.width;
4362  window_changes.height=(int) windows->widget.height;
4363  window_changes.x=windows->widget.x;
4364  window_changes.y=windows->widget.y;
4365  (void) XReconfigureWMWindow(display,windows->widget.id,
4366  windows->widget.screen,mask,&window_changes);
4367  (void) XMapRaised(display,windows->widget.id);
4368  windows->widget.mapped=MagickFalse;
4369  /*
4370  Respond to X events.
4371  */
4372  XGetWidgetInfo((char *) NULL,&slider_info);
4373  XGetWidgetInfo((char *) NULL,&north_info);
4374  XGetWidgetInfo((char *) NULL,&south_info);
4375  XGetWidgetInfo((char *) NULL,&expose_info);
4376  visible_files=0;
4377  anomaly=(LocaleCompare(action,"Composite") == 0) ||
4378  (LocaleCompare(action,"Open") == 0) || (LocaleCompare(action,"Map") == 0);
4379  *reply='\0';
4380  delay=SuspendTime << 2;
4381  state=UpdateConfigurationState;
4382  do
4383  {
4384  if (state & UpdateConfigurationState)
4385  {
4386  int
4387  id;
4388 
4389  /*
4390  Initialize button information.
4391  */
4392  XGetWidgetInfo(CancelButtonText,&cancel_info);
4393  cancel_info.width=width;
4394  cancel_info.height=(unsigned int) ((3*height) >> 1);
4395  cancel_info.x=(int)
4396  (windows->widget.width-cancel_info.width-QuantumMargin-2);
4397  cancel_info.y=(int)
4398  (windows->widget.height-cancel_info.height-QuantumMargin);
4399  XGetWidgetInfo(action,&action_info);
4400  action_info.width=width;
4401  action_info.height=(unsigned int) ((3*height) >> 1);
4402  action_info.x=cancel_info.x-(cancel_info.width+(QuantumMargin >> 1)+
4403  (action_info.bevel_width << 1));
4404  action_info.y=cancel_info.y;
4405  XGetWidgetInfo(GrabButtonText,&special_info);
4406  special_info.width=width;
4407  special_info.height=(unsigned int) ((3*height) >> 1);
4408  special_info.x=action_info.x-(action_info.width+(QuantumMargin >> 1)+
4409  (special_info.bevel_width << 1));
4410  special_info.y=action_info.y;
4411  if (anomaly == MagickFalse)
4412  {
4413  register char
4414  *p;
4415 
4416  special_info.text=(char *) FormatButtonText;
4417  p=reply+Extent(reply)-1;
4418  while ((p > (reply+1)) && (*(p-1) != '.'))
4419  p--;
4420  if ((p > (reply+1)) && (*(p-1) == '.'))
4421  (void) CopyMagickString(format,p,MagickPathExtent);
4422  }
4423  XGetWidgetInfo(UpButtonText,&up_info);
4424  up_info.width=width;
4425  up_info.height=(unsigned int) ((3*height) >> 1);
4426  up_info.x=QuantumMargin;
4427  up_info.y=((5*QuantumMargin) >> 1)+height;
4428  XGetWidgetInfo(HomeButtonText,&home_info);
4429  home_info.width=width;
4430  home_info.height=(unsigned int) ((3*height) >> 1);
4431  home_info.x=QuantumMargin;
4432  home_info.y=up_info.y+up_info.height+QuantumMargin;
4433  /*
4434  Initialize reply information.
4435  */
4436  XGetWidgetInfo(reply,&reply_info);
4437  reply_info.raised=MagickFalse;
4438  reply_info.bevel_width--;
4439  reply_info.width=windows->widget.width-width-((6*QuantumMargin) >> 1);
4440  reply_info.height=height << 1;
4441  reply_info.x=(int) (width+(QuantumMargin << 1));
4442  reply_info.y=action_info.y-reply_info.height-QuantumMargin;
4443  /*
4444  Initialize scroll information.
4445  */
4446  XGetWidgetInfo((char *) NULL,&scroll_info);
4447  scroll_info.bevel_width--;
4448  scroll_info.width=height;
4449  scroll_info.height=(unsigned int)
4450  (reply_info.y-up_info.y-(QuantumMargin >> 1));
4451  scroll_info.x=reply_info.x+(reply_info.width-scroll_info.width);
4452  scroll_info.y=up_info.y-reply_info.bevel_width;
4453  scroll_info.raised=MagickFalse;
4454  scroll_info.trough=MagickTrue;
4455  north_info=scroll_info;
4456  north_info.raised=MagickTrue;
4457  north_info.width-=(north_info.bevel_width << 1);
4458  north_info.height=north_info.width-1;
4459  north_info.x+=north_info.bevel_width;
4460  north_info.y+=north_info.bevel_width;
4461  south_info=north_info;
4462  south_info.y=scroll_info.y+scroll_info.height-scroll_info.bevel_width-
4463  south_info.height;
4464  id=slider_info.id;
4465  slider_info=north_info;
4466  slider_info.id=id;
4467  slider_info.width-=2;
4468  slider_info.min_y=north_info.y+north_info.height+north_info.bevel_width+
4469  slider_info.bevel_width+2;
4470  slider_info.height=scroll_info.height-((slider_info.min_y-
4471  scroll_info.y+1) << 1)+4;
4472  visible_files=scroll_info.height/(height+(height >> 3));
4473  if (files > visible_files)
4474  slider_info.height=(unsigned int)
4475  ((visible_files*slider_info.height)/files);
4476  slider_info.max_y=south_info.y-south_info.bevel_width-
4477  slider_info.bevel_width-2;
4478  slider_info.x=scroll_info.x+slider_info.bevel_width+1;
4479  slider_info.y=slider_info.min_y;
4480  expose_info=scroll_info;
4481  expose_info.y=slider_info.y;
4482  /*
4483  Initialize list information.
4484  */
4485  XGetWidgetInfo((char *) NULL,&list_info);
4486  list_info.raised=MagickFalse;
4487  list_info.bevel_width--;
4488  list_info.width=(unsigned int)
4489  (scroll_info.x-reply_info.x-(QuantumMargin >> 1));
4490  list_info.height=scroll_info.height;
4491  list_info.x=reply_info.x;
4492  list_info.y=scroll_info.y;
4493  if (windows->widget.mapped == MagickFalse)
4494  state|=JumpListState;
4495  /*
4496  Initialize text information.
4497  */
4498  *text='\0';
4499  XGetWidgetInfo(text,&text_info);
4500  text_info.center=MagickFalse;
4501  text_info.width=reply_info.width;
4502  text_info.height=height;
4503  text_info.x=list_info.x-(QuantumMargin >> 1);
4504  text_info.y=QuantumMargin;
4505  /*
4506  Initialize selection information.
4507  */
4508  XGetWidgetInfo((char *) NULL,&selection_info);
4509  selection_info.center=MagickFalse;
4510  selection_info.width=list_info.width;
4511  selection_info.height=(unsigned int) ((9*height) >> 3);
4512  selection_info.x=list_info.x;
4513  state&=(~UpdateConfigurationState);
4514  }
4515  if (state & RedrawWidgetState)
4516  {
4517  /*
4518  Redraw File Browser window.
4519  */
4520  x=QuantumMargin;
4521  y=text_info.y+((text_info.height-height) >> 1)+font_info->ascent;
4522  (void) XDrawString(display,windows->widget.id,
4523  windows->widget.annotate_context,x,y,DirectoryText,
4524  Extent(DirectoryText));
4525  (void) CopyMagickString(text_info.text,working_path,MagickPathExtent);
4526  (void) ConcatenateMagickString(text_info.text,DirectorySeparator,
4528  (void) ConcatenateMagickString(text_info.text,glob_pattern,
4530  XDrawWidgetText(display,&windows->widget,&text_info);
4531  XDrawBeveledButton(display,&windows->widget,&up_info);
4532  XDrawBeveledButton(display,&windows->widget,&home_info);
4533  XDrawBeveledMatte(display,&windows->widget,&list_info);
4534  XDrawBeveledMatte(display,&windows->widget,&scroll_info);
4535  XDrawTriangleNorth(display,&windows->widget,&north_info);
4536  XDrawBeveledButton(display,&windows->widget,&slider_info);
4537  XDrawTriangleSouth(display,&windows->widget,&south_info);
4538  x=QuantumMargin;
4539  y=reply_info.y+((reply_info.height-height) >> 1)+font_info->ascent;
4540  (void) XDrawString(display,windows->widget.id,
4541  windows->widget.annotate_context,x,y,FilenameText,
4542  Extent(FilenameText));
4543  XDrawBeveledMatte(display,&windows->widget,&reply_info);
4544  XDrawMatteText(display,&windows->widget,&reply_info);
4545  XDrawBeveledButton(display,&windows->widget,&special_info);
4546  XDrawBeveledButton(display,&windows->widget,&action_info);
4547  XDrawBeveledButton(display,&windows->widget,&cancel_info);
4548  XHighlightWidget(display,&windows->widget,BorderOffset,BorderOffset);
4549  selection_info.id=(~0);
4550  state|=RedrawListState;
4551  state&=(~RedrawWidgetState);
4552  }
4553  if (state & UpdateListState)
4554  {
4555  char
4556  **checklist;
4557 
4558  size_t
4559  number_files;
4560 
4561  /*
4562  Update file list.
4563  */
4564  checklist=ListFiles(working_path,glob_pattern,&number_files);
4565  if (checklist == (char **) NULL)
4566  {
4567  /*
4568  Reply is a filename, exit.
4569  */
4570  action_info.raised=MagickFalse;
4571  XDrawBeveledButton(display,&windows->widget,&action_info);
4572  break;
4573  }
4574  for (i=0; i < (ssize_t) files; i++)
4575  filelist[i]=DestroyString(filelist[i]);
4576  if (filelist != (char **) NULL)
4577  filelist=(char **) RelinquishMagickMemory(filelist);
4578  filelist=checklist;
4579  files=number_files;
4580  /*
4581  Update file list.
4582  */
4583  slider_info.height=
4584  scroll_info.height-((slider_info.min_y-scroll_info.y+1) << 1)+1;
4585  if (files > visible_files)
4586  slider_info.height=(unsigned int)
4587  ((visible_files*slider_info.height)/files);
4588  slider_info.max_y=south_info.y-south_info.bevel_width-
4589  slider_info.bevel_width-2;
4590  slider_info.id=0;
4591  slider_info.y=slider_info.min_y;
4592  expose_info.y=slider_info.y;
4593  selection_info.id=(~0);
4594  list_info.id=(~0);
4595  state|=RedrawListState;
4596  /*
4597  Redraw directory name & reply.
4598  */
4599  if (IsGlob(reply_info.text) == MagickFalse)
4600  {
4601  *reply_info.text='\0';
4602  reply_info.cursor=reply_info.text;
4603  }
4604  (void) CopyMagickString(text_info.text,working_path,MagickPathExtent);
4605  (void) ConcatenateMagickString(text_info.text,DirectorySeparator,
4607  (void) ConcatenateMagickString(text_info.text,glob_pattern,
4609  XDrawWidgetText(display,&windows->widget,&text_info);
4610  XDrawMatteText(display,&windows->widget,&reply_info);
4611  XDrawBeveledMatte(display,&windows->widget,&scroll_info);
4612  XDrawTriangleNorth(display,&windows->widget,&north_info);
4613  XDrawBeveledButton(display,&windows->widget,&slider_info);
4614  XDrawTriangleSouth(display,&windows->widget,&south_info);
4615  XHighlightWidget(display,&windows->widget,BorderOffset,BorderOffset);
4616  state&=(~UpdateListState);
4617  }
4618  if (state & JumpListState)
4619  {
4620  /*
4621  Jump scroll to match user filename.
4622  */
4623  list_info.id=(~0);
4624  for (i=0; i < (ssize_t) files; i++)
4625  if (LocaleCompare(filelist[i],reply) >= 0)
4626  {
4627  list_info.id=(int)
4628  (LocaleCompare(filelist[i],reply) == 0 ? i : ~0);
4629  break;
4630  }
4631  if ((i < (ssize_t) slider_info.id) ||
4632  (i >= (ssize_t) (slider_info.id+visible_files)))
4633  slider_info.id=(int) i-(visible_files >> 1);
4634  selection_info.id=(~0);
4635  state|=RedrawListState;
4636  state&=(~JumpListState);
4637  }
4638  if (state & RedrawListState)
4639  {
4640  /*
4641  Determine slider id and position.
4642  */
4643  if (slider_info.id >= (int) (files-visible_files))
4644  slider_info.id=(int) (files-visible_files);
4645  if ((slider_info.id < 0) || (files <= visible_files))
4646  slider_info.id=0;
4647  slider_info.y=slider_info.min_y;
4648  if (files > 0)
4649  slider_info.y+=((ssize_t) slider_info.id*(slider_info.max_y-
4650  slider_info.min_y+1)/files);
4651  if (slider_info.id != selection_info.id)
4652  {
4653  /*
4654  Redraw scroll bar and file names.
4655  */
4656  selection_info.id=slider_info.id;
4657  selection_info.y=list_info.y+(height >> 3)+2;
4658  for (i=0; i < (ssize_t) visible_files; i++)
4659  {
4660  selection_info.raised=(int) (slider_info.id+i) != list_info.id ?
4662  selection_info.text=(char *) NULL;
4663  if ((slider_info.id+i) < (ssize_t) files)
4664  selection_info.text=filelist[slider_info.id+i];
4665  XDrawWidgetText(display,&windows->widget,&selection_info);
4666  selection_info.y+=(int) selection_info.height;
4667  }
4668  /*
4669  Update slider.
4670  */
4671  if (slider_info.y > expose_info.y)
4672  {
4673  expose_info.height=(unsigned int) slider_info.y-expose_info.y;
4674  expose_info.y=slider_info.y-expose_info.height-
4675  slider_info.bevel_width-1;
4676  }
4677  else
4678  {
4679  expose_info.height=(unsigned int) expose_info.y-slider_info.y;
4680  expose_info.y=slider_info.y+slider_info.height+
4681  slider_info.bevel_width+1;
4682  }
4683  XDrawTriangleNorth(display,&windows->widget,&north_info);
4684  XDrawMatte(display,&windows->widget,&expose_info);
4685  XDrawBeveledButton(display,&windows->widget,&slider_info);
4686  XDrawTriangleSouth(display,&windows->widget,&south_info);
4687  expose_info.y=slider_info.y;
4688  }
4689  state&=(~RedrawListState);
4690  }
4691  /*
4692  Wait for next event.
4693  */
4694  if (north_info.raised && south_info.raised)
4695  (void) XIfEvent(display,&event,XScreenEvent,(char *) windows);
4696  else
4697  {
4698  /*
4699  Brief delay before advancing scroll bar.
4700  */
4701  XDelay(display,delay);
4702  delay=SuspendTime;
4703  (void) XCheckIfEvent(display,&event,XScreenEvent,(char *) windows);
4704  if (north_info.raised == MagickFalse)
4705  if (slider_info.id > 0)
4706  {
4707  /*
4708  Move slider up.
4709  */
4710  slider_info.id--;
4711  state|=RedrawListState;
4712  }
4713  if (south_info.raised == MagickFalse)
4714  if (slider_info.id < (int) files)
4715  {
4716  /*
4717  Move slider down.
4718  */
4719  slider_info.id++;
4720  state|=RedrawListState;
4721  }
4722  if (event.type != ButtonRelease)
4723  continue;
4724  }
4725  switch (event.type)
4726  {
4727  case ButtonPress:
4728  {
4729  if (MatteIsActive(slider_info,event.xbutton))
4730  {
4731  /*
4732  Track slider.
4733  */
4734  slider_info.active=MagickTrue;
4735  break;
4736  }
4737  if (MatteIsActive(north_info,event.xbutton))
4738  if (slider_info.id > 0)
4739  {
4740  /*
4741  Move slider up.
4742  */
4743  north_info.raised=MagickFalse;
4744  slider_info.id--;
4745  state|=RedrawListState;
4746  break;
4747  }
4748  if (MatteIsActive(south_info,event.xbutton))
4749  if (slider_info.id < (int) files)
4750  {
4751  /*
4752  Move slider down.
4753  */
4754  south_info.raised=MagickFalse;
4755  slider_info.id++;
4756  state|=RedrawListState;
4757  break;
4758  }
4759  if (MatteIsActive(scroll_info,event.xbutton))
4760  {
4761  /*
4762  Move slider.
4763  */
4764  if (event.xbutton.y < slider_info.y)
4765  slider_info.id-=(visible_files-1);
4766  else
4767  slider_info.id+=(visible_files-1);
4768  state|=RedrawListState;
4769  break;
4770  }
4771  if (MatteIsActive(list_info,event.xbutton))
4772  {
4773  int
4774  id;
4775 
4776  /*
4777  User pressed file matte.
4778  */
4779  id=slider_info.id+(event.xbutton.y-(list_info.y+(height >> 1))+1)/
4780  selection_info.height;
4781  if (id >= (int) files)
4782  break;
4783  (void) CopyMagickString(reply_info.text,filelist[id],MagickPathExtent);
4784  reply_info.highlight=MagickFalse;
4785  reply_info.marker=reply_info.text;
4786  reply_info.cursor=reply_info.text+Extent(reply_info.text);
4787  XDrawMatteText(display,&windows->widget,&reply_info);
4788  if (id == list_info.id)
4789  {
4790  register char
4791  *p;
4792 
4793  p=reply_info.text+strlen(reply_info.text)-1;
4794  if (*p == *DirectorySeparator)
4795  ChopPathComponents(reply_info.text,1);
4796  (void) ConcatenateMagickString(working_path,DirectorySeparator,
4798  (void) ConcatenateMagickString(working_path,reply_info.text,
4800  *reply='\0';
4801  state|=UpdateListState;
4802  }
4803  selection_info.id=(~0);
4804  list_info.id=id;
4805  state|=RedrawListState;
4806  break;
4807  }
4808  if (MatteIsActive(up_info,event.xbutton))
4809  {
4810  /*
4811  User pressed Up button.
4812  */
4813  up_info.raised=MagickFalse;
4814  XDrawBeveledButton(display,&windows->widget,&up_info);
4815  break;
4816  }
4817  if (MatteIsActive(home_info,event.xbutton))
4818  {
4819  /*
4820  User pressed Home button.
4821  */
4822  home_info.raised=MagickFalse;
4823  XDrawBeveledButton(display,&windows->widget,&home_info);
4824  break;
4825  }
4826  if (MatteIsActive(special_info,event.xbutton))
4827  {
4828  /*
4829  User pressed Special button.
4830  */
4831  special_info.raised=MagickFalse;
4832  XDrawBeveledButton(display,&windows->widget,&special_info);
4833  break;
4834  }
4835  if (MatteIsActive(action_info,event.xbutton))
4836  {
4837  /*
4838  User pressed action button.
4839  */
4840  action_info.raised=MagickFalse;
4841  XDrawBeveledButton(display,&windows->widget,&action_info);
4842  break;
4843  }
4844  if (MatteIsActive(cancel_info,event.xbutton))
4845  {
4846  /*
4847  User pressed Cancel button.
4848  */
4849  cancel_info.raised=MagickFalse;
4850  XDrawBeveledButton(display,&windows->widget,&cancel_info);
4851  break;
4852  }
4853  if (MatteIsActive(reply_info,event.xbutton) == MagickFalse)
4854  break;
4855  if (event.xbutton.button != Button2)
4856  {
4857  static Time
4858  click_time;
4859 
4860  /*
4861  Move text cursor to position of button press.
4862  */
4863  x=event.xbutton.x-reply_info.x-(QuantumMargin >> 2);
4864  for (i=1; i <= (ssize_t) Extent(reply_info.marker); i++)
4865  if (XTextWidth(font_info,reply_info.marker,(int) i) > x)
4866  break;
4867  reply_info.cursor=reply_info.marker+i-1;
4868  if (event.xbutton.time > (click_time+DoubleClick))
4869  reply_info.highlight=MagickFalse;
4870  else
4871  {
4872  /*
4873  Become the XA_PRIMARY selection owner.
4874  */
4875  (void) CopyMagickString(primary_selection,reply_info.text,
4877  (void) XSetSelectionOwner(display,XA_PRIMARY,windows->widget.id,
4878  event.xbutton.time);
4879  reply_info.highlight=XGetSelectionOwner(display,XA_PRIMARY) ==
4880  windows->widget.id ? MagickTrue : MagickFalse;
4881  }
4882  XDrawMatteText(display,&windows->widget,&reply_info);
4883  click_time=event.xbutton.time;
4884  break;
4885  }
4886  /*
4887  Request primary selection.
4888  */
4889  (void) XConvertSelection(display,XA_PRIMARY,XA_STRING,XA_STRING,
4890  windows->widget.id,event.xbutton.time);
4891  break;
4892  }
4893  case ButtonRelease:
4894  {
4895  if (windows->widget.mapped == MagickFalse)
4896  break;
4897  if (north_info.raised == MagickFalse)
4898  {
4899  /*
4900  User released up button.
4901  */
4902  delay=SuspendTime << 2;
4903  north_info.raised=MagickTrue;
4904  XDrawTriangleNorth(display,&windows->widget,&north_info);
4905  }
4906  if (south_info.raised == MagickFalse)
4907  {
4908  /*
4909  User released down button.
4910  */
4911  delay=SuspendTime << 2;
4912  south_info.raised=MagickTrue;
4913  XDrawTriangleSouth(display,&windows->widget,&south_info);
4914  }
4915  if (slider_info.active)
4916  {
4917  /*
4918  Stop tracking slider.
4919  */
4920  slider_info.active=MagickFalse;
4921  break;
4922  }
4923  if (up_info.raised == MagickFalse)
4924  {
4925  if (event.xbutton.window == windows->widget.id)
4926  if (MatteIsActive(up_info,event.xbutton))
4927  {
4928  ChopPathComponents(working_path,1);
4929  if (*working_path == '\0')
4930  (void) CopyMagickString(working_path,DirectorySeparator,
4932  state|=UpdateListState;
4933  }
4934  up_info.raised=MagickTrue;
4935  XDrawBeveledButton(display,&windows->widget,&up_info);
4936  }
4937  if (home_info.raised == MagickFalse)
4938  {
4939  if (event.xbutton.window == windows->widget.id)
4940  if (MatteIsActive(home_info,event.xbutton))
4941  {
4942  (void) CopyMagickString(working_path,home_directory,
4944  state|=UpdateListState;
4945  }
4946  home_info.raised=MagickTrue;
4947  XDrawBeveledButton(display,&windows->widget,&home_info);
4948  }
4949  if (special_info.raised == MagickFalse)
4950  {
4951  if (anomaly == MagickFalse)
4952  {
4953  char
4954  **formats;
4955 
4957  *exception;
4958 
4959  size_t
4960  number_formats;
4961 
4962  /*
4963  Let user select image format.
4964  */
4965  exception=AcquireExceptionInfo();
4966  formats=GetMagickList("*",&number_formats,exception);
4967  exception=DestroyExceptionInfo(exception);
4968  if (formats == (char **) NULL)
4969  break;
4970  (void) XCheckDefineCursor(display,windows->widget.id,
4971  windows->widget.busy_cursor);
4972  windows->popup.x=windows->widget.x+60;
4973  windows->popup.y=windows->widget.y+60;
4974  XListBrowserWidget(display,windows,&windows->popup,
4975  (const char **) formats,"Select","Select image format type:",
4976  format);
4977  XSetCursorState(display,windows,MagickTrue);
4978  (void) XCheckDefineCursor(display,windows->widget.id,
4979  windows->widget.cursor);
4980  LocaleLower(format);
4981  AppendImageFormat(format,reply_info.text);
4982  reply_info.cursor=reply_info.text+Extent(reply_info.text);
4983  XDrawMatteText(display,&windows->widget,&reply_info);
4984  special_info.raised=MagickTrue;
4985  XDrawBeveledButton(display,&windows->widget,&special_info);
4986  for (i=0; i < (ssize_t) number_formats; i++)
4987  formats[i]=DestroyString(formats[i]);
4988  formats=(char **) RelinquishMagickMemory(formats);
4989  break;
4990  }
4991  if (event.xbutton.window == windows->widget.id)
4992  if (MatteIsActive(special_info,event.xbutton))
4993  {
4994  (void) CopyMagickString(working_path,"x:",MagickPathExtent);
4995  state|=ExitState;
4996  }
4997  special_info.raised=MagickTrue;
4998  XDrawBeveledButton(display,&windows->widget,&special_info);
4999  }
5000  if (action_info.raised == MagickFalse)
5001  {
5002  if (event.xbutton.window == windows->widget.id)
5003  {
5004  if (MatteIsActive(action_info,event.xbutton))
5005  {
5006  if (*reply_info.text == '\0')
5007  (void) XBell(display,0);
5008  else
5009  state|=ExitState;
5010  }
5011  }
5012  action_info.raised=MagickTrue;
5013  XDrawBeveledButton(display,&windows->widget,&action_info);
5014  }
5015  if (cancel_info.raised == MagickFalse)
5016  {
5017  if (event.xbutton.window == windows->widget.id)
5018  if (MatteIsActive(cancel_info,event.xbutton))
5019  {
5020  *reply_info.text='\0';
5021  *reply='\0';
5022  state|=ExitState;
5023  }
5024  cancel_info.raised=MagickTrue;
5025  XDrawBeveledButton(display,&windows->widget,&cancel_info);
5026  }
5027  break;
5028  }
5029  case ClientMessage:
5030  {
5031  /*
5032  If client window delete message, exit.
5033  */
5034  if (event.xclient.message_type != windows->wm_protocols)
5035  break;
5036  if (*event.xclient.data.l == (int) windows->wm_take_focus)
5037  {
5038  (void) XSetInputFocus(display,event.xclient.window,RevertToParent,
5039  (Time) event.xclient.data.l[1]);
5040  break;
5041  }
5042  if (*event.xclient.data.l != (int) windows->wm_delete_window)
5043  break;
5044  if (event.xclient.window == windows->widget.id)
5045  {
5046  *reply_info.text='\0';
5047  state|=ExitState;
5048  break;
5049  }
5050  break;
5051  }
5052  case ConfigureNotify:
5053  {
5054  /*
5055  Update widget configuration.
5056  */
5057  if (event.xconfigure.window != windows->widget.id)
5058  break;
5059  if ((event.xconfigure.width == (int) windows->widget.width) &&
5060  (event.xconfigure.height == (int) windows->widget.height))
5061  break;
5062  windows->widget.width=(unsigned int)
5063  MagickMax(event.xconfigure.width,(int) windows->widget.min_width);
5064  windows->widget.height=(unsigned int)
5065  MagickMax(event.xconfigure.height,(int) windows->widget.min_height);
5066  state|=UpdateConfigurationState;
5067  break;
5068  }
5069  case EnterNotify:
5070  {
5071  if (event.xcrossing.window != windows->widget.id)
5072  break;
5073  state&=(~InactiveWidgetState);
5074  break;
5075  }
5076  case Expose:
5077  {
5078  if (event.xexpose.window != windows->widget.id)
5079  break;
5080  if (event.xexpose.count != 0)
5081  break;
5082  state|=RedrawWidgetState;
5083  break;
5084  }
5085  case KeyPress:
5086  {
5087  static char
5088  command[MagickPathExtent];
5089 
5090  static int
5091  length;
5092 
5093  static KeySym
5094  key_symbol;
5095 
5096  /*
5097  Respond to a user key press.
5098  */
5099  if (event.xkey.window != windows->widget.id)
5100  break;
5101  length=XLookupString((XKeyEvent *) &event.xkey,command,
5102  (int) sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5103  *(command+length)='\0';
5104  if (AreaIsActive(scroll_info,event.xkey))
5105  {
5106  /*
5107  Move slider.
5108  */
5109  switch ((int) key_symbol)
5110  {
5111  case XK_Home:
5112  case XK_KP_Home:
5113  {
5114  slider_info.id=0;
5115  break;
5116  }
5117  case XK_Up:
5118  case XK_KP_Up:
5119  {
5120  slider_info.id--;
5121  break;
5122  }
5123  case XK_Down:
5124  case XK_KP_Down:
5125  {
5126  slider_info.id++;
5127  break;
5128  }
5129  case XK_Prior:
5130  case XK_KP_Prior:
5131  {
5132  slider_info.id-=visible_files;
5133  break;
5134  }
5135  case XK_Next:
5136  case XK_KP_Next:
5137  {
5138  slider_info.id+=visible_files;
5139  break;
5140  }
5141  case XK_End:
5142  case XK_KP_End:
5143  {
5144  slider_info.id=(int) files;
5145  break;
5146  }
5147  }
5148  state|=RedrawListState;
5149  break;
5150  }
5151  if ((key_symbol == XK_Return) || (key_symbol == XK_KP_Enter))
5152  {
5153  /*
5154  Read new directory or glob patterm.
5155  */
5156  if (*reply_info.text == '\0')
5157  break;
5158  if (IsGlob(reply_info.text))
5159  (void) CopyMagickString(glob_pattern,reply_info.text,
5161  else
5162  {
5163  (void) ConcatenateMagickString(working_path,DirectorySeparator,
5165  (void) ConcatenateMagickString(working_path,reply_info.text,
5167  if (*working_path == '~')
5168  ExpandFilename(working_path);
5169  *reply='\0';
5170  }
5171  state|=UpdateListState;
5172  break;
5173  }
5174  if (key_symbol == XK_Control_L)
5175  {
5176  state|=ControlState;
5177  break;
5178  }
5179  if (state & ControlState)
5180  switch ((int) key_symbol)
5181  {
5182  case XK_u:
5183  case XK_U:
5184  {
5185  /*
5186  Erase the entire line of text.
5187  */
5188  *reply_info.text='\0';
5189  reply_info.cursor=reply_info.text;
5190  reply_info.marker=reply_info.text;
5191  reply_info.highlight=MagickFalse;
5192  break;
5193  }
5194  default:
5195  break;
5196  }
5197  XEditText(display,&reply_info,key_symbol,command,state);
5198  XDrawMatteText(display,&windows->widget,&reply_info);
5199  state|=JumpListState;
5200  break;
5201  }
5202  case KeyRelease:
5203  {
5204  static char
5205  command[MagickPathExtent];
5206 
5207  static KeySym
5208  key_symbol;
5209 
5210  /*
5211  Respond to a user key release.
5212  */
5213  if (event.xkey.window != windows->widget.id)
5214  break;
5215  (void) XLookupString((XKeyEvent *) &event.xkey,command,
5216  (int) sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5217  if (key_symbol == XK_Control_L)
5218  state&=(~ControlState);
5219  break;
5220  }
5221  case LeaveNotify:
5222  {
5223  if (event.xcrossing.window != windows->widget.id)
5224  break;
5225  state|=InactiveWidgetState;
5226  break;
5227  }
5228  case MapNotify:
5229  {
5230  mask&=(~CWX);
5231  mask&=(~CWY);
5232  break;
5233  }
5234  case MotionNotify:
5235  {
5236  /*
5237  Discard pending button motion events.
5238  */
5239  while (XCheckMaskEvent(display,ButtonMotionMask,&event)) ;
5240  if (slider_info.active)
5241  {
5242  /*
5243  Move slider matte.
5244  */
5245  slider_info.y=event.xmotion.y-
5246  ((slider_info.height+slider_info.bevel_width) >> 1)+1;
5247  if (slider_info.y < slider_info.min_y)
5248  slider_info.y=slider_info.min_y;
5249  if (slider_info.y > slider_info.max_y)
5250  slider_info.y=slider_info.max_y;
5251  slider_info.id=0;
5252  if (slider_info.y != slider_info.min_y)
5253  slider_info.id=(int) ((files*(slider_info.y-slider_info.min_y+1))/
5254  (slider_info.max_y-slider_info.min_y+1));
5255  state|=RedrawListState;
5256  break;
5257  }
5258  if (state & InactiveWidgetState)
5259  break;
5260  if (up_info.raised == MatteIsActive(up_info,event.xmotion))
5261  {
5262  /*
5263  Up button status changed.
5264  */
5265  up_info.raised=!up_info.raised;
5266  XDrawBeveledButton(display,&windows->widget,&up_info);
5267  break;
5268  }
5269  if (home_info.raised == MatteIsActive(home_info,event.xmotion))
5270  {
5271  /*
5272  Home button status changed.
5273  */
5274  home_info.raised=!home_info.raised;
5275  XDrawBeveledButton(display,&windows->widget,&home_info);
5276  break;
5277  }
5278  if (special_info.raised == MatteIsActive(special_info,event.xmotion))
5279  {
5280  /*
5281  Grab button status changed.
5282  */
5283  special_info.raised=!special_info.raised;
5284  XDrawBeveledButton(display,&windows->widget,&special_info);
5285  break;
5286  }
5287  if (action_info.raised == MatteIsActive(action_info,event.xmotion))
5288  {
5289  /*
5290  Action button status changed.
5291  */
5292  action_info.raised=action_info.raised == MagickFalse ?
5294  XDrawBeveledButton(display,&windows->widget,&action_info);
5295  break;
5296  }
5297  if (cancel_info.raised == MatteIsActive(cancel_info,event.xmotion))
5298  {
5299  /*
5300  Cancel button status changed.
5301  */
5302  cancel_info.raised=cancel_info.raised == MagickFalse ?
5304  XDrawBeveledButton(display,&windows->widget,&cancel_info);
5305  break;
5306  }
5307  break;
5308  }
5309  case SelectionClear:
5310  {
5311  reply_info.highlight=MagickFalse;
5312  XDrawMatteText(display,&windows->widget,&reply_info);
5313  break;
5314  }
5315  case SelectionNotify:
5316  {
5317  Atom
5318  type;
5319 
5320  int
5321  format;
5322 
5323  unsigned char
5324  *data;
5325 
5326  unsigned long
5327  after,
5328  length;
5329 
5330  /*
5331  Obtain response from primary selection.
5332  */
5333  if (event.xselection.property == (Atom) None)
5334  break;
5335  status=XGetWindowProperty(display,event.xselection.requestor,
5336  event.xselection.property,0L,2047L,MagickTrue,XA_STRING,&type,
5337  &format,&length,&after,&data);
5338  if ((status != Success) || (type != XA_STRING) || (format == 32) ||
5339  (length == 0))
5340  break;
5341  if ((Extent(reply_info.text)+length) >= (MagickPathExtent-1))
5342  (void) XBell(display,0);
5343  else
5344  {
5345  /*
5346  Insert primary selection in reply text.
5347  */
5348  *(data+length)='\0';
5349  XEditText(display,&reply_info,(KeySym) XK_Insert,(char *) data,
5350  state);
5351  XDrawMatteText(display,&windows->widget,&reply_info);
5352  state|=JumpListState;
5353  state|=RedrawActionState;
5354  }
5355  (void) XFree((void *) data);
5356  break;
5357  }
5358  case SelectionRequest:
5359  {
5360  XSelectionEvent
5361  notify;
5362 
5363  XSelectionRequestEvent
5364  *request;
5365 
5366  if (reply_info.highlight == MagickFalse)
5367  break;
5368  /*
5369  Set primary selection.
5370  */
5371  request=(&(event.xselectionrequest));
5372  (void) XChangeProperty(request->display,request->requestor,
5373  request->property,request->target,8,PropModeReplace,
5374  (unsigned char *) primary_selection,Extent(primary_selection));
5375  notify.type=SelectionNotify;
5376  notify.display=request->display;
5377  notify.requestor=request->requestor;
5378  notify.selection=request->selection;
5379  notify.target=request->target;
5380  notify.time=request->time;
5381  if (request->property == None)
5382  notify.property=request->target;
5383  else
5384  notify.property=request->property;
5385  (void) XSendEvent(request->display,request->requestor,False,0,
5386  (XEvent *) &notify);
5387  }
5388  default:
5389  break;
5390  }
5391  } while ((state & ExitState) == 0);
5392  XSetCursorState(display,windows,MagickFalse);
5393  (void) XWithdrawWindow(display,windows->widget.id,windows->widget.screen);
5394  XCheckRefreshWindows(display,windows);
5395  /*
5396  Free file list.
5397  */
5398  for (i=0; i < (ssize_t) files; i++)
5399  filelist[i]=DestroyString(filelist[i]);
5400  if (filelist != (char **) NULL)
5401  filelist=(char **) RelinquishMagickMemory(filelist);
5402  if (*reply != '\0')
5403  {
5404  (void) ConcatenateMagickString(working_path,DirectorySeparator,
5406  (void) ConcatenateMagickString(working_path,reply,MagickPathExtent);
5407  }
5408  (void) CopyMagickString(reply,working_path,MagickPathExtent);
5409  if (*reply == '~')
5410  ExpandFilename(reply);
5411 }
5412 
5413 /*
5414 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5415 % %
5416 % %
5417 % %
5418 % X F o n t B r o w s e r W i d g e t %
5419 % %
5420 % %
5421 % %
5422 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5423 %
5424 % XFontBrowserWidget() displays a Font Browser widget with a font query to the
5425 % user. The user keys a reply and presses the Action or Cancel button to
5426 % exit. The typed text is returned as the reply function parameter.
5427 %
5428 % The format of the XFontBrowserWidget method is:
5429 %
5430 % void XFontBrowserWidget(Display *display,XWindows *windows,
5431 % const char *action,char *reply)
5432 %
5433 % A description of each parameter follows:
5434 %
5435 % o display: Specifies a connection to an X server; returned from
5436 % XOpenDisplay.
5437 %
5438 % o window: Specifies a pointer to a XWindows structure.
5439 %
5440 % o action: Specifies a pointer to the action of this widget.
5441 %
5442 % o reply: the response from the user is returned in this parameter.
5443 %
5444 %
5445 */
5446 
5447 #if defined(__cplusplus) || defined(c_plusplus)
5448 extern "C" {
5449 #endif
5450 
5451 static int FontCompare(const void *x,const void *y)
5452 {
5453  register char
5454  *p,
5455  *q;
5456 
5457  p=(char *) *((char **) x);
5458  q=(char *) *((char **) y);
5459  while ((*p != '\0') && (*q != '\0') && (*p == *q))
5460  {
5461  p++;
5462  q++;
5463  }
5464  return(*p-(*q));
5465 }
5466 
5467 #if defined(__cplusplus) || defined(c_plusplus)
5468 }
5469 #endif
5470 
5471 MagickPrivate void XFontBrowserWidget(Display *display,XWindows *windows,
5472  const char *action,char *reply)
5473 {
5474 #define BackButtonText "Back"
5475 #define CancelButtonText "Cancel"
5476 #define FontnameText "Name:"
5477 #define FontPatternText "Pattern:"
5478 #define ResetButtonText "Reset"
5479 
5480  char
5481  back_pattern[MagickPathExtent],
5482  **fontlist,
5483  **listhead,
5484  primary_selection[MagickPathExtent],
5485  reset_pattern[MagickPathExtent],
5486  text[MagickPathExtent];
5487 
5488  int
5489  fonts,
5490  x,
5491  y;
5492 
5493  register int
5494  i;
5495 
5496  static char
5497  glob_pattern[MagickPathExtent] = "*";
5498 
5499  static MagickStatusType
5500  mask = (MagickStatusType) (CWWidth | CWHeight | CWX | CWY);
5501 
5502  Status
5503  status;
5504 
5505  unsigned int
5506  height,
5507  text_width,
5508  visible_fonts,
5509  width;
5510 
5511  size_t
5512  delay,
5513  state;
5514 
5515  XEvent
5516  event;
5517 
5518  XFontStruct
5519  *font_info;
5520 
5521  XTextProperty
5522  window_name;
5523 
5524  XWidgetInfo
5525  action_info,
5526  back_info,
5527  cancel_info,
5528  expose_info,
5529  list_info,
5530  mode_info,
5531  north_info,
5532  reply_info,
5533  reset_info,
5534  scroll_info,
5535  selection_info,
5536  slider_info,
5537  south_info,
5538  text_info;
5539 
5540  XWindowChanges
5541  window_changes;
5542 
5543  /*
5544  Get font list and sort in ascending order.
5545  */
5546  assert(display != (Display *) NULL);
5547  assert(windows != (XWindows *) NULL);
5548  assert(action != (char *) NULL);
5549  assert(reply != (char *) NULL);
5550  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",action);
5551  XSetCursorState(display,windows,MagickTrue);
5552  XCheckRefreshWindows(display,windows);
5553  (void) CopyMagickString(back_pattern,glob_pattern,MagickPathExtent);
5554  (void) CopyMagickString(reset_pattern,"*",MagickPathExtent);
5555  fontlist=XListFonts(display,glob_pattern,32767,&fonts);
5556  if (fonts == 0)
5557  {
5558  /*
5559  Pattern failed, obtain all the fonts.
5560  */
5561  XNoticeWidget(display,windows,"Unable to obtain fonts names:",
5562  glob_pattern);
5563  (void) CopyMagickString(glob_pattern,"*",MagickPathExtent);
5564  fontlist=XListFonts(display,glob_pattern,32767,&fonts);
5565  if (fontlist == (char **) NULL)
5566  {
5567  XNoticeWidget(display,windows,"Unable to obtain fonts names:",
5568  glob_pattern);
5569  return;
5570  }
5571  }
5572  /*
5573  Sort font list in ascending order.
5574  */
5575  listhead=fontlist;
5576  fontlist=(char **) AcquireQuantumMemory((size_t) fonts,sizeof(*fontlist));
5577  if (fontlist == (char **) NULL)
5578  {
5579  XNoticeWidget(display,windows,"MemoryAllocationFailed",
5580  "UnableToViewFonts");
5581  return;
5582  }
5583  for (i=0; i < fonts; i++)
5584  fontlist[i]=listhead[i];
5585  qsort((void *) fontlist,(size_t) fonts,sizeof(*fontlist),FontCompare);
5586  /*
5587  Determine Font Browser widget attributes.
5588  */
5589  font_info=windows->widget.font_info;
5590  text_width=0;
5591  for (i=0; i < fonts; i++)
5592  if (WidgetTextWidth(font_info,fontlist[i]) > text_width)
5593  text_width=WidgetTextWidth(font_info,fontlist[i]);
5594  width=WidgetTextWidth(font_info,(char *) action);
5595  if (WidgetTextWidth(font_info,CancelButtonText) > width)
5596  width=WidgetTextWidth(font_info,CancelButtonText);
5597  if (WidgetTextWidth(font_info,ResetButtonText) > width)
5598  width=WidgetTextWidth(font_info,ResetButtonText);
5599  if (WidgetTextWidth(font_info,BackButtonText) > width)
5600  width=WidgetTextWidth(font_info,BackButtonText);
5601  width+=QuantumMargin;
5602  if (WidgetTextWidth(font_info,FontPatternText) > width)
5603  width=WidgetTextWidth(font_info,FontPatternText);
5604  if (WidgetTextWidth(font_info,FontnameText) > width)
5605  width=WidgetTextWidth(font_info,FontnameText);
5606  height=(unsigned int) (font_info->ascent+font_info->descent);
5607  /*
5608  Position Font Browser widget.
5609  */
5610  windows->widget.width=width+MagickMin((int) text_width,(int) MaxTextWidth)+
5611  6*QuantumMargin;
5612  windows->widget.min_width=width+MinTextWidth+4*QuantumMargin;
5613  if (windows->widget.width < windows->widget.min_width)
5614  windows->widget.width=windows->widget.min_width;
5615  windows->widget.height=(unsigned int)
5616  (((85*height) >> 2)+((13*QuantumMargin) >> 1)+4);
5617  windows->widget.min_height=(unsigned int)
5618  (((27*height) >> 1)+((13*QuantumMargin) >> 1)+4);
5619  if (windows->widget.height < windows->widget.min_height)
5620  windows->widget.height=windows->widget.min_height;
5621  XConstrainWindowPosition(display,&windows->widget);
5622  /*
5623  Map Font Browser widget.
5624  */
5625  (void) CopyMagickString(windows->widget.name,"Browse and Select a Font",
5627  status=XStringListToTextProperty(&windows->widget.name,1,&window_name);
5628  if (status != False)
5629  {
5630  XSetWMName(display,windows->widget.id,&window_name);
5631  XSetWMIconName(display,windows->widget.id,&window_name);
5632  (void) XFree((void *) window_name.value);
5633  }
5634  window_changes.width=(int) windows->widget.width;
5635  window_changes.height=(int) windows->widget.height;
5636  window_changes.x=windows->widget.x;
5637  window_changes.y=windows->widget.y;
5638  (void) XReconfigureWMWindow(display,windows->widget.id,
5639  windows->widget.screen,mask,&window_changes);
5640  (void) XMapRaised(display,windows->widget.id);
5641  windows->widget.mapped=MagickFalse;
5642  /*
5643  Respond to X events.
5644  */
5645  XGetWidgetInfo((char *) NULL,&slider_info);
5646  XGetWidgetInfo((char *) NULL,&north_info);
5647  XGetWidgetInfo((char *) NULL,&south_info);
5648  XGetWidgetInfo((char *) NULL,&expose_info);
5649  XGetWidgetInfo((char *) NULL,&selection_info);
5650  visible_fonts=0;
5651  delay=SuspendTime << 2;
5652  state=UpdateConfigurationState;
5653  do
5654  {
5655  if (state & UpdateConfigurationState)
5656  {
5657  int
5658  id;
5659 
5660  /*
5661  Initialize button information.
5662  */
5663  XGetWidgetInfo(CancelButtonText,&cancel_info);
5664  cancel_info.width=width;
5665  cancel_info.height=(unsigned int) ((3*height) >> 1);
5666  cancel_info.x=(int)
5667  (windows->widget.width-cancel_info.width-QuantumMargin-2);
5668  cancel_info.y=(int)
5669  (windows->widget.height-cancel_info.height-QuantumMargin);
5670  XGetWidgetInfo(action,&action_info);
5671  action_info.width=width;
5672  action_info.height=(unsigned int) ((3*height) >> 1);
5673  action_info.x=cancel_info.x-(cancel_info.width+(QuantumMargin >> 1)+
5674  (action_info.bevel_width << 1));
5675  action_info.y=cancel_info.y;
5676  XGetWidgetInfo(BackButtonText,&back_info);
5677  back_info.width=width;
5678  back_info.height=(unsigned int) ((3*height) >> 1);
5679  back_info.x=QuantumMargin;
5680  back_info.y=((5*QuantumMargin) >> 1)+height;
5681  XGetWidgetInfo(ResetButtonText,&reset_info);
5682  reset_info.width=width;
5683  reset_info.height=(unsigned int) ((3*height) >> 1);
5684  reset_info.x=QuantumMargin;
5685  reset_info.y=back_info.y+back_info.height+QuantumMargin;
5686  /*
5687  Initialize reply information.
5688  */
5689  XGetWidgetInfo(reply,&reply_info);
5690  reply_info.raised=MagickFalse;
5691  reply_info.bevel_width--;
5692  reply_info.width=windows->widget.width-width-((6*QuantumMargin) >> 1);
5693  reply_info.height=height << 1;
5694  reply_info.x=(int) (width+(QuantumMargin << 1));
5695  reply_info.y=action_info.y-(action_info.height << 1)-QuantumMargin;
5696  /*
5697  Initialize mode information.
5698  */
5699  XGetWidgetInfo(reply,&mode_info);
5700  mode_info.bevel_width=0;
5701  mode_info.width=(unsigned int)
5702  (action_info.x-reply_info.x-QuantumMargin);
5703  mode_info.height=action_info.height << 1;
5704  mode_info.x=reply_info.x;
5705  mode_info.y=action_info.y-action_info.height+action_info.bevel_width;
5706  /*
5707  Initialize scroll information.
5708  */
5709  XGetWidgetInfo((char *) NULL,&scroll_info);
5710  scroll_info.bevel_width--;
5711  scroll_info.width=height;
5712  scroll_info.height=(unsigned int)
5713  (reply_info.y-back_info.y-(QuantumMargin >> 1));
5714  scroll_info.x=reply_info.x+(reply_info.width-scroll_info.width);
5715  scroll_info.y=back_info.y-reply_info.bevel_width;
5716  scroll_info.raised=MagickFalse;
5717  scroll_info.trough=MagickTrue;
5718  north_info=scroll_info;
5719  north_info.raised=MagickTrue;
5720  north_info.width-=(north_info.bevel_width << 1);
5721  north_info.height=north_info.width-1;
5722  north_info.x+=north_info.bevel_width;
5723  north_info.y+=north_info.bevel_width;
5724  south_info=north_info;
5725  south_info.y=scroll_info.y+scroll_info.height-scroll_info.bevel_width-
5726  south_info.height;
5727  id=slider_info.id;
5728  slider_info=north_info;
5729  slider_info.id=id;
5730  slider_info.width-=2;
5731  slider_info.min_y=north_info.y+north_info.height+north_info.bevel_width+
5732  slider_info.bevel_width+2;
5733  slider_info.height=scroll_info.height-((slider_info.min_y-
5734  scroll_info.y+1) << 1)+4;
5735  visible_fonts=scroll_info.height/(height+(height >> 3));
5736  if (fonts > (int) visible_fonts)
5737  slider_info.height=(visible_fonts*slider_info.height)/fonts;
5738  slider_info.max_y=south_info.y-south_info.bevel_width-
5739  slider_info.bevel_width-2;
5740  slider_info.x=scroll_info.x+slider_info.bevel_width+1;
5741  slider_info.y=slider_info.min_y;
5742  expose_info=scroll_info;
5743  expose_info.y=slider_info.y;
5744  /*
5745  Initialize list information.
5746  */
5747  XGetWidgetInfo((char *) NULL,&list_info);
5748  list_info.raised=MagickFalse;
5749  list_info.bevel_width--;
5750  list_info.width=(unsigned int)
5751  (scroll_info.x-reply_info.x-(QuantumMargin >> 1));
5752  list_info.height=scroll_info.height;
5753  list_info.x=reply_info.x;
5754  list_info.y=scroll_info.y;
5755  if (windows->widget.mapped == MagickFalse)
5756  state|=JumpListState;
5757  /*
5758  Initialize text information.
5759  */
5760  *text='\0';
5761  XGetWidgetInfo(text,&text_info);
5762  text_info.center=MagickFalse;
5763  text_info.width=reply_info.width;
5764  text_info.height=height;
5765  text_info.x=list_info.x-(QuantumMargin >> 1);
5766  text_info.y=QuantumMargin;
5767  /*
5768  Initialize selection information.
5769  */
5770  XGetWidgetInfo((char *) NULL,&selection_info);
5771  selection_info.center=MagickFalse;
5772  selection_info.width=list_info.width;
5773  selection_info.height=(unsigned int) ((9*height) >> 3);
5774  selection_info.x=list_info.x;
5775  state&=(~UpdateConfigurationState);
5776  }
5777  if (state & RedrawWidgetState)
5778  {
5779  /*
5780  Redraw Font Browser window.
5781  */
5782  x=QuantumMargin;
5783  y=text_info.y+((text_info.height-height) >> 1)+font_info->ascent;
5784  (void) XDrawString(display,windows->widget.id,
5785  windows->widget.annotate_context,x,y,FontPatternText,
5786  Extent(FontPatternText));
5787  (void) CopyMagickString(text_info.text,glob_pattern,MagickPathExtent);
5788  XDrawWidgetText(display,&windows->widget,&text_info);
5789  XDrawBeveledButton(display,&windows->widget,&back_info);
5790  XDrawBeveledButton(display,&windows->widget,&reset_info);
5791  XDrawBeveledMatte(display,&windows->widget,&list_info);
5792  XDrawBeveledMatte(display,&windows->widget,&scroll_info);
5793  XDrawTriangleNorth(display,&windows->widget,&north_info);
5794  XDrawBeveledButton(display,&windows->widget,&slider_info);
5795  XDrawTriangleSouth(display,&windows->widget,&south_info);
5796  x=QuantumMargin;
5797  y=reply_info.y+((reply_info.height-height) >> 1)+font_info->ascent;
5798  (void) XDrawString(display,windows->widget.id,
5799  windows->widget.annotate_context,x,y,FontnameText,
5800  Extent(FontnameText));
5801  XDrawBeveledMatte(display,&windows->widget,&reply_info);
5802  XDrawMatteText(display,&windows->widget,&reply_info);
5803  XDrawBeveledButton(display,&windows->widget,&action_info);
5804  XDrawBeveledButton(display,&windows->widget,&cancel_info);
5805  XHighlightWidget(display,&windows->widget,BorderOffset,BorderOffset);
5806  selection_info.id=(~0);
5807  state|=RedrawActionState;
5808  state|=RedrawListState;
5809  state&=(~RedrawWidgetState);
5810  }
5811  if (state & UpdateListState)
5812  {
5813  char
5814  **checklist;
5815 
5816  int
5817  number_fonts;
5818 
5819  /*
5820  Update font list.
5821  */
5822  checklist=XListFonts(display,glob_pattern,32767,&number_fonts);
5823  if (checklist == (char **) NULL)
5824  {
5825  if ((strchr(glob_pattern,'*') == (char *) NULL) &&
5826  (strchr(glob_pattern,'?') == (char *) NULL))
5827  {
5828  /*
5829  Might be a scaleable font-- exit.
5830  */
5831  (void) CopyMagickString(reply,glob_pattern,MagickPathExtent);
5832  (void) CopyMagickString(glob_pattern,back_pattern,MagickPathExtent);
5833  action_info.raised=MagickFalse;
5834  XDrawBeveledButton(display,&windows->widget,&action_info);
5835  break;
5836  }
5837  (void) CopyMagickString(glob_pattern,back_pattern,MagickPathExtent);
5838  (void) XBell(display,0);
5839  }
5840  else
5841  if (number_fonts == 1)
5842  {
5843  /*
5844  Reply is a single font name-- exit.
5845  */
5846  (void) CopyMagickString(reply,checklist[0],MagickPathExtent);
5847  (void) CopyMagickString(glob_pattern,back_pattern,MagickPathExtent);
5848  (void) XFreeFontNames(checklist);
5849  action_info.raised=MagickFalse;
5850  XDrawBeveledButton(display,&windows->widget,&action_info);
5851  break;
5852  }
5853  else
5854  {
5855  (void) XFreeFontNames(listhead);
5856  fontlist=(char **) RelinquishMagickMemory(fontlist);
5857  fontlist=checklist;
5858  fonts=number_fonts;
5859  }
5860  /*
5861  Sort font list in ascending order.
5862  */
5863  listhead=fontlist;
5864  fontlist=(char **) AcquireQuantumMemory((size_t) fonts,
5865  sizeof(*fontlist));
5866  if (fontlist == (char **) NULL)
5867  {
5868  XNoticeWidget(display,windows,"MemoryAllocationFailed",
5869  "UnableToViewFonts");
5870  return;
5871  }
5872  for (i=0; i < fonts; i++)
5873  fontlist[i]=listhead[i];
5874  qsort((void *) fontlist,(size_t) fonts,sizeof(*fontlist),FontCompare);
5875  slider_info.height=
5876  scroll_info.height-((slider_info.min_y-scroll_info.y+1) << 1)+1;
5877  if (fonts > (int) visible_fonts)
5878  slider_info.height=(visible_fonts*slider_info.height)/fonts;
5879  slider_info.max_y=south_info.y-south_info.bevel_width-
5880  slider_info.bevel_width-2;
5881  slider_info.id=0;
5882  slider_info.y=slider_info.min_y;
5883  expose_info.y=slider_info.y;
5884  selection_info.id=(~0);
5885  list_info.id=(~0);
5886  state|=RedrawListState;
5887  /*
5888  Redraw font name & reply.
5889  */
5890  *reply_info.text='\0';
5891  reply_info.cursor=reply_info.text;
5892  (void) CopyMagickString(text_info.text,glob_pattern,MagickPathExtent);
5893  XDrawWidgetText(display,&windows->widget,&text_info);
5894  XDrawMatteText(display,&windows->widget,&reply_info);
5895  XDrawBeveledMatte(display,&windows->widget,&scroll_info);
5896  XDrawTriangleNorth(display,&windows->widget,&north_info);
5897  XDrawBeveledButton(display,&windows->widget,&slider_info);
5898  XDrawTriangleSouth(display,&windows->widget,&south_info);
5899  XHighlightWidget(display,&windows->widget,BorderOffset,BorderOffset);
5900  state&=(~UpdateListState);
5901  }
5902  if (state & JumpListState)
5903  {
5904  /*
5905  Jump scroll to match user font.
5906  */
5907  list_info.id=(~0);
5908  for (i=0; i < fonts; i++)
5909  if (LocaleCompare(fontlist[i],reply) >= 0)
5910  {
5911  list_info.id=LocaleCompare(fontlist[i],reply) == 0 ? i : ~0;
5912  break;
5913  }
5914  if ((i < slider_info.id) || (i >= (int) (slider_info.id+visible_fonts)))
5915  slider_info.id=i-(visible_fonts >> 1);
5916  selection_info.id=(~0);
5917  state|=RedrawListState;
5918  state&=(~JumpListState);
5919  }
5920  if (state & RedrawListState)
5921  {
5922  /*
5923  Determine slider id and position.
5924  */
5925  if (slider_info.id >= (int) (fonts-visible_fonts))
5926  slider_info.id=fonts-visible_fonts;
5927  if ((slider_info.id < 0) || (fonts <= (int) visible_fonts))
5928  slider_info.id=0;
5929  slider_info.y=slider_info.min_y;
5930  if (fonts > 0)
5931  slider_info.y+=
5932  slider_info.id*(slider_info.max_y-slider_info.min_y+1)/fonts;
5933  if (slider_info.id != selection_info.id)
5934  {
5935  /*
5936  Redraw scroll bar and file names.
5937  */
5938  selection_info.id=slider_info.id;
5939  selection_info.y=list_info.y+(height >> 3)+2;
5940  for (i=0; i < (int) visible_fonts; i++)
5941  {
5942  selection_info.raised=(slider_info.id+i) != list_info.id ?
5944  selection_info.text=(char *) NULL;
5945  if ((slider_info.id+i) < fonts)
5946  selection_info.text=fontlist[slider_info.id+i];
5947  XDrawWidgetText(display,&windows->widget,&selection_info);
5948  selection_info.y+=(int) selection_info.height;
5949  }
5950  /*
5951  Update slider.
5952  */
5953  if (slider_info.y > expose_info.y)
5954  {
5955  expose_info.height=(unsigned int) slider_info.y-expose_info.y;
5956  expose_info.y=slider_info.y-expose_info.height-
5957  slider_info.bevel_width-1;
5958  }
5959  else
5960  {
5961  expose_info.height=(unsigned int) expose_info.y-slider_info.y;
5962  expose_info.y=slider_info.y+slider_info.height+
5963  slider_info.bevel_width+1;
5964  }
5965  XDrawTriangleNorth(display,&windows->widget,&north_info);
5966  XDrawMatte(display,&windows->widget,&expose_info);
5967  XDrawBeveledButton(display,&windows->widget,&slider_info);
5968  XDrawTriangleSouth(display,&windows->widget,&south_info);
5969  expose_info.y=slider_info.y;
5970  }
5971  state&=(~RedrawListState);
5972  }
5973  if (state & RedrawActionState)
5974  {
5975  XFontStruct
5976  *save_info;
5977 
5978  /*
5979  Display the selected font in a drawing area.
5980  */
5981  save_info=windows->widget.font_info;
5982  font_info=XLoadQueryFont(display,reply_info.text);
5983  if (font_info != (XFontStruct *) NULL)
5984  {
5985  windows->widget.font_info=font_info;
5986  (void) XSetFont(display,windows->widget.widget_context,
5987  font_info->fid);
5988  }
5989  XDrawBeveledButton(display,&windows->widget,&mode_info);
5990  windows->widget.font_info=save_info;
5991  if (font_info != (XFontStruct *) NULL)
5992  {
5993  (void) XSetFont(display,windows->widget.widget_context,
5994  windows->widget.font_info->fid);
5995  (void) XFreeFont(display,font_info);
5996  }
5997  XHighlightWidget(display,&windows->widget,BorderOffset,BorderOffset);
5998  XDrawMatteText(display,&windows->widget,&reply_info);
5999  state&=(~RedrawActionState);
6000  }
6001  /*
6002  Wait for next event.
6003  */
6004  if (north_info.raised && south_info.raised)
6005  (void) XIfEvent(display,&event,XScreenEvent,(char *) windows);
6006  else
6007  {
6008  /*
6009  Brief delay before advancing scroll bar.
6010  */
6011  XDelay(display,delay);
6012  delay=SuspendTime;
6013  (void) XCheckIfEvent(display,&event,XScreenEvent,(char *) windows);
6014  if (north_info.raised == MagickFalse)
6015  if (slider_info.id > 0)
6016  {
6017  /*
6018  Move slider up.
6019  */
6020  slider_info.id--;
6021  state|=RedrawListState;
6022  }
6023  if (south_info.raised == MagickFalse)
6024  if (slider_info.id < fonts)
6025  {
6026  /*
6027  Move slider down.
6028  */
6029  slider_info.id++;
6030  state|=RedrawListState;
6031  }
6032  if (event.type != ButtonRelease)
6033  continue;
6034  }
6035  switch (event.type)
6036  {
6037  case ButtonPress:
6038  {
6039  if (MatteIsActive(slider_info,event.xbutton))
6040  {
6041  /*
6042  Track slider.
6043  */
6044  slider_info.active=MagickTrue;
6045  break;
6046  }
6047  if (MatteIsActive(north_info,event.xbutton))
6048  if (slider_info.id > 0)
6049  {
6050  /*
6051  Move slider up.
6052  */
6053  north_info.raised=MagickFalse;
6054  slider_info.id--;
6055  state|=RedrawListState;
6056  break;
6057  }
6058  if (MatteIsActive(south_info,event.xbutton))
6059  if (slider_info.id < fonts)
6060  {
6061  /*
6062  Move slider down.
6063  */
6064  south_info.raised=MagickFalse;
6065  slider_info.id++;
6066  state|=RedrawListState;
6067  break;
6068  }
6069  if (MatteIsActive(scroll_info,event.xbutton))
6070  {
6071  /*
6072  Move slider.
6073  */
6074  if (event.xbutton.y < slider_info.y)
6075  slider_info.id-=(visible_fonts-1);
6076  else
6077  slider_info.id+=(visible_fonts-1);
6078  state|=RedrawListState;
6079  break;
6080  }
6081  if (MatteIsActive(list_info,event.xbutton))
6082  {
6083  int
6084  id;
6085 
6086  /*
6087  User pressed list matte.
6088  */
6089  id=slider_info.id+(event.xbutton.y-(list_info.y+(height >> 1))+1)/
6090  selection_info.height;
6091  if (id >= (int) fonts)
6092  break;
6093  (void) CopyMagickString(reply_info.text,fontlist[id],MagickPathExtent);
6094  reply_info.highlight=MagickFalse;
6095  reply_info.marker=reply_info.text;
6096  reply_info.cursor=reply_info.text+Extent(reply_info.text);
6097  XDrawMatteText(display,&windows->widget,&reply_info);
6098  state|=RedrawActionState;
6099  if (id == list_info.id)
6100  {
6101  (void) CopyMagickString(glob_pattern,reply_info.text,
6103  state|=UpdateListState;
6104  }
6105  selection_info.id=(~0);
6106  list_info.id=id;
6107  state|=RedrawListState;
6108  break;
6109  }
6110  if (MatteIsActive(back_info,event.xbutton))
6111  {
6112  /*
6113  User pressed Back button.
6114  */
6115  back_info.raised=MagickFalse;
6116  XDrawBeveledButton(display,&windows->widget,&back_info);
6117  break;
6118  }
6119  if (MatteIsActive(reset_info,event.xbutton))
6120  {
6121  /*
6122  User pressed Reset button.
6123  */
6124  reset_info.raised=MagickFalse;
6125  XDrawBeveledButton(display,&windows->widget,&reset_info);
6126  break;
6127  }
6128  if (MatteIsActive(action_info,event.xbutton))
6129  {
6130  /*
6131  User pressed action button.
6132  */
6133  action_info.raised=MagickFalse;
6134  XDrawBeveledButton(display,&windows->widget,&action_info);
6135  break;
6136  }
6137  if (MatteIsActive(cancel_info,event.xbutton))
6138  {
6139  /*
6140  User pressed Cancel button.
6141  */
6142  cancel_info.raised=MagickFalse;
6143  XDrawBeveledButton(display,&windows->widget,&cancel_info);
6144  break;
6145  }
6146  if (MatteIsActive(reply_info,event.xbutton) == MagickFalse)
6147  break;
6148  if (event.xbutton.button != Button2)
6149  {
6150  static Time
6151  click_time;
6152 
6153  /*
6154  Move text cursor to position of button press.
6155  */
6156  x=event.xbutton.x-reply_info.x-(QuantumMargin >> 2);
6157  for (i=1; i <= Extent(reply_info.marker); i++)
6158  if (XTextWidth(font_info,reply_info.marker,i) > x)
6159  break;
6160  reply_info.cursor=reply_info.marker+i-1;
6161  if (event.xbutton.time > (click_time+DoubleClick))
6162  reply_info.highlight=MagickFalse;
6163  else
6164  {
6165  /*
6166  Become the XA_PRIMARY selection owner.
6167  */
6168  (void) CopyMagickString(primary_selection,reply_info.text,
6170  (void) XSetSelectionOwner(display,XA_PRIMARY,windows->widget.id,
6171  event.xbutton.time);
6172  reply_info.highlight=XGetSelectionOwner(display,XA_PRIMARY) ==
6173  windows->widget.id ? MagickTrue : MagickFalse;
6174  }
6175  XDrawMatteText(display,&windows->widget,&reply_info);
6176  click_time=event.xbutton.time;
6177  break;
6178  }
6179  /*
6180  Request primary selection.
6181  */
6182  (void) XConvertSelection(display,XA_PRIMARY,XA_STRING,XA_STRING,
6183  windows->widget.id,event.xbutton.time);
6184  break;
6185  }
6186  case ButtonRelease:
6187  {
6188  if (windows->widget.mapped == MagickFalse)
6189  break;
6190  if (north_info.raised == MagickFalse)
6191  {
6192  /*
6193  User released up button.
6194  */
6195  delay=SuspendTime << 2;
6196  north_info.raised=MagickTrue;
6197  XDrawTriangleNorth(display,&windows->widget,&north_info);
6198  }
6199  if (south_info.raised == MagickFalse)
6200  {
6201  /*
6202  User released down button.
6203  */
6204  delay=SuspendTime << 2;
6205  south_info.raised=MagickTrue;
6206  XDrawTriangleSouth(display,&windows->widget,&south_info);
6207  }
6208  if (slider_info.active)
6209  {
6210  /*
6211  Stop tracking slider.
6212  */
6213  slider_info.active=MagickFalse;
6214  break;
6215  }
6216  if (back_info.raised == MagickFalse)
6217  {
6218  if (event.xbutton.window == windows->widget.id)
6219  if (MatteIsActive(back_info,event.xbutton))
6220  {
6221  (void) CopyMagickString(glob_pattern,back_pattern,
6223  state|=UpdateListState;
6224  }
6225  back_info.raised=MagickTrue;
6226  XDrawBeveledButton(display,&windows->widget,&back_info);
6227  }
6228  if (reset_info.raised == MagickFalse)
6229  {
6230  if (event.xbutton.window == windows->widget.id)
6231  if (MatteIsActive(reset_info,event.xbutton))
6232  {
6233  (void) CopyMagickString(back_pattern,glob_pattern,MagickPathExtent);
6234  (void) CopyMagickString(glob_pattern,reset_pattern,MagickPathExtent);
6235  state|=UpdateListState;
6236  }
6237  reset_info.raised=MagickTrue;
6238  XDrawBeveledButton(display,&windows->widget,&reset_info);
6239  }
6240  if (action_info.raised == MagickFalse)
6241  {
6242  if (event.xbutton.window == windows->widget.id)
6243  {
6244  if (MatteIsActive(action_info,event.xbutton))
6245  {
6246  if (*reply_info.text == '\0')
6247  (void) XBell(display,0);
6248  else
6249  state|=ExitState;
6250  }
6251  }
6252  action_info.raised=MagickTrue;
6253  XDrawBeveledButton(display,&windows->widget,&action_info);
6254  }
6255  if (cancel_info.raised == MagickFalse)
6256  {
6257  if (event.xbutton.window == windows->widget.id)
6258  if (MatteIsActive(cancel_info,event.xbutton))
6259  {
6260  *reply_info.text='\0';
6261  state|=ExitState;
6262  }
6263  cancel_info.raised=MagickTrue;
6264  XDrawBeveledButton(display,&windows->widget,&cancel_info);
6265  }
6266  break;
6267  }
6268  case ClientMessage:
6269  {
6270  /*
6271  If client window delete message, exit.
6272  */
6273  if (event.xclient.message_type != windows->wm_protocols)
6274  break;
6275  if (*event.xclient.data.l == (int) windows->wm_take_focus)
6276  {
6277  (void) XSetInputFocus(display,event.xclient.window,RevertToParent,
6278  (Time) event.xclient.data.l[1]);
6279  break;
6280  }
6281  if (*event.xclient.data.l != (int) windows->wm_delete_window)
6282  break;
6283  if (event.xclient.window == windows->widget.id)
6284  {
6285  *reply_info.text='\0';
6286  state|=ExitState;
6287  break;
6288  }
6289  break;
6290  }
6291  case ConfigureNotify:
6292  {
6293  /*
6294  Update widget configuration.
6295  */
6296  if (event.xconfigure.window != windows->widget.id)
6297  break;
6298  if ((event.xconfigure.width == (int) windows->widget.width) &&
6299  (event.xconfigure.height == (int) windows->widget.height))
6300  break;
6301  windows->widget.width=(unsigned int)
6302  MagickMax(event.xconfigure.width,(int) windows->widget.min_width);
6303  windows->widget.height=(unsigned int)
6304  MagickMax(event.xconfigure.height,(int) windows->widget.min_height);
6305  state|=UpdateConfigurationState;
6306  break;
6307  }
6308  case EnterNotify:
6309  {
6310  if (event.xcrossing.window != windows->widget.id)
6311  break;
6312  state&=(~InactiveWidgetState);
6313  break;
6314  }
6315  case Expose:
6316  {
6317  if (event.xexpose.window != windows->widget.id)
6318  break;
6319  if (event.xexpose.count != 0)
6320  break;
6321  state|=RedrawWidgetState;
6322  break;
6323  }
6324  case KeyPress:
6325  {
6326  static char
6327  command[MagickPathExtent];
6328 
6329  static int
6330  length;
6331 
6332  static KeySym
6333  key_symbol;
6334 
6335  /*
6336  Respond to a user key press.
6337  */
6338  if (event.xkey.window != windows->widget.id)
6339  break;
6340  length=XLookupString((XKeyEvent *) &event.xkey,command,
6341  (int) sizeof(command),&key_symbol,(XComposeStatus *) NULL);
6342  *(command+length)='\0';
6343  if (AreaIsActive(scroll_info,event.xkey))
6344  {
6345  /*
6346  Move slider.
6347  */
6348  switch ((int) key_symbol)
6349  {
6350  case XK_Home:
6351  case XK_KP_Home:
6352  {
6353  slider_info.id=0;
6354  break;
6355  }
6356  case XK_Up:
6357  case XK_KP_Up:
6358  {
6359  slider_info.id--;
6360  break;
6361  }
6362  case XK_Down:
6363  case XK_KP_Down:
6364  {
6365  slider_info.id++;
6366  break;
6367  }
6368  case XK_Prior:
6369  case XK_KP_Prior:
6370  {
6371  slider_info.id-=visible_fonts;
6372  break;
6373  }
6374  case XK_Next:
6375  case XK_KP_Next:
6376  {
6377  slider_info.id+=visible_fonts;
6378  break;
6379  }
6380  case XK_End:
6381  case XK_KP_End:
6382  {
6383  slider_info.id=fonts;
6384  break;
6385  }
6386  }
6387  state|=RedrawListState;
6388  break;
6389  }
6390  if ((key_symbol == XK_Return) || (key_symbol == XK_KP_Enter))
6391  {
6392  /*
6393  Read new font or glob patterm.
6394  */
6395  if (*reply_info.text == '\0')
6396  break;
6397  (void) CopyMagickString(back_pattern,glob_pattern,MagickPathExtent);
6398  (void) CopyMagickString(glob_pattern,reply_info.text,MagickPathExtent);
6399  state|=UpdateListState;
6400  break;
6401  }
6402  if (key_symbol == XK_Control_L)
6403  {
6404  state|=ControlState;
6405  break;
6406  }
6407  if (state & ControlState)
6408  switch ((int) key_symbol)
6409  {
6410  case XK_u:
6411  case XK_U:
6412  {
6413  /*
6414  Erase the entire line of text.
6415  */
6416  *reply_info.text='\0';
6417  reply_info.cursor=reply_info.text;
6418  reply_info.marker=reply_info.text;
6419  reply_info.highlight=MagickFalse;
6420  break;
6421  }
6422  default:
6423  break;
6424  }
6425  XEditText(display,&reply_info,key_symbol,command,state);
6426  XDrawMatteText(display,&windows->widget,&reply_info);
6427  state|=JumpListState;
6428  break;
6429  }
6430  case KeyRelease:
6431  {
6432  static char
6433  command[MagickPathExtent];
6434 
6435  static KeySym
6436  key_symbol;
6437 
6438  /*
6439  Respond to a user key release.
6440  */
6441  if (event.xkey.window != windows->widget.id)
6442  break;
6443  (void) XLookupString((XKeyEvent *) &event.xkey,command,
6444  (int) sizeof(command),&key_symbol,(XComposeStatus *) NULL);
6445  if (key_symbol == XK_Control_L)
6446  state&=(~ControlState);
6447  break;
6448  }
6449  case LeaveNotify:
6450  {
6451  if (event.xcrossing.window != windows->widget.id)
6452  break;
6453  state|=InactiveWidgetState;
6454  break;
6455  }
6456  case MapNotify:
6457  {
6458  mask&=(~CWX);
6459  mask&=(~CWY);
6460  break;
6461  }
6462  case MotionNotify:
6463  {
6464  /*
6465  Discard pending button motion events.
6466  */
6467  while (XCheckMaskEvent(display,ButtonMotionMask,&event)) ;
6468  if (slider_info.active)
6469  {
6470  /*
6471  Move slider matte.
6472  */
6473  slider_info.y=event.xmotion.y-
6474  ((slider_info.height+slider_info.bevel_width) >> 1)+1;
6475  if (slider_info.y < slider_info.min_y)
6476  slider_info.y=slider_info.min_y;
6477  if (slider_info.y > slider_info.max_y)
6478  slider_info.y=slider_info.max_y;
6479  slider_info.id=0;
6480  if (slider_info.y != slider_info.min_y)
6481  slider_info.id=(fonts*(slider_info.y-slider_info.min_y+1))/
6482  (slider_info.max_y-slider_info.min_y+1);
6483  state|=RedrawListState;
6484  break;
6485  }
6486  if (state & InactiveWidgetState)
6487  break;
6488  if (back_info.raised == MatteIsActive(back_info,event.xmotion))
6489  {
6490  /*
6491  Back button status changed.
6492  */
6493  back_info.raised=!back_info.raised;
6494  XDrawBeveledButton(display,&windows->widget,&back_info);
6495  break;
6496  }
6497  if (reset_info.raised == MatteIsActive(reset_info,event.xmotion))
6498  {
6499  /*
6500  Reset button status changed.
6501  */
6502  reset_info.raised=!reset_info.raised;
6503  XDrawBeveledButton(display,&windows->widget,&reset_info);
6504  break;
6505  }
6506  if (action_info.raised == MatteIsActive(action_info,event.xmotion))
6507  {
6508  /*
6509  Action button status changed.
6510  */
6511  action_info.raised=action_info.raised == MagickFalse ?
6513  XDrawBeveledButton(display,&windows->widget,&action_info);
6514  break;
6515  }
6516  if (cancel_info.raised == MatteIsActive(cancel_info,event.xmotion))
6517  {
6518  /*
6519  Cancel button status changed.
6520  */
6521  cancel_info.raised=cancel_info.raised == MagickFalse ?
6523  XDrawBeveledButton(display,&windows->widget,&cancel_info);
6524  break;
6525  }
6526  break;
6527  }
6528  case SelectionClear:
6529  {
6530  reply_info.highlight=MagickFalse;
6531  XDrawMatteText(display,&windows->widget,&reply_info);
6532  break;
6533  }
6534  case SelectionNotify:
6535  {
6536  Atom
6537  type;
6538 
6539  int
6540  format;
6541 
6542  unsigned char
6543  *data;
6544 
6545  unsigned long
6546  after,
6547  length;
6548 
6549  /*
6550  Obtain response from primary selection.
6551  */
6552  if (event.xselection.property == (Atom) None)
6553  break;
6554  status=XGetWindowProperty(display,event.xselection.requestor,
6555  event.xselection.property,0L,2047L,MagickTrue,XA_STRING,&type,
6556  &format,&length,&after,&data);
6557  if ((status != Success) || (type != XA_STRING) || (format == 32) ||
6558  (length == 0))
6559  break;
6560  if ((Extent(reply_info.text)+length) >= (MagickPathExtent-1))
6561  (void) XBell(display,0);
6562  else
6563  {
6564  /*
6565  Insert primary selection in reply text.
6566  */
6567  *(data+length)='\0';
6568  XEditText(display,&reply_info,(KeySym) XK_Insert,(char *) data,
6569  state);
6570  XDrawMatteText(display,&windows->widget,&reply_info);
6571  state|=JumpListState;
6572  state|=RedrawActionState;
6573  }
6574  (void) XFree((void *) data);
6575  break;
6576  }
6577  case SelectionRequest:
6578  {
6579  XSelectionEvent
6580  notify;
6581 
6582  XSelectionRequestEvent
6583  *request;
6584 
6585  /*
6586  Set XA_PRIMARY selection.
6587  */
6588  request=(&(event.xselectionrequest));
6589  (void) XChangeProperty(request->display,request->requestor,
6590  request->property,request->target,8,PropModeReplace,
6591  (unsigned char *) primary_selection,Extent(primary_selection));
6592  notify.type=SelectionNotify;
6593  notify.display=request->display;
6594  notify.requestor=request->requestor;
6595  notify.selection=request->selection;
6596  notify.target=request->target;
6597  notify.time=request->time;
6598  if (request->property == None)
6599  notify.property=request->target;
6600  else
6601  notify.property=request->property;
6602  (void) XSendEvent(request->display,request->requestor,False,0,
6603  (XEvent *) &notify);
6604  }
6605  default:
6606  break;
6607  }
6608  } while ((state & ExitState) == 0);
6609  XSetCursorState(display,windows,MagickFalse);
6610  (void) XWithdrawWindow(display,windows->widget.id,windows->widget.screen);
6611  XCheckRefreshWindows(display,windows);
6612  /*
6613  Free font list.
6614  */
6615  (void) XFreeFontNames(listhead);
6616  fontlist=(char **) RelinquishMagickMemory(fontlist);
6617 }
6618 
6619 /*
6620 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6621 % %
6622 % %
6623 % %
6624 % X I n f o W i d g e t %
6625 % %
6626 % %
6627 % %
6628 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6629 %
6630 % XInfoWidget() displays text in the Info widget. The purpose is to inform
6631 % the user that what activity is currently being performed (e.g. reading
6632 % an image, rotating an image, etc.).
6633 %
6634 % The format of the XInfoWidget method is:
6635 %
6636 % void XInfoWidget(Display *display,XWindows *windows,const char *activity)
6637 %
6638 % A description of each parameter follows:
6639 %
6640 % o display: Specifies a connection to an X server; returned from
6641 % XOpenDisplay.
6642 %
6643 % o window: Specifies a pointer to a XWindows structure.
6644 %
6645 % o activity: This character string reflects the current activity and is
6646 % displayed in the Info widget.
6647 %
6648 */
6649 MagickPrivate void XInfoWidget(Display *display,XWindows *windows,
6650  const char *activity)
6651 {
6652  unsigned int
6653  height,
6654  margin,
6655  width;
6656 
6657  XFontStruct
6658  *font_info;
6659 
6660  XWindowChanges
6661  window_changes;
6662 
6663  /*
6664  Map Info widget.
6665  */
6666  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
6667  assert(display != (Display *) NULL);
6668  assert(windows != (XWindows *) NULL);
6669  assert(activity != (char *) NULL);
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 MagickPrivate 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[MagickPathExtent];
6758 
6759  int
6760  x;
6761 
6762  register 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  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",action);
6818  XSetCursorState(display,windows,MagickTrue);
6819  XCheckRefreshWindows(display,windows);
6820  if (list == (const char **) NULL)
6821  {
6822  XNoticeWidget(display,windows,"No text to browse:",(char *) NULL);
6823  return;
6824  }
6825  for (entries=0; ; entries++)
6826  if (list[entries] == (char *) NULL)
6827  break;
6828  /*
6829  Determine Font Browser widget attributes.
6830  */
6831  font_info=window_info->font_info;
6832  text_width=WidgetTextWidth(font_info,(char *) query);
6833  for (i=0; i < (int) entries; i++)
6834  if (WidgetTextWidth(font_info,(char *) list[i]) > text_width)
6835  text_width=WidgetTextWidth(font_info,(char *) list[i]);
6836  width=WidgetTextWidth(font_info,(char *) action);
6837  if (WidgetTextWidth(font_info,CancelButtonText) > width)
6838  width=WidgetTextWidth(font_info,CancelButtonText);
6839  width+=QuantumMargin;
6840  height=(unsigned int) (font_info->ascent+font_info->descent);
6841  /*
6842  Position List Browser widget.
6843  */
6844  window_info->width=(unsigned int) MagickMin((int) text_width,(int)
6845  MaxTextWidth)+((9*QuantumMargin) >> 1);
6846  window_info->min_width=(unsigned int) (MinTextWidth+4*QuantumMargin);
6847  if (window_info->width < window_info->min_width)
6848  window_info->width=window_info->min_width;
6849  window_info->height=(unsigned int)
6850  (((81*height) >> 2)+((13*QuantumMargin) >> 1)+4);
6851  window_info->min_height=(unsigned int)
6852  (((23*height) >> 1)+((13*QuantumMargin) >> 1)+4);
6853  if (window_info->height < window_info->min_height)
6854  window_info->height=window_info->min_height;
6855  XConstrainWindowPosition(display,window_info);
6856  /*
6857  Map List Browser widget.
6858  */
6859  (void) CopyMagickString(window_info->name,"Browse",MagickPathExtent);
6860  status=XStringListToTextProperty(&window_info->name,1,&window_name);
6861  if (status != False)
6862  {
6863  XSetWMName(display,window_info->id,&window_name);
6864  XSetWMIconName(display,windows->widget.id,&window_name);
6865  (void) XFree((void *) window_name.value);
6866  }
6867  window_changes.width=(int) window_info->width;
6868  window_changes.height=(int) window_info->height;
6869  window_changes.x=window_info->x;
6870  window_changes.y=window_info->y;
6871  (void) XReconfigureWMWindow(display,window_info->id,window_info->screen,mask,
6872  &window_changes);
6873  (void) XMapRaised(display,window_info->id);
6874  window_info->mapped=MagickFalse;
6875  /*
6876  Respond to X events.
6877  */
6878  XGetWidgetInfo((char *) NULL,&slider_info);
6879  XGetWidgetInfo((char *) NULL,&north_info);
6880  XGetWidgetInfo((char *) NULL,&south_info);
6881  XGetWidgetInfo((char *) NULL,&expose_info);
6882  XGetWidgetInfo((char *) NULL,&selection_info);
6883  visible_entries=0;
6884  delay=SuspendTime << 2;
6885  state=UpdateConfigurationState;
6886  do
6887  {
6888  if (state & UpdateConfigurationState)
6889  {
6890  int
6891  id;
6892 
6893  /*
6894  Initialize button information.
6895  */
6896  XGetWidgetInfo(CancelButtonText,&cancel_info);
6897  cancel_info.width=width;
6898  cancel_info.height=(unsigned int) ((3*height) >> 1);
6899  cancel_info.x=(int)
6900  (window_info->width-cancel_info.width-QuantumMargin-2);
6901  cancel_info.y=(int)
6902  (window_info->height-cancel_info.height-QuantumMargin);
6903  XGetWidgetInfo(action,&action_info);
6904  action_info.width=width;
6905  action_info.height=(unsigned int) ((3*height) >> 1);
6906  action_info.x=cancel_info.x-(cancel_info.width+(QuantumMargin >> 1)+
6907  (action_info.bevel_width << 1));
6908  action_info.y=cancel_info.y;
6909  /*
6910  Initialize reply information.
6911  */
6912  XGetWidgetInfo(reply,&reply_info);
6913  reply_info.raised=MagickFalse;
6914  reply_info.bevel_width--;
6915  reply_info.width=window_info->width-((4*QuantumMargin) >> 1);
6916  reply_info.height=height << 1;
6917  reply_info.x=QuantumMargin;
6918  reply_info.y=action_info.y-reply_info.height-QuantumMargin;
6919  /*
6920  Initialize scroll information.
6921  */
6922  XGetWidgetInfo((char *) NULL,&scroll_info);
6923  scroll_info.bevel_width--;
6924  scroll_info.width=height;
6925  scroll_info.height=(unsigned int)
6926  (reply_info.y-((6*QuantumMargin) >> 1)-height);
6927  scroll_info.x=reply_info.x+(reply_info.width-scroll_info.width);
6928  scroll_info.y=((5*QuantumMargin) >> 1)+height-reply_info.bevel_width;
6929  scroll_info.raised=MagickFalse;
6930  scroll_info.trough=MagickTrue;
6931  north_info=scroll_info;
6932  north_info.raised=MagickTrue;
6933  north_info.width-=(north_info.bevel_width << 1);
6934  north_info.height=north_info.width-1;
6935  north_info.x+=north_info.bevel_width;
6936  north_info.y+=north_info.bevel_width;
6937  south_info=north_info;
6938  south_info.y=scroll_info.y+scroll_info.height-scroll_info.bevel_width-
6939  south_info.height;
6940  id=slider_info.id;
6941  slider_info=north_info;
6942  slider_info.id=id;
6943  slider_info.width-=2;
6944  slider_info.min_y=north_info.y+north_info.height+north_info.bevel_width+
6945  slider_info.bevel_width+2;
6946  slider_info.height=scroll_info.height-((slider_info.min_y-
6947  scroll_info.y+1) << 1)+4;
6948  visible_entries=scroll_info.height/(height+(height >> 3));
6949  if (entries > visible_entries)
6950  slider_info.height=(visible_entries*slider_info.height)/entries;
6951  slider_info.max_y=south_info.y-south_info.bevel_width-
6952  slider_info.bevel_width-2;
6953  slider_info.x=scroll_info.x+slider_info.bevel_width+1;
6954  slider_info.y=slider_info.min_y;
6955  expose_info=scroll_info;
6956  expose_info.y=slider_info.y;
6957  /*
6958  Initialize list information.
6959  */
6960  XGetWidgetInfo((char *) NULL,&list_info);
6961  list_info.raised=MagickFalse;
6962  list_info.bevel_width--;
6963  list_info.width=(unsigned int)
6964  (scroll_info.x-reply_info.x-(QuantumMargin >> 1));
6965  list_info.height=scroll_info.height;
6966  list_info.x=reply_info.x;
6967  list_info.y=scroll_info.y;
6968  if (window_info->mapped == MagickFalse)
6969  for (i=0; i < (int) entries; i++)
6970  if (LocaleCompare(list[i],reply) == 0)
6971  {
6972  list_info.id=i;
6973  slider_info.id=i-(visible_entries >> 1);
6974  if (slider_info.id < 0)
6975  slider_info.id=0;
6976  }
6977  /*
6978  Initialize text information.
6979  */
6980  XGetWidgetInfo(query,&text_info);
6981  text_info.width=reply_info.width;
6982  text_info.height=height;
6983  text_info.x=list_info.x-(QuantumMargin >> 1);
6984  text_info.y=QuantumMargin;
6985  /*
6986  Initialize selection information.
6987  */
6988  XGetWidgetInfo((char *) NULL,&selection_info);
6989  selection_info.center=MagickFalse;
6990  selection_info.width=list_info.width;
6991  selection_info.height=(unsigned int) ((9*height) >> 3);
6992  selection_info.x=list_info.x;
6993  state&=(~UpdateConfigurationState);
6994  }
6995  if (state & RedrawWidgetState)
6996  {
6997  /*
6998  Redraw List Browser window.
6999  */
7000  XDrawWidgetText(display,window_info,&text_info);
7001  XDrawBeveledMatte(display,window_info,&list_info);
7002  XDrawBeveledMatte(display,window_info,&scroll_info);
7003  XDrawTriangleNorth(display,window_info,&north_info);
7004  XDrawBeveledButton(display,window_info,&slider_info);
7005  XDrawTriangleSouth(display,window_info,&south_info);
7006  XDrawBeveledMatte(display,window_info,&reply_info);
7007  XDrawMatteText(display,window_info,&reply_info);
7008  XDrawBeveledButton(display,window_info,&action_info);
7009  XDrawBeveledButton(display,window_info,&cancel_info);
7010  XHighlightWidget(display,window_info,BorderOffset,BorderOffset);
7011  selection_info.id=(~0);
7012  state|=RedrawActionState;
7013  state|=RedrawListState;
7014  state&=(~RedrawWidgetState);
7015  }
7016  if (state & RedrawListState)
7017  {
7018  /*
7019  Determine slider id and position.
7020  */
7021  if (slider_info.id >= (int) (entries-visible_entries))
7022  slider_info.id=(int) (entries-visible_entries);
7023  if ((slider_info.id < 0) || (entries <= visible_entries))
7024  slider_info.id=0;
7025  slider_info.y=slider_info.min_y;
7026  if (entries > 0)
7027  slider_info.y+=
7028  slider_info.id*(slider_info.max_y-slider_info.min_y+1)/entries;
7029  if (slider_info.id != selection_info.id)
7030  {
7031  /*
7032  Redraw scroll bar and file names.
7033  */
7034  selection_info.id=slider_info.id;
7035  selection_info.y=list_info.y+(height >> 3)+2;
7036  for (i=0; i < (int) visible_entries; i++)
7037  {
7038  selection_info.raised=(slider_info.id+i) != list_info.id ?
7040  selection_info.text=(char *) NULL;
7041  if ((slider_info.id+i) < (int) entries)
7042  selection_info.text=(char *) list[slider_info.id+i];
7043  XDrawWidgetText(display,window_info,&selection_info);
7044  selection_info.y+=(int) selection_info.height;
7045  }
7046  /*
7047  Update slider.
7048  */
7049  if (slider_info.y > expose_info.y)
7050  {
7051  expose_info.height=(unsigned int) slider_info.y-expose_info.y;
7052  expose_info.y=slider_info.y-expose_info.height-
7053  slider_info.bevel_width-1;
7054  }
7055  else
7056  {
7057  expose_info.height=(unsigned int) expose_info.y-slider_info.y;
7058  expose_info.y=slider_info.y+slider_info.height+
7059  slider_info.bevel_width+1;
7060  }
7061  XDrawTriangleNorth(display,window_info,&north_info);
7062  XDrawMatte(display,window_info,&expose_info);
7063  XDrawBeveledButton(display,window_info,&slider_info);
7064  XDrawTriangleSouth(display,window_info,&south_info);
7065  expose_info.y=slider_info.y;
7066  }
7067  state&=(~RedrawListState);
7068  }
7069  /*
7070  Wait for next event.
7071  */
7072  if (north_info.raised && south_info.raised)
7073  (void) XIfEvent(display,&event,XScreenEvent,(char *) windows);
7074  else
7075  {
7076  /*
7077  Brief delay before advancing scroll bar.
7078  */
7079  XDelay(display,delay);
7080  delay=SuspendTime;
7081  (void) XCheckIfEvent(display,&event,XScreenEvent,(char *) windows);
7082  if (north_info.raised == MagickFalse)
7083  if (slider_info.id > 0)
7084  {
7085  /*
7086  Move slider up.
7087  */
7088  slider_info.id--;
7089  state|=RedrawListState;
7090  }
7091  if (south_info.raised == MagickFalse)
7092  if (slider_info.id < (int) entries)
7093  {
7094  /*
7095  Move slider down.
7096  */
7097  slider_info.id++;
7098  state|=RedrawListState;
7099  }
7100  if (event.type != ButtonRelease)
7101  continue;
7102  }
7103  switch (event.type)
7104  {
7105  case ButtonPress:
7106