MagickCore  7.1.0
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-2021 ImageMagick Studio LLC, a non-profit organization %
22 % dedicated to making software imaging solutions freely available. %
23 % %
24 % You may not use this file except in compliance with the License. You may %
25 % obtain a copy of the License at %
26 % %
27 % https://imagemagick.org/script/license.php %
28 % %
29 % Unless required by applicable law or agreed to in writing, software %
30 % distributed under the License is distributed on an "AS IS" BASIS, %
31 % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
32 % See the License for the specific language governing permissions and %
33 % limitations under the License. %
34 % %
35 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
36 %
37 %
38 */
39 
40 /*
41  Include declarations.
42 */
43 #include "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  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  char
1142  *p,
1143  *q;
1144 
1145  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  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=(unsigned int) (scroll_info.height*
1863  PerceptibleReciprocal((double) height+(height >> 3)));
1864  if (colors > visible_colors)
1865  slider_info.height=(unsigned int) ((visible_colors*
1866  slider_info.height)/colors);
1867  slider_info.max_y=south_info.y-south_info.bevel_width-
1868  slider_info.bevel_width-2;
1869  slider_info.x=scroll_info.x+slider_info.bevel_width+1;
1870  slider_info.y=slider_info.min_y;
1871  expose_info=scroll_info;
1872  expose_info.y=slider_info.y;
1873  /*
1874  Initialize list information.
1875  */
1876  XGetWidgetInfo((char *) NULL,&list_info);
1877  list_info.raised=MagickFalse;
1878  list_info.bevel_width--;
1879  list_info.width=(unsigned int)
1880  (scroll_info.x-reply_info.x-(QuantumMargin >> 1));
1881  list_info.height=scroll_info.height;
1882  list_info.x=reply_info.x;
1883  list_info.y=scroll_info.y;
1884  if (windows->widget.mapped == MagickFalse)
1885  state|=JumpListState;
1886  /*
1887  Initialize text information.
1888  */
1889  *text='\0';
1890  XGetWidgetInfo(text,&text_info);
1891  text_info.center=MagickFalse;
1892  text_info.width=reply_info.width;
1893  text_info.height=height;
1894  text_info.x=list_info.x-(QuantumMargin >> 1);
1895  text_info.y=QuantumMargin;
1896  /*
1897  Initialize selection information.
1898  */
1899  XGetWidgetInfo((char *) NULL,&selection_info);
1900  selection_info.center=MagickFalse;
1901  selection_info.width=list_info.width;
1902  selection_info.height=(unsigned int) ((9*height) >> 3);
1903  selection_info.x=list_info.x;
1904  state&=(~UpdateConfigurationState);
1905  }
1906  if (state & RedrawWidgetState)
1907  {
1908  /*
1909  Redraw Color Browser window.
1910  */
1911  x=QuantumMargin;
1912  y=text_info.y+((text_info.height-height) >> 1)+font_info->ascent;
1913  (void) XDrawString(display,windows->widget.id,
1914  windows->widget.annotate_context,x,y,ColorPatternText,
1915  Extent(ColorPatternText));
1916  (void) CopyMagickString(text_info.text,glob_pattern,MagickPathExtent);
1917  XDrawWidgetText(display,&windows->widget,&text_info);
1918  XDrawBeveledButton(display,&windows->widget,&grab_info);
1919  XDrawBeveledButton(display,&windows->widget,&reset_info);
1920  XDrawBeveledMatte(display,&windows->widget,&list_info);
1921  XDrawBeveledMatte(display,&windows->widget,&scroll_info);
1922  XDrawTriangleNorth(display,&windows->widget,&north_info);
1923  XDrawBeveledButton(display,&windows->widget,&slider_info);
1924  XDrawTriangleSouth(display,&windows->widget,&south_info);
1925  x=QuantumMargin;
1926  y=reply_info.y+((reply_info.height-height) >> 1)+font_info->ascent;
1927  (void) XDrawString(display,windows->widget.id,
1928  windows->widget.annotate_context,x,y,ColornameText,
1929  Extent(ColornameText));
1930  XDrawBeveledMatte(display,&windows->widget,&reply_info);
1931  XDrawMatteText(display,&windows->widget,&reply_info);
1932  XDrawBeveledButton(display,&windows->widget,&action_info);
1933  XDrawBeveledButton(display,&windows->widget,&cancel_info);
1934  XHighlightWidget(display,&windows->widget,BorderOffset,BorderOffset);
1935  selection_info.id=(~0);
1936  state|=RedrawActionState;
1937  state|=RedrawListState;
1938  state&=(~RedrawWidgetState);
1939  }
1940  if (state & UpdateListState)
1941  {
1942  char
1943  **checklist;
1944 
1945  size_t
1946  number_colors;
1947 
1948  status=XParseColor(display,windows->widget.map_info->colormap,
1949  glob_pattern,&color);
1950  if ((status != False) || (strchr(glob_pattern,'-') != (char *) NULL))
1951  {
1952  /*
1953  Reply is a single color name-- exit.
1954  */
1955  (void) CopyMagickString(reply,glob_pattern,MagickPathExtent);
1956  (void) CopyMagickString(glob_pattern,reset_pattern,MagickPathExtent);
1957  action_info.raised=MagickFalse;
1958  XDrawBeveledButton(display,&windows->widget,&action_info);
1959  break;
1960  }
1961  /*
1962  Update color list.
1963  */
1964  checklist=GetColorList(glob_pattern,&number_colors,exception);
1965  if (number_colors == 0)
1966  {
1967  (void) CopyMagickString(glob_pattern,reset_pattern,MagickPathExtent);
1968  (void) XBell(display,0);
1969  }
1970  else
1971  {
1972  for (i=0; i < (int) colors; i++)
1973  colorlist[i]=DestroyString(colorlist[i]);
1974  if (colorlist != (char **) NULL)
1975  colorlist=(char **) RelinquishMagickMemory(colorlist);
1976  colorlist=checklist;
1977  colors=number_colors;
1978  }
1979  /*
1980  Sort color list in ascending order.
1981  */
1982  slider_info.height=scroll_info.height-((slider_info.min_y-
1983  scroll_info.y+1) << 1)+1;
1984  if (colors > visible_colors)
1985  slider_info.height=(unsigned int) ((visible_colors*
1986  slider_info.height)/colors);
1987  slider_info.max_y=south_info.y-south_info.bevel_width-
1988  slider_info.bevel_width-2;
1989  slider_info.id=0;
1990  slider_info.y=slider_info.min_y;
1991  expose_info.y=slider_info.y;
1992  selection_info.id=(~0);
1993  list_info.id=(~0);
1994  state|=RedrawListState;
1995  /*
1996  Redraw color name & reply.
1997  */
1998  *reply_info.text='\0';
1999  reply_info.cursor=reply_info.text;
2000  (void) CopyMagickString(text_info.text,glob_pattern,MagickPathExtent);
2001  XDrawWidgetText(display,&windows->widget,&text_info);
2002  XDrawMatteText(display,&windows->widget,&reply_info);
2003  XDrawBeveledMatte(display,&windows->widget,&scroll_info);
2004  XDrawTriangleNorth(display,&windows->widget,&north_info);
2005  XDrawBeveledButton(display,&windows->widget,&slider_info);
2006  XDrawTriangleSouth(display,&windows->widget,&south_info);
2007  XHighlightWidget(display,&windows->widget,BorderOffset,BorderOffset);
2008  state&=(~UpdateListState);
2009  }
2010  if (state & JumpListState)
2011  {
2012  /*
2013  Jump scroll to match user color.
2014  */
2015  list_info.id=(~0);
2016  for (i=0; i < (int) colors; i++)
2017  if (LocaleCompare(colorlist[i],reply) >= 0)
2018  {
2019  list_info.id=LocaleCompare(colorlist[i],reply) == 0 ? i : ~0;
2020  break;
2021  }
2022  if ((i < slider_info.id) ||
2023  (i >= (int) (slider_info.id+visible_colors)))
2024  slider_info.id=i-(visible_colors >> 1);
2025  selection_info.id=(~0);
2026  state|=RedrawListState;
2027  state&=(~JumpListState);
2028  }
2029  if (state & RedrawListState)
2030  {
2031  /*
2032  Determine slider id and position.
2033  */
2034  if (slider_info.id >= (int) (colors-visible_colors))
2035  slider_info.id=(int) (colors-visible_colors);
2036  if ((slider_info.id < 0) || (colors <= visible_colors))
2037  slider_info.id=0;
2038  slider_info.y=slider_info.min_y;
2039  if (colors != 0)
2040  slider_info.y+=((ssize_t) slider_info.id*(slider_info.max_y-
2041  slider_info.min_y+1)/colors);
2042  if (slider_info.id != selection_info.id)
2043  {
2044  /*
2045  Redraw scroll bar and file names.
2046  */
2047  selection_info.id=slider_info.id;
2048  selection_info.y=list_info.y+(height >> 3)+2;
2049  for (i=0; i < (int) visible_colors; i++)
2050  {
2051  selection_info.raised=(slider_info.id+i) != list_info.id ?
2053  selection_info.text=(char *) NULL;
2054  if ((slider_info.id+i) < (int) colors)
2055  selection_info.text=colorlist[slider_info.id+i];
2056  XDrawWidgetText(display,&windows->widget,&selection_info);
2057  selection_info.y+=(int) selection_info.height;
2058  }
2059  /*
2060  Update slider.
2061  */
2062  if (slider_info.y > expose_info.y)
2063  {
2064  expose_info.height=(unsigned int) slider_info.y-expose_info.y;
2065  expose_info.y=slider_info.y-expose_info.height-
2066  slider_info.bevel_width-1;
2067  }
2068  else
2069  {
2070  expose_info.height=(unsigned int) expose_info.y-slider_info.y;
2071  expose_info.y=slider_info.y+slider_info.height+
2072  slider_info.bevel_width+1;
2073  }
2074  XDrawTriangleNorth(display,&windows->widget,&north_info);
2075  XDrawMatte(display,&windows->widget,&expose_info);
2076  XDrawBeveledButton(display,&windows->widget,&slider_info);
2077  XDrawTriangleSouth(display,&windows->widget,&south_info);
2078  expose_info.y=slider_info.y;
2079  }
2080  state&=(~RedrawListState);
2081  }
2082  if (state & RedrawActionState)
2083  {
2084  static char
2085  colorname[MagickPathExtent];
2086 
2087  /*
2088  Display the selected color in a drawing area.
2089  */
2090  color=windows->widget.pixel_info->matte_color;
2091  (void) XParseColor(display,windows->widget.map_info->colormap,
2092  reply_info.text,&windows->widget.pixel_info->matte_color);
2093  XBestPixel(display,windows->widget.map_info->colormap,(XColor *) NULL,
2094  (unsigned int) windows->widget.visual_info->colormap_size,
2095  &windows->widget.pixel_info->matte_color);
2096  mode_info.text=colorname;
2097  (void) FormatLocaleString(mode_info.text,MagickPathExtent,
2098  "#%02x%02x%02x",windows->widget.pixel_info->matte_color.red,
2099  windows->widget.pixel_info->matte_color.green,
2100  windows->widget.pixel_info->matte_color.blue);
2101  XDrawBeveledButton(display,&windows->widget,&mode_info);
2102  windows->widget.pixel_info->matte_color=color;
2103  state&=(~RedrawActionState);
2104  }
2105  /*
2106  Wait for next event.
2107  */
2108  if (north_info.raised && south_info.raised)
2109  (void) XIfEvent(display,&event,XScreenEvent,(char *) windows);
2110  else
2111  {
2112  /*
2113  Brief delay before advancing scroll bar.
2114  */
2115  XDelay(display,delay);
2116  delay=SuspendTime;
2117  (void) XCheckIfEvent(display,&event,XScreenEvent,(char *) windows);
2118  if (north_info.raised == MagickFalse)
2119  if (slider_info.id > 0)
2120  {
2121  /*
2122  Move slider up.
2123  */
2124  slider_info.id--;
2125  state|=RedrawListState;
2126  }
2127  if (south_info.raised == MagickFalse)
2128  if (slider_info.id < (int) colors)
2129  {
2130  /*
2131  Move slider down.
2132  */
2133  slider_info.id++;
2134  state|=RedrawListState;
2135  }
2136  if (event.type != ButtonRelease)
2137  continue;
2138  }
2139  switch (event.type)
2140  {
2141  case ButtonPress:
2142  {
2143  if (MatteIsActive(slider_info,event.xbutton))
2144  {
2145  /*
2146  Track slider.
2147  */
2148  slider_info.active=MagickTrue;
2149  break;
2150  }
2151  if (MatteIsActive(north_info,event.xbutton))
2152  if (slider_info.id > 0)
2153  {
2154  /*
2155  Move slider up.
2156  */
2157  north_info.raised=MagickFalse;
2158  slider_info.id--;
2159  state|=RedrawListState;
2160  break;
2161  }
2162  if (MatteIsActive(south_info,event.xbutton))
2163  if (slider_info.id < (int) colors)
2164  {
2165  /*
2166  Move slider down.
2167  */
2168  south_info.raised=MagickFalse;
2169  slider_info.id++;
2170  state|=RedrawListState;
2171  break;
2172  }
2173  if (MatteIsActive(scroll_info,event.xbutton))
2174  {
2175  /*
2176  Move slider.
2177  */
2178  if (event.xbutton.y < slider_info.y)
2179  slider_info.id-=(visible_colors-1);
2180  else
2181  slider_info.id+=(visible_colors-1);
2182  state|=RedrawListState;
2183  break;
2184  }
2185  if (MatteIsActive(list_info,event.xbutton))
2186  {
2187  int
2188  id;
2189 
2190  /*
2191  User pressed list matte.
2192  */
2193  id=slider_info.id+(event.xbutton.y-(list_info.y+(height >> 1))+1)/
2194  selection_info.height;
2195  if (id >= (int) colors)
2196  break;
2197  (void) CopyMagickString(reply_info.text,colorlist[id],
2199  reply_info.highlight=MagickFalse;
2200  reply_info.marker=reply_info.text;
2201  reply_info.cursor=reply_info.text+Extent(reply_info.text);
2202  XDrawMatteText(display,&windows->widget,&reply_info);
2203  state|=RedrawActionState;
2204  if (id == list_info.id)
2205  {
2206  (void) CopyMagickString(glob_pattern,reply_info.text,
2208  state|=UpdateListState;
2209  }
2210  selection_info.id=(~0);
2211  list_info.id=id;
2212  state|=RedrawListState;
2213  break;
2214  }
2215  if (MatteIsActive(grab_info,event.xbutton))
2216  {
2217  /*
2218  User pressed Grab button.
2219  */
2220  grab_info.raised=MagickFalse;
2221  XDrawBeveledButton(display,&windows->widget,&grab_info);
2222  break;
2223  }
2224  if (MatteIsActive(reset_info,event.xbutton))
2225  {
2226  /*
2227  User pressed Reset button.
2228  */
2229  reset_info.raised=MagickFalse;
2230  XDrawBeveledButton(display,&windows->widget,&reset_info);
2231  break;
2232  }
2233  if (MatteIsActive(mode_info,event.xbutton))
2234  {
2235  /*
2236  User pressed mode button.
2237  */
2238  if (mode_info.text != (char *) NULL)
2239  (void) CopyMagickString(reply_info.text,mode_info.text,
2241  (void) CopyMagickString(primary_selection,reply_info.text,
2243  (void) XSetSelectionOwner(display,XA_PRIMARY,windows->widget.id,
2244  event.xbutton.time);
2245  reply_info.highlight=XGetSelectionOwner(display,XA_PRIMARY) ==
2246  windows->widget.id ? MagickTrue : MagickFalse;
2247  reply_info.marker=reply_info.text;
2248  reply_info.cursor=reply_info.text+Extent(reply_info.text);
2249  XDrawMatteText(display,&windows->widget,&reply_info);
2250  break;
2251  }
2252  if (MatteIsActive(action_info,event.xbutton))
2253  {
2254  /*
2255  User pressed action button.
2256  */
2257  action_info.raised=MagickFalse;
2258  XDrawBeveledButton(display,&windows->widget,&action_info);
2259  break;
2260  }
2261  if (MatteIsActive(cancel_info,event.xbutton))
2262  {
2263  /*
2264  User pressed Cancel button.
2265  */
2266  cancel_info.raised=MagickFalse;
2267  XDrawBeveledButton(display,&windows->widget,&cancel_info);
2268  break;
2269  }
2270  if (MatteIsActive(reply_info,event.xbutton) == MagickFalse)
2271  break;
2272  if (event.xbutton.button != Button2)
2273  {
2274  static Time
2275  click_time;
2276 
2277  /*
2278  Move text cursor to position of button press.
2279  */
2280  x=event.xbutton.x-reply_info.x-(QuantumMargin >> 2);
2281  for (i=1; i <= Extent(reply_info.marker); i++)
2282  if (XTextWidth(font_info,reply_info.marker,i) > x)
2283  break;
2284  reply_info.cursor=reply_info.marker+i-1;
2285  if (event.xbutton.time > (click_time+DoubleClick))
2286  reply_info.highlight=MagickFalse;
2287  else
2288  {
2289  /*
2290  Become the XA_PRIMARY selection owner.
2291  */
2292  (void) CopyMagickString(primary_selection,reply_info.text,
2294  (void) XSetSelectionOwner(display,XA_PRIMARY,windows->widget.id,
2295  event.xbutton.time);
2296  reply_info.highlight=XGetSelectionOwner(display,XA_PRIMARY) ==
2297  windows->widget.id ? MagickTrue : MagickFalse;
2298  }
2299  XDrawMatteText(display,&windows->widget,&reply_info);
2300  click_time=event.xbutton.time;
2301  break;
2302  }
2303  /*
2304  Request primary selection.
2305  */
2306  (void) XConvertSelection(display,XA_PRIMARY,XA_STRING,XA_STRING,
2307  windows->widget.id,event.xbutton.time);
2308  break;
2309  }
2310  case ButtonRelease:
2311  {
2312  if (windows->widget.mapped == MagickFalse)
2313  break;
2314  if (north_info.raised == MagickFalse)
2315  {
2316  /*
2317  User released up button.
2318  */
2319  delay=SuspendTime << 2;
2320  north_info.raised=MagickTrue;
2321  XDrawTriangleNorth(display,&windows->widget,&north_info);
2322  }
2323  if (south_info.raised == MagickFalse)
2324  {
2325  /*
2326  User released down button.
2327  */
2328  delay=SuspendTime << 2;
2329  south_info.raised=MagickTrue;
2330  XDrawTriangleSouth(display,&windows->widget,&south_info);
2331  }
2332  if (slider_info.active)
2333  {
2334  /*
2335  Stop tracking slider.
2336  */
2337  slider_info.active=MagickFalse;
2338  break;
2339  }
2340  if (grab_info.raised == MagickFalse)
2341  {
2342  if (event.xbutton.window == windows->widget.id)
2343  if (MatteIsActive(grab_info,event.xbutton))
2344  {
2345  /*
2346  Select a fill color from the X server.
2347  */
2348  (void) XGetWindowColor(display,windows,reply_info.text,
2349  exception);
2350  reply_info.marker=reply_info.text;
2351  reply_info.cursor=reply_info.text+Extent(reply_info.text);
2352  XDrawMatteText(display,&windows->widget,&reply_info);
2353  state|=RedrawActionState;
2354  }
2355  grab_info.raised=MagickTrue;
2356  XDrawBeveledButton(display,&windows->widget,&grab_info);
2357  }
2358  if (reset_info.raised == MagickFalse)
2359  {
2360  if (event.xbutton.window == windows->widget.id)
2361  if (MatteIsActive(reset_info,event.xbutton))
2362  {
2363  (void) CopyMagickString(glob_pattern,reset_pattern,
2365  state|=UpdateListState;
2366  }
2367  reset_info.raised=MagickTrue;
2368  XDrawBeveledButton(display,&windows->widget,&reset_info);
2369  }
2370  if (action_info.raised == MagickFalse)
2371  {
2372  if (event.xbutton.window == windows->widget.id)
2373  {
2374  if (MatteIsActive(action_info,event.xbutton))
2375  {
2376  if (*reply_info.text == '\0')
2377  (void) XBell(display,0);
2378  else
2379  state|=ExitState;
2380  }
2381  }
2382  action_info.raised=MagickTrue;
2383  XDrawBeveledButton(display,&windows->widget,&action_info);
2384  }
2385  if (cancel_info.raised == MagickFalse)
2386  {
2387  if (event.xbutton.window == windows->widget.id)
2388  if (MatteIsActive(cancel_info,event.xbutton))
2389  {
2390  *reply_info.text='\0';
2391  state|=ExitState;
2392  }
2393  cancel_info.raised=MagickTrue;
2394  XDrawBeveledButton(display,&windows->widget,&cancel_info);
2395  }
2396  if (MatteIsActive(reply_info,event.xbutton) == MagickFalse)
2397  break;
2398  break;
2399  }
2400  case ClientMessage:
2401  {
2402  /*
2403  If client window delete message, exit.
2404  */
2405  if (event.xclient.message_type != windows->wm_protocols)
2406  break;
2407  if (*event.xclient.data.l == (int) windows->wm_take_focus)
2408  {
2409  (void) XSetInputFocus(display,event.xclient.window,RevertToParent,
2410  (Time) event.xclient.data.l[1]);
2411  break;
2412  }
2413  if (*event.xclient.data.l != (int) windows->wm_delete_window)
2414  break;
2415  if (event.xclient.window == windows->widget.id)
2416  {
2417  *reply_info.text='\0';
2418  state|=ExitState;
2419  break;
2420  }
2421  break;
2422  }
2423  case ConfigureNotify:
2424  {
2425  /*
2426  Update widget configuration.
2427  */
2428  if (event.xconfigure.window != windows->widget.id)
2429  break;
2430  if ((event.xconfigure.width == (int) windows->widget.width) &&
2431  (event.xconfigure.height == (int) windows->widget.height))
2432  break;
2433  windows->widget.width=(unsigned int)
2434  MagickMax(event.xconfigure.width,(int) windows->widget.min_width);
2435  windows->widget.height=(unsigned int)
2436  MagickMax(event.xconfigure.height,(int) windows->widget.min_height);
2437  state|=UpdateConfigurationState;
2438  break;
2439  }
2440  case EnterNotify:
2441  {
2442  if (event.xcrossing.window != windows->widget.id)
2443  break;
2444  state&=(~InactiveWidgetState);
2445  break;
2446  }
2447  case Expose:
2448  {
2449  if (event.xexpose.window != windows->widget.id)
2450  break;
2451  if (event.xexpose.count != 0)
2452  break;
2453  state|=RedrawWidgetState;
2454  break;
2455  }
2456  case KeyPress:
2457  {
2458  static char
2459  command[MagickPathExtent];
2460 
2461  static int
2462  length;
2463 
2464  static KeySym
2465  key_symbol;
2466 
2467  /*
2468  Respond to a user key press.
2469  */
2470  if (event.xkey.window != windows->widget.id)
2471  break;
2472  length=XLookupString((XKeyEvent *) &event.xkey,command,
2473  (int) sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2474  *(command+length)='\0';
2475  if (AreaIsActive(scroll_info,event.xkey))
2476  {
2477  /*
2478  Move slider.
2479  */
2480  switch ((int) key_symbol)
2481  {
2482  case XK_Home:
2483  case XK_KP_Home:
2484  {
2485  slider_info.id=0;
2486  break;
2487  }
2488  case XK_Up:
2489  case XK_KP_Up:
2490  {
2491  slider_info.id--;
2492  break;
2493  }
2494  case XK_Down:
2495  case XK_KP_Down:
2496  {
2497  slider_info.id++;
2498  break;
2499  }
2500  case XK_Prior:
2501  case XK_KP_Prior:
2502  {
2503  slider_info.id-=visible_colors;
2504  break;
2505  }
2506  case XK_Next:
2507  case XK_KP_Next:
2508  {
2509  slider_info.id+=visible_colors;
2510  break;
2511  }
2512  case XK_End:
2513  case XK_KP_End:
2514  {
2515  slider_info.id=(int) colors;
2516  break;
2517  }
2518  }
2519  state|=RedrawListState;
2520  break;
2521  }
2522  if ((key_symbol == XK_Return) || (key_symbol == XK_KP_Enter))
2523  {
2524  /*
2525  Read new color or glob patterm.
2526  */
2527  if (*reply_info.text == '\0')
2528  break;
2529  (void) CopyMagickString(glob_pattern,reply_info.text,MagickPathExtent);
2530  state|=UpdateListState;
2531  break;
2532  }
2533  if (key_symbol == XK_Control_L)
2534  {
2535  state|=ControlState;
2536  break;
2537  }
2538  if (state & ControlState)
2539  switch ((int) key_symbol)
2540  {
2541  case XK_u:
2542  case XK_U:
2543  {
2544  /*
2545  Erase the entire line of text.
2546  */
2547  *reply_info.text='\0';
2548  reply_info.cursor=reply_info.text;
2549  reply_info.marker=reply_info.text;
2550  reply_info.highlight=MagickFalse;
2551  break;
2552  }
2553  default:
2554  break;
2555  }
2556  XEditText(display,&reply_info,key_symbol,command,state);
2557  XDrawMatteText(display,&windows->widget,&reply_info);
2558  state|=JumpListState;
2559  status=XParseColor(display,windows->widget.map_info->colormap,
2560  reply_info.text,&color);
2561  if (status != False)
2562  state|=RedrawActionState;
2563  break;
2564  }
2565  case KeyRelease:
2566  {
2567  static char
2568  command[MagickPathExtent];
2569 
2570  static KeySym
2571  key_symbol;
2572 
2573  /*
2574  Respond to a user key release.
2575  */
2576  if (event.xkey.window != windows->widget.id)
2577  break;
2578  (void) XLookupString((XKeyEvent *) &event.xkey,command,
2579  (int) sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2580  if (key_symbol == XK_Control_L)
2581  state&=(~ControlState);
2582  break;
2583  }
2584  case LeaveNotify:
2585  {
2586  if (event.xcrossing.window != windows->widget.id)
2587  break;
2588  state|=InactiveWidgetState;
2589  break;
2590  }
2591  case MapNotify:
2592  {
2593  mask&=(~CWX);
2594  mask&=(~CWY);
2595  break;
2596  }
2597  case MotionNotify:
2598  {
2599  /*
2600  Discard pending button motion events.
2601  */
2602  while (XCheckMaskEvent(display,ButtonMotionMask,&event)) ;
2603  if (slider_info.active)
2604  {
2605  /*
2606  Move slider matte.
2607  */
2608  slider_info.y=event.xmotion.y-
2609  ((slider_info.height+slider_info.bevel_width) >> 1)+1;
2610  if (slider_info.y < slider_info.min_y)
2611  slider_info.y=slider_info.min_y;
2612  if (slider_info.y > slider_info.max_y)
2613  slider_info.y=slider_info.max_y;
2614  slider_info.id=0;
2615  if (slider_info.y != slider_info.min_y)
2616  slider_info.id=(int) ((colors*(slider_info.y-
2617  slider_info.min_y+1))/(slider_info.max_y-slider_info.min_y+1));
2618  state|=RedrawListState;
2619  break;
2620  }
2621  if (state & InactiveWidgetState)
2622  break;
2623  if (grab_info.raised == MatteIsActive(grab_info,event.xmotion))
2624  {
2625  /*
2626  Grab button status changed.
2627  */
2628  grab_info.raised=!grab_info.raised;
2629  XDrawBeveledButton(display,&windows->widget,&grab_info);
2630  break;
2631  }
2632  if (reset_info.raised == MatteIsActive(reset_info,event.xmotion))
2633  {
2634  /*
2635  Reset button status changed.
2636  */
2637  reset_info.raised=!reset_info.raised;
2638  XDrawBeveledButton(display,&windows->widget,&reset_info);
2639  break;
2640  }
2641  if (action_info.raised == MatteIsActive(action_info,event.xmotion))
2642  {
2643  /*
2644  Action button status changed.
2645  */
2646  action_info.raised=action_info.raised == MagickFalse ?
2648  XDrawBeveledButton(display,&windows->widget,&action_info);
2649  break;
2650  }
2651  if (cancel_info.raised == MatteIsActive(cancel_info,event.xmotion))
2652  {
2653  /*
2654  Cancel button status changed.
2655  */
2656  cancel_info.raised=cancel_info.raised == MagickFalse ?
2658  XDrawBeveledButton(display,&windows->widget,&cancel_info);
2659  break;
2660  }
2661  break;
2662  }
2663  case SelectionClear:
2664  {
2665  reply_info.highlight=MagickFalse;
2666  XDrawMatteText(display,&windows->widget,&reply_info);
2667  break;
2668  }
2669  case SelectionNotify:
2670  {
2671  Atom
2672  type;
2673 
2674  int
2675  format;
2676 
2677  unsigned char
2678  *data;
2679 
2680  unsigned long
2681  after,
2682  length;
2683 
2684  /*
2685  Obtain response from primary selection.
2686  */
2687  if (event.xselection.property == (Atom) None)
2688  break;
2689  status=XGetWindowProperty(display,event.xselection.requestor,
2690  event.xselection.property,0L,2047L,MagickTrue,XA_STRING,&type,
2691  &format,&length,&after,&data);
2692  if ((status != Success) || (type != XA_STRING) || (format == 32) ||
2693  (length == 0))
2694  break;
2695  if ((Extent(reply_info.text)+length) >= (MagickPathExtent-1))
2696  (void) XBell(display,0);
2697  else
2698  {
2699  /*
2700  Insert primary selection in reply text.
2701  */
2702  *(data+length)='\0';
2703  XEditText(display,&reply_info,(KeySym) XK_Insert,(char *) data,
2704  state);
2705  XDrawMatteText(display,&windows->widget,&reply_info);
2706  state|=JumpListState;
2707  state|=RedrawActionState;
2708  }
2709  (void) XFree((void *) data);
2710  break;
2711  }
2712  case SelectionRequest:
2713  {
2714  XSelectionEvent
2715  notify;
2716 
2717  XSelectionRequestEvent
2718  *request;
2719 
2720  if (reply_info.highlight == MagickFalse)
2721  break;
2722  /*
2723  Set primary selection.
2724  */
2725  request=(&(event.xselectionrequest));
2726  (void) XChangeProperty(request->display,request->requestor,
2727  request->property,request->target,8,PropModeReplace,
2728  (unsigned char *) primary_selection,Extent(primary_selection));
2729  notify.type=SelectionNotify;
2730  notify.send_event=MagickTrue;
2731  notify.display=request->display;
2732  notify.requestor=request->requestor;
2733  notify.selection=request->selection;
2734  notify.target=request->target;
2735  notify.time=request->time;
2736  if (request->property == None)
2737  notify.property=request->target;
2738  else
2739  notify.property=request->property;
2740  (void) XSendEvent(request->display,request->requestor,False,
2741  NoEventMask,(XEvent *) &notify);
2742  }
2743  default:
2744  break;
2745  }
2746  } while ((state & ExitState) == 0);
2747  XSetCursorState(display,windows,MagickFalse);
2748  (void) XWithdrawWindow(display,windows->widget.id,windows->widget.screen);
2749  XCheckRefreshWindows(display,windows);
2750  /*
2751  Free color list.
2752  */
2753  for (i=0; i < (int) colors; i++)
2754  colorlist[i]=DestroyString(colorlist[i]);
2755  if (colorlist != (char **) NULL)
2756  colorlist=(char **) RelinquishMagickMemory(colorlist);
2757  exception=DestroyExceptionInfo(exception);
2758  if ((*reply == '\0') || (strchr(reply,'-') != (char *) NULL))
2759  return;
2760  status=XParseColor(display,windows->widget.map_info->colormap,reply,&color);
2761  if (status != False)
2762  return;
2763  XNoticeWidget(display,windows,"Color is unknown to X server:",reply);
2764  (void) CopyMagickString(reply,"gray",MagickPathExtent);
2765 }
2766 
2767 /*
2768 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2769 % %
2770 % %
2771 % %
2772 % X C o m m a n d W i d g e t %
2773 % %
2774 % %
2775 % %
2776 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2777 %
2778 % XCommandWidget() maps a menu and returns the command pointed to by the user
2779 % when the button is released.
2780 %
2781 % The format of the XCommandWidget method is:
2782 %
2783 % int XCommandWidget(Display *display,XWindows *windows,
2784 % const char *const *selections,XEvent *event)
2785 %
2786 % A description of each parameter follows:
2787 %
2788 % o selection_number: Specifies the number of the selection that the
2789 % user choose.
2790 %
2791 % o display: Specifies a connection to an X server; returned from
2792 % XOpenDisplay.
2793 %
2794 % o window: Specifies a pointer to a XWindows structure.
2795 %
2796 % o selections: Specifies a pointer to one or more strings that comprise
2797 % the choices in the menu.
2798 %
2799 % o event: Specifies a pointer to a X11 XEvent structure.
2800 %
2801 */
2802 MagickPrivate int XCommandWidget(Display *display,XWindows *windows,
2803  const char *const *selections,XEvent *event)
2804 {
2805 #define tile_width 112
2806 #define tile_height 70
2807 
2808  static const unsigned char
2809  tile_bits[]=
2810  {
2811  0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2812  0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2813  0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2814  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00,
2815  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00,
2816  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00,
2817  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2818  0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2819  0x00, 0x00, 0x1e, 0x38, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2820  0x00, 0x00, 0x00, 0x00, 0x1e, 0xbc, 0x9f, 0x03, 0x00, 0x3e, 0x00, 0xc0,
2821  0x1f, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x1e, 0xfc, 0xff, 0x0f, 0x80, 0x3f,
2822  0x00, 0xf0, 0x1f, 0xc0, 0x0f, 0x00, 0x00, 0x00, 0x1e, 0xfc, 0xff, 0x1f,
2823  0xe0, 0x3f, 0x00, 0xfc, 0x1f, 0xf0, 0x0f, 0x00, 0x00, 0x00, 0x1e, 0xfc,
2824  0xff, 0x1f, 0xf0, 0x3f, 0x00, 0xfe, 0x1f, 0xf8, 0x0f, 0x00, 0x00, 0x00,
2825  0x1e, 0xfc, 0xfc, 0x3f, 0xf8, 0x3f, 0x00, 0xff, 0x1e, 0xfc, 0x0f, 0x00,
2826  0x00, 0x00, 0x1e, 0x7c, 0xfc, 0x3e, 0xf8, 0x3c, 0x80, 0x1f, 0x1e, 0x7c,
2827  0x0f, 0x00, 0x00, 0x00, 0x1e, 0x78, 0x78, 0x3c, 0x7c, 0x3c, 0xc0, 0x0f,
2828  0x1e, 0x3e, 0x0f, 0x00, 0x00, 0x00, 0x1e, 0x78, 0x78, 0x3c, 0x7c, 0x3c,
2829  0xc0, 0x07, 0x1e, 0x3e, 0x0f, 0x00, 0x00, 0x00, 0x1e, 0x78, 0x78, 0x3c,
2830  0x7c, 0x7c, 0xc0, 0x0f, 0x1e, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x78,
2831  0x78, 0x3c, 0xfc, 0x7c, 0x80, 0x7f, 0x1e, 0x7c, 0x00, 0x00, 0x00, 0x00,
2832  0x1e, 0xf8, 0x78, 0x7c, 0xf8, 0xff, 0x00, 0xff, 0x1f, 0xf8, 0xff, 0x00,
2833  0x00, 0x00, 0x1e, 0xf8, 0x78, 0x7c, 0xf0, 0xff, 0x07, 0xfe, 0x1f, 0xf8,
2834  0xff, 0x00, 0x00, 0x00, 0x1e, 0xf8, 0x78, 0x7c, 0xf0, 0xff, 0x07, 0xf8,
2835  0x1f, 0xf0, 0xff, 0x01, 0x00, 0x00, 0x1e, 0xf8, 0x78, 0x7c, 0xc0, 0xef,
2836  0x07, 0xe0, 0x1f, 0xc0, 0xff, 0x01, 0x00, 0x00, 0x1e, 0x70, 0x40, 0x78,
2837  0x00, 0xc7, 0x07, 0x00, 0x1e, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x1e, 0x00,
2838  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00,
2839  0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00,
2840  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00,
2841  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
2842  0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2843  0x00, 0xc0, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2844  0x00, 0x00, 0x00, 0xe0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2845  0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
2846  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x01, 0x00, 0x00, 0x00,
2847  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x00,
2848  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78,
2849  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2850  0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x02, 0x00,
2851  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x07,
2852  0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2853  0xc0, 0x0f, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2854  0x60, 0x00, 0xc0, 0x0f, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2855  0x00, 0x00, 0x78, 0x00, 0xc0, 0x8f, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00,
2856  0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0xc0, 0x8f, 0x3f, 0x00, 0x00, 0x00,
2857  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0xe0, 0x9f, 0x7f, 0x00,
2858  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0xe0, 0xdf,
2859  0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x78, 0x00,
2860  0xe0, 0xdf, 0x7b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x0c,
2861  0x78, 0x30, 0xf0, 0xff, 0x7b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e,
2862  0x00, 0x0f, 0xf8, 0x70, 0xf0, 0xff, 0x7b, 0x00, 0x00, 0x1f, 0x00, 0xe0,
2863  0x0f, 0x1e, 0x80, 0x0f, 0xf8, 0x78, 0xf0, 0xfd, 0xf9, 0x00, 0xc0, 0x1f,
2864  0x00, 0xf8, 0x0f, 0x00, 0xe0, 0x1f, 0xf8, 0x7c, 0xf0, 0xfc, 0xf9, 0x00,
2865  0xf0, 0x1f, 0x00, 0xfe, 0x0f, 0x00, 0xf0, 0x07, 0xf8, 0x3e, 0xf8, 0xfc,
2866  0xf0, 0x01, 0xf8, 0x1f, 0x00, 0xff, 0x0f, 0x1e, 0xf0, 0x03, 0xf8, 0x3f,
2867  0xf8, 0xf8, 0xf0, 0x01, 0xfc, 0x1f, 0x80, 0x7f, 0x0f, 0x1e, 0xf8, 0x00,
2868  0xf8, 0x1f, 0x78, 0x18, 0xf0, 0x01, 0x7c, 0x1e, 0xc0, 0x0f, 0x0f, 0x1e,
2869  0x7c, 0x00, 0xf0, 0x0f, 0x78, 0x00, 0xf0, 0x01, 0x3e, 0x1e, 0xe0, 0x07,
2870  0x0f, 0x1e, 0x7c, 0x00, 0xf0, 0x07, 0x7c, 0x00, 0xe0, 0x01, 0x3e, 0x1e,
2871  0xe0, 0x03, 0x0f, 0x1e, 0x3e, 0x00, 0xf0, 0x0f, 0x7c, 0x00, 0xe0, 0x03,
2872  0x3e, 0x3e, 0xe0, 0x07, 0x0f, 0x1e, 0x1e, 0x00, 0xf0, 0x1f, 0x3c, 0x00,
2873  0xe0, 0x03, 0x7e, 0x3e, 0xc0, 0x3f, 0x0f, 0x1e, 0x3e, 0x00, 0xf0, 0x1f,
2874  0x3e, 0x00, 0xe0, 0x03, 0xfc, 0x7f, 0x80, 0xff, 0x0f, 0x1e, 0xfc, 0x00,
2875  0xf0, 0x3e, 0x3e, 0x00, 0xc0, 0x03, 0xf8, 0xff, 0x03, 0xff, 0x0f, 0x1e,
2876  0xfc, 0x07, 0xf0, 0x7c, 0x1e, 0x00, 0xc0, 0x03, 0xf8, 0xff, 0x03, 0xfc,
2877  0x0f, 0x1e, 0xf8, 0x1f, 0xf0, 0xf8, 0x1e, 0x00, 0xc0, 0x03, 0xe0, 0xf7,
2878  0x03, 0xf0, 0x0f, 0x1e, 0xe0, 0x3f, 0xf0, 0x78, 0x1c, 0x00, 0x80, 0x03,
2879  0x80, 0xe3, 0x03, 0x00, 0x0f, 0x1e, 0xc0, 0x3f, 0xf0, 0x30, 0x00, 0x00,
2880  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0e, 0x00, 0x3e, 0x00, 0x00,
2881  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x0f, 0x00, 0x00, 0x10,
2882  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x0f, 0x00,
2883  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0,
2884  0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2885  0x00, 0xe0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2886  0x00, 0x00, 0x00, 0xf0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2887  0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
2888  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, 0x00,
2889  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00,
2890  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c,
2891  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2892  0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
2893  };
2894 
2895  int
2896  id,
2897  y;
2898 
2899  int
2900  i;
2901 
2902  static unsigned int
2903  number_selections;
2904 
2905  unsigned int
2906  height;
2907 
2908  size_t
2909  state;
2910 
2911  XFontStruct
2912  *font_info;
2913 
2914  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2915  assert(display != (Display *) NULL);
2916  assert(windows != (XWindows *) NULL);
2917  font_info=windows->command.font_info;
2918  height=(unsigned int) (font_info->ascent+font_info->descent);
2919  id=(~0);
2920  state=DefaultState;
2921  if (event == (XEvent *) NULL)
2922  {
2923  unsigned int
2924  width;
2925 
2926  XTextProperty
2927  window_name;
2928 
2929  XWindowChanges
2930  window_changes;
2931 
2932  /*
2933  Determine command window attributes.
2934  */
2935  assert(selections != (const char **) NULL);
2936  windows->command.width=0;
2937  for (i=0; selections[i] != (char *) NULL; i++)
2938  {
2939  width=WidgetTextWidth(font_info,(char *) selections[i]);
2940  if (width > windows->command.width)
2941  windows->command.width=width;
2942  }
2943  number_selections=(unsigned int) i;
2944  windows->command.width+=3*QuantumMargin+10;
2945  if ((int) windows->command.width < (tile_width+QuantumMargin+10))
2946  windows->command.width=(unsigned int) (tile_width+QuantumMargin+10);
2947  windows->command.height=(unsigned int) (number_selections*
2948  (((3*height) >> 1)+10)+tile_height+20);
2949  windows->command.min_width=windows->command.width;
2950  windows->command.min_height=windows->command.height;
2951  XConstrainWindowPosition(display,&windows->command);
2952  if (windows->command.id != (Window) NULL)
2953  {
2954  Status
2955  status;
2956 
2957  /*
2958  Reconfigure command window.
2959  */
2960  status=XStringListToTextProperty(&windows->command.name,1,
2961  &window_name);
2962  if (status != False)
2963  {
2964  XSetWMName(display,windows->command.id,&window_name);
2965  XSetWMIconName(display,windows->command.id,&window_name);
2966  (void) XFree((void *) window_name.value);
2967  }
2968  window_changes.width=(int) windows->command.width;
2969  window_changes.height=(int) windows->command.height;
2970  (void) XReconfigureWMWindow(display,windows->command.id,
2971  windows->command.screen,(unsigned int) (CWWidth | CWHeight),
2972  &window_changes);
2973  }
2974  /*
2975  Allocate selection info memory.
2976  */
2977  if (selection_info != (XWidgetInfo *) NULL)
2978  selection_info=(XWidgetInfo *) RelinquishMagickMemory(selection_info);
2979  selection_info=(XWidgetInfo *) AcquireQuantumMemory(number_selections,
2980  sizeof(*selection_info));
2981  if (selection_info == (XWidgetInfo *) NULL)
2982  {
2983  ThrowXWindowFatalException(ResourceLimitFatalError,
2984  "MemoryAllocationFailed","...");
2985  return(id);
2986  }
2987  state|=UpdateConfigurationState | RedrawWidgetState;
2988  }
2989  /*
2990  Wait for next event.
2991  */
2992  if (event != (XEvent *) NULL)
2993  switch (event->type)
2994  {
2995  case ButtonPress:
2996  {
2997  for (i=0; i < (int) number_selections; i++)
2998  {
2999  if (MatteIsActive(selection_info[i],event->xbutton) == MagickFalse)
3000  continue;
3001  if (i >= (int) windows->command.data)
3002  {
3003  selection_info[i].raised=MagickFalse;
3004  XDrawBeveledButton(display,&windows->command,&selection_info[i]);
3005  break;
3006  }
3007  submenu_info=selection_info[i];
3008  submenu_info.active=MagickTrue;
3009  toggle_info.y=submenu_info.y+(submenu_info.height >> 1)-
3010  (toggle_info.height >> 1);
3011  id=i;
3012  (void) XCheckWindowEvent(display,windows->widget.id,LeaveWindowMask,
3013  event);
3014  break;
3015  }
3016  break;
3017  }
3018  case ButtonRelease:
3019  {
3020  for (i=0; i < (int) number_selections; i++)
3021  {
3022  if (MatteIsActive(selection_info[i],event->xbutton) == MagickFalse)
3023  continue;
3024  id=i;
3025  if (id >= (int) windows->command.data)
3026  {
3027  selection_info[id].raised=MagickTrue;
3028  XDrawBeveledButton(display,&windows->command,&selection_info[id]);
3029  break;
3030  }
3031  break;
3032  }
3033  break;
3034  }
3035  case ClientMessage:
3036  {
3037  /*
3038  If client window delete message, withdraw command widget.
3039  */
3040  if (event->xclient.message_type != windows->wm_protocols)
3041  break;
3042  if (*event->xclient.data.l != (int) windows->wm_delete_window)
3043  break;
3044  (void) XWithdrawWindow(display,windows->command.id,
3045  windows->command.screen);
3046  break;
3047  }
3048  case ConfigureNotify:
3049  {
3050  /*
3051  Update widget configuration.
3052  */
3053  if (event->xconfigure.window != windows->command.id)
3054  break;
3055  if (event->xconfigure.send_event != 0)
3056  {
3057  windows->command.x=event->xconfigure.x;
3058  windows->command.y=event->xconfigure.y;
3059  }
3060  if ((event->xconfigure.width == (int) windows->command.width) &&
3061  (event->xconfigure.height == (int) windows->command.height))
3062  break;
3063  windows->command.width=(unsigned int)
3064  MagickMax(event->xconfigure.width,(int) windows->command.min_width);
3065  windows->command.height=(unsigned int)
3066  MagickMax(event->xconfigure.height,(int) windows->command.min_height);
3067  state|=UpdateConfigurationState;
3068  break;
3069  }
3070  case Expose:
3071  {
3072  if (event->xexpose.window != windows->command.id)
3073  break;
3074  if (event->xexpose.count != 0)
3075  break;
3076  state|=RedrawWidgetState;
3077  break;
3078  }
3079  case MotionNotify:
3080  {
3081  /*
3082  Return the ID of the highlighted menu entry.
3083  */
3084  for ( ; ; )
3085  {
3086  for (i=0; i < (int) number_selections; i++)
3087  {
3088  if (i >= (int) windows->command.data)
3089  {
3090  if (selection_info[i].raised ==
3091  MatteIsActive(selection_info[i],event->xmotion))
3092  {
3093  /*
3094  Button status changed.
3095  */
3096  selection_info[i].raised=!selection_info[i].raised;
3097  XDrawBeveledButton(display,&windows->command,
3098  &selection_info[i]);
3099  }
3100  continue;
3101  }
3102  if (MatteIsActive(selection_info[i],event->xmotion) == MagickFalse)
3103  continue;
3104  submenu_info=selection_info[i];
3105  submenu_info.active=MagickTrue;
3106  toggle_info.raised=MagickTrue;
3107  toggle_info.y=submenu_info.y+(submenu_info.height >> 1)-
3108  (toggle_info.height >> 1);
3109  XDrawTriangleEast(display,&windows->command,&toggle_info);
3110  id=i;
3111  }
3112  XDelay(display,SuspendTime);
3113  if (XCheckMaskEvent(display,ButtonMotionMask,event) == MagickFalse)
3114  break;
3115  while (XCheckMaskEvent(display,ButtonMotionMask,event)) ;
3116  toggle_info.raised=MagickFalse;
3117  if (windows->command.data != 0)
3118  XDrawTriangleEast(display,&windows->command,&toggle_info);
3119  }
3120  break;
3121  }
3122  case MapNotify:
3123  {
3124  windows->command.mapped=MagickTrue;
3125  break;
3126  }
3127  case UnmapNotify:
3128  {
3129  windows->command.mapped=MagickFalse;
3130  break;
3131  }
3132  default:
3133  break;
3134  }
3135  if (state & UpdateConfigurationState)
3136  {
3137  /*
3138  Initialize button information.
3139  */
3140  assert(selections != (const char **) NULL);
3141  y=tile_height+20;
3142  for (i=0; i < (int) number_selections; i++)
3143  {
3144  XGetWidgetInfo(selections[i],&selection_info[i]);
3145  selection_info[i].center=MagickFalse;
3146  selection_info[i].bevel_width--;
3147  selection_info[i].height=(unsigned int) ((3*height) >> 1);
3148  selection_info[i].x=(QuantumMargin >> 1)+4;
3149  selection_info[i].width=(unsigned int) (windows->command.width-
3150  (selection_info[i].x << 1));
3151  selection_info[i].y=y;
3152  y+=selection_info[i].height+(selection_info[i].bevel_width << 1)+6;
3153  }
3154  XGetWidgetInfo((char *) NULL,&toggle_info);
3155  toggle_info.bevel_width--;
3156  toggle_info.width=(unsigned int) (((5*height) >> 3)-
3157  (toggle_info.bevel_width << 1));
3158  toggle_info.height=toggle_info.width;
3159  toggle_info.x=selection_info[0].x+selection_info[0].width-
3160  toggle_info.width-(QuantumMargin >> 1);
3161  if (windows->command.mapped)
3162  (void) XClearWindow(display,windows->command.id);
3163  }
3164  if (state & RedrawWidgetState)
3165  {
3166  Pixmap
3167  tile_pixmap;
3168 
3169  /*
3170  Draw command buttons.
3171  */
3172  tile_pixmap=XCreatePixmapFromBitmapData(display,windows->command.id,
3173  (char *) tile_bits,tile_width,tile_height,1L,0L,1);
3174  if (tile_pixmap != (Pixmap) NULL)
3175  {
3176  (void) XCopyPlane(display,tile_pixmap,windows->command.id,
3177  windows->command.annotate_context,0,0,tile_width,tile_height,
3178  (int) ((windows->command.width-tile_width) >> 1),10,1L);
3179  (void) XFreePixmap(display,tile_pixmap);
3180  }
3181  for (i=0; i < (int) number_selections; i++)
3182  {
3183  XDrawBeveledButton(display,&windows->command,&selection_info[i]);
3184  if (i >= (int) windows->command.data)
3185  continue;
3186  toggle_info.raised=MagickFalse;
3187  toggle_info.y=selection_info[i].y+(selection_info[i].height >> 1)-
3188  (toggle_info.height >> 1);
3189  XDrawTriangleEast(display,&windows->command,&toggle_info);
3190  }
3191  XHighlightWidget(display,&windows->command,BorderOffset,BorderOffset);
3192  }
3193  return(id);
3194 }
3195 
3196 /*
3197 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3198 % %
3199 % %
3200 % %
3201 % X C o n f i r m W i d g e t %
3202 % %
3203 % %
3204 % %
3205 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3206 %
3207 % XConfirmWidget() displays a Confirm widget with a notice to the user. The
3208 % function returns -1 if Dismiss is pressed, 0 for Cancel, and 1 for Yes.
3209 %
3210 % The format of the XConfirmWidget method is:
3211 %
3212 % int XConfirmWidget(Display *display,XWindows *windows,
3213 % const char *reason,const char *description)
3214 %
3215 % A description of each parameter follows:
3216 %
3217 % o display: Specifies a connection to an X server; returned from
3218 % XOpenDisplay.
3219 %
3220 % o window: Specifies a pointer to a XWindows structure.
3221 %
3222 % o reason: Specifies the message to display before terminating the
3223 % program.
3224 %
3225 % o description: Specifies any description to the message.
3226 %
3227 */
3228 MagickPrivate int XConfirmWidget(Display *display,XWindows *windows,
3229  const char *reason,const char *description)
3230 {
3231 #define CancelButtonText "Cancel"
3232 #define DismissButtonText "Dismiss"
3233 #define YesButtonText "Yes"
3234 
3235  int
3236  confirm,
3237  x,
3238  y;
3239 
3240  Status
3241  status;
3242 
3243  unsigned int
3244  height,
3245  width;
3246 
3247  size_t
3248  state;
3249 
3250  XEvent
3251  event;
3252 
3253  XFontStruct
3254  *font_info;
3255 
3256  XTextProperty
3257  window_name;
3258 
3259  XWidgetInfo
3260  cancel_info,
3261  dismiss_info,
3262  yes_info;
3263 
3264  XWindowChanges
3265  window_changes;
3266 
3267  /*
3268  Determine Confirm widget attributes.
3269  */
3270  assert(display != (Display *) NULL);
3271  assert(windows != (XWindows *) NULL);
3272  assert(reason != (char *) NULL);
3273  assert(description != (char *) NULL);
3274  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",reason);
3275  XCheckRefreshWindows(display,windows);
3276  font_info=windows->widget.font_info;
3277  width=WidgetTextWidth(font_info,CancelButtonText);
3278  if (WidgetTextWidth(font_info,DismissButtonText) > width)
3279  width=WidgetTextWidth(font_info,DismissButtonText);
3280  if (WidgetTextWidth(font_info,YesButtonText) > width)
3281  width=WidgetTextWidth(font_info,YesButtonText);
3282  width<<=1;
3283  if (description != (char *) NULL)
3284  if (WidgetTextWidth(font_info,(char *) description) > width)
3285  width=WidgetTextWidth(font_info,(char *) description);
3286  height=(unsigned int) (font_info->ascent+font_info->descent);
3287  /*
3288  Position Confirm widget.
3289  */
3290  windows->widget.width=(unsigned int) (width+9*QuantumMargin);
3291  windows->widget.min_width=(unsigned int) (9*QuantumMargin+
3292  WidgetTextWidth(font_info,CancelButtonText)+
3293  WidgetTextWidth(font_info,DismissButtonText)+
3294  WidgetTextWidth(font_info,YesButtonText));
3295  if (windows->widget.width < windows->widget.min_width)
3296  windows->widget.width=windows->widget.min_width;
3297  windows->widget.height=(unsigned int) (12*height);
3298  windows->widget.min_height=(unsigned int) (7*height);
3299  if (windows->widget.height < windows->widget.min_height)
3300  windows->widget.height=windows->widget.min_height;
3301  XConstrainWindowPosition(display,&windows->widget);
3302  /*
3303  Map Confirm widget.
3304  */
3305  (void) CopyMagickString(windows->widget.name,"Confirm",MagickPathExtent);
3306  status=XStringListToTextProperty(&windows->widget.name,1,&window_name);
3307  if (status != False)
3308  {
3309  XSetWMName(display,windows->widget.id,&window_name);
3310  XSetWMIconName(display,windows->widget.id,&window_name);
3311  (void) XFree((void *) window_name.value);
3312  }
3313  window_changes.width=(int) windows->widget.width;
3314  window_changes.height=(int) windows->widget.height;
3315  window_changes.x=windows->widget.x;
3316  window_changes.y=windows->widget.y;
3317  (void) XReconfigureWMWindow(display,windows->widget.id,windows->widget.screen,
3318  (unsigned int) (CWWidth | CWHeight | CWX | CWY),&window_changes);
3319  (void) XMapRaised(display,windows->widget.id);
3320  windows->widget.mapped=MagickFalse;
3321  /*
3322  Respond to X events.
3323  */
3324  confirm=0;
3325  state=UpdateConfigurationState;
3326  XSetCursorState(display,windows,MagickTrue);
3327  do
3328  {
3329  if (state & UpdateConfigurationState)
3330  {
3331  /*
3332  Initialize button information.
3333  */
3334  XGetWidgetInfo(CancelButtonText,&cancel_info);
3335  cancel_info.width=(unsigned int) QuantumMargin+
3336  WidgetTextWidth(font_info,CancelButtonText);
3337  cancel_info.height=(unsigned int) ((3*height) >> 1);
3338  cancel_info.x=(int) (windows->widget.width-cancel_info.width-
3339  QuantumMargin);
3340  cancel_info.y=(int) (windows->widget.height-(cancel_info.height << 1));
3341  dismiss_info=cancel_info;
3342  dismiss_info.text=(char *) DismissButtonText;
3343  if (LocaleCompare(description,"Do you want to save it") == 0)
3344  dismiss_info.text=(char *) "Don't Save";
3345  dismiss_info.width=(unsigned int) QuantumMargin+
3346  WidgetTextWidth(font_info,dismiss_info.text);
3347  dismiss_info.x=(int)
3348  ((windows->widget.width >> 1)-(dismiss_info.width >> 1));
3349  yes_info=cancel_info;
3350  yes_info.text=(char *) YesButtonText;
3351  if (LocaleCompare(description,"Do you want to save it") == 0)
3352  yes_info.text=(char *) "Save";
3353  yes_info.width=(unsigned int) QuantumMargin+
3354  WidgetTextWidth(font_info,yes_info.text);
3355  if (yes_info.width < cancel_info.width)
3356  yes_info.width=cancel_info.width;
3357  yes_info.x=QuantumMargin;
3358  state&=(~UpdateConfigurationState);
3359  }
3360  if (state & RedrawWidgetState)
3361  {
3362  /*
3363  Redraw Confirm widget.
3364  */
3365  width=WidgetTextWidth(font_info,(char *) reason);
3366  x=(int) ((windows->widget.width >> 1)-(width >> 1));
3367  y=(int) ((windows->widget.height >> 1)-(height << 1));
3368  (void) XDrawString(display,windows->widget.id,
3369  windows->widget.annotate_context,x,y,(char *) reason,Extent(reason));
3370  if (description != (char *) NULL)
3371  {
3372  char
3373  question[MagickPathExtent];
3374 
3375  (void) CopyMagickString(question,description,MagickPathExtent);
3376  (void) ConcatenateMagickString(question,"?",MagickPathExtent);
3377  width=WidgetTextWidth(font_info,question);
3378  x=(int) ((windows->widget.width >> 1)-(width >> 1));
3379  y+=height;
3380  (void) XDrawString(display,windows->widget.id,
3381  windows->widget.annotate_context,x,y,question,Extent(question));
3382  }
3383  XDrawBeveledButton(display,&windows->widget,&cancel_info);
3384  XDrawBeveledButton(display,&windows->widget,&dismiss_info);
3385  XDrawBeveledButton(display,&windows->widget,&yes_info);
3386  XHighlightWidget(display,&windows->widget,BorderOffset,BorderOffset);
3387  state&=(~RedrawWidgetState);
3388  }
3389  /*
3390  Wait for next event.
3391  */
3392  (void) XIfEvent(display,&event,XScreenEvent,(char *) windows);
3393  switch (event.type)
3394  {
3395  case ButtonPress:
3396  {
3397  if (MatteIsActive(cancel_info,event.xbutton))
3398  {
3399  /*
3400  User pressed No button.
3401  */
3402  cancel_info.raised=MagickFalse;
3403  XDrawBeveledButton(display,&windows->widget,&cancel_info);
3404  break;
3405  }
3406  if (MatteIsActive(dismiss_info,event.xbutton))
3407  {
3408  /*
3409  User pressed Dismiss button.
3410  */
3411  dismiss_info.raised=MagickFalse;
3412  XDrawBeveledButton(display,&windows->widget,&dismiss_info);
3413  break;
3414  }
3415  if (MatteIsActive(yes_info,event.xbutton))
3416  {
3417  /*
3418  User pressed Yes button.
3419  */
3420  yes_info.raised=MagickFalse;
3421  XDrawBeveledButton(display,&windows->widget,&yes_info);
3422  break;
3423  }
3424  break;
3425  }
3426  case ButtonRelease:
3427  {
3428  if (windows->widget.mapped == MagickFalse)
3429  break;
3430  if (cancel_info.raised == MagickFalse)
3431  {
3432  if (event.xbutton.window == windows->widget.id)
3433  if (MatteIsActive(cancel_info,event.xbutton))
3434  {
3435  confirm=0;
3436  state|=ExitState;
3437  }
3438  cancel_info.raised=MagickTrue;
3439  XDrawBeveledButton(display,&windows->widget,&cancel_info);
3440  }
3441  if (dismiss_info.raised == MagickFalse)
3442  {
3443  if (event.xbutton.window == windows->widget.id)
3444  if (MatteIsActive(dismiss_info,event.xbutton))
3445  {
3446  confirm=(-1);
3447  state|=ExitState;
3448  }
3449  dismiss_info.raised=MagickTrue;
3450  XDrawBeveledButton(display,&windows->widget,&dismiss_info);
3451  }
3452  if (yes_info.raised == MagickFalse)
3453  {
3454  if (event.xbutton.window == windows->widget.id)
3455  if (MatteIsActive(yes_info,event.xbutton))
3456  {
3457  confirm=1;
3458  state|=ExitState;
3459  }
3460  yes_info.raised=MagickTrue;
3461  XDrawBeveledButton(display,&windows->widget,&yes_info);
3462  }
3463  break;
3464  }
3465  case ClientMessage:
3466  {
3467  /*
3468  If client window delete message, exit.
3469  */
3470  if (event.xclient.message_type != windows->wm_protocols)
3471  break;
3472  if (*event.xclient.data.l == (int) windows->wm_take_focus)
3473  {
3474  (void) XSetInputFocus(display,event.xclient.window,RevertToParent,
3475  (Time) event.xclient.data.l[1]);
3476  break;
3477  }
3478  if (*event.xclient.data.l != (int) windows->wm_delete_window)
3479  break;
3480  if (event.xclient.window == windows->widget.id)
3481  {
3482  state|=ExitState;
3483  break;
3484  }
3485  break;
3486  }
3487  case ConfigureNotify:
3488  {
3489  /*
3490  Update widget configuration.
3491  */
3492  if (event.xconfigure.window != windows->widget.id)
3493  break;
3494  if ((event.xconfigure.width == (int) windows->widget.width) &&
3495  (event.xconfigure.height == (int) windows->widget.height))
3496  break;
3497  windows->widget.width=(unsigned int)
3498  MagickMax(event.xconfigure.width,(int) windows->widget.min_width);
3499  windows->widget.height=(unsigned int)
3500  MagickMax(event.xconfigure.height,(int) windows->widget.min_height);
3501  state|=UpdateConfigurationState;
3502  break;
3503  }
3504  case EnterNotify:
3505  {
3506  if (event.xcrossing.window != windows->widget.id)
3507  break;
3508  state&=(~InactiveWidgetState);
3509  break;
3510  }
3511  case Expose:
3512  {
3513  if (event.xexpose.window != windows->widget.id)
3514  break;
3515  if (event.xexpose.count != 0)
3516  break;
3517  state|=RedrawWidgetState;
3518  break;
3519  }
3520  case KeyPress:
3521  {
3522  static char
3523  command[MagickPathExtent];
3524 
3525  static KeySym
3526  key_symbol;
3527 
3528  /*
3529  Respond to a user key press.
3530  */
3531  if (event.xkey.window != windows->widget.id)
3532  break;
3533  (void) XLookupString((XKeyEvent *) &event.xkey,command,
3534  (int) sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3535  if ((key_symbol == XK_Return) || (key_symbol == XK_KP_Enter))
3536  {
3537  yes_info.raised=MagickFalse;
3538  XDrawBeveledButton(display,&windows->widget,&yes_info);
3539  confirm=1;
3540  state|=ExitState;
3541  break;
3542  }
3543  break;
3544  }
3545  case LeaveNotify:
3546  {
3547  if (event.xcrossing.window != windows->widget.id)
3548  break;
3549  state|=InactiveWidgetState;
3550  break;
3551  }
3552  case MotionNotify:
3553  {
3554  /*
3555  Discard pending button motion events.
3556  */
3557  while (XCheckMaskEvent(display,ButtonMotionMask,&event)) ;
3558  if (state & InactiveWidgetState)
3559  break;
3560  if (cancel_info.raised == MatteIsActive(cancel_info,event.xmotion))
3561  {
3562  /*
3563  Cancel button status changed.
3564  */
3565  cancel_info.raised=cancel_info.raised == MagickFalse ?
3567  XDrawBeveledButton(display,&windows->widget,&cancel_info);
3568  break;
3569  }
3570  if (dismiss_info.raised == MatteIsActive(dismiss_info,event.xmotion))
3571  {
3572  /*
3573  Dismiss button status changed.
3574  */
3575  dismiss_info.raised=dismiss_info.raised == MagickFalse ?
3577  XDrawBeveledButton(display,&windows->widget,&dismiss_info);
3578  break;
3579  }
3580  if (yes_info.raised == MatteIsActive(yes_info,event.xmotion))
3581  {
3582  /*
3583  Yes button status changed.
3584  */
3585  yes_info.raised=yes_info.raised == MagickFalse ?
3587  XDrawBeveledButton(display,&windows->widget,&yes_info);
3588  break;
3589  }
3590  break;
3591  }
3592  default:
3593  break;
3594  }
3595  } while ((state & ExitState) == 0);
3596  XSetCursorState(display,windows,MagickFalse);
3597  (void) XWithdrawWindow(display,windows->widget.id,windows->widget.screen);
3598  XCheckRefreshWindows(display,windows);
3599  return(confirm);
3600 }
3601 
3602 /*
3603 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3604 % %
3605 % %
3606 % %
3607 % X D i a l o g W i d g e t %
3608 % %
3609 % %
3610 % %
3611 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3612 %
3613 % XDialogWidget() displays a Dialog widget with a query to the user. The user
3614 % keys a reply and presses the Ok or Cancel button to exit. The typed text is
3615 % returned as the reply function parameter.
3616 %
3617 % The format of the XDialogWidget method is:
3618 %
3619 % int XDialogWidget(Display *display,XWindows *windows,const char *action,
3620 % const char *query,char *reply)
3621 %
3622 % A description of each parameter follows:
3623 %
3624 % o display: Specifies a connection to an X server; returned from
3625 % XOpenDisplay.
3626 %
3627 % o window: Specifies a pointer to a XWindows structure.
3628 %
3629 % o action: Specifies a pointer to the action of this widget.
3630 %
3631 % o query: Specifies a pointer to the query to present to the user.
3632 %
3633 % o reply: the response from the user is returned in this parameter.
3634 %
3635 */
3636 MagickPrivate int XDialogWidget(Display *display,XWindows *windows,
3637  const char *action,const char *query,char *reply)
3638 {
3639 #define CancelButtonText "Cancel"
3640 
3641  char
3642  primary_selection[MagickPathExtent];
3643 
3644  int
3645  x;
3646 
3647  int
3648  i;
3649 
3650  static MagickBooleanType
3651  raised = MagickFalse;
3652 
3653  Status
3654  status;
3655 
3656  unsigned int
3657  anomaly,
3658  height,
3659  width;
3660 
3661  size_t
3662  state;
3663 
3664  XEvent
3665  event;
3666 
3667  XFontStruct
3668  *font_info;
3669 
3670  XTextProperty
3671  window_name;
3672 
3673  XWidgetInfo
3674  action_info,
3675  cancel_info,
3676  reply_info,
3677  special_info,
3678  text_info;
3679 
3680  XWindowChanges
3681  window_changes;
3682 
3683  /*
3684  Determine Dialog widget attributes.
3685  */
3686  assert(display != (Display *) NULL);
3687  assert(windows != (XWindows *) NULL);
3688  assert(action != (char *) NULL);
3689  assert(query != (char *) NULL);
3690  assert(reply != (char *) NULL);
3691  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",action);
3692  XCheckRefreshWindows(display,windows);
3693  font_info=windows->widget.font_info;
3694  width=WidgetTextWidth(font_info,(char *) action);
3695  if (WidgetTextWidth(font_info,CancelButtonText) > width)
3696  width=WidgetTextWidth(font_info,CancelButtonText);
3697  width+=(3*QuantumMargin) >> 1;
3698  height=(unsigned int) (font_info->ascent+font_info->descent);
3699  /*
3700  Position Dialog widget.
3701  */
3702  windows->widget.width=(unsigned int) MagickMax((int) (2*width),(int)
3703  WidgetTextWidth(font_info,(char *) query));
3704  if (windows->widget.width < WidgetTextWidth(font_info,reply))
3705  windows->widget.width=WidgetTextWidth(font_info,reply);
3706  windows->widget.width+=6*QuantumMargin;
3707  windows->widget.min_width=(unsigned int)
3708  (width+28*XTextWidth(font_info,"#",1)+4*QuantumMargin);
3709  if (windows->widget.width < windows->widget.min_width)
3710  windows->widget.width=windows->widget.min_width;
3711  windows->widget.height=(unsigned int) (7*height+(QuantumMargin << 1));
3712  windows->widget.min_height=windows->widget.height;
3713  if (windows->widget.height < windows->widget.min_height)
3714  windows->widget.height=windows->widget.min_height;
3715  XConstrainWindowPosition(display,&windows->widget);
3716  /*
3717  Map Dialog widget.
3718  */
3719  (void) CopyMagickString(windows->widget.name,"Dialog",MagickPathExtent);
3720  status=XStringListToTextProperty(&windows->widget.name,1,&window_name);
3721  if (status != False)
3722  {
3723  XSetWMName(display,windows->widget.id,&window_name);
3724  XSetWMIconName(display,windows->widget.id,&window_name);
3725  (void) XFree((void *) window_name.value);
3726  }
3727  window_changes.width=(int) windows->widget.width;
3728  window_changes.height=(int) windows->widget.height;
3729  window_changes.x=windows->widget.x;
3730  window_changes.y=windows->widget.y;
3731  (void) XReconfigureWMWindow(display,windows->widget.id,windows->widget.screen,
3732  (unsigned int) (CWWidth | CWHeight | CWX | CWY),&window_changes);
3733  (void) XMapRaised(display,windows->widget.id);
3734  windows->widget.mapped=MagickFalse;
3735  /*
3736  Respond to X events.
3737  */
3738  anomaly=(LocaleCompare(action,"Background") == 0) ||
3739  (LocaleCompare(action,"New") == 0) ||
3740  (LocaleCompare(action,"Quantize") == 0) ||
3741  (LocaleCompare(action,"Resize") == 0) ||
3742  (LocaleCompare(action,"Save") == 0) ||
3743  (LocaleCompare(action,"Shade") == 0);
3744  state=UpdateConfigurationState;
3745  XSetCursorState(display,windows,MagickTrue);
3746  do
3747  {
3748  if (state & UpdateConfigurationState)
3749  {
3750  /*
3751  Initialize button information.
3752  */
3753  XGetWidgetInfo(CancelButtonText,&cancel_info);
3754  cancel_info.width=width;
3755  cancel_info.height=(unsigned int) ((3*height) >> 1);
3756  cancel_info.x=(int)
3757  (windows->widget.width-cancel_info.width-((3*QuantumMargin) >> 1));
3758  cancel_info.y=(int)
3759  (windows->widget.height-cancel_info.height-((3*QuantumMargin) >> 1));
3760  XGetWidgetInfo(action,&action_info);
3761  action_info.width=width;
3762  action_info.height=(unsigned int) ((3*height) >> 1);
3763  action_info.x=cancel_info.x-(cancel_info.width+QuantumMargin+
3764  (action_info.bevel_width << 1));
3765  action_info.y=cancel_info.y;
3766  /*
3767  Initialize reply information.
3768  */
3769  XGetWidgetInfo(reply,&reply_info);
3770  reply_info.raised=MagickFalse;
3771  reply_info.bevel_width--;
3772  reply_info.width=windows->widget.width-(3*QuantumMargin);
3773  reply_info.height=height << 1;
3774  reply_info.x=(3*QuantumMargin) >> 1;
3775  reply_info.y=action_info.y-reply_info.height-QuantumMargin;
3776  /*
3777  Initialize option information.
3778  */
3779  XGetWidgetInfo("Dither",&special_info);
3780  special_info.raised=raised;
3781  special_info.bevel_width--;
3782  special_info.width=(unsigned int) QuantumMargin >> 1;
3783  special_info.height=(unsigned int) QuantumMargin >> 1;
3784  special_info.x=reply_info.x;
3785  special_info.y=action_info.y+action_info.height-special_info.height;
3786  if (LocaleCompare(action,"Background") == 0)
3787  special_info.text=(char *) "Backdrop";
3788  if (LocaleCompare(action,"New") == 0)
3789  special_info.text=(char *) "Gradation";
3790  if (LocaleCompare(action,"Resize") == 0)
3791  special_info.text=(char *) "Constrain ratio";
3792  if (LocaleCompare(action,"Save") == 0)
3793  special_info.text=(char *) "Non-progressive";
3794  if (LocaleCompare(action,"Shade") == 0)
3795  special_info.text=(char *) "Color shading";
3796  /*
3797  Initialize text information.
3798  */
3799  XGetWidgetInfo(query,&text_info);
3800  text_info.width=reply_info.width;
3801  text_info.height=height;
3802  text_info.x=reply_info.x-(QuantumMargin >> 1);
3803  text_info.y=QuantumMargin;
3804  text_info.center=MagickFalse;
3805  state&=(~UpdateConfigurationState);
3806  }
3807  if (state & RedrawWidgetState)
3808  {
3809  /*
3810  Redraw Dialog widget.
3811  */
3812  XDrawWidgetText(display,&windows->widget,&text_info);
3813  XDrawBeveledMatte(display,&windows->widget,&reply_info);
3814  XDrawMatteText(display,&windows->widget,&reply_info);
3815  if (anomaly)
3816  XDrawBeveledButton(display,&windows->widget,&special_info);
3817  XDrawBeveledButton(display,&windows->widget,&action_info);
3818  XDrawBeveledButton(display,&windows->widget,&cancel_info);
3819  XHighlightWidget(display,&windows->widget,BorderOffset,BorderOffset);
3820  state&=(~RedrawWidgetState);
3821  }
3822  /*
3823  Wait for next event.
3824  */
3825  (void) XIfEvent(display,&event,XScreenEvent,(char *) windows);
3826  switch (event.type)
3827  {
3828  case ButtonPress:
3829  {
3830  if (anomaly)
3831  if (MatteIsActive(special_info,event.xbutton))
3832  {
3833  /*
3834  Option button status changed.
3835  */
3836  special_info.raised=!special_info.raised;
3837  XDrawBeveledButton(display,&windows->widget,&special_info);
3838  break;
3839  }
3840  if (MatteIsActive(action_info,event.xbutton))
3841  {
3842  /*
3843  User pressed Action button.
3844  */
3845  action_info.raised=MagickFalse;
3846  XDrawBeveledButton(display,&windows->widget,&action_info);
3847  break;
3848  }
3849  if (MatteIsActive(cancel_info,event.xbutton))
3850  {
3851  /*
3852  User pressed Cancel button.
3853  */
3854  cancel_info.raised=MagickFalse;
3855  XDrawBeveledButton(display,&windows->widget,&cancel_info);
3856  break;
3857  }
3858  if (MatteIsActive(reply_info,event.xbutton) == MagickFalse)
3859  break;
3860  if (event.xbutton.button != Button2)
3861  {
3862  static Time
3863  click_time;
3864 
3865  /*
3866  Move text cursor to position of button press.
3867  */
3868  x=event.xbutton.x-reply_info.x-(QuantumMargin >> 2);
3869  for (i=1; i <= Extent(reply_info.marker); i++)
3870  if (XTextWidth(font_info,reply_info.marker,i) > x)
3871  break;
3872  reply_info.cursor=reply_info.marker+i-1;
3873  if (event.xbutton.time > (click_time+DoubleClick))
3874  reply_info.highlight=MagickFalse;
3875  else
3876  {
3877  /*
3878  Become the XA_PRIMARY selection owner.
3879  */
3880  (void) CopyMagickString(primary_selection,reply_info.text,
3882  (void) XSetSelectionOwner(display,XA_PRIMARY,windows->widget.id,
3883  event.xbutton.time);
3884  reply_info.highlight=XGetSelectionOwner(display,XA_PRIMARY) ==
3885  windows->widget.id ? MagickTrue : MagickFalse;
3886  }
3887  XDrawMatteText(display,&windows->widget,&reply_info);
3888  click_time=event.xbutton.time;
3889  break;
3890  }
3891  /*
3892  Request primary selection.
3893  */
3894  (void) XConvertSelection(display,XA_PRIMARY,XA_STRING,XA_STRING,
3895  windows->widget.id,event.xbutton.time);
3896  break;
3897  }
3898  case ButtonRelease:
3899  {
3900  if (windows->widget.mapped == MagickFalse)
3901  break;
3902  if (action_info.raised == MagickFalse)
3903  {
3904  if (event.xbutton.window == windows->widget.id)
3905  if (MatteIsActive(action_info,event.xbutton))
3906  state|=ExitState;
3907  action_info.raised=MagickTrue;
3908  XDrawBeveledButton(display,&windows->widget,&action_info);
3909  }
3910  if (cancel_info.raised == MagickFalse)
3911  {
3912  if (event.xbutton.window == windows->widget.id)
3913  if (MatteIsActive(cancel_info,event.xbutton))
3914  {
3915  *reply_info.text='\0';
3916  state|=ExitState;
3917  }
3918  cancel_info.raised=MagickTrue;
3919  XDrawBeveledButton(display,&windows->widget,&cancel_info);
3920  }
3921  break;
3922  }
3923  case ClientMessage:
3924  {
3925  /*
3926  If client window delete message, exit.
3927  */
3928  if (event.xclient.message_type != windows->wm_protocols)
3929  break;
3930  if (*event.xclient.data.l == (int) windows->wm_take_focus)
3931  {
3932  (void) XSetInputFocus(display,event.xclient.window,RevertToParent,
3933  (Time) event.xclient.data.l[1]);
3934  break;
3935  }
3936  if (*event.xclient.data.l != (int) windows->wm_delete_window)
3937  break;
3938  if (event.xclient.window == windows->widget.id)
3939  {
3940  *reply_info.text='\0';
3941  state|=ExitState;
3942  break;
3943  }
3944  break;
3945  }
3946  case ConfigureNotify:
3947  {
3948  /*
3949  Update widget configuration.
3950  */
3951  if (event.xconfigure.window != windows->widget.id)
3952  break;
3953  if ((event.xconfigure.width == (int) windows->widget.width) &&
3954  (event.xconfigure.height == (int) windows->widget.height))
3955  break;
3956  windows->widget.width=(unsigned int)
3957  MagickMax(event.xconfigure.width,(int) windows->widget.min_width);
3958  windows->widget.height=(unsigned int)
3959  MagickMax(event.xconfigure.height,(int) windows->widget.min_height);
3960  state|=UpdateConfigurationState;
3961  break;
3962  }
3963  case EnterNotify:
3964  {
3965  if (event.xcrossing.window != windows->widget.id)
3966  break;
3967  state&=(~InactiveWidgetState);
3968  break;
3969  }
3970  case Expose:
3971  {
3972  if (event.xexpose.window != windows->widget.id)
3973  break;
3974  if (event.xexpose.count != 0)
3975  break;
3976  state|=RedrawWidgetState;
3977  break;
3978  }
3979  case KeyPress:
3980  {
3981  static char
3982  command[MagickPathExtent];
3983 
3984  static int
3985  length;
3986 
3987  static KeySym
3988  key_symbol;
3989 
3990  /*
3991  Respond to a user key press.
3992  */
3993  if (event.xkey.window != windows->widget.id)
3994  break;
3995  length=XLookupString((XKeyEvent *) &event.xkey,command,
3996  (int) sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3997  *(command+length)='\0';
3998  if ((key_symbol == XK_Return) || (key_symbol == XK_KP_Enter))
3999  {
4000  action_info.raised=MagickFalse;
4001  XDrawBeveledButton(display,&windows->widget,&action_info);
4002  state|=ExitState;
4003  break;
4004  }
4005  if (key_symbol == XK_Control_L)
4006  {
4007  state|=ControlState;
4008  break;
4009  }
4010  if (state & ControlState)
4011  switch ((int) key_symbol)
4012  {
4013  case XK_u:
4014  case XK_U:
4015  {
4016  /*
4017  Erase the entire line of text.
4018  */
4019  *reply_info.text='\0';
4020  reply_info.cursor=reply_info.text;
4021  reply_info.marker=reply_info.text;
4022  reply_info.highlight=MagickFalse;
4023  break;
4024  }
4025  default:
4026  break;
4027  }
4028  XEditText(display,&reply_info,key_symbol,command,state);
4029  XDrawMatteText(display,&windows->widget,&reply_info);
4030  break;
4031  }
4032  case KeyRelease:
4033  {
4034  static char
4035  command[MagickPathExtent];
4036 
4037  static KeySym
4038  key_symbol;
4039 
4040  /*
4041  Respond to a user key release.
4042  */
4043  if (event.xkey.window != windows->widget.id)
4044  break;
4045  (void) XLookupString((XKeyEvent *) &event.xkey,command,
4046  (int) sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4047  if (key_symbol == XK_Control_L)
4048  state&=(~ControlState);
4049  break;
4050  }
4051  case LeaveNotify:
4052  {
4053  if (event.xcrossing.window != windows->widget.id)
4054  break;
4055  state|=InactiveWidgetState;
4056  break;
4057  }
4058  case MotionNotify:
4059  {
4060  /*
4061  Discard pending button motion events.
4062  */
4063  while (XCheckMaskEvent(display,ButtonMotionMask,&event)) ;
4064  if (state & InactiveWidgetState)
4065  break;
4066  if (action_info.raised == MatteIsActive(action_info,event.xmotion))
4067  {
4068  /*
4069  Action button status changed.
4070  */
4071  action_info.raised=action_info.raised == MagickFalse ?
4073  XDrawBeveledButton(display,&windows->widget,&action_info);
4074  break;
4075  }
4076  if (cancel_info.raised == MatteIsActive(cancel_info,event.xmotion))
4077  {
4078  /*
4079  Cancel button status changed.
4080  */
4081  cancel_info.raised=cancel_info.raised == MagickFalse ?
4083  XDrawBeveledButton(display,&windows->widget,&cancel_info);
4084  break;
4085  }
4086  break;
4087  }
4088  case SelectionClear:
4089  {
4090  reply_info.highlight=MagickFalse;
4091  XDrawMatteText(display,&windows->widget,&reply_info);
4092  break;
4093  }
4094  case SelectionNotify:
4095  {
4096  Atom
4097  type;
4098 
4099  int
4100  format;
4101 
4102  unsigned char
4103  *data;
4104 
4105  unsigned long
4106  after,
4107  length;
4108 
4109  /*
4110  Obtain response from primary selection.
4111  */
4112  if (event.xselection.property == (Atom) None)
4113  break;
4114  status=XGetWindowProperty(display,event.xselection.requestor,
4115  event.xselection.property,0L,2047L,MagickTrue,XA_STRING,&type,
4116  &format,&length,&after,&data);
4117  if ((status != Success) || (type != XA_STRING) || (format == 32) ||
4118  (length == 0))
4119  break;
4120  if ((Extent(reply_info.text)+length) >= (MagickPathExtent-1))
4121  (void) XBell(display,0);
4122  else
4123  {
4124  /*
4125  Insert primary selection in reply text.
4126  */
4127  *(data+length)='\0';
4128  XEditText(display,&reply_info,(KeySym) XK_Insert,(char *) data,
4129  state);
4130  XDrawMatteText(display,&windows->widget,&reply_info);
4131  }
4132  (void) XFree((void *) data);
4133  break;
4134  }
4135  case SelectionRequest:
4136  {
4137  XSelectionEvent
4138  notify;
4139 
4140  XSelectionRequestEvent
4141  *request;
4142 
4143  if (reply_info.highlight == MagickFalse)
4144  break;
4145  /*
4146  Set primary selection.
4147  */
4148  request=(&(event.xselectionrequest));
4149  (void) XChangeProperty(request->display,request->requestor,
4150  request->property,request->target,8,PropModeReplace,
4151  (unsigned char *) primary_selection,Extent(primary_selection));
4152  notify.type=SelectionNotify;
4153  notify.display=request->display;
4154  notify.requestor=request->requestor;
4155  notify.selection=request->selection;
4156  notify.target=request->target;
4157  notify.time=request->time;
4158  if (request->property == None)
4159  notify.property=request->target;
4160  else
4161  notify.property=request->property;
4162  (void) XSendEvent(request->display,request->requestor,False,0,
4163  (XEvent *) &notify);
4164  }
4165  default:
4166  break;
4167  }
4168  } while ((state & ExitState) == 0);
4169  XSetCursorState(display,windows,MagickFalse);
4170  (void) XWithdrawWindow(display,windows->widget.id,windows->widget.screen);
4171  XCheckRefreshWindows(display,windows);
4172  if (anomaly)
4173  if (special_info.raised)
4174  if (*reply != '\0')
4175  raised=MagickTrue;
4176  return(raised == MagickFalse);
4177 }
4178 
4179 /*
4180 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4181 % %
4182 % %
4183 % %
4184 % X F i l e B r o w s e r W i d g e t %
4185 % %
4186 % %
4187 % %
4188 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4189 %
4190 % XFileBrowserWidget() displays a File Browser widget with a file query to the
4191 % user. The user keys a reply and presses the Action or Cancel button to
4192 % exit. The typed text is returned as the reply function parameter.
4193 %
4194 % The format of the XFileBrowserWidget method is:
4195 %
4196 % void XFileBrowserWidget(Display *display,XWindows *windows,
4197 % const char *action,char *reply)
4198 %
4199 % A description of each parameter follows:
4200 %
4201 % o display: Specifies a connection to an X server; returned from
4202 % XOpenDisplay.
4203 %
4204 % o window: Specifies a pointer to a XWindows structure.
4205 %
4206 % o action: Specifies a pointer to the action of this widget.
4207 %
4208 % o reply: the response from the user is returned in this parameter.
4209 %
4210 */
4211 MagickPrivate void XFileBrowserWidget(Display *display,XWindows *windows,
4212  const char *action,char *reply)
4213 {
4214 #define CancelButtonText "Cancel"
4215 #define DirectoryText "Directory:"
4216 #define FilenameText "File name:"
4217 #define GrabButtonText "Grab"
4218 #define FormatButtonText "Format"
4219 #define HomeButtonText "Home"
4220 #define UpButtonText "Up"
4221 
4222  char
4223  *directory,
4224  **filelist,
4225  home_directory[MagickPathExtent],
4226  primary_selection[MagickPathExtent],
4227  text[MagickPathExtent],
4228  working_path[MagickPathExtent];
4229 
4230  int
4231  x,
4232  y;
4233 
4234  ssize_t
4235  i;
4236 
4237  static char
4238  glob_pattern[MagickPathExtent] = "*",
4239  format[MagickPathExtent] = "miff";
4240 
4241  static MagickStatusType
4242  mask = (MagickStatusType) (CWWidth | CWHeight | CWX | CWY);
4243 
4244  Status
4245  status;
4246 
4247  unsigned int
4248  anomaly,
4249  height,
4250  text_width,
4251  visible_files,
4252  width;
4253 
4254  size_t
4255  delay,
4256  files,
4257  state;
4258 
4259  XEvent
4260  event;
4261 
4262  XFontStruct
4263  *font_info;
4264 
4265  XTextProperty
4266  window_name;
4267 
4268  XWidgetInfo
4269  action_info,
4270  cancel_info,
4271  expose_info,
4272  special_info,
4273  list_info,
4274  home_info,
4275  north_info,
4276  reply_info,
4277  scroll_info,
4278  selection_info,
4279  slider_info,
4280  south_info,
4281  text_info,
4282  up_info;
4283 
4284  XWindowChanges
4285  window_changes;
4286 
4287  /*
4288  Read filelist from current directory.
4289  */
4290  assert(display != (Display *) NULL);
4291  assert(windows != (XWindows *) NULL);
4292  assert(action != (char *) NULL);
4293  assert(reply != (char *) NULL);
4294  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",action);
4295  XSetCursorState(display,windows,MagickTrue);
4296  XCheckRefreshWindows(display,windows);
4297  directory=getcwd(home_directory,MagickPathExtent);
4298  (void) directory;
4299  (void) CopyMagickString(working_path,home_directory,MagickPathExtent);
4300  filelist=ListFiles(working_path,glob_pattern,&files);
4301  if (filelist == (char **) NULL)
4302  {
4303  /*
4304  Directory read failed.
4305  */
4306  XNoticeWidget(display,windows,"Unable to read directory:",working_path);
4307  (void) XDialogWidget(display,windows,action,"Enter filename:",reply);
4308  return;
4309  }
4310  /*
4311  Determine File Browser widget attributes.
4312  */
4313  font_info=windows->widget.font_info;
4314  text_width=0;
4315  for (i=0; i < (ssize_t) files; i++)
4316  if (WidgetTextWidth(font_info,filelist[i]) > text_width)
4317  text_width=WidgetTextWidth(font_info,filelist[i]);
4318  width=WidgetTextWidth(font_info,(char *) action);
4319  if (WidgetTextWidth(font_info,GrabButtonText) > width)
4320  width=WidgetTextWidth(font_info,GrabButtonText);
4321  if (WidgetTextWidth(font_info,FormatButtonText) > width)
4322  width=WidgetTextWidth(font_info,FormatButtonText);
4323  if (WidgetTextWidth(font_info,CancelButtonText) > width)
4324  width=WidgetTextWidth(font_info,CancelButtonText);
4325  if (WidgetTextWidth(font_info,HomeButtonText) > width)
4326  width=WidgetTextWidth(font_info,HomeButtonText);
4327  if (WidgetTextWidth(font_info,UpButtonText) > width)
4328  width=WidgetTextWidth(font_info,UpButtonText);
4329  width+=QuantumMargin;
4330  if (WidgetTextWidth(font_info,DirectoryText) > width)
4331  width=WidgetTextWidth(font_info,DirectoryText);
4332  if (WidgetTextWidth(font_info,FilenameText) > width)
4333  width=WidgetTextWidth(font_info,FilenameText);
4334  height=(unsigned int) (font_info->ascent+font_info->descent);
4335  /*
4336  Position File Browser widget.
4337  */
4338  windows->widget.width=width+MagickMin((int) text_width,(int) MaxTextWidth)+
4339  6*QuantumMargin;
4340  windows->widget.min_width=width+MinTextWidth+4*QuantumMargin;
4341  if (windows->widget.width < windows->widget.min_width)
4342  windows->widget.width=windows->widget.min_width;
4343  windows->widget.height=(unsigned int)
4344  (((81*height) >> 2)+((13*QuantumMargin) >> 1)+4);
4345  windows->widget.min_height=(unsigned int)
4346  (((23*height) >> 1)+((13*QuantumMargin) >> 1)+4);
4347  if (windows->widget.height < windows->widget.min_height)
4348  windows->widget.height=windows->widget.min_height;
4349  XConstrainWindowPosition(display,&windows->widget);
4350  /*
4351  Map File Browser widget.
4352  */
4353  (void) CopyMagickString(windows->widget.name,"Browse and Select a File",
4355  status=XStringListToTextProperty(&windows->widget.name,1,&window_name);
4356  if (status != False)
4357  {
4358  XSetWMName(display,windows->widget.id,&window_name);
4359  XSetWMIconName(display,windows->widget.id,&window_name);
4360  (void) XFree((void *) window_name.value);
4361  }
4362  window_changes.width=(int) windows->widget.width;
4363  window_changes.height=(int) windows->widget.height;
4364  window_changes.x=windows->widget.x;
4365  window_changes.y=windows->widget.y;
4366  (void) XReconfigureWMWindow(display,windows->widget.id,
4367  windows->widget.screen,mask,&window_changes);
4368  (void) XMapRaised(display,windows->widget.id);
4369  windows->widget.mapped=MagickFalse;
4370  /*
4371  Respond to X events.
4372  */
4373  XGetWidgetInfo((char *) NULL,&slider_info);
4374  XGetWidgetInfo((char *) NULL,&north_info);
4375  XGetWidgetInfo((char *) NULL,&south_info);
4376  XGetWidgetInfo((char *) NULL,&expose_info);
4377  visible_files=0;
4378  anomaly=(LocaleCompare(action,"Composite") == 0) ||
4379  (LocaleCompare(action,"Open") == 0) || (LocaleCompare(action,"Map") == 0);
4380  *reply='\0';
4381  delay=SuspendTime << 2;
4382  state=UpdateConfigurationState;
4383  do
4384  {
4385  if (state & UpdateConfigurationState)
4386  {
4387  int
4388  id;
4389 
4390  /*
4391  Initialize button information.
4392  */
4393  XGetWidgetInfo(CancelButtonText,&cancel_info);
4394  cancel_info.width=width;
4395  cancel_info.height=(unsigned int) ((3*height) >> 1);
4396  cancel_info.x=(int)
4397  (windows->widget.width-cancel_info.width-QuantumMargin-2);
4398  cancel_info.y=(int)
4399  (windows->widget.height-cancel_info.height-QuantumMargin);
4400  XGetWidgetInfo(action,&action_info);
4401  action_info.width=width;
4402  action_info.height=(unsigned int) ((3*height) >> 1);
4403  action_info.x=cancel_info.x-(cancel_info.width+(QuantumMargin >> 1)+
4404  (action_info.bevel_width << 1));
4405  action_info.y=cancel_info.y;
4406  XGetWidgetInfo(GrabButtonText,&special_info);
4407  special_info.width=width;
4408  special_info.height=(unsigned int) ((3*height) >> 1);
4409  special_info.x=action_info.x-(action_info.width+(QuantumMargin >> 1)+
4410  (special_info.bevel_width << 1));
4411  special_info.y=action_info.y;
4412  if (anomaly == MagickFalse)
4413  {
4414  char
4415  *p;
4416 
4417  special_info.text=(char *) FormatButtonText;
4418  p=reply+Extent(reply)-1;
4419  while ((p > (reply+1)) && (*(p-1) != '.'))
4420  p--;
4421  if ((p > (reply+1)) && (*(p-1) == '.'))
4422  (void) CopyMagickString(format,p,MagickPathExtent);
4423  }
4424  XGetWidgetInfo(UpButtonText,&up_info);
4425  up_info.width=width;
4426  up_info.height=(unsigned int) ((3*height) >> 1);
4427  up_info.x=QuantumMargin;
4428  up_info.y=((5*QuantumMargin) >> 1)+height;
4429  XGetWidgetInfo(HomeButtonText,&home_info);
4430  home_info.width=width;
4431  home_info.height=(unsigned int) ((3*height) >> 1);
4432  home_info.x=QuantumMargin;
4433  home_info.y=up_info.y+up_info.height+QuantumMargin;
4434  /*
4435  Initialize reply information.
4436  */
4437  XGetWidgetInfo(reply,&reply_info);
4438  reply_info.raised=MagickFalse;
4439  reply_info.bevel_width--;
4440  reply_info.width=windows->widget.width-width-((6*QuantumMargin) >> 1);
4441  reply_info.height=height << 1;
4442  reply_info.x=(int) (width+(QuantumMargin << 1));
4443  reply_info.y=action_info.y-reply_info.height-QuantumMargin;
4444  /*
4445  Initialize scroll information.
4446  */
4447  XGetWidgetInfo((char *) NULL,&scroll_info);
4448  scroll_info.bevel_width--;
4449  scroll_info.width=height;
4450  scroll_info.height=(unsigned int)
4451  (reply_info.y-up_info.y-(QuantumMargin >> 1));
4452  scroll_info.x=reply_info.x+(reply_info.width-scroll_info.width);
4453  scroll_info.y=up_info.y-reply_info.bevel_width;
4454  scroll_info.raised=MagickFalse;
4455  scroll_info.trough=MagickTrue;
4456  north_info=scroll_info;
4457  north_info.raised=MagickTrue;
4458  north_info.width-=(north_info.bevel_width << 1);
4459  north_info.height=north_info.width-1;
4460  north_info.x+=north_info.bevel_width;
4461  north_info.y+=north_info.bevel_width;
4462  south_info=north_info;
4463  south_info.y=scroll_info.y+scroll_info.height-scroll_info.bevel_width-
4464  south_info.height;
4465  id=slider_info.id;
4466  slider_info=north_info;
4467  slider_info.id=id;
4468  slider_info.width-=2;
4469  slider_info.min_y=north_info.y+north_info.height+north_info.bevel_width+
4470  slider_info.bevel_width+2;
4471  slider_info.height=scroll_info.height-((slider_info.min_y-
4472  scroll_info.y+1) << 1)+4;
4473  visible_files=(unsigned int) (scroll_info.height*
4474  PerceptibleReciprocal((double) height+(height >> 3)));
4475  if (files > visible_files)
4476  slider_info.height=(unsigned int)
4477  ((visible_files*slider_info.height)/files);
4478  slider_info.max_y=south_info.y-south_info.bevel_width-
4479  slider_info.bevel_width-2;
4480  slider_info.x=scroll_info.x+slider_info.bevel_width+1;
4481  slider_info.y=slider_info.min_y;
4482  expose_info=scroll_info;
4483  expose_info.y=slider_info.y;
4484  /*
4485  Initialize list information.
4486  */
4487  XGetWidgetInfo((char *) NULL,&list_info);
4488  list_info.raised=MagickFalse;
4489  list_info.bevel_width--;
4490  list_info.width=(unsigned int)
4491  (scroll_info.x-reply_info.x-(QuantumMargin >> 1));
4492  list_info.height=scroll_info.height;
4493  list_info.x=reply_info.x;
4494  list_info.y=scroll_info.y;
4495  if (windows->widget.mapped == MagickFalse)
4496  state|=JumpListState;
4497  /*
4498  Initialize text information.
4499  */
4500  *text='\0';
4501  XGetWidgetInfo(text,&text_info);
4502  text_info.center=MagickFalse;
4503  text_info.width=reply_info.width;
4504  text_info.height=height;
4505  text_info.x=list_info.x-(QuantumMargin >> 1);
4506  text_info.y=QuantumMargin;
4507  /*
4508  Initialize selection information.
4509  */
4510  XGetWidgetInfo((char *) NULL,&selection_info);
4511  selection_info.center=MagickFalse;
4512  selection_info.width=list_info.width;
4513  selection_info.height=(unsigned int) ((9*height) >> 3);
4514  selection_info.x=list_info.x;
4515  state&=(~UpdateConfigurationState);
4516  }
4517  if (state & RedrawWidgetState)
4518  {
4519  /*
4520  Redraw File Browser window.
4521  */
4522  x=QuantumMargin;
4523  y=text_info.y+((text_info.height-height) >> 1)+font_info->ascent;
4524  (void) XDrawString(display,windows->widget.id,
4525  windows->widget.annotate_context,x,y,DirectoryText,
4526  Extent(DirectoryText));
4527  (void) CopyMagickString(text_info.text,working_path,MagickPathExtent);
4528  (void) ConcatenateMagickString(text_info.text,DirectorySeparator,
4530  (void) ConcatenateMagickString(text_info.text,glob_pattern,
4532  XDrawWidgetText(display,&windows->widget,&text_info);
4533  XDrawBeveledButton(display,&windows->widget,&up_info);
4534  XDrawBeveledButton(display,&windows->widget,&home_info);
4535  XDrawBeveledMatte(display,&windows->widget,&list_info);
4536  XDrawBeveledMatte(display,&windows->widget,&scroll_info);
4537  XDrawTriangleNorth(display,&windows->widget,&north_info);
4538  XDrawBeveledButton(display,&windows->widget,&slider_info);
4539  XDrawTriangleSouth(display,&windows->widget,&south_info);
4540  x=QuantumMargin;
4541  y=reply_info.y+((reply_info.height-height) >> 1)+font_info->ascent;
4542  (void) XDrawString(display,windows->widget.id,
4543  windows->widget.annotate_context,x,y,FilenameText,
4544  Extent(FilenameText));
4545  XDrawBeveledMatte(display,&windows->widget,&reply_info);
4546  XDrawMatteText(display,&windows->widget,&reply_info);
4547  XDrawBeveledButton(display,&windows->widget,&special_info);
4548  XDrawBeveledButton(display,&windows->widget,&action_info);
4549  XDrawBeveledButton(display,&windows->widget,&cancel_info);
4550  XHighlightWidget(display,&windows->widget,BorderOffset,BorderOffset);
4551  selection_info.id=(~0);
4552  state|=RedrawListState;
4553  state&=(~RedrawWidgetState);
4554  }
4555  if (state & UpdateListState)
4556  {
4557  char
4558  **checklist;
4559 
4560  size_t
4561  number_files;
4562 
4563  /*
4564  Update file list.
4565  */
4566  checklist=ListFiles(working_path,glob_pattern,&number_files);
4567  if (checklist == (char **) NULL)
4568  {
4569  /*
4570  Reply is a filename, exit.
4571  */
4572  action_info.raised=MagickFalse;
4573  XDrawBeveledButton(display,&windows->widget,&action_info);
4574  break;
4575  }
4576  for (i=0; i < (ssize_t) files; i++)
4577  filelist[i]=DestroyString(filelist[i]);
4578  if (filelist != (char **) NULL)
4579  filelist=(char **) RelinquishMagickMemory(filelist);
4580  filelist=checklist;
4581  files=number_files;
4582  /*
4583  Update file list.
4584  */
4585  slider_info.height=
4586  scroll_info.height-((slider_info.min_y-scroll_info.y+1) << 1)+1;
4587  if (files > visible_files)
4588  slider_info.height=(unsigned int)
4589  ((visible_files*slider_info.height)/files);
4590  slider_info.max_y=south_info.y-south_info.bevel_width-
4591  slider_info.bevel_width-2;
4592  slider_info.id=0;
4593  slider_info.y=slider_info.min_y;
4594  expose_info.y=slider_info.y;
4595  selection_info.id=(~0);
4596  list_info.id=(~0);
4597  state|=RedrawListState;
4598  /*
4599  Redraw directory name & reply.
4600  */
4601  if (IsGlob(reply_info.text) == MagickFalse)
4602  {
4603  *reply_info.text='\0';
4604  reply_info.cursor=reply_info.text;
4605  }
4606  (void) CopyMagickString(text_info.text,working_path,MagickPathExtent);
4607  (void) ConcatenateMagickString(text_info.text,DirectorySeparator,
4609  (void) ConcatenateMagickString(text_info.text,glob_pattern,
4611  XDrawWidgetText(display,&windows->widget,&text_info);
4612  XDrawMatteText(display,&windows->widget,&reply_info);
4613  XDrawBeveledMatte(display,&windows->widget,&scroll_info);
4614  XDrawTriangleNorth(display,&windows->widget,&north_info);
4615  XDrawBeveledButton(display,&windows->widget,&slider_info);
4616  XDrawTriangleSouth(display,&windows->widget,&south_info);
4617  XHighlightWidget(display,&windows->widget,BorderOffset,BorderOffset);
4618  state&=(~UpdateListState);
4619  }
4620  if (state & JumpListState)
4621  {
4622  /*
4623  Jump scroll to match user filename.
4624  */
4625  list_info.id=(~0);
4626  for (i=0; i < (ssize_t) files; i++)
4627  if (LocaleCompare(filelist[i],reply) >= 0)
4628  {
4629  list_info.id=(int)
4630  (LocaleCompare(filelist[i],reply) == 0 ? i : ~0);
4631  break;
4632  }
4633  if ((i < (ssize_t) slider_info.id) ||
4634  (i >= (ssize_t) (slider_info.id+visible_files)))
4635  slider_info.id=(int) i-(visible_files >> 1);
4636  selection_info.id=(~0);
4637  state|=RedrawListState;
4638  state&=(~JumpListState);
4639  }
4640  if (state & RedrawListState)
4641  {
4642  /*
4643  Determine slider id and position.
4644  */
4645  if (slider_info.id >= (int) (files-visible_files))
4646  slider_info.id=(int) (files-visible_files);
4647  if ((slider_info.id < 0) || (files <= visible_files))
4648  slider_info.id=0;
4649  slider_info.y=slider_info.min_y;
4650  if (files > 0)
4651  slider_info.y+=((ssize_t) slider_info.id*(slider_info.max_y-
4652  slider_info.min_y+1)/files);
4653  if (slider_info.id != selection_info.id)
4654  {
4655  /*
4656  Redraw scroll bar and file names.
4657  */
4658  selection_info.id=slider_info.id;
4659  selection_info.y=list_info.y+(height >> 3)+2;
4660  for (i=0; i < (ssize_t) visible_files; i++)
4661  {
4662  selection_info.raised=(int) (slider_info.id+i) != list_info.id ?
4664  selection_info.text=(char *) NULL;
4665  if ((slider_info.id+i) < (ssize_t) files)
4666  selection_info.text=filelist[slider_info.id+i];
4667  XDrawWidgetText(display,&windows->widget,&selection_info);
4668  selection_info.y+=(int) selection_info.height;
4669  }
4670  /*
4671  Update slider.
4672  */
4673  if (slider_info.y > expose_info.y)
4674  {
4675  expose_info.height=(unsigned int) slider_info.y-expose_info.y;
4676  expose_info.y=slider_info.y-expose_info.height-
4677  slider_info.bevel_width-1;
4678  }
4679  else
4680  {
4681  expose_info.height=(unsigned int) expose_info.y-slider_info.y;
4682  expose_info.y=slider_info.y+slider_info.height+
4683  slider_info.bevel_width+1;
4684  }
4685  XDrawTriangleNorth(display,&windows->widget,&north_info);
4686  XDrawMatte(display,&windows->widget,&expose_info);
4687  XDrawBeveledButton(display,&windows->widget,&slider_info);
4688  XDrawTriangleSouth(display,&windows->widget,&south_info);
4689  expose_info.y=slider_info.y;
4690  }
4691  state&=(~RedrawListState);
4692  }
4693  /*
4694  Wait for next event.
4695  */
4696  if (north_info.raised && south_info.raised)
4697  (void) XIfEvent(display,&event,XScreenEvent,(char *) windows);
4698  else
4699  {
4700  /*
4701  Brief delay before advancing scroll bar.
4702  */
4703  XDelay(display,delay);
4704  delay=SuspendTime;
4705  (void) XCheckIfEvent(display,&event,XScreenEvent,(char *) windows);
4706  if (north_info.raised == MagickFalse)
4707  if (slider_info.id > 0)
4708  {
4709  /*
4710  Move slider up.
4711  */
4712  slider_info.id--;
4713  state|=RedrawListState;
4714  }
4715  if (south_info.raised == MagickFalse)
4716  if (slider_info.id < (int) files)
4717  {
4718  /*
4719  Move slider down.
4720  */
4721  slider_info.id++;
4722  state|=RedrawListState;
4723  }
4724  if (event.type != ButtonRelease)
4725  continue;
4726  }
4727  switch (event.type)
4728  {
4729  case ButtonPress:
4730  {
4731  if (MatteIsActive(slider_info,event.xbutton))
4732  {
4733  /*
4734  Track slider.
4735  */
4736  slider_info.active=MagickTrue;
4737  break;
4738  }
4739  if (MatteIsActive(north_info,event.xbutton))
4740  if (slider_info.id > 0)
4741  {
4742  /*
4743  Move slider up.
4744  */
4745  north_info.raised=MagickFalse;
4746  slider_info.id--;
4747  state|=RedrawListState;
4748  break;
4749  }
4750  if (MatteIsActive(south_info,event.xbutton))
4751  if (slider_info.id < (int) files)
4752  {
4753  /*
4754  Move slider down.
4755  */
4756  south_info.raised=MagickFalse;
4757  slider_info.id++;
4758  state|=RedrawListState;
4759  break;
4760  }
4761  if (MatteIsActive(scroll_info,event.xbutton))
4762  {
4763  /*
4764  Move slider.
4765  */
4766  if (event.xbutton.y < slider_info.y)
4767  slider_info.id-=(visible_files-1);
4768  else
4769  slider_info.id+=(visible_files-1);
4770  state|=RedrawListState;
4771  break;
4772  }
4773  if (MatteIsActive(list_info,event.xbutton))
4774  {
4775  int
4776  id;
4777 
4778  /*
4779  User pressed file matte.
4780  */
4781  id=slider_info.id+(event.xbutton.y-(list_info.y+(height >> 1))+1)/
4782  selection_info.height;
4783  if (id >= (int) files)
4784  break;
4785  (void) CopyMagickString(reply_info.text,filelist[id],MagickPathExtent);
4786  reply_info.highlight=MagickFalse;
4787  reply_info.marker=reply_info.text;
4788  reply_info.cursor=reply_info.text+Extent(reply_info.text);
4789  XDrawMatteText(display,&windows->widget,&reply_info);
4790  if (id == list_info.id)
4791  {
4792  char
4793  *p;
4794 
4795  p=reply_info.text+strlen(reply_info.text)-1;
4796  if (*p == *DirectorySeparator)
4797  ChopPathComponents(reply_info.text,1);
4798  (void) ConcatenateMagickString(working_path,DirectorySeparator,
4800  (void) ConcatenateMagickString(working_path,reply_info.text,
4802  *reply='\0';
4803  state|=UpdateListState;
4804  }
4805  selection_info.id=(~0);
4806  list_info.id=id;
4807  state|=RedrawListState;
4808  break;
4809  }
4810  if (MatteIsActive(up_info,event.xbutton))
4811  {
4812  /*
4813  User pressed Up button.
4814  */
4815  up_info.raised=MagickFalse;
4816  XDrawBeveledButton(display,&windows->widget,&up_info);
4817  break;
4818  }
4819  if (MatteIsActive(home_info,event.xbutton))
4820  {
4821  /*
4822  User pressed Home button.
4823  */
4824  home_info.raised=MagickFalse;
4825  XDrawBeveledButton(display,&windows->widget,&home_info);
4826  break;
4827  }
4828  if (MatteIsActive(special_info,event.xbutton))
4829  {
4830  /*
4831  User pressed Special button.
4832  */
4833  special_info.raised=MagickFalse;
4834  XDrawBeveledButton(display,&windows->widget,&special_info);
4835  break;
4836  }
4837  if (MatteIsActive(action_info,event.xbutton))
4838  {
4839  /*
4840  User pressed action button.
4841  */
4842  action_info.raised=MagickFalse;
4843  XDrawBeveledButton(display,&windows->widget,&action_info);
4844  break;
4845  }
4846  if (MatteIsActive(cancel_info,event.xbutton))
4847  {
4848  /*
4849  User pressed Cancel button.
4850  */
4851  cancel_info.raised=MagickFalse;
4852  XDrawBeveledButton(display,&windows->widget,&cancel_info);
4853  break;
4854  }
4855  if (MatteIsActive(reply_info,event.xbutton) == MagickFalse)
4856  break;
4857  if (event.xbutton.button != Button2)
4858  {
4859  static Time
4860  click_time;
4861 
4862  /*
4863  Move text cursor to position of button press.
4864  */
4865  x=event.xbutton.x-reply_info.x-(QuantumMargin >> 2);
4866  for (i=1; i <= (ssize_t) Extent(reply_info.marker); i++)
4867  if (XTextWidth(font_info,reply_info.marker,(int) i) > x)
4868  break;
4869  reply_info.cursor=reply_info.marker+i-1;
4870  if (event.xbutton.time > (click_time+DoubleClick))
4871  reply_info.highlight=MagickFalse;
4872  else
4873  {
4874  /*
4875  Become the XA_PRIMARY selection owner.
4876  */
4877  (void) CopyMagickString(primary_selection,reply_info.text,
4879  (void) XSetSelectionOwner(display,XA_PRIMARY,windows->widget.id,
4880  event.xbutton.time);
4881  reply_info.highlight=XGetSelectionOwner(display,XA_PRIMARY) ==
4882  windows->widget.id ? MagickTrue : MagickFalse;
4883  }
4884  XDrawMatteText(display,&windows->widget,&reply_info);
4885  click_time=event.xbutton.time;
4886  break;
4887  }
4888  /*
4889  Request primary selection.
4890  */
4891  (void) XConvertSelection(display,XA_PRIMARY,XA_STRING,XA_STRING,
4892  windows->widget.id,event.xbutton.time);
4893  break;
4894  }
4895  case ButtonRelease:
4896  {
4897  if (windows->widget.mapped == MagickFalse)
4898  break;
4899  if (north_info.raised == MagickFalse)
4900  {
4901  /*
4902  User released up button.
4903  */
4904  delay=SuspendTime << 2;
4905  north_info.raised=MagickTrue;
4906  XDrawTriangleNorth(display,&windows->widget,&north_info);
4907  }
4908  if (south_info.raised == MagickFalse)
4909  {
4910  /*
4911  User released down button.
4912  */
4913  delay=SuspendTime << 2;
4914  south_info.raised=MagickTrue;
4915  XDrawTriangleSouth(display,&windows->widget,&south_info);
4916  }
4917  if (slider_info.active)
4918  {
4919  /*
4920  Stop tracking slider.
4921  */
4922  slider_info.active=MagickFalse;
4923  break;
4924  }
4925  if (up_info.raised == MagickFalse)
4926  {
4927  if (event.xbutton.window == windows->widget.id)
4928  if (MatteIsActive(up_info,event.xbutton))
4929  {
4930  ChopPathComponents(working_path,1);
4931  if (*working_path == '\0')
4932  (void) CopyMagickString(working_path,DirectorySeparator,
4934  state|=UpdateListState;
4935  }
4936  up_info.raised=MagickTrue;
4937  XDrawBeveledButton(display,&windows->widget,&up_info);
4938  }
4939  if (home_info.raised == MagickFalse)
4940  {
4941  if (event.xbutton.window == windows->widget.id)
4942  if (MatteIsActive(home_info,event.xbutton))
4943  {
4944  (void) CopyMagickString(working_path,home_directory,
4946  state|=UpdateListState;
4947  }
4948  home_info.raised=MagickTrue;
4949  XDrawBeveledButton(display,&windows->widget,&home_info);
4950  }
4951  if (special_info.raised == MagickFalse)
4952  {
4953  if (anomaly == MagickFalse)
4954  {
4955  char
4956  **formats;
4957 
4959  *exception;
4960 
4961  size_t
4962  number_formats;
4963 
4964  /*
4965  Let user select image format.
4966  */
4967  exception=AcquireExceptionInfo();
4968  formats=GetMagickList("*",&number_formats,exception);
4969  exception=DestroyExceptionInfo(exception);
4970  if (formats == (char **) NULL)
4971  break;
4972  (void) XCheckDefineCursor(display,windows->widget.id,
4973  windows->widget.busy_cursor);
4974  windows->popup.x=windows->widget.x+60;
4975  windows->popup.y=windows->widget.y+60;
4976  XListBrowserWidget(display,windows,&windows->popup,
4977  (const char **) formats,"Select","Select image format type:",
4978  format);
4979  XSetCursorState(display,windows,MagickTrue);
4980  (void) XCheckDefineCursor(display,windows->widget.id,
4981  windows->widget.cursor);
4982  LocaleLower(format);
4983  AppendImageFormat(format,reply_info.text);
4984  reply_info.cursor=reply_info.text+Extent(reply_info.text);
4985  XDrawMatteText(display,&windows->widget,&reply_info);
4986  special_info.raised=MagickTrue;
4987  XDrawBeveledButton(display,&windows->widget,&special_info);
4988  for (i=0; i < (ssize_t) number_formats; i++)
4989  formats[i]=DestroyString(formats[i]);
4990  formats=(char **) RelinquishMagickMemory(formats);
4991  break;
4992  }
4993  if (event.xbutton.window == windows->widget.id)
4994  if (MatteIsActive(special_info,event.xbutton))
4995  {
4996  (void) CopyMagickString(working_path,"x:",MagickPathExtent);
4997  state|=ExitState;
4998  }
4999  special_info.raised=MagickTrue;
5000  XDrawBeveledButton(display,&windows->widget,&special_info);
5001  }
5002  if (action_info.raised == MagickFalse)
5003  {
5004  if (event.xbutton.window == windows->widget.id)
5005  {
5006  if (MatteIsActive(action_info,event.xbutton))
5007  {
5008  if (*reply_info.text == '\0')
5009  (void) XBell(display,0);
5010  else
5011  state|=ExitState;
5012  }
5013  }
5014  action_info.raised=MagickTrue;
5015  XDrawBeveledButton(display,&windows->widget,&action_info);
5016  }
5017  if (cancel_info.raised == MagickFalse)
5018  {
5019  if (event.xbutton.window == windows->widget.id)
5020  if (MatteIsActive(cancel_info,event.xbutton))
5021  {
5022  *reply_info.text='\0';
5023  *reply='\0';
5024  state|=ExitState;
5025  }
5026  cancel_info.raised=MagickTrue;
5027  XDrawBeveledButton(display,&windows->widget,&cancel_info);
5028  }
5029  break;
5030  }
5031  case ClientMessage:
5032  {
5033  /*
5034  If client window delete message, exit.
5035  */
5036  if (event.xclient.message_type != windows->wm_protocols)
5037  break;
5038  if (*event.xclient.data.l == (int) windows->wm_take_focus)
5039  {
5040  (void) XSetInputFocus(display,event.xclient.window,RevertToParent,
5041  (Time) event.xclient.data.l[1]);
5042  break;
5043  }
5044  if (*event.xclient.data.l != (int) windows->wm_delete_window)
5045  break;
5046  if (event.xclient.window == windows->widget.id)
5047  {
5048  *reply_info.text='\0';
5049  state|=ExitState;
5050  break;
5051  }
5052  break;
5053  }
5054  case ConfigureNotify:
5055  {
5056  /*
5057  Update widget configuration.
5058  */
5059  if (event.xconfigure.window != windows->widget.id)
5060  break;
5061  if ((event.xconfigure.width == (int) windows->widget.width) &&
5062  (event.xconfigure.height == (int) windows->widget.height))
5063  break;
5064  windows->widget.width=(unsigned int)
5065  MagickMax(event.xconfigure.width,(int) windows->widget.min_width);
5066  windows->widget.height=(unsigned int)
5067  MagickMax(event.xconfigure.height,(int) windows->widget.min_height);
5068  state|=UpdateConfigurationState;
5069  break;
5070  }
5071  case EnterNotify:
5072  {
5073  if (event.xcrossing.window != windows->widget.id)
5074  break;
5075  state&=(~InactiveWidgetState);
5076  break;
5077  }
5078  case Expose:
5079  {
5080  if (event.xexpose.window != windows->widget.id)
5081  break;
5082  if (event.xexpose.count != 0)
5083  break;
5084  state|=RedrawWidgetState;
5085  break;
5086  }
5087  case KeyPress:
5088  {
5089  static char
5090  command[MagickPathExtent];
5091 
5092  static int
5093  length;
5094 
5095  static KeySym
5096  key_symbol;
5097 
5098  /*
5099  Respond to a user key press.
5100  */
5101  if (event.xkey.window != windows->widget.id)
5102  break;
5103  length=XLookupString((XKeyEvent *) &event.xkey,command,
5104  (int) sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5105  *(command+length)='\0';
5106  if (AreaIsActive(scroll_info,event.xkey))
5107  {
5108  /*
5109  Move slider.
5110  */
5111  switch ((int) key_symbol)
5112  {
5113  case XK_Home:
5114  case XK_KP_Home:
5115  {
5116  slider_info.id=0;
5117  break;
5118  }
5119  case XK_Up:
5120  case XK_KP_Up:
5121  {
5122  slider_info.id--;
5123  break;
5124  }
5125  case XK_Down:
5126  case XK_KP_Down:
5127  {
5128  slider_info.id++;
5129  break;
5130  }
5131  case XK_Prior:
5132  case XK_KP_Prior:
5133  {
5134  slider_info.id-=visible_files;
5135  break;
5136  }
5137  case XK_Next:
5138  case XK_KP_Next:
5139  {
5140  slider_info.id+=visible_files;
5141  break;
5142  }
5143  case XK_End:
5144  case XK_KP_End:
5145  {
5146  slider_info.id=(int) files;
5147  break;
5148  }
5149  }
5150  state|=RedrawListState;
5151  break;
5152  }
5153  if ((key_symbol == XK_Return) || (key_symbol == XK_KP_Enter))
5154  {
5155  /*
5156  Read new directory or glob patterm.
5157  */
5158  if (*reply_info.text == '\0')
5159  break;
5160  if (IsGlob(reply_info.text))
5161  (void) CopyMagickString(glob_pattern,reply_info.text,
5163  else
5164  {
5165  (void) ConcatenateMagickString(working_path,DirectorySeparator,
5167  (void) ConcatenateMagickString(working_path,reply_info.text,
5169  if (*working_path == '~')
5170  ExpandFilename(working_path);
5171  *reply='\0';
5172  }
5173  state|=UpdateListState;
5174  break;
5175  }
5176  if (key_symbol == XK_Control_L)
5177  {
5178  state|=ControlState;
5179  break;
5180  }
5181  if (state & ControlState)
5182  switch ((int) key_symbol)
5183  {
5184  case XK_u:
5185  case XK_U:
5186  {
5187  /*
5188  Erase the entire line of text.
5189  */
5190  *reply_info.text='\0';
5191  reply_info.cursor=reply_info.text;
5192  reply_info.marker=reply_info.text;
5193  reply_info.highlight=MagickFalse;
5194  break;
5195  }
5196  default:
5197  break;
5198  }
5199  XEditText(display,&reply_info,key_symbol,command,state);
5200  XDrawMatteText(display,&windows->widget,&reply_info);
5201  state|=JumpListState;
5202  break;
5203  }
5204  case KeyRelease:
5205  {
5206  static char
5207  command[MagickPathExtent];
5208 
5209  static KeySym
5210  key_symbol;
5211 
5212  /*
5213  Respond to a user key release.
5214  */
5215  if (event.xkey.window != windows->widget.id)
5216  break;
5217  (void) XLookupString((XKeyEvent *) &event.xkey,command,
5218  (int) sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5219  if (key_symbol == XK_Control_L)
5220  state&=(~ControlState);
5221  break;
5222  }
5223  case LeaveNotify:
5224  {
5225  if (event.xcrossing.window != windows->widget.id)
5226  break;
5227  state|=InactiveWidgetState;
5228  break;
5229  }
5230  case MapNotify:
5231  {
5232  mask&=(~CWX);
5233  mask&=(~CWY);
5234  break;
5235  }
5236  case MotionNotify:
5237  {
5238  /*
5239  Discard pending button motion events.
5240  */
5241  while (XCheckMaskEvent(display,ButtonMotionMask,&event)) ;
5242  if (slider_info.active)
5243  {
5244  /*
5245  Move slider matte.
5246  */
5247  slider_info.y=event.xmotion.y-
5248  ((slider_info.height+slider_info.bevel_width) >> 1)+1;
5249  if (slider_info.y < slider_info.min_y)
5250  slider_info.y=slider_info.min_y;
5251  if (slider_info.y > slider_info.max_y)
5252  slider_info.y=slider_info.max_y;
5253  slider_info.id=0;
5254  if (slider_info.y != slider_info.min_y)
5255  slider_info.id=(int) ((files*(slider_info.y-slider_info.min_y+1))/
5256  (slider_info.max_y-slider_info.min_y+1));
5257  state|=RedrawListState;
5258  break;
5259  }
5260  if (state & InactiveWidgetState)
5261  break;
5262  if (up_info.raised == MatteIsActive(up_info,event.xmotion))
5263  {
5264  /*
5265  Up button status changed.
5266  */
5267  up_info.raised=!up_info.raised;
5268  XDrawBeveledButton(display,&windows->widget,&up_info);
5269  break;
5270  }
5271  if (home_info.raised == MatteIsActive(home_info,event.xmotion))
5272  {
5273  /*
5274  Home button status changed.
5275  */
5276  home_info.raised=!home_info.raised;
5277  XDrawBeveledButton(display,&windows->widget,&home_info);
5278  break;
5279  }
5280  if (special_info.raised == MatteIsActive(special_info,event.xmotion))
5281  {
5282  /*
5283  Grab button status changed.
5284  */
5285  special_info.raised=!special_info.raised;
5286  XDrawBeveledButton(display,&windows->widget,&special_info);
5287  break;
5288  }
5289  if (action_info.raised == MatteIsActive(action_info,event.xmotion))
5290  {
5291  /*
5292  Action button status changed.
5293  */
5294  action_info.raised=action_info.raised == MagickFalse ?
5296  XDrawBeveledButton(display,&windows->widget,&action_info);
5297  break;
5298  }
5299  if (cancel_info.raised == MatteIsActive(cancel_info,event.xmotion))
5300  {
5301  /*
5302  Cancel button status changed.
5303  */
5304  cancel_info.raised=cancel_info.raised == MagickFalse ?
5306  XDrawBeveledButton(display,&windows->widget,&cancel_info);
5307  break;
5308  }
5309  break;
5310  }
5311  case SelectionClear:
5312  {
5313  reply_info.highlight=MagickFalse;
5314  XDrawMatteText(display,&windows->widget,&reply_info);
5315  break;
5316  }
5317  case SelectionNotify:
5318  {
5319  Atom
5320  type;
5321 
5322  int
5323  format;
5324 
5325  unsigned char
5326  *data;
5327 
5328  unsigned long
5329  after,
5330  length;
5331 
5332  /*
5333  Obtain response from primary selection.
5334  */
5335  if (event.xselection.property == (Atom) None)
5336  break;
5337  status=XGetWindowProperty(display,event.xselection.requestor,
5338  event.xselection.property,0L,2047L,MagickTrue,XA_STRING,&type,
5339  &format,&length,&after,&data);
5340  if ((status != Success) || (type != XA_STRING) || (format == 32) ||
5341  (length == 0))
5342  break;
5343  if ((Extent(reply_info.text)+length) >= (MagickPathExtent-1))
5344  (void) XBell(display,0);
5345  else
5346  {
5347  /*
5348  Insert primary selection in reply text.
5349  */
5350  *(data+length)='\0';
5351  XEditText(display,&reply_info,(KeySym) XK_Insert,(char *) data,
5352  state);
5353  XDrawMatteText(display,&windows->widget,&reply_info);
5354  state|=JumpListState;
5355  state|=RedrawActionState;
5356  }
5357  (void) XFree((void *) data);
5358  break;
5359  }
5360  case SelectionRequest:
5361  {
5362  XSelectionEvent
5363  notify;
5364 
5365  XSelectionRequestEvent
5366  *request;
5367 
5368  if (reply_info.highlight == MagickFalse)
5369  break;
5370  /*
5371  Set primary selection.
5372  */
5373  request=(&(event.xselectionrequest));
5374  (void) XChangeProperty(request->display,request->requestor,
5375  request->property,request->target,8,PropModeReplace,
5376  (unsigned char *) primary_selection,Extent(primary_selection));
5377  notify.type=SelectionNotify;
5378  notify.display=request->display;
5379  notify.requestor=request->requestor;
5380  notify.selection=request->selection;
5381  notify.target=request->target;
5382  notify.time=request->time;
5383  if (request->property == None)
5384  notify.property=request->target;
5385  else
5386  notify.property=request->property;
5387  (void) XSendEvent(request->display,request->requestor,False,0,
5388  (XEvent *) &notify);
5389  }
5390  default:
5391  break;
5392  }
5393  } while ((state & ExitState) == 0);
5394  XSetCursorState(display,windows,MagickFalse);
5395  (void) XWithdrawWindow(display,windows->widget.id,windows->widget.screen);
5396  XCheckRefreshWindows(display,windows);
5397  /*
5398  Free file list.
5399  */
5400  for (i=0; i < (ssize_t) files; i++)
5401  filelist[i]=DestroyString(filelist[i]);
5402  if (filelist != (char **) NULL)
5403  filelist=(char **) RelinquishMagickMemory(filelist);
5404  if (*reply != '\0')
5405  {
5406  (void) ConcatenateMagickString(working_path,DirectorySeparator,
5408  (void) ConcatenateMagickString(working_path,reply,MagickPathExtent);
5409  }
5410  (void) CopyMagickString(reply,working_path,MagickPathExtent);
5411  if (*reply == '~')
5412  ExpandFilename(reply);
5413 }
5414 
5415 /*
5416 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5417 % %
5418 % %
5419 % %
5420 % X F o n t B r o w s e r W i d g e t %
5421 % %
5422 % %
5423 % %
5424 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5425 %
5426 % XFontBrowserWidget() displays a Font Browser widget with a font query to the
5427 % user. The user keys a reply and presses the Action or Cancel button to
5428 % exit. The typed text is returned as the reply function parameter.
5429 %
5430 % The format of the XFontBrowserWidget method is:
5431 %
5432 % void XFontBrowserWidget(Display *display,XWindows *windows,
5433 % const char *action,char *reply)
5434 %
5435 % A description of each parameter follows:
5436 %
5437 % o display: Specifies a connection to an X server; returned from
5438 % XOpenDisplay.
5439 %
5440 % o window: Specifies a pointer to a XWindows structure.
5441 %
5442 % o action: Specifies a pointer to the action of this widget.
5443 %
5444 % o reply: the response from the user is returned in this parameter.
5445 %
5446 %
5447 */
5448 
5449 #if defined(__cplusplus) || defined(c_plusplus)
5450 extern "C" {
5451 #endif
5452 
5453 static int FontCompare(const void *x,const void *y)
5454 {
5455  char
5456  *p,
5457  *q;
5458 
5459  p=(char *) *((char **) x);
5460  q=(char *) *((char **) y);
5461  while ((*p != '\0') && (*q != '\0') && (*p == *q))
5462  {
5463  p++;
5464  q++;
5465  }
5466  return(*p-(*q));
5467 }
5468 
5469 #if defined(__cplusplus) || defined(c_plusplus)
5470 }
5471 #endif
5472 
5473 MagickPrivate void XFontBrowserWidget(Display *display,XWindows *windows,
5474  const char *action,char *reply)
5475 {
5476 #define BackButtonText "Back"
5477 #define CancelButtonText "Cancel"
5478 #define FontnameText "Name:"
5479 #define FontPatternText "Pattern:"
5480 #define ResetButtonText "Reset"
5481 
5482  char
5483  back_pattern[MagickPathExtent],
5484  **fontlist,
5485  **listhead,
5486  primary_selection[MagickPathExtent],
5487  reset_pattern[MagickPathExtent],
5488  text[MagickPathExtent];
5489 
5490  int
5491  fonts,
5492  x,
5493  y;
5494 
5495  int
5496  i;
5497 
5498  static char
5499  glob_pattern[MagickPathExtent] = "*";
5500 
5501  static MagickStatusType
5502  mask = (MagickStatusType) (CWWidth | CWHeight | CWX | CWY);
5503 
5504  Status
5505  status;
5506 
5507  unsigned int
5508  height,
5509  text_width,
5510  visible_fonts,
5511  width;
5512 
5513  size_t
5514  delay,
5515  state;
5516 
5517  XEvent
5518  event;
5519 
5520  XFontStruct
5521  *font_info;
5522 
5523  XTextProperty
5524  window_name;
5525 
5526  XWidgetInfo
5527  action_info,
5528  back_info,
5529  cancel_info,
5530  expose_info,
5531  list_info,
5532  mode_info,
5533  north_info,
5534  reply_info,
5535  reset_info,
5536  scroll_info,
5537  selection_info,
5538  slider_info,
5539  south_info,
5540  text_info;
5541 
5542  XWindowChanges
5543  window_changes;
5544 
5545  /*
5546  Get font list and sort in ascending order.
5547  */
5548  assert(display != (Display *) NULL);
5549  assert(windows != (XWindows *) NULL);
5550  assert(action != (char *) NULL);
5551  assert(reply != (char *) NULL);
5552  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",action);
5553  XSetCursorState(display,windows,MagickTrue);
5554  XCheckRefreshWindows(display,windows);
5555  (void) CopyMagickString(back_pattern,glob_pattern,MagickPathExtent);
5556  (void) CopyMagickString(reset_pattern,"*",MagickPathExtent);
5557  fontlist=XListFonts(display,glob_pattern,32767,&fonts);
5558  if (fonts == 0)
5559  {
5560  /*
5561  Pattern failed, obtain all the fonts.
5562  */
5563  XNoticeWidget(display,windows,"Unable to obtain fonts names:",
5564  glob_pattern);
5565  (void) CopyMagickString(glob_pattern,"*",MagickPathExtent);
5566  fontlist=XListFonts(display,glob_pattern,32767,&fonts);
5567  if (fontlist == (char **) NULL)
5568  {
5569  XNoticeWidget(display,windows,"Unable to obtain fonts names:",
5570  glob_pattern);
5571  return;
5572  }
5573  }
5574  /*
5575  Sort font list in ascending order.
5576  */
5577  listhead=fontlist;
5578  fontlist=(char **) AcquireQuantumMemory((size_t) fonts,sizeof(*fontlist));
5579  if (fontlist == (char **) NULL)
5580  {
5581  XNoticeWidget(display,windows,"MemoryAllocationFailed",
5582  "UnableToViewFonts");
5583  return;
5584  }
5585  for (i=0; i < fonts; i++)
5586  fontlist[i]=listhead[i];
5587  qsort((void *) fontlist,(size_t) fonts,sizeof(*fontlist),FontCompare);
5588  /*
5589  Determine Font Browser widget attributes.
5590  */
5591  font_info=windows->widget.font_info;
5592  text_width=0;
5593  for (i=0; i < fonts; i++)
5594  if (WidgetTextWidth(font_info,fontlist[i]) > text_width)
5595  text_width=WidgetTextWidth(font_info,fontlist[i]);
5596  width=WidgetTextWidth(font_info,(char *) action);
5597  if (WidgetTextWidth(font_info,CancelButtonText) > width)
5598  width=WidgetTextWidth(font_info,CancelButtonText);
5599  if (WidgetTextWidth(font_info,ResetButtonText) > width)
5600  width=WidgetTextWidth(font_info,ResetButtonText);
5601  if (WidgetTextWidth(font_info,BackButtonText) > width)
5602  width=WidgetTextWidth(font_info,BackButtonText);
5603  width+=QuantumMargin;
5604  if (WidgetTextWidth(font_info,FontPatternText) > width)
5605  width=WidgetTextWidth(font_info,FontPatternText);
5606  if (WidgetTextWidth(font_info,FontnameText) > width)
5607  width=WidgetTextWidth(font_info,FontnameText);
5608  height=(unsigned int) (font_info->ascent+font_info->descent);
5609  /*
5610  Position Font Browser widget.
5611  */
5612  windows->widget.width=width+MagickMin((int) text_width,(int) MaxTextWidth)+
5613  6*QuantumMargin;
5614  windows->widget.min_width=width+MinTextWidth+4*QuantumMargin;
5615  if (windows->widget.width < windows->widget.min_width)
5616  windows->widget.width=windows->widget.min_width;
5617  windows->widget.height=(unsigned int)
5618  (((85*height) >> 2)+((13*QuantumMargin) >> 1)+4);
5619  windows->widget.min_height=(unsigned int)
5620  (((27*height) >> 1)+((13*QuantumMargin) >> 1)+4);
5621  if (windows->widget.height < windows->widget.min_height)
5622  windows->widget.height=windows->widget.min_height;
5623  XConstrainWindowPosition(display,&windows->widget);
5624  /*
5625  Map Font Browser widget.
5626  */
5627  (void) CopyMagickString(windows->widget.name,"Browse and Select a Font",
5629  status=XStringListToTextProperty(&windows->widget.name,1,&window_name);
5630  if (status != False)
5631  {
5632  XSetWMName(display,windows->widget.id,&window_name);
5633  XSetWMIconName(display,windows->widget.id,&window_name);
5634  (void) XFree((void *) window_name.value);
5635  }
5636  window_changes.width=(int) windows->widget.width;
5637  window_changes.height=(int) windows->widget.height;
5638  window_changes.x=windows->widget.x;
5639  window_changes.y=windows->widget.y;
5640  (void) XReconfigureWMWindow(display,windows->widget.id,
5641  windows->widget.screen,mask,&window_changes);
5642  (void) XMapRaised(display,windows->widget.id);
5643  windows->widget.mapped=MagickFalse;
5644  /*
5645  Respond to X events.
5646  */
5647  XGetWidgetInfo((char *) NULL,&slider_info);
5648  XGetWidgetInfo((char *) NULL,&north_info);
5649  XGetWidgetInfo((char *) NULL,&south_info);
5650  XGetWidgetInfo((char *) NULL,&expose_info);
5651  XGetWidgetInfo((char *) NULL,&selection_info);
5652  visible_fonts=0;
5653  delay=SuspendTime << 2;
5654  state=UpdateConfigurationState;
5655  do
5656  {
5657  if (state & UpdateConfigurationState)
5658  {
5659  int
5660  id;
5661 
5662  /*
5663  Initialize button information.
5664  */
5665  XGetWidgetInfo(CancelButtonText,&cancel_info);
5666  cancel_info.width=width;
5667  cancel_info.height=(unsigned int) ((3*height) >> 1);
5668  cancel_info.x=(int)
5669  (windows->widget.width-cancel_info.width-QuantumMargin-2);
5670  cancel_info.y=(int)
5671  (windows->widget.height-cancel_info.height-QuantumMargin);
5672  XGetWidgetInfo(action,&action_info);
5673  action_info.width=width;
5674  action_info.height=(unsigned int) ((3*height) >> 1);
5675  action_info.x=cancel_info.x-(cancel_info.width+(QuantumMargin >> 1)+
5676  (action_info.bevel_width << 1));
5677  action_info.y=cancel_info.y;
5678  XGetWidgetInfo(BackButtonText,&back_info);
5679  back_info.width=width;
5680  back_info.height=(unsigned int) ((3*height) >> 1);
5681  back_info.x=QuantumMargin;
5682  back_info.y=((5*QuantumMargin) >> 1)+height;
5683  XGetWidgetInfo(ResetButtonText,&reset_info);
5684  reset_info.width=width;
5685  reset_info.height=(unsigned int) ((3*height) >> 1);
5686  reset_info.x=QuantumMargin;
5687  reset_info.y=back_info.y+back_info.height+QuantumMargin;
5688  /*
5689  Initialize reply information.
5690  */
5691  XGetWidgetInfo(reply,&reply_info);
5692  reply_info.raised=MagickFalse;
5693  reply_info.bevel_width--;
5694  reply_info.width=windows->widget.width-width-((6*QuantumMargin) >> 1);
5695  reply_info.height=height << 1;
5696  reply_info.x=(int) (width+(QuantumMargin << 1));
5697  reply_info.y=action_info.y-(action_info.height << 1)-QuantumMargin;
5698  /*
5699  Initialize mode information.
5700  */
5701  XGetWidgetInfo(reply,&mode_info);
5702  mode_info.bevel_width=0;
5703  mode_info.width=(unsigned int)
5704  (action_info.x-reply_info.x-QuantumMargin);
5705  mode_info.height=action_info.height << 1;
5706  mode_info.x=reply_info.x;
5707  mode_info.y=action_info.y-action_info.height+action_info.bevel_width;
5708  /*
5709  Initialize scroll information.
5710  */
5711  XGetWidgetInfo((char *) NULL,&scroll_info);
5712  scroll_info.bevel_width--;
5713  scroll_info.width=height;
5714  scroll_info.height=(unsigned int)
5715  (reply_info.y-back_info.y-(QuantumMargin >> 1));
5716  scroll_info.x=reply_info.x+(reply_info.width-scroll_info.width);
5717  scroll_info.y=back_info.y-reply_info.bevel_width;
5718  scroll_info.raised=MagickFalse;
5719  scroll_info.trough=MagickTrue;
5720  north_info=scroll_info;
5721  north_info.raised=MagickTrue;
5722  north_info.width-=(north_info.bevel_width << 1);
5723  north_info.height=north_info.width-1;
5724  north_info.x+=north_info.bevel_width;
5725  north_info.y+=north_info.bevel_width;
5726  south_info=north_info;
5727  south_info.y=scroll_info.y+scroll_info.height-scroll_info.bevel_width-
5728  south_info.height;
5729  id=slider_info.id;
5730  slider_info=north_info;
5731  slider_info.id=id;
5732  slider_info.width-=2;
5733  slider_info.min_y=north_info.y+north_info.height+north_info.bevel_width+
5734  slider_info.bevel_width+2;
5735  slider_info.height=scroll_info.height-((slider_info.min_y-
5736  scroll_info.y+1) << 1)+4;
5737  visible_fonts=(unsigned int) (scroll_info.height*
5738  PerceptibleReciprocal((double) height+(height >> 3)));
5739  if (fonts > (int) visible_fonts)
5740  slider_info.height=(visible_fonts*slider_info.height)/fonts;
5741  slider_info.max_y=south_info.y-south_info.bevel_width-
5742  slider_info.bevel_width-2;
5743  slider_info.x=scroll_info.x+slider_info.bevel_width+1;
5744  slider_info.y=slider_info.min_y;
5745  expose_info=scroll_info;
5746  expose_info.y=slider_info.y;
5747  /*
5748  Initialize list information.
5749  */
5750  XGetWidgetInfo((char *) NULL,&list_info);
5751  list_info.raised=MagickFalse;
5752  list_info.bevel_width--;
5753  list_info.width=(unsigned int)
5754  (scroll_info.x-reply_info.x-(QuantumMargin >> 1));
5755  list_info.height=scroll_info.height;
5756  list_info.x=reply_info.x;
5757  list_info.y=scroll_info.y;
5758  if (windows->widget.mapped == MagickFalse)
5759  state|=JumpListState;
5760  /*
5761  Initialize text information.
5762  */
5763  *text='\0';
5764  XGetWidgetInfo(text,&text_info);
5765  text_info.center=MagickFalse;
5766  text_info.width=reply_info.width;
5767  text_info.height=height;
5768  text_info.x=list_info.x-(QuantumMargin >> 1);
5769  text_info.y=QuantumMargin;
5770  /*
5771  Initialize selection information.
5772  */
5773  XGetWidgetInfo((char *) NULL,&selection_info);
5774  selection_info.center=MagickFalse;
5775  selection_info.width=list_info.width;
5776  selection_info.height=(unsigned int) ((9*height) >> 3);
5777  selection_info.x=list_info.x;
5778  state&=(~UpdateConfigurationState);
5779  }
5780  if (state & RedrawWidgetState)
5781  {
5782  /*
5783  Redraw Font Browser window.
5784  */
5785  x=QuantumMargin;
5786  y=text_info.y+((text_info.height-height) >> 1)+font_info->ascent;
5787  (void) XDrawString(display,windows->widget.id,
5788  windows->widget.annotate_context,x,y,FontPatternText,
5789  Extent(FontPatternText));
5790  (void) CopyMagickString(text_info.text,glob_pattern,MagickPathExtent);
5791  XDrawWidgetText(display,&windows->widget,&text_info);
5792  XDrawBeveledButton(display,&windows->widget,&back_info);
5793  XDrawBeveledButton(display,&windows->widget,&reset_info);
5794  XDrawBeveledMatte(display,&windows->widget,&list_info);
5795  XDrawBeveledMatte(display,&windows->widget,&scroll_info);
5796  XDrawTriangleNorth(display,&windows->widget,&north_info);
5797  XDrawBeveledButton(display,&windows->widget,&slider_info);
5798  XDrawTriangleSouth(display,&windows->widget,&south_info);
5799  x=QuantumMargin;
5800  y=reply_info.y+((reply_info.height-height) >> 1)+font_info->ascent;
5801  (void) XDrawString(display,windows->widget.id,
5802  windows->widget.annotate_context,x,y,FontnameText,
5803  Extent(FontnameText));
5804  XDrawBeveledMatte(display,&windows->widget,&reply_info);
5805  XDrawMatteText(display,&windows->widget,&reply_info);
5806  XDrawBeveledButton(display,&windows->widget,&action_info);
5807  XDrawBeveledButton(display,&windows->widget,&cancel_info);
5808  XHighlightWidget(display,&windows->widget,BorderOffset,BorderOffset);
5809  selection_info.id=(~0);
5810  state|=RedrawActionState;
5811  state|=RedrawListState;
5812  state&=(~RedrawWidgetState);
5813  }
5814  if (state & UpdateListState)
5815  {
5816  char
5817  **checklist;
5818 
5819  int
5820  number_fonts;
5821 
5822  /*
5823  Update font list.
5824  */
5825  checklist=XListFonts(display,glob_pattern,32767,&number_fonts);
5826  if (checklist == (char **) NULL)
5827  {
5828  if ((strchr(glob_pattern,'*') == (char *) NULL) &&
5829  (strchr(glob_pattern,'?') == (char *) NULL))
5830  {
5831  /*
5832  Might be a scaleable font-- exit.
5833  */
5834  (void) CopyMagickString(reply,glob_pattern,MagickPathExtent);
5835  (void) CopyMagickString(glob_pattern,back_pattern,MagickPathExtent);
5836  action_info.raised=MagickFalse;
5837  XDrawBeveledButton(display,&windows->widget,&action_info);
5838  break;
5839  }
5840  (void) CopyMagickString(glob_pattern,back_pattern,MagickPathExtent);
5841  (void) XBell(display,0);
5842  }
5843  else
5844  if (number_fonts == 1)
5845  {
5846  /*
5847  Reply is a single font name-- exit.
5848  */
5849  (void) CopyMagickString(reply,checklist[0],MagickPathExtent);
5850  (void) CopyMagickString(glob_pattern,back_pattern,MagickPathExtent);
5851  (void) XFreeFontNames(checklist);
5852  action_info.raised=MagickFalse;
5853  XDrawBeveledButton(display,&windows->widget,&action_info);
5854  break;
5855  }
5856  else
5857  {
5858  (void) XFreeFontNames(listhead);
5859  fontlist=(char **) RelinquishMagickMemory(fontlist);
5860  fontlist=checklist;
5861  fonts=number_fonts;
5862  }
5863  /*
5864  Sort font list in ascending order.
5865  */
5866  listhead=fontlist;
5867  fontlist=(char **) AcquireQuantumMemory((size_t) fonts,
5868  sizeof(*fontlist));
5869  if (fontlist == (char **) NULL)
5870  {
5871  XNoticeWidget(display,windows,"MemoryAllocationFailed",
5872  "UnableToViewFonts");
5873  return;
5874  }
5875  for (i=0; i < fonts; i++)
5876  fontlist[i]=listhead[i];
5877  qsort((void *) fontlist,(size_t) fonts,sizeof(*fontlist),FontCompare);
5878  slider_info.height=
5879  scroll_info.height-((slider_info.min_y-scroll_info.y+1) << 1)+1;
5880  if (fonts > (int) visible_fonts)
5881  slider_info.height=(visible_fonts*slider_info.height)/fonts;
5882  slider_info.max_y=south_info.y-south_info.bevel_width-
5883  slider_info.bevel_width-2;
5884  slider_info.id=0;
5885  slider_info.y=slider_info.min_y;
5886  expose_info.y=slider_info.y;
5887  selection_info.id=(~0);
5888  list_info.id=(~0);
5889  state|=RedrawListState;
5890  /*
5891  Redraw font name & reply.
5892  */
5893  *reply_info.text='\0';
5894  reply_info.cursor=reply_info.text;
5895  (void) CopyMagickString(text_info.text,glob_pattern,MagickPathExtent);
5896  XDrawWidgetText(display,&windows->widget,&text_info);
5897  XDrawMatteText(display,&windows->widget,&reply_info);
5898  XDrawBeveledMatte(display,&windows->widget,&scroll_info);
5899  XDrawTriangleNorth(display,&windows->widget,&north_info);
5900  XDrawBeveledButton(display,&windows->widget,&slider_info);
5901  XDrawTriangleSouth(display,&windows->widget,&south_info);
5902  XHighlightWidget(display,&windows->widget,BorderOffset,BorderOffset);
5903  state&=(~UpdateListState);
5904  }
5905  if (state & JumpListState)
5906  {
5907  /*
5908  Jump scroll to match user font.
5909  */
5910  list_info.id=(~0);
5911  for (i=0; i < fonts; i++)
5912  if (LocaleCompare(fontlist[i],reply) >= 0)
5913  {
5914  list_info.id=LocaleCompare(fontlist[i],reply) == 0 ? i : ~0;
5915  break;
5916  }
5917  if ((i < slider_info.id) || (i >= (int) (slider_info.id+visible_fonts)))
5918  slider_info.id=i-(visible_fonts >> 1);
5919  selection_info.id=(~0);
5920  state|=RedrawListState;
5921  state&=(~JumpListState);
5922  }
5923  if (state & RedrawListState)
5924  {
5925  /*
5926  Determine slider id and position.
5927  */
5928  if (slider_info.id >= (int) (fonts-visible_fonts))
5929  slider_info.id=fonts-visible_fonts;
5930  if ((slider_info.id < 0) || (fonts <= (int) visible_fonts))
5931  slider_info.id=0;
5932  slider_info.y=slider_info.min_y;
5933  if (fonts > 0)
5934  slider_info.y+=
5935  slider_info.id*(slider_info.max_y-slider_info.min_y+1)/fonts;
5936  if (slider_info.id != selection_info.id)
5937  {
5938  /*
5939  Redraw scroll bar and file names.
5940  */
5941  selection_info.id=slider_info.id;
5942  selection_info.y=list_info.y+(height >> 3)+2;
5943  for (i=0; i < (int) visible_fonts; i++)
5944  {
5945  selection_info.raised=(slider_info.id+i) != list_info.id ?
5947  selection_info.text=(char *) NULL;
5948  if ((slider_info.id+i) < fonts)
5949  selection_info.text=fontlist[slider_info.id+i];
5950  XDrawWidgetText(display,&windows->widget,&selection_info);
5951  selection_info.y+=(int) selection_info.height;
5952  }
5953  /*
5954  Update slider.
5955  */
5956  if (slider_info.y > expose_info.y)
5957  {
5958  expose_info.height=(unsigned int) slider_info.y-expose_info.y;
5959  expose_info.y=slider_info.y-expose_info.height-
5960  slider_info.bevel_width-1;
5961  }
5962  else
5963  {
5964  expose_info.height=(unsigned int) expose_info.y-slider_info.y;
5965  expose_info.y=slider_info.y+slider_info.height+
5966  slider_info.bevel_width+1;
5967  }
5968  XDrawTriangleNorth(display,&windows->widget,&north_info);
5969  XDrawMatte(display,&windows->widget,&expose_info);
5970  XDrawBeveledButton(display,&windows->widget,&slider_info);
5971  XDrawTriangleSouth(display,&windows->widget,&south_info);
5972  expose_info.y=slider_info.y;
5973  }
5974  state&=(~RedrawListState);
5975  }
5976  if (state & RedrawActionState)
5977  {
5978  XFontStruct
5979  *save_info;
5980 
5981  /*
5982  Display the selected font in a drawing area.
5983  */
5984  save_info=windows->widget.font_info;
5985  font_info=XLoadQueryFont(display,reply_info.text);
5986  if (font_info != (XFontStruct *) NULL)
5987  {
5988  windows->widget.font_info=font_info;
5989  (void) XSetFont(display,windows->widget.widget_context,
5990  font_info->fid);
5991  }
5992  XDrawBeveledButton(display,&windows->widget,&mode_info);
5993  windows->widget.font_info=save_info;
5994  if (font_info != (XFontStruct *) NULL)
5995  {
5996  (void) XSetFont(display,windows->widget.widget_context,
5997  windows->widget.font_info->fid);
5998  (void) XFreeFont(display,font_info);
5999  }
6000  XHighlightWidget(display,&windows->widget,BorderOffset,BorderOffset);
6001  XDrawMatteText(display,&windows->widget,&reply_info);
6002  state&=(~RedrawActionState);
6003  }
6004  /*
6005  Wait for next event.
6006  */
6007  if (north_info.raised && south_info.raised)
6008  (void) XIfEvent(display,&event,XScreenEvent,(char *) windows);
6009  else
6010  {
6011  /*
6012  Brief delay before advancing scroll bar.
6013  */
6014  XDelay(display,delay);
6015  delay=SuspendTime;
6016  (void) XCheckIfEvent(display,&event,XScreenEvent,(char *) windows);
6017  if (north_info.raised == MagickFalse)
6018  if (slider_info.id > 0)
6019  {
6020  /*
6021  Move slider up.
6022  */
6023  slider_info.id--;
6024  state|=RedrawListState;
6025  }
6026  if (south_info.raised == MagickFalse)
6027  if (slider_info.id < fonts)
6028  {
6029  /*
6030  Move slider down.
6031  */
6032  slider_info.id++;
6033  state|=RedrawListState;
6034  }
6035  if (event.type != ButtonRelease)
6036  continue;
6037  }
6038  switch (event.type)
6039  {
6040  case ButtonPress:
6041  {
6042  if (MatteIsActive(slider_info,event.xbutton))
6043  {
6044  /*
6045  Track slider.
6046  */
6047  slider_info.active=MagickTrue;
6048  break;
6049  }
6050  if (MatteIsActive(north_info,event.xbutton))
6051  if (slider_info.id > 0)
6052  {
6053  /*
6054  Move slider up.
6055  */
6056  north_info.raised=MagickFalse;
6057  slider_info.id--;
6058  state|=RedrawListState;
6059  break;
6060  }
6061  if (MatteIsActive(south_info,event.xbutton))
6062  if (slider_info.id < fonts)
6063  {
6064  /*
6065  Move slider down.
6066  */
6067  south_info.raised=MagickFalse;
6068  slider_info.id++;
6069  state|=RedrawListState;
6070  break;
6071  }
6072  if (MatteIsActive(scroll_info,event.xbutton))
6073  {
6074  /*
6075  Move slider.
6076  */
6077  if (event.xbutton.y < slider_info.y)
6078  slider_info.id-=(visible_fonts-1);
6079  else
6080  slider_info.id+=(visible_fonts-1);
6081  state|=RedrawListState;
6082  break;
6083  }
6084  if (MatteIsActive(list_info,event.xbutton))
6085  {
6086  int
6087  id;
6088 
6089  /*
6090  User pressed list matte.
6091  */
6092  id=slider_info.id+(event.xbutton.y-(list_info.y+(height >> 1))+1)/
6093  selection_info.height;
6094  if (id >= (int) fonts)
6095  break;
6096  (void) CopyMagickString(reply_info.text,fontlist[id],MagickPathExtent);
6097  reply_info.highlight=MagickFalse;
6098  reply_info.marker=reply_info.text;
6099  reply_info.cursor=reply_info.text+Extent(reply_info.text);
6100  XDrawMatteText(display,&windows->widget,&reply_info);
6101  state|=RedrawActionState;
6102  if (id == list_info.id)
6103  {
6104  (void) CopyMagickString(glob_pattern,reply_info.text,
6106  state|=UpdateListState;
6107  }
6108  selection_info.id=(~0);
6109  list_info.id=id;
6110  state|=RedrawListState;
6111  break;
6112  }
6113  if (MatteIsActive(back_info,event.xbutton))
6114  {
6115  /*
6116  User pressed Back button.
6117  */
6118  back_info.raised=MagickFalse;
6119  XDrawBeveledButton(display,&windows->widget,&back_info);
6120  break;
6121  }
6122  if (MatteIsActive(reset_info,event.xbutton))
6123  {
6124  /*
6125  User pressed Reset button.
6126  */
6127  reset_info.raised=MagickFalse;
6128  XDrawBeveledButton(display,&windows->widget,&reset_info);
6129  break;
6130  }
6131  if (MatteIsActive(action_info,event.xbutton))
6132  {
6133  /*
6134  User pressed action button.
6135  */
6136  action_info.raised=MagickFalse;
6137  XDrawBeveledButton(display,&windows->widget,&action_info);
6138  break;
6139  }
6140  if (MatteIsActive(cancel_info,event.xbutton))
6141  {
6142  /*
6143  User pressed Cancel button.
6144  */
6145  cancel_info.raised=MagickFalse;
6146  XDrawBeveledButton(display,&windows->widget,&cancel_info);
6147  break;
6148  }
6149  if (MatteIsActive(reply_info,event.xbutton) == MagickFalse)
6150  break;
6151  if (event.xbutton.button != Button2)
6152  {
6153  static Time
6154  click_time;
6155 
6156  /*
6157  Move text cursor to position of button press.
6158  */
6159  x=event.xbutton.x-reply_info.x-(QuantumMargin >> 2);
6160  for (i=1; i <= Extent(reply_info.marker); i++)
6161  if (XTextWidth(font_info,reply_info.marker,i) > x)
6162  break;
6163  reply_info.cursor=reply_info.marker+i-1;
6164  if (event.xbutton.time > (click_time+DoubleClick))
6165  reply_info.highlight=MagickFalse;
6166  else
6167  {
6168  /*
6169  Become the XA_PRIMARY selection owner.
6170  */
6171  (void) CopyMagickString(primary_selection,reply_info.text,
6173  (void) XSetSelectionOwner(display,XA_PRIMARY,windows->widget.id,
6174  event.xbutton.time);
6175  reply_info.highlight=XGetSelectionOwner(display,XA_PRIMARY) ==
6176  windows->widget.id ? MagickTrue : MagickFalse;
6177  }
6178  XDrawMatteText(display,&windows->widget,&reply_info);
6179  click_time=event.xbutton.time;
6180  break;
6181  }
6182  /*
6183  Request primary selection.
6184  */
6185  (void) XConvertSelection(display,XA_PRIMARY,XA_STRING,XA_STRING,
6186  windows->widget.id,event.xbutton.time);
6187  break;
6188  }
6189  case ButtonRelease:
6190  {
6191  if (windows->widget.mapped == MagickFalse)
6192  break;
6193  if (north_info.raised == MagickFalse)
6194  {
6195  /*
6196  User released up button.
6197  */
6198  delay=SuspendTime << 2;
6199  north_info.raised=MagickTrue;
6200  XDrawTriangleNorth(display,&windows->widget,&north_info);
6201  }
6202  if (south_info.raised == MagickFalse)
6203  {
6204  /*
6205  User released down button.
6206  */
6207  delay=SuspendTime << 2;
6208  south_info.raised=MagickTrue;
6209  XDrawTriangleSouth(display,&windows->widget,&south_info);
6210  }
6211  if (slider_info.active)
6212  {
6213  /*
6214  Stop tracking slider.
6215  */
6216  slider_info.active=MagickFalse;
6217  break;
6218  }
6219  if (back_info.raised == MagickFalse)
6220  {
6221  if (event.xbutton.window == windows->widget.id)
6222  if (MatteIsActive(back_info,event.xbutton))
6223  {
6224  (void) CopyMagickString(glob_pattern,back_pattern,
6226  state|=UpdateListState;
6227  }
6228  back_info.raised=MagickTrue;
6229  XDrawBeveledButton(display,&windows->widget,&back_info);
6230  }
6231  if (reset_info.raised == MagickFalse)
6232  {
6233  if (event.xbutton.window == windows->widget.id)
6234  if (MatteIsActive(reset_info,event.xbutton))
6235  {
6236  (void) CopyMagickString(back_pattern,glob_pattern,MagickPathExtent);
6237  (void) CopyMagickString(glob_pattern,reset_pattern,MagickPathExtent);
6238  state|=UpdateListState;
6239  }
6240  reset_info.raised=MagickTrue;
6241  XDrawBeveledButton(display,&windows->widget,&reset_info);
6242  }
6243  if (action_info.raised == MagickFalse)
6244  {
6245  if (event.xbutton.window == windows->widget.id)
6246  {
6247  if (MatteIsActive(action_info,event.xbutton))
6248  {
6249  if (*reply_info.text == '\0')
6250  (void) XBell(display,0);
6251  else
6252  state|=ExitState;
6253  }
6254  }
6255  action_info.raised=MagickTrue;
6256  XDrawBeveledButton(display,&windows->widget,&action_info);
6257  }
6258  if (cancel_info.raised == MagickFalse)
6259  {
6260  if (event.xbutton.window == windows->widget.id)
6261  if (MatteIsActive(cancel_info,event.xbutton))
6262  {
6263  *reply_info.text='\0';
6264  state|=ExitState;
6265  }
6266  cancel_info.raised=MagickTrue;
6267  XDrawBeveledButton(display,&windows->widget,&cancel_info);
6268  }
6269  break;
6270  }
6271  case ClientMessage:
6272  {
6273  /*
6274  If client window delete message, exit.
6275  */
6276  if (event.xclient.message_type != windows->wm_protocols)
6277  break;
6278  if (*event.xclient.data.l == (int) windows->wm_take_focus)
6279  {
6280  (void) XSetInputFocus(display,event.xclient.window,RevertToParent,
6281  (Time) event.xclient.data.l[1]);
6282  break;
6283  }
6284  if (*event.xclient.data.l != (int) windows->wm_delete_window)
6285  break;
6286  if (event.xclient.window == windows->widget.id)
6287  {
6288  *reply_info.text='\0';
6289  state|=ExitState;
6290  break;
6291  }
6292  break;
6293  }
6294  case ConfigureNotify:
6295  {
6296  /*
6297  Update widget configuration.
6298  */
6299  if (event.xconfigure.window != windows->widget.id)
6300  break;
6301  if ((event.xconfigure.width == (int) windows->widget.width) &&
6302  (event.xconfigure.height == (int) windows->widget.height))
6303  break;
6304  windows->widget.width=(unsigned int)
6305  MagickMax(event.xconfigure.width,(int) windows->widget.min_width);
6306  windows->widget.height=(unsigned int)
6307  MagickMax(event.xconfigure.height,(int) windows->widget.min_height);
6308  state|=UpdateConfigurationState;
6309  break;
6310  }
6311  case EnterNotify:
6312  {
6313  if (event.xcrossing.window != windows->widget.id)
6314  break;
6315  state&=(~InactiveWidgetState);
6316  break;
6317  }
6318  case Expose:
6319  {
6320  if (event.xexpose.window != windows->widget.id)
6321  break;
6322  if (event.xexpose.count != 0)
6323  break;
6324  state|=RedrawWidgetState;
6325  break;
6326  }
6327  case KeyPress:
6328  {
6329  static char
6330  command[MagickPathExtent];
6331 
6332  static int
6333  length;
6334 
6335  static KeySym
6336  key_symbol;
6337 
6338  /*
6339  Respond to a user key press.
6340  */
6341  if (event.xkey.window != windows->widget.id)
6342  break;
6343  length=XLookupString((XKeyEvent *) &event.xkey,command,
6344  (int) sizeof(command),&key_symbol,(XComposeStatus *) NULL);
6345  *(command+length)='\0';
6346  if (AreaIsActive(scroll_info,event.xkey))
6347  {
6348  /*
6349  Move slider.
6350  */
6351  switch ((int) key_symbol)
6352  {
6353  case XK_Home:
6354  case XK_KP_Home:
6355  {
6356  slider_info.id=0;
6357  break;
6358  }
6359  case XK_Up:
6360  case XK_KP_Up:
6361  {
6362  slider_info.id--;
6363  break;
6364  }
6365  case XK_Down:
6366  case XK_KP_Down:
6367  {
6368  slider_info.id++;
6369  break;
6370  }
6371  case XK_Prior:
6372  case XK_KP_Prior:
6373  {
6374  slider_info.id-=visible_fonts;
6375  break;
6376  }
6377  case XK_Next:
6378  case XK_KP_Next:
6379  {
6380  slider_info.id+=visible_fonts;
6381  break;
6382  }
6383  case XK_End:
6384  case XK_KP_End:
6385  {
6386  slider_info.id=fonts;
6387  break;
6388  }
6389  }
6390  state|=RedrawListState;
6391  break;
6392  }
6393  if ((key_symbol == XK_Return) || (key_symbol == XK_KP_Enter))
6394  {
6395  /*
6396  Read new font or glob patterm.
6397  */
6398  if (*reply_info.text == '\0')
6399  break;
6400  (void) CopyMagickString(back_pattern,glob_pattern,MagickPathExtent);
6401  (void) CopyMagickString(glob_pattern,reply_info.text,MagickPathExtent);
6402  state|=UpdateListState;
6403  break;
6404  }
6405  if (key_symbol == XK_Control_L)
6406  {
6407  state|=ControlState;
6408  break;
6409  }
6410  if (state & ControlState)
6411  switch ((int) key_symbol)
6412  {
6413  case XK_u:
6414  case XK_U:
6415  {
6416  /*
6417  Erase the entire line of text.
6418  */
6419  *reply_info.text='\0';
6420  reply_info.cursor=reply_info.text;
6421  reply_info.marker=reply_info.text;
6422  reply_info.highlight=MagickFalse;
6423  break;
6424  }
6425  default:
6426  break;
6427  }
6428  XEditText(display,&reply_info,key_symbol,command,state);
6429  XDrawMatteText(display,&windows->widget,&reply_info);
6430  state|=JumpListState;
6431  break;
6432  }
6433  case KeyRelease:
6434  {
6435  static char
6436  command[MagickPathExtent];
6437 
6438  static KeySym
6439  key_symbol;
6440 
6441  /*
6442  Respond to a user key release.
6443  */
6444  if (event.xkey.window != windows->widget.id)
6445  break;
6446  (void) XLookupString((XKeyEvent *) &event.xkey,command,
6447  (int) sizeof(command),&key_symbol,(XComposeStatus *) NULL);
6448  if (key_symbol == XK_Control_L)
6449  state&=(~ControlState);
6450  break;
6451  }
6452  case LeaveNotify:
6453  {
6454  if (event.xcrossing.window != windows->widget.id)
6455  break;
6456  state|=InactiveWidgetState;
6457  break;
6458  }
6459  case MapNotify:
6460  {
6461  mask&=(~CWX);
6462  mask&=(~CWY);
6463  break;
6464  }
6465  case MotionNotify:
6466  {
6467  /*
6468  Discard pending button motion events.
6469  */
6470  while (XCheckMaskEvent(display,ButtonMotionMask,&event)) ;
6471  if (slider_info.active)
6472  {
6473  /*
6474  Move slider matte.
6475  */
6476  slider_info.y=event.xmotion.y-
6477  ((slider_info.height+slider_info.bevel_width) >> 1)+1;
6478  if (slider_info.y < slider_info.min_y)
6479  slider_info.y=slider_info.min_y;
6480  if (slider_info.y > slider_info.max_y)
6481  slider_info.y=slider_info.max_y;
6482  slider_info.id=0;
6483  if (slider_info.y != slider_info.min_y)
6484  slider_info.id=(fonts*(slider_info.y-slider_info.min_y+1))/
6485  (slider_info.max_y-slider_info.min_y+1);
6486  state|=RedrawListState;
6487  break;
6488  }
6489  if (state & InactiveWidgetState)
6490  break;
6491  if (back_info.raised == MatteIsActive(back_info,event.xmotion))
6492  {
6493  /*
6494  Back button status changed.
6495  */
6496  back_info.raised=!back_info.raised;
6497  XDrawBeveledButton(display,&windows->widget,&back_info);
6498  break;
6499  }
6500  if (reset_info.raised == MatteIsActive(reset_info,event.xmotion))
6501  {
6502  /*
6503  Reset button status changed.
6504  */
6505  reset_info.raised=!reset_info.raised;
6506  XDrawBeveledButton(display,&windows->widget,&reset_info);
6507  break;
6508  }
6509  if (action_info.raised == MatteIsActive(action_info,event.xmotion))
6510  {
6511  /*
6512  Action button status changed.
6513  */
6514  action_info.raised=action_info.raised == MagickFalse ?
6516  XDrawBeveledButton(display,&windows->widget,&action_info);
6517  break;
6518  }
6519  if (cancel_info.raised == MatteIsActive(cancel_info,event.xmotion))
6520  {
6521  /*
6522  Cancel button status changed.
6523  */
6524  cancel_info.raised=cancel_info.raised == MagickFalse ?
6526  XDrawBeveledButton(display,&windows->widget,&cancel_info);
6527  break;
6528  }
6529  break;
6530  }
6531  case SelectionClear:
6532  {
6533  reply_info.highlight=MagickFalse;
6534  XDrawMatteText(display,&windows->widget,&reply_info);
6535  break;
6536  }
6537  case SelectionNotify:
6538  {
6539  Atom
6540  type;
6541 
6542  int
6543  format;
6544 
6545  unsigned char
6546  *data;
6547 
6548  unsigned long
6549  after,
6550  length;
6551 
6552  /*
6553  Obtain response from primary selection.
6554  */
6555  if (event.xselection.property == (Atom) None)
6556  break;
6557  status=XGetWindowProperty(display,event.xselection.requestor,
6558  event.xselection.property,0L,2047L,MagickTrue,XA_STRING,&type,
6559  &format,&length,&after,&data);
6560  if ((status != Success) || (type != XA_STRING) || (format == 32) ||
6561  (length == 0))
6562  break;
6563  if ((Extent(reply_info.text)+length) >= (MagickPathExtent-1))
6564  (void) XBell(display,0);
6565  else
6566  {
6567  /*
6568  Insert primary selection in reply text.
6569  */
6570  *(data+length)='\0';
6571  XEditText(display,&reply_info,(KeySym) XK_Insert,(char *) data,
6572  state);
6573  XDrawMatteText(display,&windows->widget,&reply_info);
6574  state|=JumpListState;
6575  state|=RedrawActionState;
6576  }
6577  (void) XFree((void *) data);
6578  break;
6579  }
6580  case SelectionRequest:
6581  {
6582  XSelectionEvent
6583  notify;
6584 
6585  XSelectionRequestEvent
6586  *request;
6587 
6588  /*
6589  Set XA_PRIMARY selection.
6590  */
6591  request=(&(event.xselectionrequest));
6592  (void) XChangeProperty(request->display,request->requestor,
6593  request->property,request->target,8,PropModeReplace,
6594  (unsigned char *) primary_selection,Extent(primary_selection));
6595  notify.type=SelectionNotify;
6596  notify.display=request->display;
6597  notify.requestor=request->requestor;
6598  notify.selection=request->selection;
6599  notify.target=request->target;
6600  notify.time=request->time;
6601  if (request->property == None)
6602  notify.property=request->target;
6603  else
6604  notify.property=request->property;
6605  (void) XSendEvent(request->display,request->requestor,False,0,
6606  (XEvent *) &notify);
6607  }
6608  default:
6609  break;
6610  }
6611  } while ((state & ExitState) == 0);
6612  XSetCursorState(display,windows,MagickFalse);
6613  (void) XWithdrawWindow(display,windows->widget.id,windows->widget.screen);
6614  XCheckRefreshWindows(display,windows);
6615  /*
6616  Free font list.
6617  */
6618  (void) XFreeFontNames(listhead);
6619  fontlist=(char **) RelinquishMagickMemory(fontlist);
6620 }
6621 
6622 /*
6623 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6624 % %
6625 % %
6626 % %
6627 % X I n f o W i d g e t %
6628 % %
6629 % %
6630 % %
6631 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6632 %
6633 % XInfoWidget() displays text in the Info widget. The purpose is to inform
6634 % the user that what activity is currently being performed (e.g. reading
6635 % an image, rotating an image, etc.).
6636 %
6637 % The format of the XInfoWidget method is:
6638 %
6639 % void XInfoWidget(Display *display,XWindows *windows,const char *activity)
6640 %
6641 % A description of each parameter follows:
6642 %
6643 % o display: Specifies a connection to an X server; returned from
6644 % XOpenDisplay.
6645 %
6646 % o window: Specifies a pointer to a XWindows structure.
6647 %
6648 % o activity: This character string reflects the current activity and is
6649 % displayed in the Info widget.
6650 %
6651 */
6652 MagickPrivate void XInfoWidget(Display *display,XWindows *windows,
6653  const char *activity)
6654 {
6655  unsigned int
6656  height,
6657  margin,
6658  width;
6659 
6660  XFontStruct
6661  *font_info;
6662 
6663  XWindowChanges
6664  window_changes;
6665 
6666  /*
6667  Map Info widget.
6668  */
6669  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
6670  assert(display != (Display *) NULL);
6671  assert(windows != (XWindows *) NULL);
6672  assert(activity != (char *) NULL);
6673  font_info=windows->info.font_info;
6674  width=WidgetTextWidth(font_info,(char *) activity)+((3*QuantumMargin) >> 1)+4;
6675  height=(unsigned int) (((6*(font_info->ascent+font_info->descent)) >> 2)+4);
6676  if ((windows->info.width != width) || (windows->info.height != height))
6677  {
6678  /*
6679  Size Info widget to accommodate the activity text.
6680  */
6681  windows->info.width=width;
6682  windows->info.height=height;
6683  window_changes.width=(int) width;
6684  window_changes.height=(int) height;
6685  (void) XReconfigureWMWindow(display,windows->info.id,windows->info.screen,
6686  (unsigned int) (CWWidth | CWHeight),&window_changes);
6687  }
6688  if (windows->info.mapped == MagickFalse)
6689  {
6690  (void) XMapRaised(display,windows->info.id);
6691  windows->info.mapped=MagickTrue;
6692  }
6693  /*
6694  Initialize Info matte information.
6695  */
6696  height=(unsigned int) (font_info->ascent+font_info->descent);
6697  XGetWidgetInfo(activity,&monitor_info);
6698  monitor_info.bevel_width--;
6699  margin=monitor_info.bevel_width+((windows->info.height-height) >> 1)-2;
6700  monitor_info.center=MagickFalse;
6701  monitor_info.x=(int) margin;
6702  monitor_info.y=(int) margin;
6703  monitor_info.width=windows->info.width-(margin << 1);
6704  monitor_info.height=windows->info.height-(margin << 1)+1;
6705  /*
6706  Draw Info widget.
6707  */
6708  monitor_info.raised=MagickFalse;
6709  XDrawBeveledMatte(display,&windows->info,&monitor_info);
6710  monitor_info.raised=MagickTrue;
6711  XDrawWidgetText(display,&windows->info,&monitor_info);
6712 }
6713 
6714 /*
6715 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6716 % %
6717 % %
6718 % %
6719 % X L i s t B r o w s e r W i d g e t %
6720 % %
6721 % %
6722 % %
6723 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6724 %
6725 % XListBrowserWidget() displays a List Browser widget with a query to the
6726 % user. The user keys a reply or select a reply from the list. Finally, the
6727 % user presses the Action or Cancel button to exit. The typed text is
6728 % returned as the reply function parameter.
6729 %
6730 % The format of the XListBrowserWidget method is:
6731 %
6732 % void XListBrowserWidget(Display *display,XWindows *windows,
6733 % XWindowInfo *window_info,const char *const *list,const char *action,
6734 % const char *query,char *reply)
6735 %
6736 % A description of each parameter follows:
6737 %
6738 % o display: Specifies a connection to an X server; returned from
6739 % XOpenDisplay.
6740 %
6741 % o window: Specifies a pointer to a XWindows structure.
6742 %
6743 % o list: Specifies a pointer to an array of strings. The user can
6744 % select from these strings as a possible reply value.
6745 %
6746 % o action: Specifies a pointer to the action of this widget.
6747 %
6748 % o query: Specifies a pointer to the query to present to the user.
6749 %
6750 % o reply: the response from the user is returned in this parameter.
6751 %
6752 */
6753 MagickPrivate void XListBrowserWidget(Display *display,XWindows *windows,
6754  XWindowInfo *window_info,const char *const *list,const char *action,
6755  const char *query,char *reply)
6756 {
6757 #define CancelButtonText "Cancel"
6758 
6759  char
6760  primary_selection[MagickPathExtent];
6761 
6762  int
6763  x;
6764 
6765  int
6766  i;
6767 
6768  static MagickStatusType
6769  mask = (MagickStatusType) (CWWidth | CWHeight | CWX | CWY);
6770 
6771  Status
6772  status;
6773 
6774  unsigned int
6775  entries,
6776  height,
6777  text_width,
6778  visible_entries,
6779  width;
6780 
6781  size_t
6782  delay,
6783  state;
6784 
6785  XEvent
6786  event;
6787 
6788  XFontStruct
6789  *font_info;
6790 
6791  XTextProperty
6792  window_name;
6793 
6794  XWidgetInfo
6795  action_info,
6796  cancel_info,
6797  expose_info,
6798  list_info,
6799  north_info,
6800  reply_info,
6801  scroll_info,
6802  selection_info,
6803  slider_info,
6804  south_info,
6805  text_info;
6806 
6807  XWindowChanges
6808  window_changes;
6809 
6810  /*
6811  Count the number of entries in the list.
6812  */
6813  assert(display != (Display *) NULL);
6814  assert(windows != (XWindows *) NULL);
6815  assert(window_info != (XWindowInfo *) NULL);
6816  assert(list != (const char **) NULL);
6817  assert(action != (char *) NULL);
6818  assert(query != (char *) NULL);
6819  assert(reply != (char *) NULL);
6820  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",action);
6821  XSetCursorState(display,windows,MagickTrue);
6822  XCheckRefreshWindows(display,windows);
6823  if (list == (const char **) NULL)
6824  {
6825  XNoticeWidget(display,windows,"No text to browse:",(char *) NULL);
6826  return;
6827  }
6828  for (entries=0; ; entries++)
6829  if (list[entries] == (char *) NULL)
6830  break;
6831  /*
6832  Determine Font Browser widget attributes.
6833  */
6834  font_info=window_info->font_info;
6835  text_width=WidgetTextWidth(font_info,(char *) query);
6836  for (i=0; i < (int) entries; i++)
6837  if (WidgetTextWidth(font_info,(char *) list[i]) > text_width)
6838  text_width=WidgetTextWidth(font_info,(char *) list[i]);
6839  width=WidgetTextWidth(font_info,(char *) action);
6840  if (WidgetTextWidth(font_info,CancelButtonText) > width)
6841  width=WidgetTextWidth(font_info,CancelButtonText);
6842  width+=QuantumMargin;
6843  height=(unsigned int) (font_info->ascent+font_info->descent);
6844  /*
6845  Position List Browser widget.
6846  */
6847  window_info->width=(unsigned int) MagickMin((int) text_width,(int)
6848  MaxTextWidth)+((9*QuantumMargin) >> 1);
6849  window_info->min_width=(unsigned int) (MinTextWidth+4*QuantumMargin);
6850  if (window_info->width < window_info->min_width)
6851  window_info->width=window_info->min_width;
6852  window_info->height=(unsigned int)
6853  (((81*height) >> 2)+((13*QuantumMargin) >> 1)+4);
6854  window_info->min_height=(unsigned int)
6855  (((23*height) >> 1)+((13*QuantumMargin) >> 1)+4);
6856  if (window_info->height < window_info->min_height)
6857  window_info->height=window_info->min_height;
6858  XConstrainWindowPosition(display,window_info);
6859  /*
6860  Map List Browser widget.
6861  */
6862  (void) CopyMagickString(window_info->name,"Browse",MagickPathExtent);
6863  status=XStringListToTextProperty(&window_info->name,1,&window_name);
6864  if (status != False)
6865  {
6866  XSetWMName(display,window_info->id,&window_name);
6867  XSetWMIconName(display,windows->widget.id,&window_name);
6868  (void) XFree((void *) window_name.value);
6869  }
6870  window_changes.width=(int) window_info->width;
6871  window_changes.height=(int) window_info->height;
6872  window_changes.x=window_info->x;
6873  window_changes.y=window_info->y;
6874  (void) XReconfigureWMWindow(display,window_info->id,window_info->screen,mask,
6875  &window_changes);
6876  (void) XMapRaised(display,window_info->id);
6877  window_info->mapped=MagickFalse;
6878  /*
6879  Respond to X events.
6880  */
6881  XGetWidgetInfo((char *) NULL,&slider_info);
6882  XGetWidgetInfo((char *) NULL,&north_info);
6883  XGetWidgetInfo((char *) NULL,&south_info);
6884  XGetWidgetInfo((char *) NULL,&expose_info);
6885  XGetWidgetInfo((char *) NULL,&selection_info);
6886  visible_entries=0;
6887  delay=SuspendTime << 2;
6888  state=UpdateConfigurationState;
6889  do
6890  {
6891  if (state & UpdateConfigurationState)
6892  {
6893  int
6894  id;
6895 
6896  /*
6897  Initialize button information.
6898  */
6899  XGetWidgetInfo(CancelButtonText,&cancel_info);
6900  cancel_info.width=width;
6901  cancel_info.height=(unsigned int) ((3*height) >> 1);
6902  cancel_info.x=(int)
6903  (window_info->width-cancel_info.width-QuantumMargin-2);
6904  cancel_info.y=(int)
6905  (window_info->height-cancel_info.height-QuantumMargin);
6906  XGetWidgetInfo(action,&action_info);
6907  action_info.width=width;
6908  action_info.height=(unsigned int) ((3*height) >> 1);
6909  action_info.x=cancel_info.x-(cancel_info.width+(QuantumMargin >> 1)+
6910  (action_info.bevel_width << 1));
6911  action_info.y=cancel_info.y;
6912  /*
6913  Initialize reply information.
6914  */
6915  XGetWidgetInfo(reply,&reply_info);
6916  reply_info.raised=MagickFalse;
6917  reply_info.bevel_width--;
6918  reply_info.width=window_info->width-((4*QuantumMargin) >> 1);
6919  reply_info.height=height << 1;
6920  reply_info.x=QuantumMargin;
6921  reply_info.y=action_info.y-reply_info.height-QuantumMargin;
6922  /*
6923  Initialize scroll information.
6924  */
6925  XGetWidgetInfo((char *) NULL,&scroll_info);
6926  scroll_info.bevel_width--;
6927  scroll_info.width=height;
6928  scroll_info.height=(unsigned int)
6929  (reply_info.y-((6*QuantumMargin) >> 1)-height);
6930  scroll_info.x=reply_info.x+(reply_info.width-scroll_info.width);
6931  scroll_info.y=((5*QuantumMargin) >> 1)+height-reply_info.bevel_width;
6932  scroll_info.raised=MagickFalse;
6933  scroll_info.trough=MagickTrue;
6934  north_info=scroll_info;
6935  north_info.raised=MagickTrue;
6936  north_info.width-=(north_info.bevel_width << 1);
6937  north_info.height=north_info.width-1;
6938  north_info.x+=north_info.bevel_width;
6939  north_info.y+=north_info.bevel_width;
6940  south_info=north_info;
6941  south_info.y=scroll_info.y+scroll_info.height-scroll_info.bevel_width-
6942  south_info.height;
6943  id=slider_info.id;
6944  slider_info=north_info;
6945  slider_info.id=id;
6946  slider_info.width-=2;
6947  slider_info.min_y=north_info.y+north_info.height+north_info.bevel_width+
6948  slider_info.bevel_width+2;
6949  slider_info.height=scroll_info.height-((slider_info.min_y-
6950  scroll_info.y+1) << 1)+4;
6951  visible_entries=(unsigned int) (scroll_info.height*
6952  PerceptibleReciprocal((double) height+(height >> 3)));
6953  if (entries > visible_entries)
6954  slider_info.height=(visible_entries*slider_info.height)/entries;
6955  slider_info.max_y=south_info.y-south_info.bevel_width-
6956  slider_info.bevel_width-2;
6957  slider_info.x=scroll_info.x+slider_info.bevel_width+1;
6958  slider_info.y=slider_info.min_y;
6959  expose_info=scroll_info;
6960  expose_info.y=slider_info.y;
6961  /*
6962  Initialize list information.
6963  */
6964  XGetWidgetInfo((char *) NULL,&list_info);
6965  list_info.raised=MagickFalse;
6966  list_info.bevel_width--;
6967  list_info.width=(unsigned int)
6968  (scroll_info.x-reply_info.x-(QuantumMargin >> 1));
6969  list_info.height=scroll_info.height;
6970  list_info.x=reply_info.x;
6971  list_info.y=scroll_info.y;
6972  if (window_info->mapped == MagickFalse)
6973  for (i=0; i < (int) entries; i++)
6974  if (LocaleCompare(list[i],reply) == 0)
6975  {
6976  list_info.id=i;
6977  slider_info.id=i-(visible_entries >> 1);
6978  if (slider_info.id < 0)
6979  slider_info.id=0;
6980  }
6981  /*
6982  Initialize text information.
6983  */
6984  XGetWidgetInfo(query,&text_info);
6985  text_info.width=reply_info.width;
6986  text_info.height=height;
6987  text_info.x=list_info.x-(QuantumMargin >> 1);
6988  text_info.y=QuantumMargin;
6989  /*
6990  Initialize selection information.
6991  */
6992  XGetWidgetInfo((char *) NULL,&selection_info);
6993  selection_info.center=MagickFalse;
6994  selection_info.width=list_info.width;
6995  selection_info.height=(unsigned int) ((9*height) >> 3);
6996  selection_info.x=list_info.x;
6997  state&=(~UpdateConfigurationState);
6998  }
6999  if (state & RedrawWidgetState)
7000  {
7001  /*
7002  Redraw List Browser window.
7003  */
7004  XDrawWidgetText(display,window_info,&text_info);
7005  XDrawBeveledMatte(display,window_info,&list_info);
7006  XDrawBeveledMatte(display,window_info,&scroll_info);
7007  XDrawTriangleNorth(display,window_info,&north_info);
7008  XDrawBeveledButton(display,window_info,&slider_info);
7009  XDrawTriangleSouth(display,window_info,&south_info);
7010  XDrawBeveledMatte(display,window_info,&reply_info);
7011  XDrawMatteText(display,window_info,&reply_info);
7012  XDrawBeveledButton(display,window_info,&action_info);
7013  XDrawBeveledButton(display,window_info,&cancel_info);
7014  XHighlightWidget(display,window_info,BorderOffset,BorderOffset);
7015  selection_info.id=(~0);
7016  state|=RedrawActionState;
7017  state|=RedrawListState;
7018  state&=(~RedrawWidgetState);
7019  }
7020  if (state & RedrawListState)
7021  {
7022  /*
7023  Determine slider id and position.
7024  */
7025  if (slider_info.id >= (int) (entries-visible_entries))
7026  slider_info.id=(int) (entries-visible_entries);
7027  if ((slider_info.id < 0) || (entries <= visible_entries))
7028  slider_info.id=0;
7029  slider_info.y=slider_info.min_y;
7030  if (entries > 0)
7031  slider_info.y+=
7032  slider_info.id*(slider_info.max_y-slider_info.min_y+1)/entries;
7033  if (slider_info.id != selection_info.id)
7034  {
7035  /*
7036  Redraw scroll bar and file names.
7037  */
7038  selection_info.id=slider_info.id;
7039  selection_info.y=list_info.y+(height >> 3)+2;
7040  for (i=0; i < (int) visible_entries; i++)
7041  {
7042  selection_info.raised=(slider_info.id+i) != list_info.id ?
7044  selection_info.text=(char *) NULL;
7045  if ((slider_info.id+i) < (int) entries)
7046  selection_info.text=(char *) list[slider_info.id+i];
7047  XDrawWidgetText(display,window_info,&selection_info);
7048  selection_info.y+=(int) selection_info.height;
7049  }
7050  /*
7051  Update slider.
7052  */
7053  if (slider_info.y > expose_info.y)
7054  {
7055  expose_info.height=(unsigned int) slider_info.y-expose_info.y;
7056  expose_info.y=slider_info.y-expose_info.height-
7057  slider_info.bevel_width-1;
7058  }
7059  else
7060  {
7061  expose_info.height=(unsigned int) expose_info.y-slider_info.y;
7062  expose_info.y=slider_info.y+slider_info.height+
7063  slider_info.bevel_width+1;
7064  }
7065  XDrawTriangleNorth(display,window_info,&north_info);
7066  XDrawMatte(display,window_info,&expose_info);
7067  XDrawBeveledButton(display,window_info,&slider_info);
7068  XDrawTriangleSouth(display,window_info,&south_info);
7069  expose_info.y=slider_info.y;
7070  }
7071  state&=(~RedrawListState);
7072  }
7073  /*
7074  Wait for next event.
7075  */
7076  if (north_info.raised && south_info.raised)
7077  (void) XIfEvent(display,&event,XScreenEvent,(char *) windows);
7078  else
7079  {
7080  /*
7081  Brief delay before advancing scroll bar.
7082  */
7083  XDelay(display,delay);
7084  delay=SuspendTime;
7085  (void) XCheckIfEvent(display,&event,XScreenEvent,(char *) windows);
7086  if (north_info.raised == MagickFalse)
7087  if (slider_info.id > 0)
7088  {
7089  /*
7090  Move slider up.
7091  */
7092  slider_info.id--;
7093  state|=RedrawListState;
7094  }
7095  if (south_info.raised == MagickFalse)
7096  if (slider_info.id < (int) entries)
7097  {
7098  /*
7099  Move slider down.
7100  */
7101  slider_info.id++;
7102  state|=RedrawListState;
7103  }
7104  if (event.type != ButtonRelease)
7105  continue;
7106  }
7107  switch (event.type)
7108  {
7109  case ButtonPress:
7110  {
7111  if (MatteIsActive(slider_info,event.xbutton))
7112  {
7113  /*
7114  Track slider.
7115  */
7116  slider_info.active=MagickTrue;
7117  break;
7118  }
7119  if (MatteIsActive(north_info,event.xbutton))
7120  if (slider_info.id > 0)
7121  {
7122  /*
7123  Move slider up.
7124  */
7125  north_info.raised=MagickFalse;
7126  slider_info.id--;
7127  state|=RedrawListState;
7128  break;
7129  }
7130  if (MatteIsActive(south_info,event.xbutton))
7131  if (slider_info.id < (int) entries)
7132  {
7133  /*
7134  Move slider down.
7135  */
7136  south_info.raised=MagickFalse;
7137  slider_info.id++;
7138  state|=RedrawListState;
7139  break;
7140  }
7141  if (MatteIsActive(scroll_info,event.xbutton))
7142  {
7143  /*
7144  Move slider.
7145  */
7146  if (event.xbutton.y < slider_info.y)
7147  slider_info.id-=(visible_entries-1);
7148  else
7149  slider_info.id+=(visible_entries-1);
7150  state|=RedrawListState;
7151  break;
7152  }
7153  if (MatteIsActive(list_info,event.xbutton))
7154  {
7155  int
7156  id;
7157 
7158  /*
7159  User pressed list matte.
7160  */
7161  id=slider_info.id+(event.xbutton.y-(list_info.y+(height >> 1))+1)/
7162  selection_info.height;
7163  if (id >= (int) entries)
7164