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