MagickCore  6.9.12-95
Convert, Edit, Or Compose Bitmap Images
 All Data Structures
widget.c
1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 % %
4 % %
5 % %
6 % %
7 % W W IIIII DDDD GGGG EEEEE TTTTT %
8 % W W I D D G E T %
9 % W W W I D D G GG EEE T %
10 % WW WW I D D G G E T %
11 % W W IIIII DDDD GGGG EEEEE T %
12 % %
13 % %
14 % MagickCore X11 User Interface Methods %
15 % %
16 % Software Design %
17 % Cristy %
18 % September 1993 %
19 % %
20 % %
21 % Copyright 1999 ImageMagick Studio LLC, a non-profit organization %
22 % dedicated to making software imaging solutions freely available. %
23 % %
24 % You may not use this file except in compliance with the License. You may %
25 % obtain a copy of the License at %
26 % %
27 % https://imagemagick.org/script/license.php %
28 % %
29 % Unless required by applicable law or agreed to in writing, software %
30 % distributed under the License is distributed on an "AS IS" BASIS, %
31 % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
32 % See the License for the specific language governing permissions and %
33 % limitations under the License. %
34 % %
35 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
36 %
37 %
38 */
39 
40 /*
41  Include declarations.
42 */
43 #include "magick/studio.h"
44 #include "magick/color.h"
45 #include "magick/color-private.h"
46 #include "magick/exception.h"
47 #include "magick/exception-private.h"
48 #include "magick/image.h"
49 #include "magick/magick.h"
50 #include "magick/memory_.h"
51 #include "magick/string_.h"
52 #include "magick/timer-private.h"
53 #include "magick/token.h"
54 #include "magick/utility.h"
55 #include "magick/xwindow-private.h"
56 #include "magick/widget.h"
57 
58 #if defined(MAGICKCORE_X11_DELEGATE)
59 DisableMSCWarning(4389)
60 DisableMSCWarning(4701)
61 
62 /*
63  Define declarations.
64 */
65 #define AreaIsActive(matte_info,position) ( \
66  ((position.y >= (int) (matte_info.y-matte_info.bevel_width)) && \
67  (position.y < (int) (matte_info.y+matte_info.height+matte_info.bevel_width))) \
68  ? MagickTrue : MagickFalse)
69 #define Extent(s) ((int) strlen(s))
70 #define MatteIsActive(matte_info,position) ( \
71  ((position.x >= (int) (matte_info.x-matte_info.bevel_width)) && \
72  (position.y >= (int) (matte_info.y-matte_info.bevel_width)) && \
73  (position.x < (int) (matte_info.x+matte_info.width+matte_info.bevel_width)) && \
74  (position.y < (int) (matte_info.y+matte_info.height+matte_info.bevel_width))) \
75  ? MagickTrue : MagickFalse)
76 #define MaxTextWidth ((unsigned int) (255*XTextWidth(font_info,"_",1)))
77 #define MinTextWidth (26*XTextWidth(font_info,"_",1))
78 #define QuantumMargin MagickMax(font_info->max_bounds.width,12)
79 #define WidgetTextWidth(font_info,text) \
80  ((unsigned int) XTextWidth(font_info,text,Extent(text)))
81 #define WindowIsActive(window_info,position) ( \
82  ((position.x >= 0) && (position.y >= 0) && \
83  (position.x < (int) window_info.width) && \
84  (position.y < (int) window_info.height)) ? MagickTrue : MagickFalse)
85 
86 /*
87  Enum declarations.
88 */
89 typedef enum
90 {
91  ControlState = 0x0001,
92  InactiveWidgetState = 0x0004,
93  JumpListState = 0x0008,
94  RedrawActionState = 0x0010,
95  RedrawListState = 0x0020,
96  RedrawWidgetState = 0x0040,
97  UpdateListState = 0x0100
98 } WidgetState;
99 
100 /*
101  Typedef declarations.
102 */
103 typedef struct _XWidgetInfo
104 {
105  char
106  *cursor,
107  *text,
108  *marker;
109 
110  int
111  id;
112 
113  unsigned int
114  bevel_width,
115  width,
116  height;
117 
118  int
119  x,
120  y,
121  min_y,
122  max_y;
123 
124  MagickStatusType
125  raised,
126  active,
127  center,
128  trough,
129  highlight;
130 } XWidgetInfo;
131 
132 /*
133  Variable declarations.
134 */
135 static XWidgetInfo
136  monitor_info =
137  {
138  (char *) NULL, (char *) NULL, (char *) NULL, 0, 0, 0, 0, 0, 0, 0, 0,
139  MagickFalse, MagickFalse, MagickFalse, MagickFalse, MagickFalse
140  },
141  submenu_info =
142  {
143  (char *) NULL, (char *) NULL, (char *) NULL, 0, 0, 0, 0, 0, 0, 0, 0,
144  MagickFalse, MagickFalse, MagickFalse, MagickFalse, MagickFalse
145  },
146  *selection_info = (XWidgetInfo *) NULL,
147  toggle_info =
148  {
149  (char *) NULL, (char *) NULL, (char *) NULL, 0, 0, 0, 0, 0, 0, 0, 0,
150  MagickFalse, MagickFalse, MagickFalse, MagickFalse, MagickFalse
151  };
152 
153 /*
154  Constant declarations.
155 */
156 static const int
157  BorderOffset = 4,
158  DoubleClick = 250;
159 
160 /*
161  Method prototypes.
162 */
163 static void
164  XDrawMatte(Display *,const XWindowInfo *,const XWidgetInfo *),
165  XSetBevelColor(Display *,const XWindowInfo *,const MagickStatusType),
166  XSetMatteColor(Display *,const XWindowInfo *,const MagickStatusType),
167  XSetTextColor(Display *,const XWindowInfo *,const MagickStatusType);
168 
169 /*
170 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
171 % %
172 % %
173 % %
174 % D e s t r o y X W i d g e t %
175 % %
176 % %
177 % %
178 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
179 %
180 % DestroyXWidget() destroys resources associated with the X widget.
181 %
182 % The format of the DestroyXWidget method is:
183 %
184 % void DestroyXWidget()
185 %
186 % A description of each parameter follows:
187 %
188 */
189 MagickExport void DestroyXWidget(void)
190 {
191  if (selection_info != (XWidgetInfo *) NULL)
192  selection_info=(XWidgetInfo *) RelinquishMagickMemory(selection_info);
193 }
194 
195 /*
196 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
197 % %
198 % %
199 % %
200 + X D r a w B e v e l %
201 % %
202 % %
203 % %
204 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
205 %
206 % XDrawBevel() "sets off" an area with a highlighted upper and left bevel and
207 % a shadowed lower and right bevel. The highlighted and shadowed bevels
208 % create a 3-D effect.
209 %
210 % The format of the XDrawBevel function is:
211 %
212 % XDrawBevel(display,window_info,bevel_info)
213 %
214 % A description of each parameter follows:
215 %
216 % o display: Specifies a pointer to the Display structure; returned from
217 % XOpenDisplay.
218 %
219 % o window_info: Specifies a pointer to a X11 XWindowInfo structure.
220 %
221 % o bevel_info: Specifies a pointer to a XWidgetInfo structure. It
222 % contains the extents of the bevel.
223 %
224 */
225 static void XDrawBevel(Display *display,const XWindowInfo *window_info,
226  const XWidgetInfo *bevel_info)
227 {
228  int
229  x1,
230  x2,
231  y1,
232  y2;
233 
234  unsigned int
235  bevel_width;
236 
237  XPoint
238  points[6];
239 
240  /*
241  Draw upper and left beveled border.
242  */
243  x1=bevel_info->x;
244  y1=bevel_info->y+bevel_info->height;
245  x2=bevel_info->x+bevel_info->width;
246  y2=bevel_info->y;
247  bevel_width=bevel_info->bevel_width;
248  points[0].x=x1;
249  points[0].y=y1;
250  points[1].x=x1;
251  points[1].y=y2;
252  points[2].x=x2;
253  points[2].y=y2;
254  points[3].x=x2+bevel_width;
255  points[3].y=y2-bevel_width;
256  points[4].x=x1-bevel_width;
257  points[4].y=y2-bevel_width;
258  points[5].x=x1-bevel_width;
259  points[5].y=y1+bevel_width;
260  XSetBevelColor(display,window_info,bevel_info->raised);
261  (void) XFillPolygon(display,window_info->id,window_info->widget_context,
262  points,6,Complex,CoordModeOrigin);
263  /*
264  Draw lower and right beveled border.
265  */
266  points[0].x=x1;
267  points[0].y=y1;
268  points[1].x=x2;
269  points[1].y=y1;
270  points[2].x=x2;
271  points[2].y=y2;
272  points[3].x=x2+bevel_width;
273  points[3].y=y2-bevel_width;
274  points[4].x=x2+bevel_width;
275  points[4].y=y1+bevel_width;
276  points[5].x=x1-bevel_width;
277  points[5].y=y1+bevel_width;
278  XSetBevelColor(display,window_info,!bevel_info->raised);
279  (void) XFillPolygon(display,window_info->id,window_info->widget_context,
280  points,6,Complex,CoordModeOrigin);
281  (void) XSetFillStyle(display,window_info->widget_context,FillSolid);
282 }
283 
284 /*
285 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
286 % %
287 % %
288 % %
289 + X D r a w B e v e l e d B u t t o n %
290 % %
291 % %
292 % %
293 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
294 %
295 % XDrawBeveledButton() draws a button with a highlighted upper and left bevel
296 % and a shadowed lower and right bevel. The highlighted and shadowed bevels
297 % create a 3-D effect.
298 %
299 % The format of the XDrawBeveledButton function is:
300 %
301 % XDrawBeveledButton(display,window_info,button_info)
302 %
303 % A description of each parameter follows:
304 %
305 % o display: Specifies a pointer to the Display structure; returned from
306 % XOpenDisplay.
307 %
308 % o window_info: Specifies a pointer to a X11 XWindowInfo structure.
309 %
310 % o button_info: Specifies a pointer to a XWidgetInfo structure. It
311 % contains the extents of the button.
312 %
313 */
314 static void XDrawBeveledButton(Display *display,const XWindowInfo *window_info,
315  const XWidgetInfo *button_info)
316 {
317  int
318  x,
319  y;
320 
321  unsigned int
322  width;
323 
324  XFontStruct
325  *font_info;
326 
327  XRectangle
328  crop_info;
329 
330  /*
331  Draw matte.
332  */
333  XDrawBevel(display,window_info,button_info);
334  XSetMatteColor(display,window_info,button_info->raised);
335  (void) XFillRectangle(display,window_info->id,window_info->widget_context,
336  button_info->x,button_info->y,button_info->width,button_info->height);
337  x=button_info->x-button_info->bevel_width-1;
338  y=button_info->y-button_info->bevel_width-1;
339  (void) XSetForeground(display,window_info->widget_context,
340  window_info->pixel_info->trough_color.pixel);
341  if (button_info->raised || (window_info->depth == 1))
342  (void) XDrawRectangle(display,window_info->id,window_info->widget_context,
343  x,y,button_info->width+(button_info->bevel_width << 1)+1,
344  button_info->height+(button_info->bevel_width << 1)+1);
345  if (button_info->text == (char *) NULL)
346  return;
347  /*
348  Set cropping region.
349  */
350  crop_info.width=(unsigned short) button_info->width;
351  crop_info.height=(unsigned short) button_info->height;
352  crop_info.x=button_info->x;
353  crop_info.y=button_info->y;
354  /*
355  Draw text.
356  */
357  font_info=window_info->font_info;
358  width=WidgetTextWidth(font_info,button_info->text);
359  x=button_info->x+(QuantumMargin >> 1);
360  if (button_info->center)
361  x=button_info->x+(button_info->width >> 1)-(width >> 1);
362  y=button_info->y+((button_info->height-
363  (font_info->ascent+font_info->descent)) >> 1)+font_info->ascent;
364  if ((int) button_info->width == (QuantumMargin >> 1))
365  {
366  /*
367  Option button-- write label to right of button.
368  */
369  XSetTextColor(display,window_info,MagickTrue);
370  x=button_info->x+button_info->width+button_info->bevel_width+
371  (QuantumMargin >> 1);
372  (void) XDrawString(display,window_info->id,window_info->widget_context,
373  x,y,button_info->text,Extent(button_info->text));
374  return;
375  }
376  (void) XSetClipRectangles(display,window_info->widget_context,0,0,&crop_info,
377  1,Unsorted);
378  XSetTextColor(display,window_info,button_info->raised);
379  (void) XDrawString(display,window_info->id,window_info->widget_context,x,y,
380  button_info->text,Extent(button_info->text));
381  (void) XSetClipMask(display,window_info->widget_context,None);
382  if (button_info->raised == MagickFalse)
383  XDelay(display,SuspendTime << 2);
384 }
385 
386 /*
387 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
388 % %
389 % %
390 % %
391 + X D r a w B e v e l e d M a t t e %
392 % %
393 % %
394 % %
395 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
396 %
397 % XDrawBeveledMatte() draws a matte with a shadowed upper and left bevel and
398 % a highlighted lower and right bevel. The highlighted and shadowed bevels
399 % create a 3-D effect.
400 %
401 % The format of the XDrawBeveledMatte function is:
402 %
403 % XDrawBeveledMatte(display,window_info,matte_info)
404 %
405 % A description of each parameter follows:
406 %
407 % o display: Specifies a pointer to the Display structure; returned from
408 % XOpenDisplay.
409 %
410 % o window_info: Specifies a pointer to a X11 XWindowInfo structure.
411 %
412 % o matte_info: Specifies a pointer to a XWidgetInfo structure. It
413 % contains the extents of the matte.
414 %
415 */
416 static void XDrawBeveledMatte(Display *display,const XWindowInfo *window_info,
417  const XWidgetInfo *matte_info)
418 {
419  /*
420  Draw matte.
421  */
422  XDrawBevel(display,window_info,matte_info);
423  XDrawMatte(display,window_info,matte_info);
424 }
425 
426 /*
427 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
428 % %
429 % %
430 % %
431 + X D r a w M a t t e %
432 % %
433 % %
434 % %
435 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
436 %
437 % XDrawMatte() fills a rectangular area with the matte color.
438 %
439 % The format of the XDrawMatte function is:
440 %
441 % XDrawMatte(display,window_info,matte_info)
442 %
443 % A description of each parameter follows:
444 %
445 % o display: Specifies a pointer to the Display structure; returned from
446 % XOpenDisplay.
447 %
448 % o window_info: Specifies a pointer to a X11 XWindowInfo structure.
449 %
450 % o matte_info: Specifies a pointer to a XWidgetInfo structure. It
451 % contains the extents of the matte.
452 %
453 */
454 static void XDrawMatte(Display *display,const XWindowInfo *window_info,
455  const XWidgetInfo *matte_info)
456 {
457  /*
458  Draw matte.
459  */
460  if ((matte_info->trough == MagickFalse) || (window_info->depth == 1))
461  (void) XFillRectangle(display,window_info->id,
462  window_info->highlight_context,matte_info->x,matte_info->y,
463  matte_info->width,matte_info->height);
464  else
465  {
466  (void) XSetForeground(display,window_info->widget_context,
467  window_info->pixel_info->trough_color.pixel);
468  (void) XFillRectangle(display,window_info->id,window_info->widget_context,
469  matte_info->x,matte_info->y,matte_info->width,matte_info->height);
470  }
471 }
472 
473 /*
474 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
475 % %
476 % %
477 % %
478 + X D r a w M a t t e T e x t %
479 % %
480 % %
481 % %
482 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
483 %
484 % XDrawMatteText() draws a matte with text. If the text exceeds the extents
485 % of the text, a portion of the text relative to the cursor is displayed.
486 %
487 % The format of the XDrawMatteText function is:
488 %
489 % XDrawMatteText(display,window_info,text_info)
490 %
491 % A description of each parameter follows:
492 %
493 % o display: Specifies a pointer to the Display structure; returned from
494 % XOpenDisplay.
495 %
496 % o window_info: Specifies a pointer to a X11 XWindowInfo structure.
497 %
498 % o text_info: Specifies a pointer to a XWidgetInfo structure. It
499 % contains the extents of the text.
500 %
501 */
502 static void XDrawMatteText(Display *display,const XWindowInfo *window_info,
503  XWidgetInfo *text_info)
504 {
505  const char
506  *text;
507 
508  int
509  n,
510  x,
511  y;
512 
513  int
514  i;
515 
516  unsigned int
517  height,
518  width;
519 
520  XFontStruct
521  *font_info;
522 
523  XRectangle
524  crop_info;
525 
526  /*
527  Clear the text area.
528  */
529  XSetMatteColor(display,window_info,MagickFalse);
530  (void) XFillRectangle(display,window_info->id,window_info->widget_context,
531  text_info->x,text_info->y,text_info->width,text_info->height);
532  if (text_info->text == (char *) NULL)
533  return;
534  XSetTextColor(display,window_info,text_info->highlight);
535  font_info=window_info->font_info;
536  x=text_info->x+(QuantumMargin >> 2);
537  y=text_info->y+font_info->ascent+(text_info->height >> 2);
538  width=text_info->width-(QuantumMargin >> 1);
539  height=(unsigned int) (font_info->ascent+font_info->descent);
540  if (*text_info->text == '\0')
541  {
542  /*
543  No text-- just draw cursor.
544  */
545  (void) XDrawLine(display,window_info->id,window_info->annotate_context,
546  x,y+3,x,y-height+3);
547  return;
548  }
549  /*
550  Set cropping region.
551  */
552  crop_info.width=(unsigned short) text_info->width;
553  crop_info.height=(unsigned short) text_info->height;
554  crop_info.x=text_info->x;
555  crop_info.y=text_info->y;
556  /*
557  Determine beginning of the visible text.
558  */
559  if (text_info->cursor < text_info->marker)
560  text_info->marker=text_info->cursor;
561  else
562  {
563  text=text_info->marker;
564  if (XTextWidth(font_info,(char *) text,(int) (text_info->cursor-text)) >
565  (int) width)
566  {
567  text=text_info->text;
568  for (i=0; i < Extent(text); i++)
569  {
570  n=XTextWidth(font_info,(char *) text+i,(int)
571  (text_info->cursor-text-i));
572  if (n <= (int) width)
573  break;
574  }
575  text_info->marker=(char *) text+i;
576  }
577  }
578  /*
579  Draw text and cursor.
580  */
581  if (text_info->highlight == MagickFalse)
582  {
583  (void) XSetClipRectangles(display,window_info->widget_context,0,0,
584  &crop_info,1,Unsorted);
585  (void) XDrawString(display,window_info->id,window_info->widget_context,
586  x,y,text_info->marker,Extent(text_info->marker));
587  (void) XSetClipMask(display,window_info->widget_context,None);
588  }
589  else
590  {
591  (void) XSetClipRectangles(display,window_info->annotate_context,0,0,
592  &crop_info,1,Unsorted);
593  width=WidgetTextWidth(font_info,text_info->marker);
594  (void) XFillRectangle(display,window_info->id,
595  window_info->annotate_context,x,y-font_info->ascent,width,height);
596  (void) XSetClipMask(display,window_info->annotate_context,None);
597  (void) XSetClipRectangles(display,window_info->highlight_context,0,0,
598  &crop_info,1,Unsorted);
599  (void) XDrawString(display,window_info->id,
600  window_info->highlight_context,x,y,text_info->marker,
601  Extent(text_info->marker));
602  (void) XSetClipMask(display,window_info->highlight_context,None);
603  }
604  x+=XTextWidth(font_info,text_info->marker,(int)
605  (text_info->cursor-text_info->marker));
606  (void) XDrawLine(display,window_info->id,window_info->annotate_context,x,y+3,
607  x,y-height+3);
608 }
609 
610 /*
611 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
612 % %
613 % %
614 % %
615 + X D r a w T r i a n g l e E a s t %
616 % %
617 % %
618 % %
619 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
620 %
621 % XDrawTriangleEast() draws a triangle with a highlighted left bevel and a
622 % shadowed right and lower bevel. The highlighted and shadowed bevels create
623 % a 3-D effect.
624 %
625 % The format of the XDrawTriangleEast function is:
626 %
627 % XDrawTriangleEast(display,window_info,triangle_info)
628 %
629 % A description of each parameter follows:
630 %
631 % o display: Specifies a pointer to the Display structure; returned from
632 % XOpenDisplay.
633 %
634 % o window_info: Specifies a pointer to a X11 XWindowInfo structure.
635 %
636 % o triangle_info: Specifies a pointer to a XWidgetInfo structure. It
637 % contains the extents of the triangle.
638 %
639 */
640 static void XDrawTriangleEast(Display *display,const XWindowInfo *window_info,
641  const XWidgetInfo *triangle_info)
642 {
643  int
644  x1,
645  x2,
646  x3,
647  y1,
648  y2,
649  y3;
650 
651  unsigned int
652  bevel_width;
653 
654  XFontStruct
655  *font_info;
656 
657  XPoint
658  points[4];
659 
660  /*
661  Draw triangle matte.
662  */
663  x1=triangle_info->x;
664  y1=triangle_info->y;
665  x2=triangle_info->x+triangle_info->width;
666  y2=triangle_info->y+(triangle_info->height >> 1);
667  x3=triangle_info->x;
668  y3=triangle_info->y+triangle_info->height;
669  bevel_width=triangle_info->bevel_width;
670  points[0].x=x1;
671  points[0].y=y1;
672  points[1].x=x2;
673  points[1].y=y2;
674  points[2].x=x3;
675  points[2].y=y3;
676  XSetMatteColor(display,window_info,triangle_info->raised);
677  (void) XFillPolygon(display,window_info->id,window_info->widget_context,
678  points,3,Complex,CoordModeOrigin);
679  /*
680  Draw bottom bevel.
681  */
682  points[0].x=x2;
683  points[0].y=y2;
684  points[1].x=x3;
685  points[1].y=y3;
686  points[2].x=x3-bevel_width;
687  points[2].y=y3+bevel_width;
688  points[3].x=x2+bevel_width;
689  points[3].y=y2;
690  XSetBevelColor(display,window_info,!triangle_info->raised);
691  (void) XFillPolygon(display,window_info->id,window_info->widget_context,
692  points,4,Complex,CoordModeOrigin);
693  /*
694  Draw Left bevel.
695  */
696  points[0].x=x3;
697  points[0].y=y3;
698  points[1].x=x1;
699  points[1].y=y1;
700  points[2].x=x1-bevel_width+1;
701  points[2].y=y1-bevel_width;
702  points[3].x=x3-bevel_width+1;
703  points[3].y=y3+bevel_width;
704  XSetBevelColor(display,window_info,triangle_info->raised);
705  (void) XFillPolygon(display,window_info->id,window_info->widget_context,
706  points,4,Complex,CoordModeOrigin);
707  /*
708  Draw top bevel.
709  */
710  points[0].x=x1;
711  points[0].y=y1;
712  points[1].x=x2;
713  points[1].y=y2;
714  points[2].x=x2+bevel_width;
715  points[2].y=y2;
716  points[3].x=x1-bevel_width;
717  points[3].y=y1-bevel_width;
718  (void) XFillPolygon(display,window_info->id,window_info->widget_context,
719  points,4,Complex,CoordModeOrigin);
720  (void) XSetFillStyle(display,window_info->widget_context,FillSolid);
721  if (triangle_info->text == (char *) NULL)
722  return;
723  /*
724  Write label to right of triangle.
725  */
726  font_info=window_info->font_info;
727  XSetTextColor(display,window_info,MagickTrue);
728  x1=triangle_info->x+triangle_info->width+triangle_info->bevel_width+
729  (QuantumMargin >> 1);
730  y1=triangle_info->y+((triangle_info->height-
731  (font_info->ascent+font_info->descent)) >> 1)+font_info->ascent;
732  (void) XDrawString(display,window_info->id,window_info->widget_context,x1,y1,
733  triangle_info->text,Extent(triangle_info->text));
734 }
735 
736 /*
737 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
738 % %
739 % %
740 % %
741 + X D r a w T r i a n g l e N o r t h %
742 % %
743 % %
744 % %
745 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
746 %
747 % XDrawTriangleNorth() draws a triangle with a highlighted left bevel and a
748 % shadowed right and lower bevel. The highlighted and shadowed bevels create
749 % a 3-D effect.
750 %
751 % The format of the XDrawTriangleNorth function is:
752 %
753 % XDrawTriangleNorth(display,window_info,triangle_info)
754 %
755 % A description of each parameter follows:
756 %
757 % o display: Specifies a pointer to the Display structure; returned from
758 % XOpenDisplay.
759 %
760 % o window_info: Specifies a pointer to a X11 XWindowInfo structure.
761 %
762 % o triangle_info: Specifies a pointer to a XWidgetInfo structure. It
763 % contains the extents of the triangle.
764 %
765 */
766 static void XDrawTriangleNorth(Display *display,const XWindowInfo *window_info,
767  const XWidgetInfo *triangle_info)
768 {
769  int
770  x1,
771  x2,
772  x3,
773  y1,
774  y2,
775  y3;
776 
777  unsigned int
778  bevel_width;
779 
780  XPoint
781  points[4];
782 
783  /*
784  Draw triangle matte.
785  */
786  x1=triangle_info->x;
787  y1=triangle_info->y+triangle_info->height;
788  x2=triangle_info->x+(triangle_info->width >> 1);
789  y2=triangle_info->y;
790  x3=triangle_info->x+triangle_info->width;
791  y3=triangle_info->y+triangle_info->height;
792  bevel_width=triangle_info->bevel_width;
793  points[0].x=x1;
794  points[0].y=y1;
795  points[1].x=x2;
796  points[1].y=y2;
797  points[2].x=x3;
798  points[2].y=y3;
799  XSetMatteColor(display,window_info,triangle_info->raised);
800  (void) XFillPolygon(display,window_info->id,window_info->widget_context,
801  points,3,Complex,CoordModeOrigin);
802  /*
803  Draw left bevel.
804  */
805  points[0].x=x1;
806  points[0].y=y1;
807  points[1].x=x2;
808  points[1].y=y2;
809  points[2].x=x2;
810  points[2].y=y2-bevel_width-2;
811  points[3].x=x1-bevel_width-1;
812  points[3].y=y1+bevel_width;
813  XSetBevelColor(display,window_info,triangle_info->raised);
814  (void) XFillPolygon(display,window_info->id,window_info->widget_context,
815  points,4,Complex,CoordModeOrigin);
816  /*
817  Draw right bevel.
818  */
819  points[0].x=x2;
820  points[0].y=y2;
821  points[1].x=x3;
822  points[1].y=y3;
823  points[2].x=x3+bevel_width;
824  points[2].y=y3+bevel_width;
825  points[3].x=x2;
826  points[3].y=y2-bevel_width;
827  XSetBevelColor(display,window_info,!triangle_info->raised);
828  (void) XFillPolygon(display,window_info->id,window_info->widget_context,
829  points,4,Complex,CoordModeOrigin);
830  /*
831  Draw lower bevel.
832  */
833  points[0].x=x3;
834  points[0].y=y3;
835  points[1].x=x1;
836  points[1].y=y1;
837  points[2].x=x1-bevel_width;
838  points[2].y=y1+bevel_width;
839  points[3].x=x3+bevel_width;
840  points[3].y=y3+bevel_width;
841  (void) XFillPolygon(display,window_info->id,window_info->widget_context,
842  points,4,Complex,CoordModeOrigin);
843  (void) XSetFillStyle(display,window_info->widget_context,FillSolid);
844 }
845 
846 /*
847 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
848 % %
849 % %
850 % %
851 + X D r a w T r i a n g l e S o u t h %
852 % %
853 % %
854 % %
855 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
856 %
857 % XDrawTriangleSouth() draws a border with a highlighted left and right bevel
858 % and a shadowed lower bevel. The highlighted and shadowed bevels create a
859 % 3-D effect.
860 %
861 % The format of the XDrawTriangleSouth function is:
862 %
863 % XDrawTriangleSouth(display,window_info,triangle_info)
864 %
865 % A description of each parameter follows:
866 %
867 % o display: Specifies a pointer to the Display structure; returned from
868 % XOpenDisplay.
869 %
870 % o window_info: Specifies a pointer to a X11 XWindowInfo structure.
871 %
872 % o triangle_info: Specifies a pointer to a XWidgetInfo structure. It
873 % contains the extents of the triangle.
874 %
875 */
876 static void XDrawTriangleSouth(Display *display,const XWindowInfo *window_info,
877  const XWidgetInfo *triangle_info)
878 {
879  int
880  x1,
881  x2,
882  x3,
883  y1,
884  y2,
885  y3;
886 
887  unsigned int
888  bevel_width;
889 
890  XPoint
891  points[4];
892 
893  /*
894  Draw triangle matte.
895  */
896  x1=triangle_info->x;
897  y1=triangle_info->y;
898  x2=triangle_info->x+(triangle_info->width >> 1);
899  y2=triangle_info->y+triangle_info->height;
900  x3=triangle_info->x+triangle_info->width;
901  y3=triangle_info->y;
902  bevel_width=triangle_info->bevel_width;
903  points[0].x=x1;
904  points[0].y=y1;
905  points[1].x=x2;
906  points[1].y=y2;
907  points[2].x=x3;
908  points[2].y=y3;
909  XSetMatteColor(display,window_info,triangle_info->raised);
910  (void) XFillPolygon(display,window_info->id,window_info->widget_context,
911  points,3,Complex,CoordModeOrigin);
912  /*
913  Draw top bevel.
914  */
915  points[0].x=x3;
916  points[0].y=y3;
917  points[1].x=x1;
918  points[1].y=y1;
919  points[2].x=x1-bevel_width;
920  points[2].y=y1-bevel_width;
921  points[3].x=x3+bevel_width;
922  points[3].y=y3-bevel_width;
923  XSetBevelColor(display,window_info,triangle_info->raised);
924  (void) XFillPolygon(display,window_info->id,window_info->widget_context,
925  points,4,Complex,CoordModeOrigin);
926  /*
927  Draw right bevel.
928  */
929  points[0].x=x2;
930  points[0].y=y2;
931  points[1].x=x3+1;
932  points[1].y=y3-bevel_width;
933  points[2].x=x3+bevel_width;
934  points[2].y=y3-bevel_width;
935  points[3].x=x2;
936  points[3].y=y2+bevel_width;
937  XSetBevelColor(display,window_info,!triangle_info->raised);
938  (void) XFillPolygon(display,window_info->id,window_info->widget_context,
939  points,4,Complex,CoordModeOrigin);
940  /*
941  Draw left bevel.
942  */
943  points[0].x=x1;
944  points[0].y=y1;
945  points[1].x=x2;
946  points[1].y=y2;
947  points[2].x=x2;
948  points[2].y=y2+bevel_width;
949  points[3].x=x1-bevel_width;
950  points[3].y=y1-bevel_width;
951  XSetBevelColor(display,window_info,triangle_info->raised);
952  (void) XFillPolygon(display,window_info->id,window_info->widget_context,
953  points,4,Complex,CoordModeOrigin);
954  (void) XSetFillStyle(display,window_info->widget_context,FillSolid);
955 }
956 
957 /*
958 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
959 % %
960 % %
961 % %
962 + X D r a w W i d g e t T e x t %
963 % %
964 % %
965 % %
966 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
967 %
968 % XDrawWidgetText() first clears the widget and draws a text string justified
969 % left (or center) in the x-direction and centered within the y-direction.
970 %
971 % The format of the XDrawWidgetText function is:
972 %
973 % XDrawWidgetText(display,window_info,text_info)
974 %
975 % A description of each parameter follows:
976 %
977 % o display: Specifies a pointer to the Display structure; returned from
978 % XOpenDisplay.
979 %
980 % o window_info: Specifies a pointer to a XWindowText structure.
981 %
982 % o text_info: Specifies a pointer to XWidgetInfo structure.
983 %
984 */
985 static void XDrawWidgetText(Display *display,const XWindowInfo *window_info,
986  XWidgetInfo *text_info)
987 {
988  GC
989  widget_context;
990 
991  int
992  x,
993  y;
994 
995  unsigned int
996  height,
997  width;
998 
999  XFontStruct
1000  *font_info;
1001 
1002  XRectangle
1003  crop_info;
1004 
1005  /*
1006  Clear the text area.
1007  */
1008  widget_context=window_info->annotate_context;
1009  if (text_info->raised)
1010  (void) XClearArea(display,window_info->id,text_info->x,text_info->y,
1011  text_info->width,text_info->height,MagickFalse);
1012  else
1013  {
1014  (void) XFillRectangle(display,window_info->id,widget_context,text_info->x,
1015  text_info->y,text_info->width,text_info->height);
1016  widget_context=window_info->highlight_context;
1017  }
1018  if (text_info->text == (char *) NULL)
1019  return;
1020  if (*text_info->text == '\0')
1021  return;
1022  /*
1023  Set cropping region.
1024  */
1025  font_info=window_info->font_info;
1026  crop_info.width=(unsigned short) text_info->width;
1027  crop_info.height=(unsigned short) text_info->height;
1028  crop_info.x=text_info->x;
1029  crop_info.y=text_info->y;
1030  /*
1031  Draw text.
1032  */
1033  width=WidgetTextWidth(font_info,text_info->text);
1034  x=text_info->x+(QuantumMargin >> 1);
1035  if (text_info->center)
1036  x=text_info->x+(text_info->width >> 1)-(width >> 1);
1037  if (text_info->raised)
1038  if (width > (text_info->width-QuantumMargin))
1039  x+=(text_info->width-QuantumMargin-width);
1040  height=(unsigned int) (font_info->ascent+font_info->descent);
1041  y=text_info->y+((text_info->height-height) >> 1)+font_info->ascent;
1042  (void) XSetClipRectangles(display,widget_context,0,0,&crop_info,1,Unsorted);
1043  (void) XDrawString(display,window_info->id,widget_context,x,y,text_info->text,
1044  Extent(text_info->text));
1045  (void) XSetClipMask(display,widget_context,None);
1046  if (x < text_info->x)
1047  (void) XDrawLine(display,window_info->id,window_info->annotate_context,
1048  text_info->x,text_info->y,text_info->x,text_info->y+text_info->height-1);
1049 }
1050 
1051 /*
1052 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1053 % %
1054 % %
1055 % %
1056 + X E d i t T e x t %
1057 % %
1058 % %
1059 % %
1060 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1061 %
1062 % XEditText() edits a text string as indicated by the key symbol.
1063 %
1064 % The format of the XEditText function is:
1065 %
1066 % XEditText(display,text_info,key_symbol,text,state)
1067 %
1068 % A description of each parameter follows:
1069 %
1070 % o display: Specifies a connection to an X server; returned from
1071 % XOpenDisplay.
1072 %
1073 % o text_info: Specifies a pointer to a XWidgetInfo structure. It
1074 % contains the extents of the text.
1075 %
1076 % o key_symbol: A X11 KeySym that indicates what editing function to
1077 % perform to the text.
1078 %
1079 % o text: A character string to insert into the text.
1080 %
1081 % o state: An size_t that indicates whether the key symbol is a
1082 % control character or not.
1083 %
1084 */
1085 static void XEditText(Display *display,XWidgetInfo *text_info,
1086  const KeySym key_symbol,char *text,const size_t state)
1087 {
1088  switch ((int) key_symbol)
1089  {
1090  case XK_BackSpace:
1091  case XK_Delete:
1092  {
1093  if (text_info->highlight)
1094  {
1095  /*
1096  Erase the entire line of text.
1097  */
1098  *text_info->text='\0';
1099  text_info->cursor=text_info->text;
1100  text_info->marker=text_info->text;
1101  text_info->highlight=MagickFalse;
1102  }
1103  /*
1104  Erase one character.
1105  */
1106  if (text_info->cursor != text_info->text)
1107  {
1108  text_info->cursor--;
1109  (void) memmove(text_info->cursor,text_info->cursor+1,
1110  strlen(text_info->cursor+1)+1);
1111  text_info->highlight=MagickFalse;
1112  break;
1113  }
1114  magick_fallthrough;
1115  }
1116  case XK_Left:
1117  case XK_KP_Left:
1118  {
1119  /*
1120  Move cursor one position left.
1121  */
1122  if (text_info->cursor == text_info->text)
1123  break;
1124  text_info->cursor--;
1125  break;
1126  }
1127  case XK_Right:
1128  case XK_KP_Right:
1129  {
1130  /*
1131  Move cursor one position right.
1132  */
1133  if (text_info->cursor == (text_info->text+Extent(text_info->text)))
1134  break;
1135  text_info->cursor++;
1136  break;
1137  }
1138  default:
1139  {
1140  char
1141  *p,
1142  *q;
1143 
1144  int
1145  i;
1146 
1147  if (state & ControlState)
1148  break;
1149  if (*text == '\0')
1150  break;
1151  if ((Extent(text_info->text)+1) >= (int) MaxTextExtent)
1152  (void) XBell(display,0);
1153  else
1154  {
1155  if (text_info->highlight)
1156  {
1157  /*
1158  Erase the entire line of text.
1159  */
1160  *text_info->text='\0';
1161  text_info->cursor=text_info->text;
1162  text_info->marker=text_info->text;
1163  text_info->highlight=MagickFalse;
1164  }
1165  /*
1166  Insert a string into the text.
1167  */
1168  q=text_info->text+Extent(text_info->text)+strlen(text);
1169  for (i=0; i <= Extent(text_info->cursor); i++)
1170  {
1171  *q=(*(q-Extent(text)));
1172  q--;
1173  }
1174  p=text;
1175  for (i=0; i < Extent(text); i++)
1176  *text_info->cursor++=(*p++);
1177  }
1178  break;
1179  }
1180  }
1181 }
1182 
1183 /*
1184 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1185 % %
1186 % %
1187 % %
1188 + X G e t W i d g e t I n f o %
1189 % %
1190 % %
1191 % %
1192 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1193 %
1194 % XGetWidgetInfo() initializes the XWidgetInfo structure.
1195 %
1196 % The format of the XGetWidgetInfo function is:
1197 %
1198 % XGetWidgetInfo(text,widget_info)
1199 %
1200 % A description of each parameter follows:
1201 %
1202 % o text: A string of characters associated with the widget.
1203 %
1204 % o widget_info: Specifies a pointer to a X11 XWidgetInfo structure.
1205 %
1206 */
1207 static void XGetWidgetInfo(const char *text,XWidgetInfo *widget_info)
1208 {
1209  /*
1210  Initialize widget info.
1211  */
1212  widget_info->id=(~0);
1213  widget_info->bevel_width=3;
1214  widget_info->width=1;
1215  widget_info->height=1;
1216  widget_info->x=0;
1217  widget_info->y=0;
1218  widget_info->min_y=0;
1219  widget_info->max_y=0;
1220  widget_info->raised=MagickTrue;
1221  widget_info->active=MagickFalse;
1222  widget_info->center=MagickTrue;
1223  widget_info->trough=MagickFalse;
1224  widget_info->highlight=MagickFalse;
1225  widget_info->text=(char *) text;
1226  widget_info->cursor=(char *) text;
1227  if (text != (char *) NULL)
1228  widget_info->cursor+=Extent(text);
1229  widget_info->marker=(char *) text;
1230 }
1231 
1232 /*
1233 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1234 % %
1235 % %
1236 % %
1237 + X H i g h l i g h t W i d g e t %
1238 % %
1239 % %
1240 % %
1241 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1242 %
1243 % XHighlightWidget() draws a highlighted border around a window.
1244 %
1245 % The format of the XHighlightWidget function is:
1246 %
1247 % XHighlightWidget(display,window_info,x,y)
1248 %
1249 % A description of each parameter follows:
1250 %
1251 % o display: Specifies a pointer to the Display structure; returned from
1252 % XOpenDisplay.
1253 %
1254 % o window_info: Specifies a pointer to a X11 XWindowInfo structure.
1255 %
1256 % o x: Specifies an integer representing the rectangle offset in the
1257 % x-direction.
1258 %
1259 % o y: Specifies an integer representing the rectangle offset in the
1260 % y-direction.
1261 %
1262 */
1263 static void XHighlightWidget(Display *display,const XWindowInfo *window_info,
1264  const int x,const int y)
1265 {
1266  /*
1267  Draw the widget highlighting rectangle.
1268  */
1269  XSetBevelColor(display,window_info,MagickTrue);
1270  (void) XDrawRectangle(display,window_info->id,window_info->widget_context,x,y,
1271  window_info->width-(x << 1),window_info->height-(y << 1));
1272  (void) XDrawRectangle(display,window_info->id,window_info->widget_context,
1273  x-1,y-1,window_info->width-(x << 1)+1,window_info->height-(y << 1)+1);
1274  XSetBevelColor(display,window_info,MagickFalse);
1275  (void) XDrawRectangle(display,window_info->id,window_info->widget_context,
1276  x-1,y-1,window_info->width-(x << 1),window_info->height-(y << 1));
1277  (void) XSetFillStyle(display,window_info->widget_context,FillSolid);
1278 }
1279 
1280 /*
1281 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1282 % %
1283 % %
1284 % %
1285 + X S c r e e n E v e n t %
1286 % %
1287 % %
1288 % %
1289 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1290 %
1291 % XScreenEvent() returns MagickTrue if the any event on the X server queue is
1292 % associated with the widget window.
1293 %
1294 % The format of the XScreenEvent function is:
1295 %
1296 % int XScreenEvent(Display *display,XEvent *event,char *data)
1297 %
1298 % A description of each parameter follows:
1299 %
1300 % o display: Specifies a pointer to the Display structure; returned from
1301 % XOpenDisplay.
1302 %
1303 % o event: Specifies a pointer to a X11 XEvent structure.
1304 %
1305 % o data: Specifies a pointer to a XWindows structure.
1306 %
1307 */
1308 
1309 #if defined(__cplusplus) || defined(c_plusplus)
1310 extern "C" {
1311 #endif
1312 
1313 static int XScreenEvent(Display *display,XEvent *event,char *data)
1314 {
1315  XWindows
1316  *windows;
1317 
1318  windows=(XWindows *) data;
1319  if (event->xany.window == windows->popup.id)
1320  {
1321  if (event->type == MapNotify)
1322  windows->popup.mapped=MagickTrue;
1323  if (event->type == UnmapNotify)
1324  windows->popup.mapped=MagickFalse;
1325  return(MagickTrue);
1326  }
1327  if (event->xany.window == windows->widget.id)
1328  {
1329  if (event->type == MapNotify)
1330  windows->widget.mapped=MagickTrue;
1331  if (event->type == UnmapNotify)
1332  windows->widget.mapped=MagickFalse;
1333  return(MagickTrue);
1334  }
1335  switch (event->type)
1336  {
1337  case ButtonPress:
1338  {
1339  if ((event->xbutton.button == Button3) &&
1340  (event->xbutton.state & Mod1Mask))
1341  {
1342  /*
1343  Convert Alt-Button3 to Button2.
1344  */
1345  event->xbutton.button=Button2;
1346  event->xbutton.state&=(~Mod1Mask);
1347  }
1348  return(MagickTrue);
1349  }
1350  case Expose:
1351  {
1352  if (event->xexpose.window == windows->image.id)
1353  {
1354  XRefreshWindow(display,&windows->image,event);
1355  break;
1356  }
1357  if (event->xexpose.window == windows->magnify.id)
1358  if (event->xexpose.count == 0)
1359  if (windows->magnify.mapped)
1360  {
1361  XMakeMagnifyImage(display,windows);
1362  break;
1363  }
1364  if (event->xexpose.window == windows->command.id)
1365  if (event->xexpose.count == 0)
1366  {
1367  (void) XCommandWidget(display,windows,(const char *const *) NULL,
1368  event);
1369  break;
1370  }
1371  break;
1372  }
1373  case FocusOut:
1374  {
1375  /*
1376  Set input focus for backdrop window.
1377  */
1378  if (event->xfocus.window == windows->image.id)
1379  (void) XSetInputFocus(display,windows->image.id,RevertToNone,
1380  CurrentTime);
1381  return(MagickTrue);
1382  }
1383  case ButtonRelease:
1384  case KeyPress:
1385  case KeyRelease:
1386  case MotionNotify:
1387  case SelectionNotify:
1388  return(MagickTrue);
1389  default:
1390  break;
1391  }
1392  return(MagickFalse);
1393 }
1394 
1395 #if defined(__cplusplus) || defined(c_plusplus)
1396 }
1397 #endif
1398 
1399 /*
1400 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1401 % %
1402 % %
1403 % %
1404 + X S e t B e v e l C o l o r %
1405 % %
1406 % %
1407 % %
1408 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1409 %
1410 % XSetBevelColor() sets the graphic context for drawing a beveled border.
1411 %
1412 % The format of the XSetBevelColor function is:
1413 %
1414 % XSetBevelColor(display,window_info,raised)
1415 %
1416 % A description of each parameter follows:
1417 %
1418 % o display: Specifies a pointer to the Display structure; returned from
1419 % XOpenDisplay.
1420 %
1421 % o window_info: Specifies a pointer to a X11 XWindowInfo structure.
1422 %
1423 % o raised: A value other than zero indicates the color show be a
1424 % "highlight" color, otherwise the "shadow" color is set.
1425 %
1426 */
1427 static void XSetBevelColor(Display *display,const XWindowInfo *window_info,
1428  const MagickStatusType raised)
1429 {
1430  if (window_info->depth == 1)
1431  {
1432  Pixmap
1433  stipple;
1434 
1435  /*
1436  Monochrome window.
1437  */
1438  (void) XSetBackground(display,window_info->widget_context,
1439  XBlackPixel(display,window_info->screen));
1440  (void) XSetForeground(display,window_info->widget_context,
1441  XWhitePixel(display,window_info->screen));
1442  (void) XSetFillStyle(display,window_info->widget_context,
1443  FillOpaqueStippled);
1444  stipple=window_info->highlight_stipple;
1445  if (raised == MagickFalse)
1446  stipple=window_info->shadow_stipple;
1447  (void) XSetStipple(display,window_info->widget_context,stipple);
1448  }
1449  else
1450  if (raised)
1451  (void) XSetForeground(display,window_info->widget_context,
1452  window_info->pixel_info->highlight_color.pixel);
1453  else
1454  (void) XSetForeground(display,window_info->widget_context,
1455  window_info->pixel_info->shadow_color.pixel);
1456 }
1457 
1458 /*
1459 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1460 % %
1461 % %
1462 % %
1463 + X S e t M a t t e C o l o r %
1464 % %
1465 % %
1466 % %
1467 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1468 %
1469 % XSetMatteColor() sets the graphic context for drawing the matte.
1470 %
1471 % The format of the XSetMatteColor function is:
1472 %
1473 % XSetMatteColor(display,window_info,raised)
1474 %
1475 % A description of each parameter follows:
1476 %
1477 % o display: Specifies a pointer to the Display structure; returned from
1478 % XOpenDisplay.
1479 %
1480 % o window_info: Specifies a pointer to a X11 XWindowInfo structure.
1481 %
1482 % o raised: A value other than zero indicates the matte is active.
1483 %
1484 */
1485 static void XSetMatteColor(Display *display,const XWindowInfo *window_info,
1486  const MagickStatusType raised)
1487 {
1488  if (window_info->depth == 1)
1489  {
1490  /*
1491  Monochrome window.
1492  */
1493  if (raised)
1494  (void) XSetForeground(display,window_info->widget_context,
1495  XWhitePixel(display,window_info->screen));
1496  else
1497  (void) XSetForeground(display,window_info->widget_context,
1498  XBlackPixel(display,window_info->screen));
1499  }
1500  else
1501  if (raised)
1502  (void) XSetForeground(display,window_info->widget_context,
1503  window_info->pixel_info->matte_color.pixel);
1504  else
1505  (void) XSetForeground(display,window_info->widget_context,
1506  window_info->pixel_info->depth_color.pixel);
1507 }
1508 
1509 /*
1510 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1511 % %
1512 % %
1513 % %
1514 + X S e t T e x t C o l o r %
1515 % %
1516 % %
1517 % %
1518 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1519 %
1520 % XSetTextColor() sets the graphic context for drawing text on a matte.
1521 %
1522 % The format of the XSetTextColor function is:
1523 %
1524 % XSetTextColor(display,window_info,raised)
1525 %
1526 % A description of each parameter follows:
1527 %
1528 % o display: Specifies a pointer to the Display structure; returned from
1529 % XOpenDisplay.
1530 %
1531 % o window_info: Specifies a pointer to a X11 XWindowInfo structure.
1532 %
1533 % o raised: A value other than zero indicates the color show be a
1534 % "highlight" color, otherwise the "shadow" color is set.
1535 %
1536 */
1537 static void XSetTextColor(Display *display,const XWindowInfo *window_info,
1538  const MagickStatusType raised)
1539 {
1540  ssize_t
1541  foreground,
1542  matte;
1543 
1544  if (window_info->depth == 1)
1545  {
1546  /*
1547  Monochrome window.
1548  */
1549  if (raised)
1550  (void) XSetForeground(display,window_info->widget_context,
1551  XBlackPixel(display,window_info->screen));
1552  else
1553  (void) XSetForeground(display,window_info->widget_context,
1554  XWhitePixel(display,window_info->screen));
1555  return;
1556  }
1557  foreground=(ssize_t) XPixelIntensity(
1558  &window_info->pixel_info->foreground_color);
1559  matte=(ssize_t) XPixelIntensity(&window_info->pixel_info->matte_color);
1560  if (MagickAbsoluteValue((int) (foreground-matte)) > (65535L >> 3))
1561  (void) XSetForeground(display,window_info->widget_context,
1562  window_info->pixel_info->foreground_color.pixel);
1563  else
1564  (void) XSetForeground(display,window_info->widget_context,
1565  window_info->pixel_info->background_color.pixel);
1566 }
1567 
1568 /*
1569 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1570 % %
1571 % %
1572 % %
1573 % X C o l o r B r o w s e r W i d g e t %
1574 % %
1575 % %
1576 % %
1577 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1578 %
1579 % XColorBrowserWidget() displays a Color Browser widget with a color query
1580 % to the user. The user keys a reply and presses the Action or Cancel button
1581 % to exit. The typed text is returned as the reply function parameter.
1582 %
1583 % The format of the XColorBrowserWidget method is:
1584 %
1585 % void XColorBrowserWidget(Display *display,XWindows *windows,
1586 % const char *action,char *reply)
1587 %
1588 % A description of each parameter follows:
1589 %
1590 % o display: Specifies a connection to an X server; returned from
1591 % XOpenDisplay.
1592 %
1593 % o window: Specifies a pointer to a XWindows structure.
1594 %
1595 % o action: Specifies a pointer to the action of this widget.
1596 %
1597 % o reply: the response from the user is returned in this parameter.
1598 %
1599 */
1600 MagickExport void XColorBrowserWidget(Display *display,XWindows *windows,
1601  const char *action,char *reply)
1602 {
1603 #define CancelButtonText "Cancel"
1604 #define ColornameText "Name:"
1605 #define ColorPatternText "Pattern:"
1606 #define GrabButtonText "Grab"
1607 #define ResetButtonText "Reset"
1608 
1609  char
1610  **colorlist,
1611  primary_selection[MaxTextExtent] = "",
1612  reset_pattern[MaxTextExtent],
1613  text[MaxTextExtent];
1614 
1616  *exception;
1617 
1618  int
1619  x,
1620  y;
1621 
1622  int
1623  i;
1624 
1625  static char
1626  glob_pattern[MaxTextExtent] = "*";
1627 
1628  static MagickStatusType
1629  mask = (MagickStatusType) (CWWidth | CWHeight | CWX | CWY);
1630 
1631  Status
1632  status;
1633 
1634  unsigned int
1635  height,
1636  text_width,
1637  visible_colors,
1638  width;
1639 
1640  size_t
1641  colors,
1642  delay,
1643  state;
1644 
1645  XColor
1646  color;
1647 
1648  XEvent
1649  event;
1650 
1651  XFontStruct
1652  *font_info;
1653 
1654  XTextProperty
1655  window_name;
1656 
1657  XWidgetInfo
1658  action_info,
1659  cancel_info,
1660  expose_info,
1661  grab_info,
1662  list_info,
1663  mode_info,
1664  north_info,
1665  reply_info,
1666  reset_info,
1667  scroll_info,
1668  selection_info,
1669  slider_info,
1670  south_info,
1671  text_info;
1672 
1673  XWindowChanges
1674  window_changes;
1675 
1676  /*
1677  Get color list and sort in ascending order.
1678  */
1679  assert(display != (Display *) NULL);
1680  assert(windows != (XWindows *) NULL);
1681  assert(action != (char *) NULL);
1682  assert(reply != (char *) NULL);
1683  if (IsEventLogging() != MagickFalse)
1684  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",action);
1685  XSetCursorState(display,windows,MagickTrue);
1686  XCheckRefreshWindows(display,windows);
1687  (void) CopyMagickString(reset_pattern,"*",MaxTextExtent);
1688  exception=AcquireExceptionInfo();
1689  colorlist=GetColorList(glob_pattern,&colors,exception);
1690  if (colorlist == (char **) NULL)
1691  {
1692  /*
1693  Pattern failed, obtain all the colors.
1694  */
1695  (void) CopyMagickString(glob_pattern,"*",MaxTextExtent);
1696  colorlist=GetColorList(glob_pattern,&colors,exception);
1697  if (colorlist == (char **) NULL)
1698  {
1699  XNoticeWidget(display,windows,"Unable to obtain colors names:",
1700  glob_pattern);
1701  (void) XDialogWidget(display,windows,action,"Enter color name:",
1702  reply);
1703  return;
1704  }
1705  }
1706  /*
1707  Determine Color Browser widget attributes.
1708  */
1709  font_info=windows->widget.font_info;
1710  text_width=0;
1711  for (i=0; i < (int) colors; i++)
1712  if (WidgetTextWidth(font_info,colorlist[i]) > text_width)
1713  text_width=WidgetTextWidth(font_info,colorlist[i]);
1714  width=WidgetTextWidth(font_info,(char *) action);
1715  if (WidgetTextWidth(font_info,CancelButtonText) > width)
1716  width=WidgetTextWidth(font_info,CancelButtonText);
1717  if (WidgetTextWidth(font_info,ResetButtonText) > width)
1718  width=WidgetTextWidth(font_info,ResetButtonText);
1719  if (WidgetTextWidth(font_info,GrabButtonText) > width)
1720  width=WidgetTextWidth(font_info,GrabButtonText);
1721  width+=QuantumMargin;
1722  if (WidgetTextWidth(font_info,ColorPatternText) > width)
1723  width=WidgetTextWidth(font_info,ColorPatternText);
1724  if (WidgetTextWidth(font_info,ColornameText) > width)
1725  width=WidgetTextWidth(font_info,ColornameText);
1726  height=(unsigned int) (font_info->ascent+font_info->descent);
1727  /*
1728  Position Color Browser widget.
1729  */
1730  windows->widget.width=(unsigned int)
1731  (width+MagickMin((int) text_width,(int) MaxTextWidth)+6*QuantumMargin);
1732  windows->widget.min_width=(unsigned int)
1733  (width+MinTextWidth+4*QuantumMargin);
1734  if (windows->widget.width < windows->widget.min_width)
1735  windows->widget.width=windows->widget.min_width;
1736  windows->widget.height=(unsigned int)
1737  ((81*height) >> 2)+((13*QuantumMargin) >> 1)+4;
1738  windows->widget.min_height=(unsigned int)
1739  (((23*height) >> 1)+((13*QuantumMargin) >> 1)+4);
1740  if (windows->widget.height < windows->widget.min_height)
1741  windows->widget.height=windows->widget.min_height;
1742  XConstrainWindowPosition(display,&windows->widget);
1743  /*
1744  Map Color Browser widget.
1745  */
1746  (void) CopyMagickString(windows->widget.name,"Browse and Select a Color",
1747  MaxTextExtent);
1748  status=XStringListToTextProperty(&windows->widget.name,1,&window_name);
1749  if (status != False)
1750  {
1751  XSetWMName(display,windows->widget.id,&window_name);
1752  XSetWMIconName(display,windows->widget.id,&window_name);
1753  (void) XFree((void *) window_name.value);
1754  }
1755  window_changes.width=(int) windows->widget.width;
1756  window_changes.height=(int) windows->widget.height;
1757  window_changes.x=windows->widget.x;
1758  window_changes.y=windows->widget.y;
1759  (void) XReconfigureWMWindow(display,windows->widget.id,windows->widget.screen,
1760  mask,&window_changes);
1761  (void) XMapRaised(display,windows->widget.id);
1762  windows->widget.mapped=MagickFalse;
1763  /*
1764  Respond to X events.
1765  */
1766  XGetWidgetInfo((char *) NULL,&mode_info);
1767  XGetWidgetInfo((char *) NULL,&slider_info);
1768  XGetWidgetInfo((char *) NULL,&north_info);
1769  XGetWidgetInfo((char *) NULL,&south_info);
1770  XGetWidgetInfo((char *) NULL,&expose_info);
1771  XGetWidgetInfo((char *) NULL,&selection_info);
1772  visible_colors=0;
1773  delay=SuspendTime << 2;
1774  state=UpdateConfigurationState;
1775  do
1776  {
1777  if (state & UpdateConfigurationState)
1778  {
1779  int
1780  id;
1781 
1782  /*
1783  Initialize button information.
1784  */
1785  XGetWidgetInfo(CancelButtonText,&cancel_info);
1786  cancel_info.width=width;
1787  cancel_info.height=(unsigned int) ((3*height) >> 1);
1788  cancel_info.x=(int)
1789  (windows->widget.width-cancel_info.width-QuantumMargin-2);
1790  cancel_info.y=(int)
1791  (windows->widget.height-cancel_info.height-QuantumMargin);
1792  XGetWidgetInfo(action,&action_info);
1793  action_info.width=width;
1794  action_info.height=(unsigned int) ((3*height) >> 1);
1795  action_info.x=cancel_info.x-(cancel_info.width+(QuantumMargin >> 1)+
1796  (action_info.bevel_width << 1));
1797  action_info.y=cancel_info.y;
1798  XGetWidgetInfo(GrabButtonText,&grab_info);
1799  grab_info.width=width;
1800  grab_info.height=(unsigned int) ((3*height) >> 1);
1801  grab_info.x=QuantumMargin;
1802  grab_info.y=((5*QuantumMargin) >> 1)+height;
1803  XGetWidgetInfo(ResetButtonText,&reset_info);
1804  reset_info.width=width;
1805  reset_info.height=(unsigned int) ((3*height) >> 1);
1806  reset_info.x=QuantumMargin;
1807  reset_info.y=grab_info.y+grab_info.height+QuantumMargin;
1808  /*
1809  Initialize reply information.
1810  */
1811  XGetWidgetInfo(reply,&reply_info);
1812  reply_info.raised=MagickFalse;
1813  reply_info.bevel_width--;
1814  reply_info.width=windows->widget.width-width-((6*QuantumMargin) >> 1);
1815  reply_info.height=height << 1;
1816  reply_info.x=(int) (width+(QuantumMargin << 1));
1817  reply_info.y=action_info.y-reply_info.height-QuantumMargin;
1818  /*
1819  Initialize mode information.
1820  */
1821  XGetWidgetInfo((char *) NULL,&mode_info);
1822  mode_info.active=MagickTrue;
1823  mode_info.bevel_width=0;
1824  mode_info.width=(unsigned int) (action_info.x-(QuantumMargin << 1));
1825  mode_info.height=action_info.height;
1826  mode_info.x=QuantumMargin;
1827  mode_info.y=action_info.y;
1828  /*
1829  Initialize scroll information.
1830  */
1831  XGetWidgetInfo((char *) NULL,&scroll_info);
1832  scroll_info.bevel_width--;
1833  scroll_info.width=height;
1834  scroll_info.height=(unsigned int) (reply_info.y-grab_info.y-
1835  (QuantumMargin >> 1));
1836  scroll_info.x=reply_info.x+(reply_info.width-scroll_info.width);
1837  scroll_info.y=grab_info.y-reply_info.bevel_width;
1838  scroll_info.raised=MagickFalse;
1839  scroll_info.trough=MagickTrue;
1840  north_info=scroll_info;
1841  north_info.raised=MagickTrue;
1842  north_info.width-=(north_info.bevel_width << 1);
1843  north_info.height=north_info.width-1;
1844  north_info.x+=north_info.bevel_width;
1845  north_info.y+=north_info.bevel_width;
1846  south_info=north_info;
1847  south_info.y=scroll_info.y+scroll_info.height-scroll_info.bevel_width-
1848  south_info.height;
1849  id=slider_info.id;
1850  slider_info=north_info;
1851  slider_info.id=id;
1852  slider_info.width-=2;
1853  slider_info.min_y=north_info.y+north_info.height+north_info.bevel_width+
1854  slider_info.bevel_width+2;
1855  slider_info.height=scroll_info.height-((slider_info.min_y-
1856  scroll_info.y+1) << 1)+4;
1857  visible_colors=(unsigned int) (scroll_info.height*
1858  PerceptibleReciprocal((double) height+(height >> 3)));
1859  if (colors > visible_colors)
1860  slider_info.height=(unsigned int) ((visible_colors*
1861  slider_info.height)/colors);
1862  slider_info.max_y=south_info.y-south_info.bevel_width-
1863  slider_info.bevel_width-2;
1864  slider_info.x=scroll_info.x+slider_info.bevel_width+1;
1865  slider_info.y=slider_info.min_y;
1866  expose_info=scroll_info;
1867  expose_info.y=slider_info.y;
1868  /*
1869  Initialize list information.
1870  */
1871  XGetWidgetInfo((char *) NULL,&list_info);
1872  list_info.raised=MagickFalse;
1873  list_info.bevel_width--;
1874  list_info.width=(unsigned int)
1875  (scroll_info.x-reply_info.x-(QuantumMargin >> 1));
1876  list_info.height=scroll_info.height;
1877  list_info.x=reply_info.x;
1878  list_info.y=scroll_info.y;
1879  if (windows->widget.mapped == MagickFalse)
1880  state|=JumpListState;
1881  /*
1882  Initialize text information.
1883  */
1884  *text='\0';
1885  XGetWidgetInfo(text,&text_info);
1886  text_info.center=MagickFalse;
1887  text_info.width=reply_info.width;
1888  text_info.height=height;
1889  text_info.x=list_info.x-(QuantumMargin >> 1);
1890  text_info.y=QuantumMargin;
1891  /*
1892  Initialize selection information.
1893  */
1894  XGetWidgetInfo((char *) NULL,&selection_info);
1895  selection_info.center=MagickFalse;
1896  selection_info.width=list_info.width;
1897  selection_info.height=(unsigned int) ((9*height) >> 3);
1898  selection_info.x=list_info.x;
1899  state&=(~UpdateConfigurationState);
1900  }
1901  if (state & RedrawWidgetState)
1902  {
1903  /*
1904  Redraw Color Browser window.
1905  */
1906  x=QuantumMargin;
1907  y=text_info.y+((text_info.height-height) >> 1)+font_info->ascent;
1908  (void) XDrawString(display,windows->widget.id,
1909  windows->widget.annotate_context,x,y,ColorPatternText,
1910  Extent(ColorPatternText));
1911  (void) CopyMagickString(text_info.text,glob_pattern,MaxTextExtent);
1912  XDrawWidgetText(display,&windows->widget,&text_info);
1913  XDrawBeveledButton(display,&windows->widget,&grab_info);
1914  XDrawBeveledButton(display,&windows->widget,&reset_info);
1915  XDrawBeveledMatte(display,&windows->widget,&list_info);
1916  XDrawBeveledMatte(display,&windows->widget,&scroll_info);
1917  XDrawTriangleNorth(display,&windows->widget,&north_info);
1918  XDrawBeveledButton(display,&windows->widget,&slider_info);
1919  XDrawTriangleSouth(display,&windows->widget,&south_info);
1920  x=QuantumMargin;
1921  y=reply_info.y+((reply_info.height-height) >> 1)+font_info->ascent;
1922  (void) XDrawString(display,windows->widget.id,
1923  windows->widget.annotate_context,x,y,ColornameText,
1924  Extent(ColornameText));
1925  XDrawBeveledMatte(display,&windows->widget,&reply_info);
1926  XDrawMatteText(display,&windows->widget,&reply_info);
1927  XDrawBeveledButton(display,&windows->widget,&action_info);
1928  XDrawBeveledButton(display,&windows->widget,&cancel_info);
1929  XHighlightWidget(display,&windows->widget,BorderOffset,BorderOffset);
1930  selection_info.id=(~0);
1931  state|=RedrawActionState;
1932  state|=RedrawListState;
1933  state&=(~RedrawWidgetState);
1934  }
1935  if (state & UpdateListState)
1936  {
1937  char
1938  **checklist;
1939 
1940  size_t
1941  number_colors;
1942 
1943  status=XParseColor(display,windows->widget.map_info->colormap,
1944  glob_pattern,&color);
1945  if ((status != False) || (strchr(glob_pattern,'-') != (char *) NULL))
1946  {
1947  /*
1948  Reply is a single color name-- exit.
1949  */
1950  (void) CopyMagickString(reply,glob_pattern,MaxTextExtent);
1951  (void) CopyMagickString(glob_pattern,reset_pattern,MaxTextExtent);
1952  action_info.raised=MagickFalse;
1953  XDrawBeveledButton(display,&windows->widget,&action_info);
1954  break;
1955  }
1956  /*
1957  Update color list.
1958  */
1959  checklist=GetColorList(glob_pattern,&number_colors,exception);
1960  if (number_colors == 0)
1961  {
1962  (void) CopyMagickString(glob_pattern,reset_pattern,MaxTextExtent);
1963  (void) XBell(display,0);
1964  }
1965  else
1966  {
1967  for (i=0; i < (int) colors; i++)
1968  colorlist[i]=DestroyString(colorlist[i]);
1969  if (colorlist != (char **) NULL)
1970  colorlist=(char **) RelinquishMagickMemory(colorlist);
1971  colorlist=checklist;
1972  colors=number_colors;
1973  }
1974  /*
1975  Sort color list in ascending order.
1976  */
1977  slider_info.height=
1978  scroll_info.height-((slider_info.min_y-scroll_info.y+1) << 1)+1;
1979  if (colors > visible_colors)
1980  slider_info.height=(unsigned int)
1981  ((visible_colors*slider_info.height)/colors);
1982  slider_info.max_y=south_info.y-south_info.bevel_width-
1983  slider_info.bevel_width-2;
1984  slider_info.id=0;
1985  slider_info.y=slider_info.min_y;
1986  expose_info.y=slider_info.y;
1987  selection_info.id=(~0);
1988  list_info.id=(~0);
1989  state|=RedrawListState;
1990  /*
1991  Redraw color name & reply.
1992  */
1993  *reply_info.text='\0';
1994  reply_info.cursor=reply_info.text;
1995  (void) CopyMagickString(text_info.text,glob_pattern,MaxTextExtent);
1996  XDrawWidgetText(display,&windows->widget,&text_info);
1997  XDrawMatteText(display,&windows->widget,&reply_info);
1998  XDrawBeveledMatte(display,&windows->widget,&scroll_info);
1999  XDrawTriangleNorth(display,&windows->widget,&north_info);
2000  XDrawBeveledButton(display,&windows->widget,&slider_info);
2001  XDrawTriangleSouth(display,&windows->widget,&south_info);
2002  XHighlightWidget(display,&windows->widget,BorderOffset,BorderOffset);
2003  state&=(~UpdateListState);
2004  }
2005  if (state & JumpListState)
2006  {
2007  /*
2008  Jump scroll to match user color.
2009  */
2010  list_info.id=(~0);
2011  for (i=0; i < (int) colors; i++)
2012  if (LocaleCompare(colorlist[i],reply) >= 0)
2013  {
2014  list_info.id=LocaleCompare(colorlist[i],reply) == 0 ? i : ~0;
2015  break;
2016  }
2017  if ((i < slider_info.id) ||
2018  (i >= (int) (slider_info.id+visible_colors)))
2019  slider_info.id=i-(visible_colors >> 1);
2020  selection_info.id=(~0);
2021  state|=RedrawListState;
2022  state&=(~JumpListState);
2023  }
2024  if (state & RedrawListState)
2025  {
2026  /*
2027  Determine slider id and position.
2028  */
2029  if (slider_info.id >= (int) (colors-visible_colors))
2030  slider_info.id=(int) (colors-visible_colors);
2031  if ((slider_info.id < 0) || (colors <= visible_colors))
2032  slider_info.id=0;
2033  slider_info.y=slider_info.min_y;
2034  if (colors != 0)
2035  slider_info.y+=((ssize_t) slider_info.id*(slider_info.max_y-
2036  slider_info.min_y+1)/colors);
2037  if (slider_info.id != selection_info.id)
2038  {
2039  /*
2040  Redraw scroll bar and file names.
2041  */
2042  selection_info.id=slider_info.id;
2043  selection_info.y=list_info.y+(height >> 3)+2;
2044  for (i=0; i < (int) visible_colors; i++)
2045  {
2046  selection_info.raised=(slider_info.id+i) != list_info.id ?
2047  MagickTrue : MagickFalse;
2048  selection_info.text=(char *) NULL;
2049  if ((slider_info.id+i) < (int) colors)
2050  selection_info.text=colorlist[slider_info.id+i];
2051  XDrawWidgetText(display,&windows->widget,&selection_info);
2052  selection_info.y+=(int) selection_info.height;
2053  }
2054  /*
2055  Update slider.
2056  */
2057  if (slider_info.y > expose_info.y)
2058  {
2059  expose_info.height=(unsigned int) slider_info.y-expose_info.y;
2060  expose_info.y=slider_info.y-expose_info.height-
2061  slider_info.bevel_width-1;
2062  }
2063  else
2064  {
2065  expose_info.height=(unsigned int) expose_info.y-slider_info.y;
2066  expose_info.y=slider_info.y+slider_info.height+
2067  slider_info.bevel_width+1;
2068  }
2069  XDrawTriangleNorth(display,&windows->widget,&north_info);
2070  XDrawMatte(display,&windows->widget,&expose_info);
2071  XDrawBeveledButton(display,&windows->widget,&slider_info);
2072  XDrawTriangleSouth(display,&windows->widget,&south_info);
2073  expose_info.y=slider_info.y;
2074  }
2075  state&=(~RedrawListState);
2076  }
2077  if (state & RedrawActionState)
2078  {
2079  static char
2080  colorname[MaxTextExtent];
2081 
2082  /*
2083  Display the selected color in a drawing area.
2084  */
2085  color=windows->widget.pixel_info->matte_color;
2086  (void) XParseColor(display,windows->widget.map_info->colormap,
2087  reply_info.text,&windows->widget.pixel_info->matte_color);
2088  XBestPixel(display,windows->widget.map_info->colormap,(XColor *) NULL,
2089  (unsigned int) windows->widget.visual_info->colormap_size,
2090  &windows->widget.pixel_info->matte_color);
2091  mode_info.text=colorname;
2092  (void) FormatLocaleString(mode_info.text,MaxTextExtent,"#%02x%02x%02x",
2093  windows->widget.pixel_info->matte_color.red,
2094  windows->widget.pixel_info->matte_color.green,
2095  windows->widget.pixel_info->matte_color.blue);
2096  XDrawBeveledButton(display,&windows->widget,&mode_info);
2097  windows->widget.pixel_info->matte_color=color;
2098  state&=(~RedrawActionState);
2099  }
2100  /*
2101  Wait for next event.
2102  */
2103  if (north_info.raised && south_info.raised)
2104  (void) XIfEvent(display,&event,XScreenEvent,(char *) windows);
2105  else
2106  {
2107  /*
2108  Brief delay before advancing scroll bar.
2109  */
2110  XDelay(display,delay);
2111  delay=SuspendTime;
2112  (void) XCheckIfEvent(display,&event,XScreenEvent,(char *) windows);
2113  if (north_info.raised == MagickFalse)
2114  if (slider_info.id > 0)
2115  {
2116  /*
2117  Move slider up.
2118  */
2119  slider_info.id--;
2120  state|=RedrawListState;
2121  }
2122  if (south_info.raised == MagickFalse)
2123  if (slider_info.id < (int) colors)
2124  {
2125  /*
2126  Move slider down.
2127  */
2128  slider_info.id++;
2129  state|=RedrawListState;
2130  }
2131  if (event.type != ButtonRelease)
2132  continue;
2133  }
2134  switch (event.type)
2135  {
2136  case ButtonPress:
2137  {
2138  if (MatteIsActive(slider_info,event.xbutton))
2139  {
2140  /*
2141  Track slider.
2142  */
2143  slider_info.active=MagickTrue;
2144  break;
2145  }
2146  if (MatteIsActive(north_info,event.xbutton))
2147  if (slider_info.id > 0)
2148  {
2149  /*
2150  Move slider up.
2151  */
2152  north_info.raised=MagickFalse;
2153  slider_info.id--;
2154  state|=RedrawListState;
2155  break;
2156  }
2157  if (MatteIsActive(south_info,event.xbutton))
2158  if (slider_info.id < (int) colors)
2159  {
2160  /*
2161  Move slider down.
2162  */
2163  south_info.raised=MagickFalse;
2164  slider_info.id++;
2165  state|=RedrawListState;
2166  break;
2167  }
2168  if (MatteIsActive(scroll_info,event.xbutton))
2169  {
2170  /*
2171  Move slider.
2172  */
2173  if (event.xbutton.y < slider_info.y)
2174  slider_info.id-=(visible_colors-1);
2175  else
2176  slider_info.id+=(visible_colors-1);
2177  state|=RedrawListState;
2178  break;
2179  }
2180  if (MatteIsActive(list_info,event.xbutton))
2181  {
2182  int
2183  id;
2184 
2185  /*
2186  User pressed list matte.
2187  */
2188  id=slider_info.id+(event.xbutton.y-(list_info.y+(height >> 1))+1)/
2189  selection_info.height;
2190  if (id >= (int) colors)
2191  break;
2192  (void) CopyMagickString(reply_info.text,colorlist[id],
2193  MaxTextExtent);
2194  reply_info.highlight=MagickFalse;
2195  reply_info.marker=reply_info.text;
2196  reply_info.cursor=reply_info.text+Extent(reply_info.text);
2197  XDrawMatteText(display,&windows->widget,&reply_info);
2198  state|=RedrawActionState;
2199  if (id == list_info.id)
2200  {
2201  (void) CopyMagickString(glob_pattern,reply_info.text,
2202  MaxTextExtent);
2203  state|=UpdateListState;
2204  }
2205  selection_info.id=(~0);
2206  list_info.id=id;
2207  state|=RedrawListState;
2208  break;
2209  }
2210  if (MatteIsActive(grab_info,event.xbutton))
2211  {
2212  /*
2213  User pressed Grab button.
2214  */
2215  grab_info.raised=MagickFalse;
2216  XDrawBeveledButton(display,&windows->widget,&grab_info);
2217  break;
2218  }
2219  if (MatteIsActive(reset_info,event.xbutton))
2220  {
2221  /*
2222  User pressed Reset button.
2223  */
2224  reset_info.raised=MagickFalse;
2225  XDrawBeveledButton(display,&windows->widget,&reset_info);
2226  break;
2227  }
2228  if (MatteIsActive(mode_info,event.xbutton))
2229  {
2230  /*
2231  User pressed mode button.
2232  */
2233  if (mode_info.text != (char *) NULL)
2234  (void) CopyMagickString(reply_info.text,mode_info.text,
2235  MaxTextExtent);
2236  (void) CopyMagickString(primary_selection,reply_info.text,
2237  MaxTextExtent);
2238  (void) XSetSelectionOwner(display,XA_PRIMARY,windows->widget.id,
2239  event.xbutton.time);
2240  reply_info.highlight=XGetSelectionOwner(display,XA_PRIMARY) ==
2241  windows->widget.id ? MagickTrue : MagickFalse;
2242  reply_info.marker=reply_info.text;
2243  reply_info.cursor=reply_info.text+Extent(reply_info.text);
2244  XDrawMatteText(display,&windows->widget,&reply_info);
2245  break;
2246  }
2247  if (MatteIsActive(action_info,event.xbutton))
2248  {
2249  /*
2250  User pressed action button.
2251  */
2252  action_info.raised=MagickFalse;
2253  XDrawBeveledButton(display,&windows->widget,&action_info);
2254  break;
2255  }
2256  if (MatteIsActive(cancel_info,event.xbutton))
2257  {
2258  /*
2259  User pressed Cancel button.
2260  */
2261  cancel_info.raised=MagickFalse;
2262  XDrawBeveledButton(display,&windows->widget,&cancel_info);
2263  break;
2264  }
2265  if (MatteIsActive(reply_info,event.xbutton) == MagickFalse)
2266  break;
2267  if (event.xbutton.button != Button2)
2268  {
2269  static Time
2270  click_time;
2271 
2272  /*
2273  Move text cursor to position of button press.
2274  */
2275  x=event.xbutton.x-reply_info.x-(QuantumMargin >> 2);
2276  for (i=1; i <= Extent(reply_info.marker); i++)
2277  if (XTextWidth(font_info,reply_info.marker,i) > x)
2278  break;
2279  reply_info.cursor=reply_info.marker+i-1;
2280  if (event.xbutton.time > (click_time+DoubleClick))
2281  reply_info.highlight=MagickFalse;
2282  else
2283  {
2284  /*
2285  Become the XA_PRIMARY selection owner.
2286  */
2287  (void) CopyMagickString(primary_selection,reply_info.text,
2288  MaxTextExtent);
2289  (void) XSetSelectionOwner(display,XA_PRIMARY,windows->widget.id,
2290  event.xbutton.time);
2291  reply_info.highlight=XGetSelectionOwner(display,XA_PRIMARY) ==
2292  windows->widget.id ? MagickTrue : MagickFalse;
2293  }
2294  XDrawMatteText(display,&windows->widget,&reply_info);
2295  click_time=event.xbutton.time;
2296  break;
2297  }
2298  /*
2299  Request primary selection.
2300  */
2301  (void) XConvertSelection(display,XA_PRIMARY,XA_STRING,XA_STRING,
2302  windows->widget.id,event.xbutton.time);
2303  break;
2304  }
2305  case ButtonRelease:
2306  {
2307  if (windows->widget.mapped == MagickFalse)
2308  break;
2309  if (north_info.raised == MagickFalse)
2310  {
2311  /*
2312  User released up button.
2313  */
2314  delay=SuspendTime << 2;
2315  north_info.raised=MagickTrue;
2316  XDrawTriangleNorth(display,&windows->widget,&north_info);
2317  }
2318  if (south_info.raised == MagickFalse)
2319  {
2320  /*
2321  User released down button.
2322  */
2323  delay=SuspendTime << 2;
2324  south_info.raised=MagickTrue;
2325  XDrawTriangleSouth(display,&windows->widget,&south_info);
2326  }
2327  if (slider_info.active)
2328  {
2329  /*
2330  Stop tracking slider.
2331  */
2332  slider_info.active=MagickFalse;
2333  break;
2334  }
2335  if (grab_info.raised == MagickFalse)
2336  {
2337  if (event.xbutton.window == windows->widget.id)
2338  if (MatteIsActive(grab_info,event.xbutton))
2339  {
2340  /*
2341  Select a pen color from the X server.
2342  */
2343  (void) XGetWindowColor(display,windows,reply_info.text);
2344  reply_info.marker=reply_info.text;
2345  reply_info.cursor=reply_info.text+Extent(reply_info.text);
2346  XDrawMatteText(display,&windows->widget,&reply_info);
2347  state|=RedrawActionState;
2348  }
2349  grab_info.raised=MagickTrue;
2350  XDrawBeveledButton(display,&windows->widget,&grab_info);
2351  }
2352  if (reset_info.raised == MagickFalse)
2353  {
2354  if (event.xbutton.window == windows->widget.id)
2355  if (MatteIsActive(reset_info,event.xbutton))
2356  {
2357  (void) CopyMagickString(glob_pattern,reset_pattern,
2358  MaxTextExtent);
2359  state|=UpdateListState;
2360  }
2361  reset_info.raised=MagickTrue;
2362  XDrawBeveledButton(display,&windows->widget,&reset_info);
2363  }
2364  if (action_info.raised == MagickFalse)
2365  {
2366  if (event.xbutton.window == windows->widget.id)
2367  {
2368  if (MatteIsActive(action_info,event.xbutton))
2369  {
2370  if (*reply_info.text == '\0')
2371  (void) XBell(display,0);
2372  else
2373  state|=ExitState;
2374  }
2375  }
2376  action_info.raised=MagickTrue;
2377  XDrawBeveledButton(display,&windows->widget,&action_info);
2378  }
2379  if (cancel_info.raised == MagickFalse)
2380  {
2381  if (event.xbutton.window == windows->widget.id)
2382  if (MatteIsActive(cancel_info,event.xbutton))
2383  {
2384  *reply_info.text='\0';
2385  state|=ExitState;
2386  }
2387  cancel_info.raised=MagickTrue;
2388  XDrawBeveledButton(display,&windows->widget,&cancel_info);
2389  }
2390  if (MatteIsActive(reply_info,event.xbutton) == MagickFalse)
2391  break;
2392  break;
2393  }
2394  case ClientMessage:
2395  {
2396  /*
2397  If client window delete message, exit.
2398  */
2399  if (event.xclient.message_type != windows->wm_protocols)
2400  break;
2401  if (*event.xclient.data.l == (int) windows->wm_take_focus)
2402  {
2403  (void) XSetInputFocus(display,event.xclient.window,RevertToParent,
2404  (Time) event.xclient.data.l[1]);
2405  break;
2406  }
2407  if (*event.xclient.data.l != (int) windows->wm_delete_window)
2408  break;
2409  if (event.xclient.window == windows->widget.id)
2410  {
2411  *reply_info.text='\0';
2412  state|=ExitState;
2413  break;
2414  }
2415  break;
2416  }
2417  case ConfigureNotify:
2418  {
2419  /*
2420  Update widget configuration.
2421  */
2422  if (event.xconfigure.window != windows->widget.id)
2423  break;
2424  if ((event.xconfigure.width == (int) windows->widget.width) &&
2425  (event.xconfigure.height == (int) windows->widget.height))
2426  break;
2427  windows->widget.width=(unsigned int)
2428  MagickMax(event.xconfigure.width,(int) windows->widget.min_width);
2429  windows->widget.height=(unsigned int)
2430  MagickMax(event.xconfigure.height,(int) windows->widget.min_height);
2431  state|=UpdateConfigurationState;
2432  break;
2433  }
2434  case EnterNotify:
2435  {
2436  if (event.xcrossing.window != windows->widget.id)
2437  break;
2438  state&=(~InactiveWidgetState);
2439  break;
2440  }
2441  case Expose:
2442  {
2443  if (event.xexpose.window != windows->widget.id)
2444  break;
2445  if (event.xexpose.count != 0)
2446  break;
2447  state|=RedrawWidgetState;
2448  break;
2449  }
2450  case KeyPress:
2451  {
2452  static char
2453  command[MaxTextExtent];
2454 
2455  static int
2456  length;
2457 
2458  static KeySym
2459  key_symbol;
2460 
2461  /*
2462  Respond to a user key press.
2463  */
2464  if (event.xkey.window != windows->widget.id)
2465  break;
2466  length=XLookupString((XKeyEvent *) &event.xkey,command,
2467  (int) sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2468  *(command+length)='\0';
2469  if (AreaIsActive(scroll_info,event.xkey))
2470  {
2471  /*
2472  Move slider.
2473  */
2474  switch ((int) key_symbol)
2475  {
2476  case XK_Home:
2477  case XK_KP_Home:
2478  {
2479  slider_info.id=0;
2480  break;
2481  }
2482  case XK_Up:
2483  case XK_KP_Up:
2484  {
2485  slider_info.id--;
2486  break;
2487  }
2488  case XK_Down:
2489  case XK_KP_Down:
2490  {
2491  slider_info.id++;
2492  break;
2493  }
2494  case XK_Prior:
2495  case XK_KP_Prior:
2496  {
2497  slider_info.id-=visible_colors;
2498  break;
2499  }
2500  case XK_Next:
2501  case XK_KP_Next:
2502  {
2503  slider_info.id+=visible_colors;
2504  break;
2505  }
2506  case XK_End:
2507  case XK_KP_End:
2508  {
2509  slider_info.id=(int) colors;
2510  break;
2511  }
2512  }
2513  state|=RedrawListState;
2514  break;
2515  }
2516  if ((key_symbol == XK_Return) || (key_symbol == XK_KP_Enter))
2517  {
2518  /*
2519  Read new color or glob pattern.
2520  */
2521  if (*reply_info.text == '\0')
2522  break;
2523  (void) CopyMagickString(glob_pattern,reply_info.text,MaxTextExtent);
2524  state|=UpdateListState;
2525  break;
2526  }
2527  if (key_symbol == XK_Control_L)
2528  {
2529  state|=ControlState;
2530  break;
2531  }
2532  if (state & ControlState)
2533  switch ((int) key_symbol)
2534  {
2535  case XK_u:
2536  case XK_U:
2537  {
2538  /*
2539  Erase the entire line of text.
2540  */
2541  *reply_info.text='\0';
2542  reply_info.cursor=reply_info.text;
2543  reply_info.marker=reply_info.text;
2544  reply_info.highlight=MagickFalse;
2545  break;
2546  }
2547  default:
2548  break;
2549  }
2550  XEditText(display,&reply_info,key_symbol,command,state);
2551  XDrawMatteText(display,&windows->widget,&reply_info);
2552  state|=JumpListState;
2553  status=XParseColor(display,windows->widget.map_info->colormap,
2554  reply_info.text,&color);
2555  if (status != False)
2556  state|=RedrawActionState;
2557  break;
2558  }
2559  case KeyRelease:
2560  {
2561  static char
2562  command[MaxTextExtent];
2563 
2564  static KeySym
2565  key_symbol;
2566 
2567  /*
2568  Respond to a user key release.
2569  */
2570  if (event.xkey.window != windows->widget.id)
2571  break;
2572  (void) XLookupString((XKeyEvent *) &event.xkey,command,
2573  (int) sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2574  if (key_symbol == XK_Control_L)
2575  state&=(~ControlState);
2576  break;
2577  }
2578  case LeaveNotify:
2579  {
2580  if (event.xcrossing.window != windows->widget.id)
2581  break;
2582  state|=InactiveWidgetState;
2583  break;
2584  }
2585  case MapNotify:
2586  {
2587  mask&=(~CWX);
2588  mask&=(~CWY);
2589  break;
2590  }
2591  case MotionNotify:
2592  {
2593  /*
2594  Discard pending button motion events.
2595  */
2596  while (XCheckMaskEvent(display,ButtonMotionMask,&event)) ;
2597  if (slider_info.active)
2598  {
2599  /*
2600  Move slider matte.
2601  */
2602  slider_info.y=event.xmotion.y-
2603  ((slider_info.height+slider_info.bevel_width) >> 1)+1;
2604  if (slider_info.y < slider_info.min_y)
2605  slider_info.y=slider_info.min_y;
2606  if (slider_info.y > slider_info.max_y)
2607  slider_info.y=slider_info.max_y;
2608  slider_info.id=0;
2609  if (slider_info.y != slider_info.min_y)
2610  slider_info.id=(int) ((colors*(slider_info.y-
2611  slider_info.min_y+1))/(slider_info.max_y-slider_info.min_y+1));
2612  state|=RedrawListState;
2613  break;
2614  }
2615  if (state & InactiveWidgetState)
2616  break;
2617  if (grab_info.raised == MatteIsActive(grab_info,event.xmotion))
2618  {
2619  /*
2620  Grab button status changed.
2621  */
2622  grab_info.raised=!grab_info.raised;
2623  XDrawBeveledButton(display,&windows->widget,&grab_info);
2624  break;
2625  }
2626  if (reset_info.raised == MatteIsActive(reset_info,event.xmotion))
2627  {
2628  /*
2629  Reset button status changed.
2630  */
2631  reset_info.raised=!reset_info.raised;
2632  XDrawBeveledButton(display,&windows->widget,&reset_info);
2633  break;
2634  }
2635  if (action_info.raised == MatteIsActive(action_info,event.xmotion))
2636  {
2637  /*
2638  Action button status changed.
2639  */
2640  action_info.raised=action_info.raised == MagickFalse ?
2641  MagickTrue : MagickFalse;
2642  XDrawBeveledButton(display,&windows->widget,&action_info);
2643  break;
2644  }
2645  if (cancel_info.raised == MatteIsActive(cancel_info,event.xmotion))
2646  {
2647  /*
2648  Cancel button status changed.
2649  */
2650  cancel_info.raised=cancel_info.raised == MagickFalse ?
2651  MagickTrue : MagickFalse;
2652  XDrawBeveledButton(display,&windows->widget,&cancel_info);
2653  break;
2654  }
2655  break;
2656  }
2657  case SelectionClear:
2658  {
2659  reply_info.highlight=MagickFalse;
2660  XDrawMatteText(display,&windows->widget,&reply_info);
2661  break;
2662  }
2663  case SelectionNotify:
2664  {
2665  Atom
2666  type;
2667 
2668  int
2669  format;
2670 
2671  unsigned char
2672  *data;
2673 
2674  unsigned long
2675  after,
2676  length;
2677 
2678  /*
2679  Obtain response from primary selection.
2680  */
2681  if (event.xselection.property == (Atom) None)
2682  break;
2683  status=XGetWindowProperty(display,event.xselection.requestor,
2684  event.xselection.property,0L,2047L,MagickTrue,XA_STRING,&type,
2685  &format,&length,&after,&data);
2686  if ((status != Success) || (type != XA_STRING) || (format == 32) ||
2687  (length == 0))
2688  break;
2689  if ((Extent(reply_info.text)+length) >= (MaxTextExtent-1))
2690  (void) XBell(display,0);
2691  else
2692  {
2693  /*
2694  Insert primary selection in reply text.
2695  */
2696  *(data+length)='\0';
2697  XEditText(display,&reply_info,(KeySym) XK_Insert,(char *) data,
2698  state);
2699  XDrawMatteText(display,&windows->widget,&reply_info);
2700  state|=JumpListState;
2701  state|=RedrawActionState;
2702  }
2703  (void) XFree((void *) data);
2704  break;
2705  }
2706  case SelectionRequest:
2707  {
2708  XSelectionEvent
2709  notify;
2710 
2711  XSelectionRequestEvent
2712  *request;
2713 
2714  if (reply_info.highlight == MagickFalse)
2715  break;
2716  /*
2717  Set primary selection.
2718  */
2719  request=(&(event.xselectionrequest));
2720  (void) XChangeProperty(request->display,request->requestor,
2721  request->property,request->target,8,PropModeReplace,
2722  (unsigned char *) primary_selection,Extent(primary_selection));
2723  notify.type=SelectionNotify;
2724  notify.send_event=MagickTrue;
2725  notify.display=request->display;
2726  notify.requestor=request->requestor;
2727  notify.selection=request->selection;
2728  notify.target=request->target;
2729  notify.time=request->time;
2730  if (request->property == None)
2731  notify.property=request->target;
2732  else
2733  notify.property=request->property;
2734  (void) XSendEvent(request->display,request->requestor,False,
2735  NoEventMask,(XEvent *) &notify);
2736  }
2737  default:
2738  break;
2739  }
2740  } while ((state & ExitState) == 0);
2741  XSetCursorState(display,windows,MagickFalse);
2742  (void) XWithdrawWindow(display,windows->widget.id,windows->widget.screen);
2743  XCheckRefreshWindows(display,windows);
2744  /*
2745  Free color list.
2746  */
2747  for (i=0; i < (int) colors; i++)
2748  colorlist[i]=DestroyString(colorlist[i]);
2749  if (colorlist != (char **) NULL)
2750  colorlist=(char **) RelinquishMagickMemory(colorlist);
2751  exception=DestroyExceptionInfo(exception);
2752  if ((*reply == '\0') || (strchr(reply,'-') != (char *) NULL))
2753  return;
2754  status=XParseColor(display,windows->widget.map_info->colormap,reply,&color);
2755  if (status != False)
2756  return;
2757  XNoticeWidget(display,windows,"Color is unknown to X server:",reply);
2758  (void) CopyMagickString(reply,"gray",MaxTextExtent);
2759 }
2760 
2761 /*
2762 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2763 % %
2764 % %
2765 % %
2766 % X C o m m a n d W i d g e t %
2767 % %
2768 % %
2769 % %
2770 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2771 %
2772 % XCommandWidget() maps a menu and returns the command pointed to by the user
2773 % when the button is released.
2774 %
2775 % The format of the XCommandWidget method is:
2776 %
2777 % int XCommandWidget(Display *display,XWindows *windows,
2778 % const char *const *selections,XEvent *event)
2779 %
2780 % A description of each parameter follows:
2781 %
2782 % o selection_number: Specifies the number of the selection that the
2783 % user choose.
2784 %
2785 % o display: Specifies a connection to an X server; returned from
2786 % XOpenDisplay.
2787 %
2788 % o window: Specifies a pointer to a XWindows structure.
2789 %
2790 % o selections: Specifies a pointer to one or more strings that comprise
2791 % the choices in the menu.
2792 %
2793 % o event: Specifies a pointer to a X11 XEvent structure.
2794 %
2795 */
2796 MagickExport int XCommandWidget(Display *display,XWindows *windows,
2797  const char *const *selections,XEvent *event)
2798 {
2799 #define tile_width 112
2800 #define tile_height 70
2801 
2802  static const unsigned char
2803  tile_bits[]=
2804  {
2805  0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2806  0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2807  0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2808  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00,
2809  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00,
2810  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00,
2811  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2812  0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2813  0x00, 0x00, 0x1e, 0x38, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2814  0x00, 0x00, 0x00, 0x00, 0x1e, 0xbc, 0x9f, 0x03, 0x00, 0x3e, 0x00, 0xc0,
2815  0x1f, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x1e, 0xfc, 0xff, 0x0f, 0x80, 0x3f,
2816  0x00, 0xf0, 0x1f, 0xc0, 0x0f, 0x00, 0x00, 0x00, 0x1e, 0xfc, 0xff, 0x1f,
2817  0xe0, 0x3f, 0x00, 0xfc, 0x1f, 0xf0, 0x0f, 0x00, 0x00, 0x00, 0x1e, 0xfc,
2818  0xff, 0x1f, 0xf0, 0x3f, 0x00, 0xfe, 0x1f, 0xf8, 0x0f, 0x00, 0x00, 0x00,
2819  0x1e, 0xfc, 0xfc, 0x3f, 0xf8, 0x3f, 0x00, 0xff, 0x1e, 0xfc, 0x0f, 0x00,
2820  0x00, 0x00, 0x1e, 0x7c, 0xfc, 0x3e, 0xf8, 0x3c, 0x80, 0x1f, 0x1e, 0x7c,
2821  0x0f, 0x00, 0x00, 0x00, 0x1e, 0x78, 0x78, 0x3c, 0x7c, 0x3c, 0xc0, 0x0f,
2822  0x1e, 0x3e, 0x0f, 0x00, 0x00, 0x00, 0x1e, 0x78, 0x78, 0x3c, 0x7c, 0x3c,
2823  0xc0, 0x07, 0x1e, 0x3e, 0x0f, 0x00, 0x00, 0x00, 0x1e, 0x78, 0x78, 0x3c,
2824  0x7c, 0x7c, 0xc0, 0x0f, 0x1e, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x78,
2825  0x78, 0x3c, 0xfc, 0x7c, 0x80, 0x7f, 0x1e, 0x7c, 0x00, 0x00, 0x00, 0x00,
2826  0x1e, 0xf8, 0x78, 0x7c, 0xf8, 0xff, 0x00, 0xff, 0x1f, 0xf8, 0xff, 0x00,
2827  0x00, 0x00, 0x1e, 0xf8, 0x78, 0x7c, 0xf0, 0xff, 0x07, 0xfe, 0x1f, 0xf8,
2828  0xff, 0x00, 0x00, 0x00, 0x1e, 0xf8, 0x78, 0x7c, 0xf0, 0xff, 0x07, 0xf8,
2829  0x1f, 0xf0, 0xff, 0x01, 0x00, 0x00, 0x1e, 0xf8, 0x78, 0x7c, 0xc0, 0xef,
2830  0x07, 0xe0, 0x1f, 0xc0, 0xff, 0x01, 0x00, 0x00, 0x1e, 0x70, 0x40, 0x78,
2831  0x00, 0xc7, 0x07, 0x00, 0x1e, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x1e, 0x00,
2832  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00,
2833  0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00,
2834  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00,
2835  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
2836  0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2837  0x00, 0xc0, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2838  0x00, 0x00, 0x00, 0xe0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2839  0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
2840  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x01, 0x00, 0x00, 0x00,
2841  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x00,
2842  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78,
2843  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2844  0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x02, 0x00,
2845  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x07,
2846  0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2847  0xc0, 0x0f, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2848  0x60, 0x00, 0xc0, 0x0f, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2849  0x00, 0x00, 0x78, 0x00, 0xc0, 0x8f, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00,
2850  0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0xc0, 0x8f, 0x3f, 0x00, 0x00, 0x00,
2851  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0xe0, 0x9f, 0x7f, 0x00,
2852  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0xe0, 0xdf,
2853  0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x78, 0x00,
2854  0xe0, 0xdf, 0x7b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x0c,
2855  0x78, 0x30, 0xf0, 0xff, 0x7b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e,
2856  0x00, 0x0f, 0xf8, 0x70, 0xf0, 0xff, 0x7b, 0x00, 0x00, 0x1f, 0x00, 0xe0,
2857  0x0f, 0x1e, 0x80, 0x0f, 0xf8, 0x78, 0xf0, 0xfd, 0xf9, 0x00, 0xc0, 0x1f,
2858  0x00, 0xf8, 0x0f, 0x00, 0xe0, 0x1f, 0xf8, 0x7c, 0xf0, 0xfc, 0xf9, 0x00,
2859  0xf0, 0x1f, 0x00, 0xfe, 0x0f, 0x00, 0xf0, 0x07, 0xf8, 0x3e, 0xf8, 0xfc,
2860  0xf0, 0x01, 0xf8, 0x1f, 0x00, 0xff, 0x0f, 0x1e, 0xf0, 0x03, 0xf8, 0x3f,
2861  0xf8, 0xf8, 0xf0, 0x01, 0xfc, 0x1f, 0x80, 0x7f, 0x0f, 0x1e, 0xf8, 0x00,
2862  0xf8, 0x1f, 0x78, 0x18, 0xf0, 0x01, 0x7c, 0x1e, 0xc0, 0x0f, 0x0f, 0x1e,
2863  0x7c, 0x00, 0xf0, 0x0f, 0x78, 0x00, 0xf0, 0x01, 0x3e, 0x1e, 0xe0, 0x07,
2864  0x0f, 0x1e, 0x7c, 0x00, 0xf0, 0x07, 0x7c, 0x00, 0xe0, 0x01, 0x3e, 0x1e,
2865  0xe0, 0x03, 0x0f, 0x1e, 0x3e, 0x00, 0xf0, 0x0f, 0x7c, 0x00, 0xe0, 0x03,
2866  0x3e, 0x3e, 0xe0, 0x07, 0x0f, 0x1e, 0x1e, 0x00, 0xf0, 0x1f, 0x3c, 0x00,
2867  0xe0, 0x03, 0x7e, 0x3e, 0xc0, 0x3f, 0x0f, 0x1e, 0x3e, 0x00, 0xf0, 0x1f,
2868  0x3e, 0x00, 0xe0, 0x03, 0xfc, 0x7f, 0x80, 0xff, 0x0f, 0x1e, 0xfc, 0x00,
2869  0xf0, 0x3e, 0x3e, 0x00, 0xc0, 0x03, 0xf8, 0xff, 0x03, 0xff, 0x0f, 0x1e,
2870  0xfc, 0x07, 0xf0, 0x7c, 0x1e, 0x00, 0xc0, 0x03, 0xf8, 0xff, 0x03, 0xfc,
2871  0x0f, 0x1e, 0xf8, 0x1f, 0xf0, 0xf8, 0x1e, 0x00, 0xc0, 0x03, 0xe0, 0xf7,
2872  0x03, 0xf0, 0x0f, 0x1e, 0xe0, 0x3f, 0xf0, 0x78, 0x1c, 0x00, 0x80, 0x03,
2873  0x80, 0xe3, 0x03, 0x00, 0x0f, 0x1e, 0xc0, 0x3f, 0xf0, 0x30, 0x00, 0x00,
2874  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0e, 0x00, 0x3e, 0x00, 0x00,
2875  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x0f, 0x00, 0x00, 0x10,
2876  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x0f, 0x00,
2877  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0,
2878  0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2879  0x00, 0xe0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2880  0x00, 0x00, 0x00, 0xf0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2881  0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
2882  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, 0x00,
2883  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00,
2884  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c,
2885  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2886  0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
2887  };
2888 
2889  int
2890  id,
2891  y;
2892 
2893  int
2894  i;
2895 
2896  static unsigned int
2897  number_selections;
2898 
2899  unsigned int
2900  height;
2901 
2902  size_t
2903  state;
2904 
2905  XFontStruct
2906  *font_info;
2907 
2908  assert(display != (Display *) NULL);
2909  assert(windows != (XWindows *) NULL);
2910  if (IsEventLogging() != MagickFalse)
2911  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2912  font_info=windows->command.font_info;
2913  height=(unsigned int) (font_info->ascent+font_info->descent);
2914  id=(~0);
2915  state=DefaultState;
2916  if (event == (XEvent *) NULL)
2917  {
2918  unsigned int
2919  width;
2920 
2921  XTextProperty
2922  window_name;
2923 
2924  XWindowChanges
2925  window_changes;
2926 
2927  /*
2928  Determine command window attributes.
2929  */
2930  assert(selections != (const char **) NULL);
2931  windows->command.width=0;
2932  for (i=0; selections[i] != (char *) NULL; i++)
2933  {
2934  width=WidgetTextWidth(font_info,(char *) selections[i]);
2935  if (width > windows->command.width)
2936  windows->command.width=width;
2937  }
2938  number_selections=(unsigned int) i;
2939  windows->command.width+=3*QuantumMargin+10;
2940  if ((int) windows->command.width < (tile_width+QuantumMargin+10))
2941  windows->command.width=(unsigned int) (tile_width+QuantumMargin+10);
2942  windows->command.height=(unsigned int) (number_selections*
2943  (((3*height) >> 1)+10)+tile_height+20);
2944  windows->command.min_width=windows->command.width;
2945  windows->command.min_height=windows->command.height;
2946  XConstrainWindowPosition(display,&windows->command);
2947  if (windows->command.id != (Window) NULL)
2948  {
2949  Status
2950  status;
2951 
2952  /*
2953  Reconfigure command window.
2954  */
2955  status=XStringListToTextProperty(&windows->command.name,1,
2956  &window_name);
2957  if (status != False)
2958  {
2959  XSetWMName(display,windows->command.id,&window_name);
2960  XSetWMIconName(display,windows->command.id,&window_name);
2961  (void) XFree((void *) window_name.value);
2962  }
2963  window_changes.width=(int) windows->command.width;
2964  window_changes.height=(int) windows->command.height;
2965  (void) XReconfigureWMWindow(display,windows->command.id,
2966  windows->command.screen,(unsigned int) (CWWidth | CWHeight),
2967  &window_changes);
2968  }
2969  /*
2970  Allocate selection info memory.
2971  */
2972  if (selection_info != (XWidgetInfo *) NULL)
2973  selection_info=(XWidgetInfo *) RelinquishMagickMemory(selection_info);
2974  selection_info=(XWidgetInfo *) AcquireQuantumMemory(number_selections,
2975  sizeof(*selection_info));
2976  if (selection_info == (XWidgetInfo *) NULL)
2977  ThrowXWindowFatalException(ResourceLimitFatalError,
2978  "MemoryAllocationFailed","...");
2979  state|=UpdateConfigurationState | RedrawWidgetState;
2980  }
2981  /*
2982  Wait for next event.
2983  */
2984  if (event != (XEvent *) NULL)
2985  switch (event->type)
2986  {
2987  case ButtonPress:
2988  {
2989  for (i=0; i < (int) number_selections; i++)
2990  {
2991  if (MatteIsActive(selection_info[i],event->xbutton) == MagickFalse)
2992  continue;
2993  if (i >= (int) windows->command.data)
2994  {
2995  selection_info[i].raised=MagickFalse;
2996  XDrawBeveledButton(display,&windows->command,&selection_info[i]);
2997  break;
2998  }
2999  submenu_info=selection_info[i];
3000  submenu_info.active=MagickTrue;
3001  toggle_info.y=submenu_info.y+(submenu_info.height >> 1)-
3002  (toggle_info.height >> 1);
3003  id=i;
3004  (void) XCheckWindowEvent(display,windows->widget.id,LeaveWindowMask,
3005  event);
3006  break;
3007  }
3008  break;
3009  }
3010  case ButtonRelease:
3011  {
3012  for (i=0; i < (int) number_selections; i++)
3013  {
3014  if (MatteIsActive(selection_info[i],event->xbutton) == MagickFalse)
3015  continue;
3016  id=i;
3017  if (id >= (int) windows->command.data)
3018  {
3019  selection_info[id].raised=MagickTrue;
3020  XDrawBeveledButton(display,&windows->command,&selection_info[id]);
3021  break;
3022  }
3023  break;
3024  }
3025  break;
3026  }
3027  case ClientMessage:
3028  {
3029  /*
3030  If client window delete message, withdraw command widget.
3031  */
3032  if (event->xclient.message_type != windows->wm_protocols)
3033  break;
3034  if (*event->xclient.data.l != (int) windows->wm_delete_window)
3035  break;
3036  (void) XWithdrawWindow(display,windows->command.id,
3037  windows->command.screen);
3038  break;
3039  }
3040  case ConfigureNotify:
3041  {
3042  /*
3043  Update widget configuration.
3044  */
3045  if (event->xconfigure.window != windows->command.id)
3046  break;
3047  if (event->xconfigure.send_event != 0)
3048  {
3049  windows->command.x=event->xconfigure.x;
3050  windows->command.y=event->xconfigure.y;
3051  }
3052  if ((event->xconfigure.width == (int) windows->command.width) &&
3053  (event->xconfigure.height == (int) windows->command.height))
3054  break;
3055  windows->command.width=(unsigned int)
3056  MagickMax(event->xconfigure.width,(int) windows->command.min_width);
3057  windows->command.height=(unsigned int)
3058  MagickMax(event->xconfigure.height,(int) windows->command.min_height);
3059  state|=UpdateConfigurationState;
3060  break;
3061  }
3062  case Expose:
3063  {
3064  if (event->xexpose.window != windows->command.id)
3065  break;
3066  if (event->xexpose.count != 0)
3067  break;
3068  state|=RedrawWidgetState;
3069  break;
3070  }
3071  case MotionNotify:
3072  {
3073  /*
3074  Return the ID of the highlighted menu entry.
3075  */
3076  for ( ; ; )
3077  {
3078  for (i=0; i < (int) number_selections; i++)
3079  {
3080  if (i >= (int) windows->command.data)
3081  {
3082  if (selection_info[i].raised ==
3083  MatteIsActive(selection_info[i],event->xmotion))
3084  {
3085  /*
3086  Button status changed.
3087  */
3088  selection_info[i].raised=!selection_info[i].raised;
3089  XDrawBeveledButton(display,&windows->command,
3090  &selection_info[i]);
3091  }
3092  continue;
3093  }
3094  if (MatteIsActive(selection_info[i],event->xmotion) == MagickFalse)
3095  continue;
3096  submenu_info=selection_info[i];
3097  submenu_info.active=MagickTrue;
3098  toggle_info.raised=MagickTrue;
3099  toggle_info.y=submenu_info.y+(submenu_info.height >> 1)-
3100  (toggle_info.height >> 1);
3101  XDrawTriangleEast(display,&windows->command,&toggle_info);
3102  id=i;
3103  }
3104  XDelay(display,SuspendTime);
3105  if (XCheckMaskEvent(display,ButtonMotionMask,event) == MagickFalse)
3106  break;
3107  while (XCheckMaskEvent(display,ButtonMotionMask,event)) ;
3108  toggle_info.raised=MagickFalse;
3109  if (windows->command.data != 0)
3110  XDrawTriangleEast(display,&windows->command,&toggle_info);
3111  }
3112  break;
3113  }
3114  case MapNotify:
3115  {
3116  windows->command.mapped=MagickTrue;
3117  break;
3118  }
3119  case UnmapNotify:
3120  {
3121  windows->command.mapped=MagickFalse;
3122  break;
3123  }
3124  default:
3125  break;
3126  }
3127  if (state & UpdateConfigurationState)
3128  {
3129  /*
3130  Initialize button information.
3131  */
3132  assert(selections != (const char **) NULL);
3133  y=tile_height+20;
3134  for (i=0; i < (int) number_selections; i++)
3135  {
3136  XGetWidgetInfo(selections[i],&selection_info[i]);
3137  selection_info[i].center=MagickFalse;
3138  selection_info[i].bevel_width--;
3139  selection_info[i].height=(unsigned int) ((3*height) >> 1);
3140  selection_info[i].x=(QuantumMargin >> 1)+4;
3141  selection_info[i].width=(unsigned int) (windows->command.width-
3142  (selection_info[i].x << 1));
3143  selection_info[i].y=y;
3144  y+=selection_info[i].height+(selection_info[i].bevel_width << 1)+6;
3145  }
3146  XGetWidgetInfo((char *) NULL,&toggle_info);
3147  toggle_info.bevel_width--;
3148  toggle_info.width=(unsigned int) (((5*height) >> 3)-
3149  (toggle_info.bevel_width << 1));
3150  toggle_info.height=toggle_info.width;
3151  toggle_info.x=selection_info[0].x+selection_info[0].width-
3152  toggle_info.width-(QuantumMargin >> 1);
3153  if (windows->command.mapped)
3154  (void) XClearWindow(display,windows->command.id);
3155  }
3156  if (state & RedrawWidgetState)
3157  {
3158  Pixmap
3159  tile_pixmap;
3160 
3161  /*
3162  Draw command buttons.
3163  */
3164  tile_pixmap=XCreatePixmapFromBitmapData(display,windows->command.id,
3165  (char *) tile_bits,tile_width,tile_height,1L,0L,1);
3166  if (tile_pixmap != (Pixmap) NULL)
3167  {
3168  (void) XCopyPlane(display,tile_pixmap,windows->command.id,
3169  windows->command.annotate_context,0,0,tile_width,tile_height,
3170  (int) ((windows->command.width-tile_width) >> 1),10,1L);
3171  (void) XFreePixmap(display,tile_pixmap);
3172  }
3173  for (i=0; i < (int) number_selections; i++)
3174  {
3175  XDrawBeveledButton(display,&windows->command,&selection_info[i]);
3176  if (i >= (int) windows->command.data)
3177  continue;
3178  toggle_info.raised=MagickFalse;
3179  toggle_info.y=selection_info[i].y+(selection_info[i].height >> 1)-
3180  (toggle_info.height >> 1);
3181  XDrawTriangleEast(display,&windows->command,&toggle_info);
3182  }
3183  XHighlightWidget(display,&windows->command,BorderOffset,BorderOffset);
3184  }
3185  return(id);
3186 }
3187 
3188 /*
3189 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3190 % %
3191 % %
3192 % %
3193 % X C o n f i r m W i d g e t %
3194 % %
3195 % %
3196 % %
3197 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3198 %
3199 % XConfirmWidget() displays a Confirm widget with a notice to the user. The
3200 % function returns -1 if Dismiss is pressed, 0 for Cancel, and 1 for Yes.
3201 %
3202 % The format of the XConfirmWidget method is:
3203 %
3204 % int XConfirmWidget(Display *display,XWindows *windows,
3205 % const char *reason,const char *description)
3206 %
3207 % A description of each parameter follows:
3208 %
3209 % o display: Specifies a connection to an X server; returned from
3210 % XOpenDisplay.
3211 %
3212 % o window: Specifies a pointer to a XWindows structure.
3213 %
3214 % o reason: Specifies the message to display before terminating the
3215 % program.
3216 %
3217 % o description: Specifies any description to the message.
3218 %
3219 */
3220 MagickExport int XConfirmWidget(Display *display,XWindows *windows,
3221  const char *reason,const char *description)
3222 {
3223 #define CancelButtonText "Cancel"
3224 #define DismissButtonText "Dismiss"
3225 #define YesButtonText "Yes"
3226 
3227  int
3228  confirm,
3229  x,
3230  y;
3231 
3232  Status
3233  status;
3234 
3235  unsigned int
3236  height,
3237  width;
3238 
3239  size_t
3240  state;
3241 
3242  XEvent
3243  event;
3244 
3245  XFontStruct
3246  *font_info;
3247 
3248  XTextProperty
3249  window_name;
3250 
3251  XWidgetInfo
3252  cancel_info,
3253  dismiss_info,
3254  yes_info;
3255 
3256  XWindowChanges
3257  window_changes;
3258 
3259  /*
3260  Determine Confirm widget attributes.
3261  */
3262  assert(display != (Display *) NULL);
3263  assert(windows != (XWindows *) NULL);
3264  assert(reason != (char *) NULL);
3265  assert(description != (char *) NULL);
3266  if (IsEventLogging() != MagickFalse)
3267  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",reason);
3268  XCheckRefreshWindows(display,windows);
3269  font_info=windows->widget.font_info;
3270  width=WidgetTextWidth(font_info,CancelButtonText);
3271  if (WidgetTextWidth(font_info,DismissButtonText) > width)
3272  width=WidgetTextWidth(font_info,DismissButtonText);
3273  if (WidgetTextWidth(font_info,YesButtonText) > width)
3274  width=WidgetTextWidth(font_info,YesButtonText);
3275  width<<=1;
3276  if (description != (char *) NULL)
3277  if (WidgetTextWidth(font_info,(char *) description) > width)
3278  width=WidgetTextWidth(font_info,(char *) description);
3279  height=(unsigned int) (font_info->ascent+font_info->descent);
3280  /*
3281  Position Confirm widget.
3282  */
3283  windows->widget.width=(unsigned int) (width+9*QuantumMargin);
3284  windows->widget.min_width=(unsigned int) (9*QuantumMargin+
3285  WidgetTextWidth(font_info,CancelButtonText)+
3286  WidgetTextWidth(font_info,DismissButtonText)+
3287  WidgetTextWidth(font_info,YesButtonText));
3288  if (windows->widget.width < windows->widget.min_width)
3289  windows->widget.width=windows->widget.min_width;
3290  windows->widget.height=(unsigned int) (12*height);
3291  windows->widget.min_height=(unsigned int) (7*height);
3292  if (windows->widget.height < windows->widget.min_height)
3293  windows->widget.height=windows->widget.min_height;
3294  XConstrainWindowPosition(display,&windows->widget);
3295  /*
3296  Map Confirm widget.
3297  */
3298  (void) CopyMagickString(windows->widget.name,"Confirm",MaxTextExtent);
3299  status=XStringListToTextProperty(&windows->widget.name,1,&window_name);
3300  if (status != False)
3301  {
3302  XSetWMName(display,windows->widget.id,&window_name);
3303  XSetWMIconName(display,windows->widget.id,&window_name);
3304  (void) XFree((void *) window_name.value);
3305  }
3306  window_changes.width=(int) windows->widget.width;
3307  window_changes.height=(int) windows->widget.height;
3308  window_changes.x=windows->widget.x;
3309  window_changes.y=windows->widget.y;
3310  (void) XReconfigureWMWindow(display,windows->widget.id,windows->widget.screen,
3311  (unsigned int) (CWWidth | CWHeight | CWX | CWY),&window_changes);
3312  (void) XMapRaised(display,windows->widget.id);
3313  windows->widget.mapped=MagickFalse;
3314  /*
3315  Respond to X events.
3316  */
3317  confirm=0;
3318  state=UpdateConfigurationState;
3319  XSetCursorState(display,windows,MagickTrue);
3320  do
3321  {
3322  if (state & UpdateConfigurationState)
3323  {
3324  /*
3325  Initialize button information.
3326  */
3327  XGetWidgetInfo(CancelButtonText,&cancel_info);
3328  cancel_info.width=(unsigned int) QuantumMargin+
3329  WidgetTextWidth(font_info,CancelButtonText);
3330  cancel_info.height=(unsigned int) ((3*height) >> 1);
3331  cancel_info.x=(int) (windows->widget.width-cancel_info.width-
3332  QuantumMargin);
3333  cancel_info.y=(int) (windows->widget.height-(cancel_info.height << 1));
3334  dismiss_info=cancel_info;
3335  dismiss_info.text=(char *) DismissButtonText;
3336  if (LocaleCompare(description,"Do you want to save it") == 0)
3337  dismiss_info.text=(char *) "Don't Save";
3338  dismiss_info.width=(unsigned int) QuantumMargin+
3339  WidgetTextWidth(font_info,dismiss_info.text);
3340  dismiss_info.x=(int)
3341  ((windows->widget.width >> 1)-(dismiss_info.width >> 1));
3342  yes_info=cancel_info;
3343  yes_info.text=(char *) YesButtonText;
3344  if (LocaleCompare(description,"Do you want to save it") == 0)
3345  yes_info.text=(char *) "Save";
3346  yes_info.width=(unsigned int) QuantumMargin+
3347  WidgetTextWidth(font_info,yes_info.text);
3348  if (yes_info.width < cancel_info.width)
3349  yes_info.width=cancel_info.width;
3350  yes_info.x=QuantumMargin;
3351  state&=(~UpdateConfigurationState);
3352  }
3353  if (state & RedrawWidgetState)
3354  {
3355  /*
3356  Redraw Confirm widget.
3357  */
3358  width=WidgetTextWidth(font_info,(char *) reason);
3359  x=(int) ((windows->widget.width >> 1)-(width >> 1));
3360  y=(int) ((windows->widget.height >> 1)-(height << 1));
3361  (void) XDrawString(display,windows->widget.id,
3362  windows->widget.annotate_context,x,y,(char *) reason,Extent(reason));
3363  if (description != (char *) NULL)
3364  {
3365  char
3366  question[MaxTextExtent];
3367 
3368  (void) CopyMagickString(question,description,MaxTextExtent);
3369  (void) ConcatenateMagickString(question,"?",MaxTextExtent);
3370  width=WidgetTextWidth(font_info,question);
3371  x=(int) ((windows->widget.width >> 1)-(width >> 1));
3372  y+=height;
3373  (void) XDrawString(display,windows->widget.id,
3374  windows->widget.annotate_context,x,y,question,Extent(question));
3375  }
3376  XDrawBeveledButton(display,&windows->widget,&cancel_info);
3377  XDrawBeveledButton(display,&windows->widget,&dismiss_info);
3378  XDrawBeveledButton(display,&windows->widget,&yes_info);
3379  XHighlightWidget(display,&windows->widget,BorderOffset,BorderOffset);
3380  state&=(~RedrawWidgetState);
3381  }
3382  /*
3383  Wait for next event.
3384  */
3385  (void) XIfEvent(display,&event,XScreenEvent,(char *) windows);
3386  switch (event.type)
3387  {
3388  case ButtonPress:
3389  {
3390  if (MatteIsActive(cancel_info,event.xbutton))
3391  {
3392  /*
3393  User pressed No button.
3394  */
3395  cancel_info.raised=MagickFalse;
3396  XDrawBeveledButton(display,&windows->widget,&cancel_info);
3397  break;
3398  }
3399  if (MatteIsActive(dismiss_info,event.xbutton))
3400  {
3401  /*
3402  User pressed Dismiss button.
3403  */
3404  dismiss_info.raised=MagickFalse;
3405  XDrawBeveledButton(display,&windows->widget,&dismiss_info);
3406  break;
3407  }
3408  if (MatteIsActive(yes_info,event.xbutton))
3409  {
3410  /*
3411  User pressed Yes button.
3412  */
3413  yes_info.raised=MagickFalse;
3414  XDrawBeveledButton(display,&windows->widget,&yes_info);
3415  break;
3416  }
3417  break;
3418  }
3419  case ButtonRelease:
3420  {
3421  if (windows->widget.mapped == MagickFalse)
3422  break;
3423  if (cancel_info.raised == MagickFalse)
3424  {
3425  if (event.xbutton.window == windows->widget.id)
3426  if (MatteIsActive(cancel_info,event.xbutton))
3427  {
3428  confirm=0;
3429  state|=ExitState;
3430  }
3431  cancel_info.raised=MagickTrue;
3432  XDrawBeveledButton(display,&windows->widget,&cancel_info);
3433  }
3434  if (dismiss_info.raised == MagickFalse)
3435  {
3436  if (event.xbutton.window == windows->widget.id)
3437  if (MatteIsActive(dismiss_info,event.xbutton))
3438  {
3439  confirm=(-1);
3440  state|=ExitState;
3441  }
3442  dismiss_info.raised=MagickTrue;
3443  XDrawBeveledButton(display,&windows->widget,&dismiss_info);
3444  }
3445  if (yes_info.raised == MagickFalse)
3446  {
3447  if (event.xbutton.window == windows->widget.id)
3448  if (MatteIsActive(yes_info,event.xbutton))
3449  {
3450  confirm=1;
3451  state|=ExitState;
3452  }
3453  yes_info.raised=MagickTrue;
3454  XDrawBeveledButton(display,&windows->widget,&yes_info);
3455  }
3456  break;
3457  }
3458  case ClientMessage:
3459  {
3460  /*
3461  If client window delete message, exit.
3462  */
3463  if (event.xclient.message_type != windows->wm_protocols)
3464  break;
3465  if (*event.xclient.data.l == (int) windows->wm_take_focus)
3466  {
3467  (void) XSetInputFocus(display,event.xclient.window,RevertToParent,
3468  (Time) event.xclient.data.l[1]);
3469  break;
3470  }
3471  if (*event.xclient.data.l != (int) windows->wm_delete_window)
3472  break;
3473  if (event.xclient.window == windows->widget.id)
3474  {
3475  state|=ExitState;
3476  break;
3477  }
3478  break;
3479  }
3480  case ConfigureNotify:
3481  {
3482  /*
3483  Update widget configuration.
3484  */
3485  if (event.xconfigure.window != windows->widget.id)
3486  break;
3487  if ((event.xconfigure.width == (int) windows->widget.width) &&
3488  (event.xconfigure.height == (int) windows->widget.height))
3489  break;
3490  windows->widget.width=(unsigned int)
3491  MagickMax(event.xconfigure.width,(int) windows->widget.min_width);
3492  windows->widget.height=(unsigned int)
3493  MagickMax(event.xconfigure.height,(int) windows->widget.min_height);
3494  state|=UpdateConfigurationState;
3495  break;
3496  }
3497  case EnterNotify:
3498  {
3499  if (event.xcrossing.window != windows->widget.id)
3500  break;
3501  state&=(~InactiveWidgetState);
3502  break;
3503  }
3504  case Expose:
3505  {
3506  if (event.xexpose.window != windows->widget.id)
3507  break;
3508  if (event.xexpose.count != 0)
3509  break;
3510  state|=RedrawWidgetState;
3511  break;
3512  }
3513  case KeyPress:
3514  {
3515  static char
3516  command[MaxTextExtent];
3517 
3518  static KeySym
3519  key_symbol;
3520 
3521  /*
3522  Respond to a user key press.
3523  */
3524  if (event.xkey.window != windows->widget.id)
3525  break;
3526  (void) XLookupString((XKeyEvent *) &event.xkey,command,
3527  (int) sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3528  if ((key_symbol == XK_Return) || (key_symbol == XK_KP_Enter))
3529  {
3530  yes_info.raised=MagickFalse;
3531  XDrawBeveledButton(display,&windows->widget,&yes_info);
3532  confirm=1;
3533  state|=ExitState;
3534  break;
3535  }
3536  break;
3537  }
3538  case LeaveNotify:
3539  {
3540  if (event.xcrossing.window != windows->widget.id)
3541  break;
3542  state|=InactiveWidgetState;
3543  break;
3544  }
3545  case MotionNotify:
3546  {
3547  /*
3548  Discard pending button motion events.
3549  */
3550  while (XCheckMaskEvent(display,ButtonMotionMask,&event)) ;
3551  if (state & InactiveWidgetState)
3552  break;
3553  if (cancel_info.raised == MatteIsActive(cancel_info,event.xmotion))
3554  {
3555  /*
3556  Cancel button status changed.
3557  */
3558  cancel_info.raised=cancel_info.raised == MagickFalse ?
3559  MagickTrue : MagickFalse;
3560  XDrawBeveledButton(display,&windows->widget,&cancel_info);
3561  break;
3562  }
3563  if (dismiss_info.raised == MatteIsActive(dismiss_info,event.xmotion))
3564  {
3565  /*
3566  Dismiss button status changed.
3567  */
3568  dismiss_info.raised=dismiss_info.raised == MagickFalse ?
3569  MagickTrue : MagickFalse;
3570  XDrawBeveledButton(display,&windows->widget,&dismiss_info);
3571  break;
3572  }
3573  if (yes_info.raised == MatteIsActive(yes_info,event.xmotion))
3574  {
3575  /*
3576  Yes button status changed.
3577  */
3578  yes_info.raised=yes_info.raised == MagickFalse ?
3579  MagickTrue : MagickFalse;
3580  XDrawBeveledButton(display,&windows->widget,&yes_info);
3581  break;
3582  }
3583  break;
3584  }
3585  default:
3586  break;
3587  }
3588  } while ((state & ExitState) == 0);
3589  XSetCursorState(display,windows,MagickFalse);
3590  (void) XWithdrawWindow(display,windows->widget.id,windows->widget.screen);
3591  XCheckRefreshWindows(display,windows);
3592  return(confirm);
3593 }
3594 
3595 /*
3596 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3597 % %
3598 % %
3599 % %
3600 % X D i a l o g W i d g e t %
3601 % %
3602 % %
3603 % %
3604 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3605 %
3606 % XDialogWidget() displays a Dialog widget with a query to the user. The user
3607 % keys a reply and presses the Ok or Cancel button to exit. The typed text is
3608 % returned as the reply function parameter.
3609 %
3610 % The format of the XDialogWidget method is:
3611 %
3612 % int XDialogWidget(Display *display,XWindows *windows,const char *action,
3613 % const char *query,char *reply)
3614 %
3615 % A description of each parameter follows:
3616 %
3617 % o display: Specifies a connection to an X server; returned from
3618 % XOpenDisplay.
3619 %
3620 % o window: Specifies a pointer to a XWindows structure.
3621 %
3622 % o action: Specifies a pointer to the action of this widget.
3623 %
3624 % o query: Specifies a pointer to the query to present to the user.
3625 %
3626 % o reply: the response from the user is returned in this parameter.
3627 %
3628 */
3629 MagickExport int XDialogWidget(Display *display,XWindows *windows,
3630  const char *action,const char *query,char *reply)
3631 {
3632 #define CancelButtonText "Cancel"
3633 
3634  char
3635  primary_selection[MaxTextExtent];
3636 
3637  int
3638  x;
3639 
3640  int
3641  i;
3642 
3643  static MagickBooleanType
3644  raised = MagickFalse;
3645 
3646  Status
3647  status;
3648 
3649  unsigned int
3650  anomaly,
3651  height,
3652  width;
3653 
3654  size_t
3655  state;
3656 
3657  XEvent
3658  event;
3659 
3660  XFontStruct
3661  *font_info;
3662 
3663  XTextProperty
3664  window_name;
3665 
3666  XWidgetInfo
3667  action_info,
3668  cancel_info,
3669  reply_info,
3670  special_info,
3671  text_info;
3672 
3673  XWindowChanges
3674  window_changes;
3675 
3676  /*
3677  Determine Dialog widget attributes.
3678  */
3679  assert(display != (Display *) NULL);
3680  assert(windows != (XWindows *) NULL);
3681  assert(action != (char *) NULL);
3682  assert(query != (char *) NULL);
3683  assert(reply != (char *) NULL);
3684  if (IsEventLogging() != MagickFalse)
3685  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",action);
3686  XCheckRefreshWindows(display,windows);
3687  font_info=windows->widget.font_info;
3688  width=WidgetTextWidth(font_info,(char *) action);
3689  if (WidgetTextWidth(font_info,CancelButtonText) > width)
3690  width=WidgetTextWidth(font_info,CancelButtonText);
3691  width+=(3*QuantumMargin) >> 1;
3692  height=(unsigned int) (font_info->ascent+font_info->descent);
3693  /*
3694  Position Dialog widget.
3695  */
3696  windows->widget.width=(unsigned int) MagickMax((int) (2*width),(int)
3697  WidgetTextWidth(font_info,(char *) query));
3698  if (windows->widget.width < WidgetTextWidth(font_info,reply))
3699  windows->widget.width=WidgetTextWidth(font_info,reply);
3700  windows->widget.width+=6*QuantumMargin;
3701  windows->widget.min_width=(unsigned int)
3702  (width+28*XTextWidth(font_info,"#",1)+4*QuantumMargin);
3703  if (windows->widget.width < windows->widget.min_width)
3704  windows->widget.width=windows->widget.min_width;
3705  windows->widget.height=(unsigned int) (7*height+(QuantumMargin << 1));
3706  windows->widget.min_height=windows->widget.height;
3707  if (windows->widget.height < windows->widget.min_height)
3708  windows->widget.height=windows->widget.min_height;
3709  XConstrainWindowPosition(display,&windows->widget);
3710  /*
3711  Map Dialog widget.
3712  */
3713  (void) CopyMagickString(windows->widget.name,"Dialog",MaxTextExtent);
3714  status=XStringListToTextProperty(&windows->widget.name,1,&window_name);
3715  if (status != False)
3716  {
3717  XSetWMName(display,windows->widget.id,&window_name);
3718  XSetWMIconName(display,windows->widget.id,&window_name);
3719  (void) XFree((void *) window_name.value);
3720  }
3721  window_changes.width=(int) windows->widget.width;
3722  window_changes.height=(int) windows->widget.height;
3723  window_changes.x=windows->widget.x;
3724  window_changes.y=windows->widget.y;
3725  (void) XReconfigureWMWindow(display,windows->widget.id,windows->widget.screen,
3726  (unsigned int) (CWWidth | CWHeight | CWX | CWY),&window_changes);
3727  (void) XMapRaised(display,windows->widget.id);
3728  windows->widget.mapped=MagickFalse;
3729  /*
3730  Respond to X events.
3731  */
3732  anomaly=(LocaleCompare(action,"Background") == 0) ||
3733  (LocaleCompare(action,"New") == 0) ||
3734  (LocaleCompare(action,"Quantize") == 0) ||
3735  (LocaleCompare(action,"Resize") == 0) ||
3736  (LocaleCompare(action,"Save") == 0) ||
3737  (LocaleCompare(action,"Shade") == 0);
3738  state=UpdateConfigurationState;
3739  XSetCursorState(display,windows,MagickTrue);
3740  do
3741  {
3742  if (state & UpdateConfigurationState)
3743  {
3744  /*
3745  Initialize button information.
3746  */
3747  XGetWidgetInfo(CancelButtonText,&cancel_info);
3748  cancel_info.width=width;
3749  cancel_info.height=(unsigned int) ((3*height) >> 1);
3750  cancel_info.x=(int)
3751  (windows->widget.width-cancel_info.width-((3*QuantumMargin) >> 1));
3752  cancel_info.y=(int)
3753  (windows->widget.height-cancel_info.height-((3*QuantumMargin) >> 1));
3754  XGetWidgetInfo(action,&action_info);
3755  action_info.width=width;
3756  action_info.height=(unsigned int) ((3*height) >> 1);
3757  action_info.x=cancel_info.x-(cancel_info.width+QuantumMargin+
3758  (action_info.bevel_width << 1));
3759  action_info.y=cancel_info.y;
3760  /*
3761  Initialize reply information.
3762  */
3763  XGetWidgetInfo(reply,&reply_info);
3764  reply_info.raised=MagickFalse;
3765  reply_info.bevel_width--;
3766  reply_info.width=windows->widget.width-(3*QuantumMargin);
3767  reply_info.height=height << 1;
3768  reply_info.x=(3*QuantumMargin) >> 1;
3769  reply_info.y=action_info.y-reply_info.height-QuantumMargin;
3770  /*
3771  Initialize option information.
3772  */
3773  XGetWidgetInfo("Dither",&special_info);
3774  special_info.raised=raised;
3775  special_info.bevel_width--;
3776  special_info.width=(unsigned int) QuantumMargin >> 1;
3777  special_info.height=(unsigned int) QuantumMargin >> 1;
3778  special_info.x=reply_info.x;
3779  special_info.y=action_info.y+action_info.height-special_info.height;
3780  if (LocaleCompare(action,"Background") == 0)
3781  special_info.text=(char *) "Backdrop";
3782  if (LocaleCompare(action,"New") == 0)
3783  special_info.text=(char *) "Gradation";
3784  if (LocaleCompare(action,"Resize") == 0)
3785  special_info.text=(char *) "Constrain ratio";
3786  if (LocaleCompare(action,"Save") == 0)
3787  special_info.text=(char *) "Non-progressive";
3788  if (LocaleCompare(action,"Shade") == 0)
3789  special_info.text=(char *) "Color shading";
3790  /*
3791  Initialize text information.
3792  */
3793  XGetWidgetInfo(query,&text_info);
3794  text_info.width=reply_info.width;
3795  text_info.height=height;
3796  text_info.x=reply_info.x-(QuantumMargin >> 1);
3797  text_info.y=QuantumMargin;
3798  text_info.center=MagickFalse;
3799  state&=(~UpdateConfigurationState);
3800  }
3801  if (state & RedrawWidgetState)
3802  {
3803  /*
3804  Redraw Dialog widget.
3805  */
3806  XDrawWidgetText(display,&windows->widget,&text_info);
3807  XDrawBeveledMatte(display,&windows->widget,&reply_info);
3808  XDrawMatteText(display,&windows->widget,&reply_info);
3809  if (anomaly)
3810  XDrawBeveledButton(display,&windows->widget,&special_info);
3811  XDrawBeveledButton(display,&windows->widget,&action_info);
3812  XDrawBeveledButton(display,&windows->widget,&cancel_info);
3813  XHighlightWidget(display,&windows->widget,BorderOffset,BorderOffset);
3814  state&=(~RedrawWidgetState);
3815  }
3816  /*
3817  Wait for next event.
3818  */
3819  (void) XIfEvent(display,&event,XScreenEvent,(char *) windows);
3820  switch (event.type)
3821  {
3822  case ButtonPress:
3823  {
3824  if (anomaly)
3825  if (MatteIsActive(special_info,event.xbutton))
3826  {
3827  /*
3828  Option button status changed.
3829  */
3830  special_info.raised=!special_info.raised;
3831  XDrawBeveledButton(display,&windows->widget,&special_info);
3832  break;
3833  }
3834  if (MatteIsActive(action_info,event.xbutton))
3835  {
3836  /*
3837  User pressed Action button.
3838  */
3839  action_info.raised=MagickFalse;
3840  XDrawBeveledButton(display,&windows->widget,&action_info);
3841  break;
3842  }
3843  if (MatteIsActive(cancel_info,event.xbutton))
3844  {
3845  /*
3846  User pressed Cancel button.
3847  */
3848  cancel_info.raised=MagickFalse;
3849  XDrawBeveledButton(display,&windows->widget,&cancel_info);
3850  break;
3851  }
3852  if (MatteIsActive(reply_info,event.xbutton) == MagickFalse)
3853  break;
3854  if (event.xbutton.button != Button2)
3855  {
3856  static Time
3857  click_time;
3858 
3859  /*
3860  Move text cursor to position of button press.
3861  */
3862  x=event.xbutton.x-reply_info.x-(QuantumMargin >> 2);
3863  for (i=1; i <= Extent(reply_info.marker); i++)
3864  if (XTextWidth(font_info,reply_info.marker,i) > x)
3865  break;
3866  reply_info.cursor=reply_info.marker+i-1;
3867  if (event.xbutton.time > (click_time+DoubleClick))
3868  reply_info.highlight=MagickFalse;
3869  else
3870  {
3871  /*
3872  Become the XA_PRIMARY selection owner.
3873  */
3874  (void) CopyMagickString(primary_selection,reply_info.text,
3875  MaxTextExtent);
3876  (void) XSetSelectionOwner(display,XA_PRIMARY,windows->widget.id,
3877  event.xbutton.time);
3878  reply_info.highlight=XGetSelectionOwner(display,XA_PRIMARY) ==
3879  windows->widget.id ? MagickTrue : MagickFalse;
3880  }
3881  XDrawMatteText(display,&windows->widget,&reply_info);
3882  click_time=event.xbutton.time;
3883  break;
3884  }
3885  /*
3886  Request primary selection.
3887  */
3888  (void) XConvertSelection(display,XA_PRIMARY,XA_STRING,XA_STRING,
3889  windows->widget.id,event.xbutton.time);
3890  break;
3891  }
3892  case ButtonRelease:
3893  {
3894  if (windows->widget.mapped == MagickFalse)
3895  break;
3896  if (action_info.raised == MagickFalse)
3897  {
3898  if (event.xbutton.window == windows->widget.id)
3899  if (MatteIsActive(action_info,event.xbutton))
3900  state|=ExitState;
3901  action_info.raised=MagickTrue;
3902  XDrawBeveledButton(display,&windows->widget,&action_info);
3903  }
3904  if (cancel_info.raised == MagickFalse)
3905  {
3906  if (event.xbutton.window == windows->widget.id)
3907  if (MatteIsActive(cancel_info,event.xbutton))
3908  {
3909  *reply_info.text='\0';
3910  state|=ExitState;
3911  }
3912  cancel_info.raised=MagickTrue;
3913  XDrawBeveledButton(display,&windows->widget,&cancel_info);
3914  }
3915  break;
3916  }
3917  case ClientMessage:
3918  {
3919  /*
3920  If client window delete message, exit.
3921  */
3922  if (event.xclient.message_type != windows->wm_protocols)
3923  break;
3924  if (*event.xclient.data.l == (int) windows->wm_take_focus)
3925  {
3926  (void) XSetInputFocus(display,event.xclient.window,RevertToParent,
3927  (Time) event.xclient.data.l[1]);
3928  break;
3929  }
3930  if (*event.xclient.data.l != (int) windows->wm_delete_window)
3931  break;
3932  if (event.xclient.window == windows->widget.id)
3933  {
3934  *reply_info.text='\0';
3935  state|=ExitState;
3936  break;
3937  }
3938  break;
3939  }
3940  case ConfigureNotify:
3941  {
3942  /*
3943  Update widget configuration.
3944  */
3945  if (event.xconfigure.window != windows->widget.id)
3946  break;
3947  if ((event.xconfigure.width == (int) windows->widget.width) &&
3948  (event.xconfigure.height == (int) windows->widget.height))
3949  break;
3950  windows->widget.width=(unsigned int)
3951  MagickMax(event.xconfigure.width,(int) windows->widget.min_width);
3952  windows->widget.height=(unsigned int)
3953  MagickMax(event.xconfigure.height,(int) windows->widget.min_height);
3954  state|=UpdateConfigurationState;
3955  break;
3956  }
3957  case EnterNotify:
3958  {
3959  if (event.xcrossing.window != windows->widget.id)
3960  break;
3961  state&=(~InactiveWidgetState);
3962  break;
3963  }
3964  case Expose:
3965  {
3966  if (event.xexpose.window != windows->widget.id)
3967  break;
3968  if (event.xexpose.count != 0)
3969  break;
3970  state|=RedrawWidgetState;
3971  break;
3972  }
3973  case KeyPress:
3974  {
3975  static char
3976  command[MaxTextExtent];
3977 
3978  static int
3979  length;
3980 
3981  static KeySym
3982  key_symbol;
3983 
3984  /*
3985  Respond to a user key press.
3986  */
3987  if (event.xkey.window != windows->widget.id)
3988  break;
3989  length=XLookupString((XKeyEvent *) &event.xkey,command,
3990  (int) sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3991  *(command+length)='\0';
3992  if ((key_symbol == XK_Return) || (key_symbol == XK_KP_Enter))
3993  {
3994  action_info.raised=MagickFalse;
3995  XDrawBeveledButton(display,&windows->widget,&action_info);
3996  state|=ExitState;
3997  break;
3998  }
3999  if (key_symbol == XK_Control_L)
4000  {
4001  state|=ControlState;
4002  break;
4003  }
4004  if (state & ControlState)
4005  switch ((int) key_symbol)
4006  {
4007  case XK_u:
4008  case XK_U:
4009  {
4010  /*
4011  Erase the entire line of text.
4012  */
4013  *reply_info.text='\0';
4014  reply_info.cursor=reply_info.text;
4015  reply_info.marker=reply_info.text;
4016  reply_info.highlight=MagickFalse;
4017  break;
4018  }
4019  default:
4020  break;
4021  }
4022  XEditText(display,&reply_info,key_symbol,command,state);
4023  XDrawMatteText(display,&windows->widget,&reply_info);
4024  break;
4025  }
4026  case KeyRelease:
4027  {
4028  static char
4029  command[MaxTextExtent];
4030 
4031  static KeySym
4032  key_symbol;
4033 
4034  /*
4035  Respond to a user key release.
4036  */
4037  if (event.xkey.window != windows->widget.id)
4038  break;
4039  (void) XLookupString((XKeyEvent *) &event.xkey,command,
4040  (int) sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4041  if (key_symbol == XK_Control_L)
4042  state&=(~ControlState);
4043  break;
4044  }
4045  case LeaveNotify:
4046  {
4047  if (event.xcrossing.window != windows->widget.id)
4048  break;
4049  state|=InactiveWidgetState;
4050  break;
4051  }
4052  case MotionNotify:
4053  {
4054  /*
4055  Discard pending button motion events.
4056  */
4057  while (XCheckMaskEvent(display,ButtonMotionMask,&event)) ;
4058  if (state & InactiveWidgetState)
4059  break;
4060  if (action_info.raised == MatteIsActive(action_info,event.xmotion))
4061  {
4062  /*
4063  Action button status changed.
4064  */
4065  action_info.raised=action_info.raised == MagickFalse ?
4066  MagickTrue : MagickFalse;
4067  XDrawBeveledButton(display,&windows->widget,&action_info);
4068  break;
4069  }
4070  if (cancel_info.raised == MatteIsActive(cancel_info,event.xmotion))
4071  {
4072  /*
4073  Cancel button status changed.
4074  */
4075  cancel_info.raised=cancel_info.raised == MagickFalse ?
4076  MagickTrue : MagickFalse;
4077  XDrawBeveledButton(display,&windows->widget,&cancel_info);
4078  break;
4079  }
4080  break;
4081  }
4082  case SelectionClear:
4083  {
4084  reply_info.highlight=MagickFalse;
4085  XDrawMatteText(display,&windows->widget,&reply_info);
4086  break;
4087  }
4088  case SelectionNotify:
4089  {
4090  Atom
4091  type;
4092 
4093  int
4094  format;
4095 
4096  unsigned char
4097  *data;
4098 
4099  unsigned long
4100  after,
4101  length;
4102 
4103  /*
4104  Obtain response from primary selection.
4105  */
4106  if (event.xselection.property == (Atom) None)
4107  break;
4108  status=XGetWindowProperty(display,event.xselection.requestor,
4109  event.xselection.property,0L,2047L,MagickTrue,XA_STRING,&type,
4110  &format,&length,&after,&data);
4111  if ((status != Success) || (type != XA_STRING) || (format == 32) ||
4112  (length == 0))
4113  break;
4114  if ((Extent(reply_info.text)+length) >= (MaxTextExtent-1))
4115  (void) XBell(display,0);
4116  else
4117  {
4118  /*
4119  Insert primary selection in reply text.
4120  */
4121  *(data+length)='\0';
4122  XEditText(display,&reply_info,(KeySym) XK_Insert,(char *) data,
4123  state);
4124  XDrawMatteText(display,&windows->widget,&reply_info);
4125  }
4126  (void) XFree((void *) data);
4127  break;
4128  }
4129  case SelectionRequest:
4130  {
4131  XSelectionEvent
4132  notify;
4133 
4134  XSelectionRequestEvent
4135  *request;
4136 
4137  if (reply_info.highlight == MagickFalse)
4138  break;
4139  /*
4140  Set primary selection.
4141  */
4142  request=(&(event.xselectionrequest));
4143  (void) XChangeProperty(request->display,request->requestor,
4144  request->property,request->target,8,PropModeReplace,
4145  (unsigned char *) primary_selection,Extent(primary_selection));
4146  notify.type=SelectionNotify;
4147  notify.display=request->display;
4148  notify.requestor=request->requestor;
4149  notify.selection=request->selection;
4150  notify.target=request->target;
4151  notify.time=request->time;
4152  if (request->property == None)
4153  notify.property=request->target;
4154  else
4155  notify.property=request->property;
4156  (void) XSendEvent(request->display,request->requestor,False,0,
4157  (XEvent *) &notify);
4158  }
4159  default:
4160  break;
4161  }
4162  } while ((state & ExitState) == 0);
4163  XSetCursorState(display,windows,MagickFalse);
4164  (void) XWithdrawWindow(display,windows->widget.id,windows->widget.screen);
4165  XCheckRefreshWindows(display,windows);
4166  if (anomaly)
4167  if (special_info.raised)
4168  if (*reply != '\0')
4169  raised=MagickTrue;
4170  return(raised == MagickFalse);
4171 }
4172 
4173 /*
4174 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4175 % %
4176 % %
4177 % %
4178 % X F i l e B r o w s e r W i d g e t %
4179 % %
4180 % %
4181 % %
4182 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4183 %
4184 % XFileBrowserWidget() displays a File Browser widget with a file query to the
4185 % user. The user keys a reply and presses the Action or Cancel button to
4186 % exit. The typed text is returned as the reply function parameter.
4187 %
4188 % The format of the XFileBrowserWidget method is:
4189 %
4190 % void XFileBrowserWidget(Display *display,XWindows *windows,
4191 % const char *action,char *reply)
4192 %
4193 % A description of each parameter follows:
4194 %
4195 % o display: Specifies a connection to an X server; returned from
4196 % XOpenDisplay.
4197 %
4198 % o window: Specifies a pointer to a XWindows structure.
4199 %
4200 % o action: Specifies a pointer to the action of this widget.
4201 %
4202 % o reply: the response from the user is returned in this parameter.
4203 %
4204 */
4205 MagickExport void XFileBrowserWidget(Display *display,XWindows *windows,
4206  const char *action,char *reply)
4207 {
4208 #define CancelButtonText "Cancel"
4209 #define DirectoryText "Directory:"
4210 #define FilenameText "File name:"
4211 #define GrabButtonText "Grab"
4212 #define FormatButtonText "Format"
4213 #define HomeButtonText "Home"
4214 #define UpButtonText "Up"
4215 
4216  char
4217  *directory,
4218  **filelist,
4219  home_directory[MaxTextExtent],
4220  primary_selection[MaxTextExtent],
4221  text[MaxTextExtent],
4222  working_path[MaxTextExtent];
4223 
4224  int
4225  x,
4226  y;
4227 
4228  ssize_t
4229  i;
4230 
4231  static char
4232  glob_pattern[MaxTextExtent] = "*",
4233  format[MaxTextExtent] = "miff";
4234 
4235  static MagickStatusType
4236  mask = (MagickStatusType) (CWWidth | CWHeight | CWX | CWY);
4237 
4238  Status
4239  status;
4240 
4241  unsigned int
4242  anomaly,
4243  height,
4244  text_width,
4245  visible_files,
4246  width;
4247 
4248  size_t
4249  delay,
4250  files,
4251  state;
4252 
4253  XEvent
4254  event;
4255 
4256  XFontStruct
4257  *font_info;
4258 
4259  XTextProperty
4260  window_name;
4261 
4262  XWidgetInfo
4263  action_info,
4264  cancel_info,
4265  expose_info,
4266  special_info,
4267  list_info,
4268  home_info,
4269  north_info,
4270  reply_info,
4271  scroll_info,
4272  selection_info,
4273  slider_info,
4274  south_info,
4275  text_info,
4276  up_info;
4277 
4278  XWindowChanges
4279  window_changes;
4280 
4281  /*
4282  Read filelist from current directory.
4283  */
4284  assert(display != (Display *) NULL);
4285  assert(windows != (XWindows *) NULL);
4286  assert(action != (char *) NULL);
4287  assert(reply != (char *) NULL);
4288  if (IsEventLogging() != MagickFalse)
4289  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",action);
4290  XSetCursorState(display,windows,MagickTrue);
4291  XCheckRefreshWindows(display,windows);
4292  directory=getcwd(home_directory,MaxTextExtent);
4293  (void) directory;
4294  (void) CopyMagickString(working_path,home_directory,MaxTextExtent);
4295  filelist=ListFiles(working_path,glob_pattern,&files);
4296  if (filelist == (char **) NULL)
4297  {
4298  /*
4299  Directory read failed.
4300  */
4301  XNoticeWidget(display,windows,"Unable to read directory:",working_path);
4302  (void) XDialogWidget(display,windows,action,"Enter filename:",reply);
4303  return;
4304  }
4305  /*
4306  Determine File Browser widget attributes.
4307  */
4308  font_info=windows->widget.font_info;
4309  text_width=0;
4310  for (i=0; i < (ssize_t) files; i++)
4311  if (WidgetTextWidth(font_info,filelist[i]) > text_width)
4312  text_width=WidgetTextWidth(font_info,filelist[i]);
4313  width=WidgetTextWidth(font_info,(char *) action);
4314  if (WidgetTextWidth(font_info,GrabButtonText) > width)
4315  width=WidgetTextWidth(font_info,GrabButtonText);
4316  if (WidgetTextWidth(font_info,FormatButtonText) > width)
4317  width=WidgetTextWidth(font_info,FormatButtonText);
4318  if (WidgetTextWidth(font_info,CancelButtonText) > width)
4319  width=WidgetTextWidth(font_info,CancelButtonText);
4320  if (WidgetTextWidth(font_info,HomeButtonText) > width)
4321  width=WidgetTextWidth(font_info,HomeButtonText);
4322  if (WidgetTextWidth(font_info,UpButtonText) > width)
4323  width=WidgetTextWidth(font_info,UpButtonText);
4324  width+=QuantumMargin;
4325  if (WidgetTextWidth(font_info,DirectoryText) > width)
4326  width=WidgetTextWidth(font_info,DirectoryText);
4327  if (WidgetTextWidth(font_info,FilenameText) > width)
4328  width=WidgetTextWidth(font_info,FilenameText);
4329  height=(unsigned int) (font_info->ascent+font_info->descent);
4330  /*
4331  Position File Browser widget.
4332  */
4333  windows->widget.width=width+MagickMin((int) text_width,(int) MaxTextWidth)+
4334  6*QuantumMargin;
4335  windows->widget.min_width=width+MinTextWidth+4*QuantumMargin;
4336  if (windows->widget.width < windows->widget.min_width)
4337  windows->widget.width=windows->widget.min_width;
4338  windows->widget.height=(unsigned int)
4339  (((81*height) >> 2)+((13*QuantumMargin) >> 1)+4);
4340  windows->widget.min_height=(unsigned int)
4341  (((23*height) >> 1)+((13*QuantumMargin) >> 1)+4);
4342  if (windows->widget.height < windows->widget.min_height)
4343  windows->widget.height=windows->widget.min_height;
4344  XConstrainWindowPosition(display,&windows->widget);
4345  /*
4346  Map File Browser widget.
4347  */
4348  (void) CopyMagickString(windows->widget.name,"Browse and Select a File",
4349  MaxTextExtent);
4350  status=XStringListToTextProperty(&windows->widget.name,1,&window_name);
4351  if (status != False)
4352  {
4353  XSetWMName(display,windows->widget.id,&window_name);
4354  XSetWMIconName(display,windows->widget.id,&window_name);
4355  (void) XFree((void *) window_name.value);
4356  }
4357  window_changes.width=(int) windows->widget.width;
4358  window_changes.height=(int) windows->widget.height;
4359  window_changes.x=windows->widget.x;
4360  window_changes.y=windows->widget.y;
4361  (void) XReconfigureWMWindow(display,windows->widget.id,
4362  windows->widget.screen,mask,&window_changes);
4363  (void) XMapRaised(display,windows->widget.id);
4364  windows->widget.mapped=MagickFalse;
4365  /*
4366  Respond to X events.
4367  */
4368  XGetWidgetInfo((char *) NULL,&slider_info);
4369  XGetWidgetInfo((char *) NULL,&north_info);
4370  XGetWidgetInfo((char *) NULL,&south_info);
4371  XGetWidgetInfo((char *) NULL,&expose_info);
4372  visible_files=0;
4373  anomaly=(LocaleCompare(action,"Composite") == 0) ||
4374  (LocaleCompare(action,"Open") == 0) || (LocaleCompare(action,"Map") == 0);
4375  delay=SuspendTime << 2;
4376  state=UpdateConfigurationState;
4377  do
4378  {
4379  if (state & UpdateConfigurationState)
4380  {
4381  int
4382  id;
4383 
4384  /*
4385  Initialize button information.
4386  */
4387  XGetWidgetInfo(CancelButtonText,&cancel_info);
4388  cancel_info.width=width;
4389  cancel_info.height=(unsigned int) ((3*height) >> 1);
4390  cancel_info.x=(int)
4391  (windows->widget.width-cancel_info.width-QuantumMargin-2);
4392  cancel_info.y=(int)
4393  (windows->widget.height-cancel_info.height-QuantumMargin);
4394  XGetWidgetInfo(action,&action_info);
4395  action_info.width=width;
4396  action_info.height=(unsigned int) ((3*height) >> 1);
4397  action_info.x=cancel_info.x-(cancel_info.width+(QuantumMargin >> 1)+
4398  (action_info.bevel_width << 1));
4399  action_info.y=cancel_info.y;
4400  XGetWidgetInfo(GrabButtonText,&special_info);
4401  special_info.width=width;
4402  special_info.height=(unsigned int) ((3*height) >> 1);
4403  special_info.x=action_info.x-(action_info.width+(QuantumMargin >> 1)+
4404  (special_info.bevel_width << 1));
4405  special_info.y=action_info.y;
4406  if (anomaly == MagickFalse)
4407  {
4408  char
4409  *p;
4410 
4411  special_info.text=(char *) FormatButtonText;
4412  p=reply+Extent(reply)-1;
4413  while ((p > (reply+1)) && (*(p-1) != '.'))
4414  p--;
4415  if ((p > (reply+1)) && (*(p-1) == '.'))
4416  (void) CopyMagickString(format,p,MaxTextExtent);
4417  }
4418  XGetWidgetInfo(UpButtonText,&up_info);
4419  up_info.width=width;
4420  up_info.height=(unsigned int) ((3*height) >> 1);
4421  up_info.x=QuantumMargin;
4422  up_info.y=((5*QuantumMargin) >> 1)+height;
4423  XGetWidgetInfo(HomeButtonText,&home_info);
4424  home_info.width=width;
4425  home_info.height=(unsigned int) ((3*height) >> 1);
4426  home_info.x=QuantumMargin;
4427  home_info.y=up_info.y+up_info.height+QuantumMargin;
4428  /*
4429  Initialize reply information.
4430  */
4431  XGetWidgetInfo(reply,&reply_info);
4432  reply_info.raised=MagickFalse;
4433  reply_info.bevel_width--;
4434  reply_info.width=windows->widget.width-width-((6*QuantumMargin) >> 1);
4435  reply_info.height=height << 1;
4436  reply_info.x=(int) (width+(QuantumMargin << 1));
4437  reply_info.y=action_info.y-reply_info.height-QuantumMargin;
4438  /*
4439  Initialize scroll information.
4440  */
4441  XGetWidgetInfo((char *) NULL,&scroll_info);
4442  scroll_info.bevel_width--;
4443  scroll_info.width=height;
4444  scroll_info.height=(unsigned int)
4445  (reply_info.y-up_info.y-(QuantumMargin >> 1));
4446  scroll_info.x=reply_info.x+(reply_info.width-scroll_info.width);
4447  scroll_info.y=up_info.y-reply_info.bevel_width;
4448  scroll_info.raised=MagickFalse;
4449  scroll_info.trough=MagickTrue;
4450  north_info=scroll_info;
4451  north_info.raised=MagickTrue;
4452  north_info.width-=(north_info.bevel_width << 1);
4453  north_info.height=north_info.width-1;
4454  north_info.x+=north_info.bevel_width;
4455  north_info.y+=north_info.bevel_width;
4456  south_info=north_info;
4457  south_info.y=scroll_info.y+scroll_info.height-scroll_info.bevel_width-
4458  south_info.height;
4459  id=slider_info.id;
4460  slider_info=north_info;
4461  slider_info.id=id;
4462  slider_info.width-=2;
4463  slider_info.min_y=north_info.y+north_info.height+north_info.bevel_width+
4464  slider_info.bevel_width+2;
4465  slider_info.height=scroll_info.height-((slider_info.min_y-
4466  scroll_info.y+1) << 1)+4;
4467  visible_files=(unsigned int) (scroll_info.height*
4468  PerceptibleReciprocal((double) height+(height >> 3)));
4469  if (files > visible_files)
4470  slider_info.height=(unsigned int) ((visible_files*
4471  slider_info.height)/files);
4472  slider_info.max_y=south_info.y-south_info.bevel_width-
4473  slider_info.bevel_width-2;
4474  slider_info.x=scroll_info.x+slider_info.bevel_width+1;
4475  slider_info.y=slider_info.min_y;
4476  expose_info=scroll_info;
4477  expose_info.y=slider_info.y;
4478  /*
4479  Initialize list information.
4480  */
4481  XGetWidgetInfo((char *) NULL,&list_info);
4482  list_info.raised=MagickFalse;
4483  list_info.bevel_width--;
4484  list_info.width=(unsigned int)
4485  (scroll_info.x-reply_info.x-(QuantumMargin >> 1));
4486  list_info.height=scroll_info.height;
4487  list_info.x=reply_info.x;
4488  list_info.y=scroll_info.y;
4489  if (windows->widget.mapped == MagickFalse)
4490  state|=JumpListState;
4491  /*
4492  Initialize text information.
4493  */
4494  *text='\0';
4495  XGetWidgetInfo(text,&text_info);
4496  text_info.center=MagickFalse;
4497  text_info.width=reply_info.width;
4498  text_info.height=height;
4499  text_info.x=list_info.x-(QuantumMargin >> 1);
4500  text_info.y=QuantumMargin;
4501  /*
4502  Initialize selection information.
4503  */
4504  XGetWidgetInfo((char *) NULL,&selection_info);
4505  selection_info.center=MagickFalse;
4506  selection_info.width=list_info.width;
4507  selection_info.height=(unsigned int) ((9*height) >> 3);
4508  selection_info.x=list_info.x;
4509  state&=(~UpdateConfigurationState);
4510  }
4511  if (state & RedrawWidgetState)
4512  {
4513  /*
4514  Redraw File Browser window.
4515  */
4516  x=QuantumMargin;
4517  y=text_info.y+((text_info.height-height) >> 1)+font_info->ascent;
4518  (void) XDrawString(display,windows->widget.id,
4519  windows->widget.annotate_context,x,y,DirectoryText,
4520  Extent(DirectoryText));
4521  (void) CopyMagickString(text_info.text,working_path,MaxTextExtent);
4522  (void) ConcatenateMagickString(text_info.text,DirectorySeparator,
4523  MaxTextExtent);
4524  (void) ConcatenateMagickString(text_info.text,glob_pattern,
4525  MaxTextExtent);
4526  XDrawWidgetText(display,&windows->widget,&text_info);
4527  XDrawBeveledButton(display,&windows->widget,&up_info);
4528  XDrawBeveledButton(display,&windows->widget,&home_info);
4529  XDrawBeveledMatte(display,&windows->widget,&list_info);
4530  XDrawBeveledMatte(display,&windows->widget,&scroll_info);
4531  XDrawTriangleNorth(display,&windows->widget,&north_info);
4532  XDrawBeveledButton(display,&windows->widget,&slider_info);
4533  XDrawTriangleSouth(display,&windows->widget,&south_info);
4534  x=QuantumMargin;
4535  y=reply_info.y+((reply_info.height-height) >> 1)+font_info->ascent;
4536  (void) XDrawString(display,windows->widget.id,
4537  windows->widget.annotate_context,x,y,FilenameText,
4538  Extent(FilenameText));
4539  XDrawBeveledMatte(display,&windows->widget,&reply_info);
4540  XDrawMatteText(display,&windows->widget,&reply_info);
4541  XDrawBeveledButton(display,&windows->widget,&special_info);
4542  XDrawBeveledButton(display,&windows->widget,&action_info);
4543  XDrawBeveledButton(display,&windows->widget,&cancel_info);
4544  XHighlightWidget(display,&windows->widget,BorderOffset,BorderOffset);
4545  selection_info.id=(~0);
4546  state|=RedrawListState;
4547  state&=(~RedrawWidgetState);
4548  }
4549  if (state & UpdateListState)
4550  {
4551  char
4552  **checklist;
4553 
4554  size_t
4555  number_files;
4556 
4557  /*
4558  Update file list.
4559  */
4560  checklist=ListFiles(working_path,glob_pattern,&number_files);
4561  if (checklist == (char **) NULL)
4562  {
4563  /*
4564  Reply is a filename, exit.
4565  */
4566  action_info.raised=MagickFalse;
4567  XDrawBeveledButton(display,&windows->widget,&action_info);
4568  break;
4569  }
4570  for (i=0; i < (ssize_t) files; i++)
4571  filelist[i]=DestroyString(filelist[i]);
4572  if (filelist != (char **) NULL)
4573  filelist=(char **) RelinquishMagickMemory(filelist);
4574  filelist=checklist;
4575  files=number_files;
4576  /*
4577  Update file list.
4578  */
4579  slider_info.height=
4580  scroll_info.height-((slider_info.min_y-scroll_info.y+1) << 1)+1;
4581  if (files > visible_files)
4582  slider_info.height=(unsigned int)
4583  ((visible_files*slider_info.height)/files);
4584  slider_info.max_y=south_info.y-south_info.bevel_width-
4585  slider_info.bevel_width-2;
4586  slider_info.id=0;
4587  slider_info.y=slider_info.min_y;
4588  expose_info.y=slider_info.y;
4589  selection_info.id=(~0);
4590  list_info.id=(~0);
4591  state|=RedrawListState;
4592  /*
4593  Redraw directory name & reply.
4594  */
4595  if (IsGlob(reply_info.text) == MagickFalse)
4596  {
4597  *reply_info.text='\0';
4598  reply_info.cursor=reply_info.text;
4599  }
4600  (void) CopyMagickString(text_info.text,working_path,MaxTextExtent);
4601  (void) ConcatenateMagickString(text_info.text,DirectorySeparator,
4602  MaxTextExtent);
4603  (void) ConcatenateMagickString(text_info.text,glob_pattern,
4604  MaxTextExtent);
4605  XDrawWidgetText(display,&windows->widget,&text_info);
4606  XDrawMatteText(display,&windows->widget,&reply_info);
4607  XDrawBeveledMatte(display,&windows->widget,&scroll_info);
4608  XDrawTriangleNorth(display,&windows->widget,&north_info);
4609  XDrawBeveledButton(display,&windows->widget,&slider_info);
4610  XDrawTriangleSouth(display,&windows->widget,&south_info);
4611  XHighlightWidget(display,&windows->widget,BorderOffset,BorderOffset);
4612  state&=(~UpdateListState);
4613  }
4614  if (state & JumpListState)
4615  {
4616  /*
4617  Jump scroll to match user filename.
4618  */
4619  list_info.id=(~0);
4620  for (i=0; i < (ssize_t) files; i++)
4621  if (LocaleCompare(filelist[i],reply) >= 0)
4622  {
4623  list_info.id=(int)
4624  (LocaleCompare(filelist[i],reply) == 0 ? i : ~0);
4625  break;
4626  }
4627  if ((i < (ssize_t) slider_info.id) ||
4628  (i >= (ssize_t) (slider_info.id+visible_files)))
4629  slider_info.id=(int) i-(visible_files >> 1);
4630  selection_info.id=(~0);
4631  state|=RedrawListState;
4632  state&=(~JumpListState);
4633  }
4634  if (state & RedrawListState)
4635  {
4636  /*
4637  Determine slider id and position.
4638  */
4639  if (slider_info.id >= (int) (files-visible_files))
4640  slider_info.id=(int) (files-visible_files);
4641  if ((slider_info.id < 0) || (files <= visible_files))
4642  slider_info.id=0;
4643  slider_info.y=slider_info.min_y;
4644  if (files > 0)
4645  slider_info.y+=((ssize_t) slider_info.id*(slider_info.max_y-
4646  slider_info.min_y+1)/files);
4647  if (slider_info.id != selection_info.id)
4648  {
4649  /*
4650  Redraw scroll bar and file names.
4651  */
4652  selection_info.id=slider_info.id;
4653  selection_info.y=list_info.y+(height >> 3)+2;
4654  for (i=0; i < (ssize_t) visible_files; i++)
4655  {
4656  selection_info.raised=(int) (slider_info.id+i) != list_info.id ?
4657  MagickTrue : MagickFalse;
4658  selection_info.text=(char *) NULL;
4659  if ((slider_info.id+i) < (ssize_t) files)
4660  selection_info.text=filelist[slider_info.id+i];
4661  XDrawWidgetText(display,&windows->widget,&selection_info);
4662  selection_info.y+=(int) selection_info.height;
4663  }
4664  /*
4665  Update slider.
4666  */
4667  if (slider_info.y > expose_info.y)
4668  {
4669  expose_info.height=(unsigned int) slider_info.y-expose_info.y;
4670  expose_info.y=slider_info.y-expose_info.height-
4671  slider_info.bevel_width-1;
4672  }
4673  else
4674  {
4675  expose_info.height=(unsigned int) expose_info.y-slider_info.y;
4676  expose_info.y=slider_info.y+slider_info.height+
4677  slider_info.bevel_width+1;
4678  }
4679  XDrawTriangleNorth(display,&windows->widget,&north_info);
4680  XDrawMatte(display,&windows->widget,&expose_info);
4681  XDrawBeveledButton(display,&windows->widget,&slider_info);
4682  XDrawTriangleSouth(display,&windows->widget,&south_info);
4683  expose_info.y=slider_info.y;
4684  }
4685  state&=(~RedrawListState);
4686  }
4687  /*
4688  Wait for next event.
4689  */
4690  if (north_info.raised && south_info.raised)
4691  (void) XIfEvent(display,&event,XScreenEvent,(char *) windows);
4692  else
4693  {
4694  /*
4695  Brief delay before advancing scroll bar.
4696  */
4697  XDelay(display,delay);
4698  delay=SuspendTime;
4699  (void) XCheckIfEvent(display,&event,XScreenEvent,(char *) windows);
4700  if (north_info.raised == MagickFalse)
4701  if (slider_info.id > 0)
4702  {
4703  /*
4704  Move slider up.
4705  */
4706  slider_info.id--;
4707  state|=RedrawListState;
4708  }
4709  if (south_info.raised == MagickFalse)
4710  if (slider_info.id < (int) files)
4711  {
4712  /*
4713  Move slider down.
4714  */
4715  slider_info.id++;
4716  state|=RedrawListState;
4717  }
4718  if (event.type != ButtonRelease)
4719  continue;
4720  }
4721  switch (event.type)
4722  {
4723  case ButtonPress:
4724  {
4725  if (MatteIsActive(slider_info,event.xbutton))
4726  {
4727  /*
4728  Track slider.
4729  */
4730  slider_info.active=MagickTrue;
4731  break;
4732  }
4733  if (MatteIsActive(north_info,event.xbutton))
4734  if (slider_info.id > 0)
4735  {
4736  /*
4737  Move slider up.
4738  */
4739  north_info.raised=MagickFalse;
4740  slider_info.id--;
4741  state|=RedrawListState;
4742  break;
4743  }
4744  if (MatteIsActive(south_info,event.xbutton))
4745  if (slider_info.id < (int) files)
4746  {
4747  /*
4748  Move slider down.
4749  */
4750  south_info.raised=MagickFalse;
4751  slider_info.id++;
4752  state|=RedrawListState;
4753  break;
4754  }
4755  if (MatteIsActive(scroll_info,event.xbutton))
4756  {
4757  /*
4758  Move slider.
4759  */
4760  if (event.xbutton.y < slider_info.y)
4761  slider_info.id-=(visible_files-1);
4762  else
4763  slider_info.id+=(visible_files-1);
4764  state|=RedrawListState;
4765  break;
4766  }
4767  if (MatteIsActive(list_info,event.xbutton))
4768  {
4769  int
4770  id;
4771 
4772  /*
4773  User pressed file matte.
4774  */
4775  id=slider_info.id+(event.xbutton.y-(list_info.y+(height >> 1))+1)/
4776  selection_info.height;
4777  if (id >= (int) files)
4778  break;
4779  (void) CopyMagickString(reply_info.text,filelist[id],MaxTextExtent);
4780  reply_info.highlight=MagickFalse;
4781  reply_info.marker=reply_info.text;
4782  reply_info.cursor=reply_info.text+Extent(reply_info.text);
4783  XDrawMatteText(display,&windows->widget,&reply_info);
4784  if (id == list_info.id)
4785  {
4786  char
4787  *p;
4788 
4789  p=reply_info.text+strlen(reply_info.text)-1;
4790  if (*p == *DirectorySeparator)
4791  ChopPathComponents(reply_info.text,1);
4792  (void) ConcatenateMagickString(working_path,DirectorySeparator,
4793  MaxTextExtent);
4794  (void) ConcatenateMagickString(working_path,reply_info.text,
4795  MaxTextExtent);
4796  *reply='\0';
4797  state|=UpdateListState;
4798  }
4799  selection_info.id=(~0);
4800  list_info.id=id;
4801  state|=RedrawListState;
4802  break;
4803  }
4804  if (MatteIsActive(up_info,event.xbutton))
4805  {
4806  /*
4807  User pressed Up button.
4808  */
4809  up_info.raised=MagickFalse;
4810  XDrawBeveledButton(display,&windows->widget,&up_info);
4811  break;
4812  }
4813  if (MatteIsActive(home_info,event.xbutton))
4814  {
4815  /*
4816  User pressed Home button.
4817  */
4818  home_info.raised=MagickFalse;
4819  XDrawBeveledButton(display,&windows->widget,&home_info);
4820  break;
4821  }
4822  if (MatteIsActive(special_info,event.xbutton))
4823  {
4824  /*
4825  User pressed Special button.
4826  */
4827  special_info.raised=MagickFalse;
4828  XDrawBeveledButton(display,&windows->widget,&special_info);
4829  break;
4830  }
4831  if (MatteIsActive(action_info,event.xbutton))
4832  {
4833  /*
4834  User pressed action button.
4835  */
4836  action_info.raised=MagickFalse;
4837  XDrawBeveledButton(display,&windows->widget,&action_info);
4838  break;
4839  }
4840  if (MatteIsActive(cancel_info,event.xbutton))
4841  {
4842  /*
4843  User pressed Cancel button.
4844  */
4845  cancel_info.raised=MagickFalse;
4846  XDrawBeveledButton(display,&windows->widget,&cancel_info);
4847  break;
4848  }
4849  if (MatteIsActive(reply_info,event.xbutton) == MagickFalse)
4850  break;
4851  if (event.xbutton.button != Button2)
4852  {
4853  static Time
4854  click_time;
4855 
4856  /*
4857  Move text cursor to position of button press.
4858  */
4859  x=event.xbutton.x-reply_info.x-(QuantumMargin >> 2);
4860  for (i=1; i <= (ssize_t) Extent(reply_info.marker); i++)
4861  if (XTextWidth(font_info,reply_info.marker,(int) i) > x)
4862  break;
4863  reply_info.cursor=reply_info.marker+i-1;
4864  if (event.xbutton.time > (click_time+DoubleClick))
4865  reply_info.highlight=MagickFalse;
4866  else
4867  {
4868  /*
4869  Become the XA_PRIMARY selection owner.
4870  */
4871  (void) CopyMagickString(primary_selection,reply_info.text,
4872  MaxTextExtent);
4873  (void) XSetSelectionOwner(display,XA_PRIMARY,windows->widget.id,
4874  event.xbutton.time);
4875  reply_info.highlight=XGetSelectionOwner(display,XA_PRIMARY) ==
4876  windows->widget.id ? MagickTrue : MagickFalse;
4877  }
4878  XDrawMatteText(display,&windows->widget,&reply_info);
4879  click_time=event.xbutton.time;
4880  break;
4881  }
4882  /*
4883  Request primary selection.
4884  */
4885  (void) XConvertSelection(display,XA_PRIMARY,XA_STRING,XA_STRING,
4886  windows->widget.id,event.xbutton.time);
4887  break;
4888  }
4889  case ButtonRelease:
4890  {
4891  if (windows->widget.mapped == MagickFalse)
4892  break;
4893  if (north_info.raised == MagickFalse)
4894  {
4895  /*
4896  User released up button.
4897  */
4898  delay=SuspendTime << 2;
4899  north_info.raised=MagickTrue;
4900  XDrawTriangleNorth(display,&windows->widget,&north_info);
4901  }
4902  if (south_info.raised == MagickFalse)
4903  {
4904  /*
4905  User released down button.
4906  */
4907  delay=SuspendTime << 2;
4908  south_info.raised=MagickTrue;
4909  XDrawTriangleSouth(display,&windows->widget,&south_info);
4910  }
4911  if (slider_info.active)
4912  {
4913  /*
4914  Stop tracking slider.
4915  */
4916  slider_info.active=MagickFalse;
4917  break;
4918  }
4919  if (up_info.raised == MagickFalse)
4920  {
4921  if (event.xbutton.window == windows->widget.id)
4922  if (MatteIsActive(up_info,event.xbutton))
4923  {
4924  ChopPathComponents(working_path,1);
4925  if (*working_path == '\0')
4926  (void) CopyMagickString(working_path,DirectorySeparator,
4927  MaxTextExtent);
4928  state|=UpdateListState;
4929  }
4930  up_info.raised=MagickTrue;
4931  XDrawBeveledButton(display,&windows->widget,&up_info);
4932  }
4933  if (home_info.raised == MagickFalse)
4934  {
4935  if (event.xbutton.window == windows->widget.id)
4936  if (MatteIsActive(home_info,event.xbutton))
4937  {
4938  (void) CopyMagickString(working_path,home_directory,
4939  MaxTextExtent);
4940  state|=UpdateListState;
4941  }
4942  home_info.raised=MagickTrue;
4943  XDrawBeveledButton(display,&windows->widget,&home_info);
4944  }
4945  if (special_info.raised == MagickFalse)
4946  {
4947  if (anomaly == MagickFalse)
4948  {
4949  char
4950  **formats;
4951 
4953  *exception;
4954 
4955  size_t
4956  number_formats;
4957 
4958  /*
4959  Let user select image format.
4960  */
4961  exception=AcquireExceptionInfo();
4962  formats=GetMagickList("*",&number_formats,exception);
4963  exception=DestroyExceptionInfo(exception);
4964  if (formats == (char **) NULL)
4965  break;
4966  (void) XCheckDefineCursor(display,windows->widget.id,
4967  windows->widget.busy_cursor);
4968  windows->popup.x=windows->widget.x+60;
4969  windows->popup.y=windows->widget.y+60;
4970  XListBrowserWidget(display,windows,&windows->popup,
4971  (const char **) formats,"Select","Select image format type:",
4972  format);
4973  XSetCursorState(display,windows,MagickTrue);
4974  (void) XCheckDefineCursor(display,windows->widget.id,
4975  windows->widget.cursor);
4976  LocaleLower(format);
4977  AppendImageFormat(format,reply_info.text);
4978  reply_info.cursor=reply_info.text+Extent(reply_info.text);
4979  XDrawMatteText(display,&windows->widget,&reply_info);
4980  special_info.raised=MagickTrue;
4981  XDrawBeveledButton(display,&windows->widget,&special_info);
4982  for (i=0; i < (ssize_t) number_formats; i++)
4983  formats[i]=DestroyString(formats[i]);
4984  formats=(char **) RelinquishMagickMemory(formats);
4985  break;
4986  }
4987  if (event.xbutton.window == windows->widget.id)
4988  if (MatteIsActive(special_info,event.xbutton))
4989  {
4990  (void) CopyMagickString(working_path,"x:",MaxTextExtent);
4991  state|=ExitState;
4992  }
4993  special_info.raised=MagickTrue;
4994  XDrawBeveledButton(display,&windows->widget,&special_info);
4995  }
4996  if (action_info.raised == MagickFalse)
4997  {
4998  if (event.xbutton.window == windows->widget.id)
4999  {
5000  if (MatteIsActive(action_info,event.xbutton))
5001  {
5002  if (*reply_info.text == '\0')
5003  (void) XBell(display,0);
5004  else
5005  state|=ExitState;
5006  }
5007  }
5008  action_info.raised=MagickTrue;
5009  XDrawBeveledButton(display,&windows->widget,&action_info);
5010  }
5011  if (cancel_info.raised == MagickFalse)
5012  {
5013  if (event.xbutton.window == windows->widget.id)
5014  if (MatteIsActive(cancel_info,event.xbutton))
5015  {
5016  *reply_info.text='\0';
5017  *reply='\0';
5018  state|=ExitState;
5019  }
5020  cancel_info.raised=MagickTrue;
5021  XDrawBeveledButton(display,&windows->widget,&cancel_info);
5022  }
5023  break;
5024  }
5025  case ClientMessage:
5026  {
5027  /*
5028  If client window delete message, exit.
5029  */
5030  if (event.xclient.message_type != windows->wm_protocols)
5031  break;
5032  if (*event.xclient.data.l == (int) windows->wm_take_focus)
5033  {
5034  (void) XSetInputFocus(display,event.xclient.window,RevertToParent,
5035  (Time) event.xclient.data.l[1]);
5036  break;
5037  }
5038  if (*event.xclient.data.l != (int) windows->wm_delete_window)
5039  break;
5040  if (event.xclient.window == windows->widget.id)
5041  {
5042  *reply_info.text='\0';
5043  state|=ExitState;
5044  break;
5045  }
5046  break;
5047  }
5048  case ConfigureNotify:
5049  {
5050  /*
5051  Update widget configuration.
5052  */
5053  if (event.xconfigure.window != windows->widget.id)
5054  break;
5055  if ((event.xconfigure.width == (int) windows->widget.width) &&
5056  (event.xconfigure.height == (int) windows->widget.height))
5057  break;
5058  windows->widget.width=(unsigned int)
5059  MagickMax(event.xconfigure.width,(int) windows->widget.min_width);
5060  windows->widget.height=(unsigned int)
5061  MagickMax(event.xconfigure.height,(int) windows->widget.min_height);
5062  state|=UpdateConfigurationState;
5063  break;
5064  }
5065  case EnterNotify:
5066  {
5067  if (event.xcrossing.window != windows->widget.id)
5068  break;
5069  state&=(~InactiveWidgetState);
5070  break;
5071  }
5072  case Expose:
5073  {
5074  if (event.xexpose.window != windows->widget.id)
5075  break;
5076  if (event.xexpose.count != 0)
5077  break;
5078  state|=RedrawWidgetState;
5079  break;
5080  }
5081  case KeyPress:
5082  {
5083  static char
5084  command[MaxTextExtent];
5085 
5086  static int
5087  length;
5088 
5089  static KeySym
5090  key_symbol;
5091 
5092  /*
5093  Respond to a user key press.
5094  */
5095  if (event.xkey.window != windows->widget.id)
5096  break;
5097  length=XLookupString((XKeyEvent *) &event.xkey,command,
5098  (int) sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5099  *(command+length)='\0';
5100  if (AreaIsActive(scroll_info,event.xkey))
5101  {
5102  /*
5103  Move slider.
5104  */
5105  switch ((int) key_symbol)
5106  {
5107  case XK_Home:
5108  case XK_KP_Home:
5109  {
5110  slider_info.id=0;
5111  break;
5112  }
5113  case XK_Up:
5114  case XK_KP_Up:
5115  {
5116  slider_info.id--;
5117  break;
5118  }
5119  case XK_Down:
5120  case XK_KP_Down:
5121  {
5122  slider_info.id++;
5123  break;
5124  }
5125  case XK_Prior:
5126  case XK_KP_Prior:
5127  {
5128  slider_info.id-=visible_files;
5129  break;
5130  }
5131  case XK_Next:
5132  case XK_KP_Next:
5133  {
5134  slider_info.id+=visible_files;
5135  break;
5136  }
5137  case XK_End:
5138  case XK_KP_End:
5139  {
5140  slider_info.id=(int) files;
5141  break;
5142  }
5143  }
5144  state|=RedrawListState;
5145  break;
5146  }
5147  if ((key_symbol == XK_Return) || (key_symbol == XK_KP_Enter))
5148  {
5149  /*
5150  Read new directory or glob pattern.
5151  */
5152  if (*reply_info.text == '\0')
5153  break;
5154  if (IsGlob(reply_info.text))
5155  (void) CopyMagickString(glob_pattern,reply_info.text,
5156  MaxTextExtent);
5157  else
5158  {
5159  (void) ConcatenateMagickString(working_path,DirectorySeparator,
5160  MaxTextExtent);
5161  (void) ConcatenateMagickString(working_path,reply_info.text,
5162  MaxTextExtent);
5163  if (*working_path == '~')
5164  ExpandFilename(working_path);
5165  *reply='\0';
5166  }
5167  state|=UpdateListState;
5168  break;
5169  }
5170  if (key_symbol == XK_Control_L)
5171  {
5172  state|=ControlState;
5173  break;
5174  }
5175  if (state & ControlState)
5176  switch ((int) key_symbol)
5177  {
5178  case XK_u:
5179  case XK_U:
5180  {
5181  /*
5182  Erase the entire line of text.
5183  */
5184  *reply_info.text='\0';
5185  reply_info.cursor=reply_info.text;
5186  reply_info.marker=reply_info.text;
5187  reply_info.highlight=MagickFalse;
5188  break;
5189  }
5190  default:
5191  break;
5192  }
5193  XEditText(display,&reply_info,key_symbol,command,state);
5194  XDrawMatteText(display,&windows->widget,&reply_info);
5195  state|=JumpListState;
5196  break;
5197  }
5198  case KeyRelease:
5199  {
5200  static char
5201  command[MaxTextExtent];
5202 
5203  static KeySym
5204  key_symbol;
5205 
5206  /*
5207  Respond to a user key release.
5208  */
5209  if (event.xkey.window != windows->widget.id)
5210  break;
5211  (void) XLookupString((XKeyEvent *) &event.xkey,command,
5212  (int) sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5213  if (key_symbol == XK_Control_L)
5214  state&=(~ControlState);
5215  break;
5216  }
5217  case LeaveNotify:
5218  {
5219  if (event.xcrossing.window != windows->widget.id)
5220  break;
5221  state|=InactiveWidgetState;
5222  break;
5223  }
5224  case MapNotify:
5225  {
5226  mask&=(~CWX);
5227  mask&=(~CWY);
5228  break;
5229  }
5230  case MotionNotify:
5231  {
5232  /*
5233  Discard pending button motion events.
5234  */
5235  while (XCheckMaskEvent(display,ButtonMotionMask,&event)) ;
5236  if (slider_info.active)
5237  {
5238  /*
5239  Move slider matte.
5240  */
5241  slider_info.y=event.xmotion.y-
5242  ((slider_info.height+slider_info.bevel_width) >> 1)+1;
5243  if (slider_info.y < slider_info.min_y)
5244  slider_info.y=slider_info.min_y;
5245  if (slider_info.y > slider_info.max_y)
5246  slider_info.y=slider_info.max_y;
5247  slider_info.id=0;
5248  if (slider_info.y != slider_info.min_y)
5249  slider_info.id=(int) ((files*(slider_info.y-slider_info.min_y+1))/
5250  (slider_info.max_y-slider_info.min_y+1));
5251  state|=RedrawListState;
5252  break;
5253  }
5254  if (state & InactiveWidgetState)
5255  break;
5256  if (up_info.raised == MatteIsActive(up_info,event.xmotion))
5257  {
5258  /*
5259  Up button status changed.
5260  */
5261  up_info.raised=!up_info.raised;
5262  XDrawBeveledButton(display,&windows->widget,&up_info);
5263  break;
5264  }
5265  if (home_info.raised == MatteIsActive(home_info,event.xmotion))
5266  {
5267  /*
5268  Home button status changed.
5269  */
5270  home_info.raised=!home_info.raised;
5271  XDrawBeveledButton(display,&windows->widget,&home_info);
5272  break;
5273  }
5274  if (special_info.raised == MatteIsActive(special_info,event.xmotion))
5275  {
5276  /*
5277  Grab button status changed.
5278  */
5279  special_info.raised=!special_info.raised;
5280  XDrawBeveledButton(display,&windows->widget,&special_info);
5281  break;
5282  }
5283  if (action_info.raised == MatteIsActive(action_info,event.xmotion))
5284  {
5285  /*
5286  Action button status changed.
5287  */
5288  action_info.raised=action_info.raised == MagickFalse ?
5289  MagickTrue : MagickFalse;
5290  XDrawBeveledButton(display,&windows->widget,&action_info);
5291  break;
5292  }
5293  if (cancel_info.raised == MatteIsActive(cancel_info,event.xmotion))
5294  {
5295  /*
5296  Cancel button status changed.
5297  */
5298  cancel_info.raised=cancel_info.raised == MagickFalse ?
5299  MagickTrue : MagickFalse;
5300  XDrawBeveledButton(display,&windows->widget,&cancel_info);
5301  break;
5302  }
5303  break;
5304  }
5305  case SelectionClear:
5306  {
5307  reply_info.highlight=MagickFalse;
5308  XDrawMatteText(display,&windows->widget,&reply_info);
5309  break;
5310  }
5311  case SelectionNotify:
5312  {
5313  Atom
5314  type;
5315 
5316  int
5317  format;
5318 
5319  unsigned char
5320  *data;
5321 
5322  unsigned long
5323  after,
5324  length;
5325 
5326  /*
5327  Obtain response from primary selection.
5328  */
5329  if (event.xselection.property == (Atom) None)
5330  break;
5331  status=XGetWindowProperty(display,event.xselection.requestor,
5332  event.xselection.property,0L,2047L,MagickTrue,XA_STRING,&type,
5333  &format,&length,&after,&data);
5334  if ((status != Success) || (type != XA_STRING) || (format == 32) ||
5335  (length == 0))
5336  break;
5337  if ((Extent(reply_info.text)+length) >= (MaxTextExtent-1))
5338  (void) XBell(display,0);
5339  else
5340  {
5341  /*
5342  Insert primary selection in reply text.
5343  */
5344  *(data+length)='\0';
5345  XEditText(display,&reply_info,(KeySym) XK_Insert,(char *) data,
5346  state);
5347  XDrawMatteText(display,&windows->widget,&reply_info);
5348  state|=JumpListState;
5349  state|=RedrawActionState;
5350  }
5351  (void) XFree((void *) data);
5352  break;
5353  }
5354  case SelectionRequest:
5355  {
5356  XSelectionEvent
5357  notify;
5358 
5359  XSelectionRequestEvent
5360  *request;
5361 
5362  if (reply_info.highlight == MagickFalse)
5363  break;
5364  /*
5365  Set primary selection.
5366  */
5367  request=(&(event.xselectionrequest));
5368  (void) XChangeProperty(request->display,request->requestor,
5369  request->property,request->target,8,PropModeReplace,
5370  (unsigned char *) primary_selection,Extent(primary_selection));
5371  notify.type=SelectionNotify;
5372  notify.display=request->display;
5373  notify.requestor=request->requestor;
5374  notify.selection=request->selection;
5375  notify.target=request->target;
5376  notify.time=request->time;
5377  if (request->property == None)
5378  notify.property=request->target;
5379  else
5380  notify.property=request->property;
5381  (void) XSendEvent(request->display,request->requestor,False,0,
5382  (XEvent *) &notify);
5383  }
5384  default:
5385  break;
5386  }
5387  } while ((state & ExitState) == 0);
5388  XSetCursorState(display,windows,MagickFalse);
5389  (void) XWithdrawWindow(display,windows->widget.id,windows->widget.screen);
5390  XCheckRefreshWindows(display,windows);
5391  /*
5392  Free file list.
5393  */
5394  for (i=0; i < (ssize_t) files; i++)
5395  filelist[i]=DestroyString(filelist[i]);
5396  if (filelist != (char **) NULL)
5397  filelist=(char **) RelinquishMagickMemory(filelist);
5398  if (*reply != '\0')
5399  {
5400  (void) ConcatenateMagickString(working_path,DirectorySeparator,
5401  MaxTextExtent);
5402  (void) ConcatenateMagickString(working_path,reply,MaxTextExtent);
5403  }
5404  (void) CopyMagickString(reply,working_path,MaxTextExtent);
5405  if (*reply == '~')
5406  ExpandFilename(reply);
5407 }
5408 
5409 /*
5410 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5411 % %
5412 % %
5413 % %
5414 % X F o n t B r o w s e r W i d g e t %
5415 % %
5416 % %
5417 % %
5418 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5419 %
5420 % XFontBrowserWidget() displays a Font Browser widget with a font query to the
5421 % user. The user keys a reply and presses the Action or Cancel button to
5422 % exit. The typed text is returned as the reply function parameter.
5423 %
5424 % The format of the XFontBrowserWidget method is:
5425 %
5426 % void XFontBrowserWidget(Display *display,XWindows *windows,
5427 % const char *action,char *reply)
5428 %
5429 % A description of each parameter follows:
5430 %
5431 % o display: Specifies a connection to an X server; returned from
5432 % XOpenDisplay.
5433 %
5434 % o window: Specifies a pointer to a XWindows structure.
5435 %
5436 % o action: Specifies a pointer to the action of this widget.
5437 %
5438 % o reply: the response from the user is returned in this parameter.
5439 %
5440 %
5441 */
5442 
5443 #if defined(__cplusplus) || defined(c_plusplus)
5444 extern "C" {
5445 #endif
5446 
5447 static int FontCompare(const void *x,const void *y)
5448 {
5449  char
5450  *p,
5451  *q;
5452 
5453  p=(char *) *((char **) x);
5454  q=(char *) *((char **) y);
5455  while ((*p != '\0') && (*q != '\0') && (*p == *q))
5456  {
5457  p++;
5458  q++;
5459  }
5460  return(*p-(*q));
5461 }
5462 
5463 #if defined(__cplusplus) || defined(c_plusplus)
5464 }
5465 #endif
5466 
5467 MagickExport void XFontBrowserWidget(Display *display,XWindows *windows,
5468  const char *action,char *reply)
5469 {
5470 #define BackButtonText "Back"
5471 #define CancelButtonText "Cancel"
5472 #define FontnameText "Name:"
5473 #define FontPatternText "Pattern:"
5474 #define ResetButtonText "Reset"
5475 
5476  char
5477  back_pattern[MaxTextExtent] = "",
5478  **fontlist,
5479  **listhead,
5480  primary_selection[MaxTextExtent] = "",
5481  reset_pattern[MaxTextExtent] = "",
5482  text[MaxTextExtent] = "";
5483 
5484  int
5485  fonts,
5486  x,
5487  y;
5488 
5489  int
5490  i;
5491 
5492  static char
5493  glob_pattern[MaxTextExtent] = "*";
5494 
5495  static MagickStatusType
5496  mask = (MagickStatusType) (CWWidth | CWHeight | CWX | CWY);
5497 
5498  Status
5499  status;
5500 
5501  unsigned int
5502  height,
5503  text_width,
5504  visible_fonts,
5505  width;
5506 
5507  size_t
5508  delay,
5509  state;
5510 
5511  XEvent
5512  event;
5513 
5514  XFontStruct
5515  *font_info;
5516 
5517  XTextProperty
5518  window_name;
5519 
5520  XWidgetInfo
5521  action_info,
5522  back_info,
5523  cancel_info,
5524  expose_info,
5525  list_info,
5526  mode_info,
5527  north_info,
5528  reply_info,
5529  reset_info,
5530  scroll_info,
5531  selection_info,
5532  slider_info,
5533  south_info,
5534  text_info;
5535 
5536  XWindowChanges
5537  window_changes;
5538 
5539  /*
5540  Get font list and sort in ascending order.
5541  */
5542  assert(display != (Display *) NULL);
5543  assert(windows != (XWindows *) NULL);
5544  assert(action != (char *) NULL);
5545  assert(reply != (char *) NULL);
5546  if (IsEventLogging() != MagickFalse)
5547  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",action);
5548  XSetCursorState(display,windows,MagickTrue);
5549  XCheckRefreshWindows(display,windows);
5550  (void) CopyMagickString(back_pattern,glob_pattern,MaxTextExtent);
5551  (void) CopyMagickString(reset_pattern,"*",MaxTextExtent);
5552  fontlist=XListFonts(display,glob_pattern,32767,&fonts);
5553  if (fonts == 0)
5554  {
5555  /*
5556  Pattern failed, obtain all the fonts.
5557  */
5558  XNoticeWidget(display,windows,"Unable to obtain fonts names:",
5559  glob_pattern);
5560  (void) CopyMagickString(glob_pattern,"*",MaxTextExtent);
5561  fontlist=XListFonts(display,glob_pattern,32767,&fonts);
5562  if (fontlist == (char **) NULL)
5563  {
5564  XNoticeWidget(display,windows,"Unable to obtain fonts names:",
5565  glob_pattern);
5566  return;
5567  }
5568  }
5569  /*
5570  Sort font list in ascending order.
5571  */
5572  listhead=fontlist;
5573  fontlist=(char **) AcquireQuantumMemory((size_t) fonts,sizeof(*fontlist));
5574  if (fontlist == (char **) NULL)
5575  {
5576  XNoticeWidget(display,windows,"MemoryAllocationFailed",
5577  "UnableToViewFonts");
5578  return;
5579  }
5580  for (i=0; i < fonts; i++)
5581  fontlist[i]=listhead[i];
5582  qsort((void *) fontlist,(size_t) fonts,sizeof(*fontlist),FontCompare);
5583  /*
5584  Determine Font Browser widget attributes.
5585  */
5586  font_info=windows->widget.font_info;
5587  text_width=0;
5588  for (i=0; i < fonts; i++)
5589  if (WidgetTextWidth(font_info,fontlist[i]) > text_width)
5590  text_width=WidgetTextWidth(font_info,fontlist[i]);
5591  width=WidgetTextWidth(font_info,(char *) action);
5592  if (WidgetTextWidth(font_info,CancelButtonText) > width)
5593  width=WidgetTextWidth(font_info,CancelButtonText);
5594  if (WidgetTextWidth(font_info,ResetButtonText) > width)
5595  width=WidgetTextWidth(font_info,ResetButtonText);
5596  if (WidgetTextWidth(font_info,BackButtonText) > width)
5597  width=WidgetTextWidth(font_info,BackButtonText);
5598  width+=QuantumMargin;
5599  if (WidgetTextWidth(font_info,FontPatternText) > width)
5600  width=WidgetTextWidth(font_info,FontPatternText);
5601  if (WidgetTextWidth(font_info,FontnameText) > width)
5602  width=WidgetTextWidth(font_info,FontnameText);
5603  height=(unsigned int) (font_info->ascent+font_info->descent);
5604  /*
5605  Position Font Browser widget.
5606  */
5607  windows->widget.width=width+MagickMin((int) text_width,(int) MaxTextWidth)+
5608  6*QuantumMargin;
5609  windows->widget.min_width=width+MinTextWidth+4*QuantumMargin;
5610  if (windows->widget.width < windows->widget.min_width)
5611  windows->widget.width=windows->widget.min_width;
5612  windows->widget.height=(unsigned int)
5613  (((85*height) >> 2)+((13*QuantumMargin) >> 1)+4);
5614  windows->widget.min_height=(unsigned int)
5615  (((27*height) >> 1)+((13*QuantumMargin) >> 1)+4);
5616  if (windows->widget.height < windows->widget.min_height)
5617  windows->widget.height=windows->widget.min_height;
5618  XConstrainWindowPosition(display,&windows->widget);
5619  /*
5620  Map Font Browser widget.
5621  */
5622  (void) CopyMagickString(windows->widget.name,"Browse and Select a Font",
5623  MaxTextExtent);
5624  status=XStringListToTextProperty(&windows->widget.name,1,&window_name);
5625  if (status != False)
5626  {
5627  XSetWMName(display,windows->widget.id,&window_name);
5628  XSetWMIconName(display,windows->widget.id,&window_name);
5629  (void) XFree((void *) window_name.value);
5630  }
5631  window_changes.width=(int) windows->widget.width;
5632  window_changes.height=(int) windows->widget.height;
5633  window_changes.x=windows->widget.x;
5634  window_changes.y=windows->widget.y;
5635  (void) XReconfigureWMWindow(display,windows->widget.id,
5636  windows->widget.screen,mask,&window_changes);
5637  (void) XMapRaised(display,windows->widget.id);
5638  windows->widget.mapped=MagickFalse;
5639  /*
5640  Respond to X events.
5641  */
5642  XGetWidgetInfo((char *) NULL,&slider_info);
5643  XGetWidgetInfo((char *) NULL,&north_info);
5644  XGetWidgetInfo((char *) NULL,&south_info);
5645  XGetWidgetInfo((char *) NULL,&expose_info);
5646  XGetWidgetInfo((char *) NULL,&selection_info);
5647  visible_fonts=0;
5648  delay=SuspendTime << 2;
5649  state=UpdateConfigurationState;
5650  do
5651  {
5652  if (state & UpdateConfigurationState)
5653  {
5654  int
5655  id;
5656 
5657  /*
5658  Initialize button information.
5659  */
5660  XGetWidgetInfo(CancelButtonText,&cancel_info);
5661  cancel_info.width=width;
5662  cancel_info.height=(unsigned int) ((3*height) >> 1);
5663  cancel_info.x=(int)
5664  (windows->widget.width-cancel_info.width-QuantumMargin-2);
5665  cancel_info.y=(int)
5666  (windows->widget.height-cancel_info.height-QuantumMargin);
5667  XGetWidgetInfo(action,&action_info);
5668  action_info.width=width;
5669  action_info.height=(unsigned int) ((3*height) >> 1);
5670  action_info.x=cancel_info.x-(cancel_info.width+(QuantumMargin >> 1)+
5671  (action_info.bevel_width << 1));
5672  action_info.y=cancel_info.y;
5673  XGetWidgetInfo(BackButtonText,&back_info);
5674  back_info.width=width;
5675  back_info.height=(unsigned int) ((3*height) >> 1);
5676  back_info.x=QuantumMargin;
5677  back_info.y=((5*QuantumMargin) >> 1)+height;
5678  XGetWidgetInfo(ResetButtonText,&reset_info);
5679  reset_info.width=width;
5680  reset_info.height=(unsigned int) ((3*height) >> 1);
5681  reset_info.x=QuantumMargin;
5682  reset_info.y=back_info.y+back_info.height+QuantumMargin;
5683  /*
5684  Initialize reply information.
5685  */
5686  XGetWidgetInfo(reply,&reply_info);
5687  reply_info.raised=MagickFalse;
5688  reply_info.bevel_width--;
5689  reply_info.width=windows->widget.width-width-((6*QuantumMargin) >> 1);
5690  reply_info.height=height << 1;
5691  reply_info.x=(int) (width+(QuantumMargin << 1));
5692  reply_info.y=action_info.y-(action_info.height << 1)-QuantumMargin;
5693  /*
5694  Initialize mode information.
5695  */
5696  XGetWidgetInfo(reply,&mode_info);
5697  mode_info.bevel_width=0;
5698  mode_info.width=(unsigned int)
5699  (action_info.x-reply_info.x-QuantumMargin);
5700  mode_info.height=action_info.height << 1;
5701  mode_info.x=reply_info.x;
5702  mode_info.y=action_info.y-action_info.height+action_info.bevel_width;
5703  /*
5704  Initialize scroll information.
5705  */
5706  XGetWidgetInfo((char *) NULL,&scroll_info);
5707  scroll_info.bevel_width--;
5708  scroll_info.width=height;
5709  scroll_info.height=(unsigned int)
5710  (reply_info.y-back_info.y-(QuantumMargin >> 1));
5711  scroll_info.x=reply_info.x+(reply_info.width-scroll_info.width);
5712  scroll_info.y=back_info.y-reply_info.bevel_width;
5713  scroll_info.raised=MagickFalse;
5714  scroll_info.trough=MagickTrue;
5715  north_info=scroll_info;
5716  north_info.raised=MagickTrue;
5717  north_info.width-=(north_info.bevel_width << 1);
5718  north_info.height=north_info.width-1;
5719  north_info.x+=north_info.bevel_width;
5720  north_info.y+=north_info.bevel_width;
5721  south_info=north_info;
5722  south_info.y=scroll_info.y+scroll_info.height-scroll_info.bevel_width-
5723  south_info.height;
5724  id=slider_info.id;
5725  slider_info=north_info;
5726  slider_info.id=id;
5727  slider_info.width-=2;
5728  slider_info.min_y=north_info.y+north_info.height+north_info.bevel_width+
5729  slider_info.bevel_width+2;
5730  slider_info.height=scroll_info.height-((slider_info.min_y-
5731  scroll_info.y+1) << 1)+4;
5732  visible_fonts=(unsigned int) (scroll_info.height*
5733  PerceptibleReciprocal((double) 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,MaxTextExtent);
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,MaxTextExtent);
5830  (void) CopyMagickString(glob_pattern,back_pattern,MaxTextExtent);
5831  action_info.raised=MagickFalse;
5832  XDrawBeveledButton(display,&windows->widget,&action_info);
5833  break;
5834  }
5835  (void) CopyMagickString(glob_pattern,back_pattern,MaxTextExtent);
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],MaxTextExtent);
5845  (void) CopyMagickString(glob_pattern,back_pattern,MaxTextExtent);
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,MaxTextExtent);
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 ?
5941  MagickTrue : MagickFalse;
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],MaxTextExtent);
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,
6100  MaxTextExtent);
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,
6167  MaxTextExtent);
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,
6220  MaxTextExtent);
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,MaxTextExtent);
6232  (void) CopyMagickString(glob_pattern,reset_pattern,MaxTextExtent);
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[MaxTextExtent];
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 pattern.
6392  */
6393  if (*reply_info.text == '\0')
6394  break;
6395  (void) CopyMagickString(back_pattern,glob_pattern,MaxTextExtent);
6396  (void) CopyMagickString(glob_pattern,reply_info.text,MaxTextExtent);
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[MaxTextExtent];
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 ?
6510  MagickTrue : 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 ?
6520  MagickTrue : 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) >= (MaxTextExtent-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 MagickExport 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  assert(display != (Display *) NULL);
6665  assert(windows != (XWindows *) NULL);
6666  assert(activity != (char *) NULL);
6667  if (IsEventLogging() != MagickFalse)
6668  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
6669  font_info=windows->info.font_info;
6670  width=WidgetTextWidth(font_info,(char *) activity)+((3*QuantumMargin) >> 1)+4;
6671  height=(unsigned int) (((6*(font_info->ascent+font_info->descent)) >> 2)+4);
6672  if ((windows->info.width != width) || (windows->info.height != height))
6673  {
6674  /*
6675  Size Info widget to accommodate the activity text.
6676  */
6677  windows->info.width=width;
6678  windows->info.height=height;
6679  window_changes.width=(int) width;
6680  window_changes.height=(int) height;
6681  (void) XReconfigureWMWindow(display,windows->info.id,windows->info.screen,
6682  (unsigned int) (CWWidth | CWHeight),&window_changes);
6683  }
6684  if (windows->info.mapped == MagickFalse)
6685  {
6686  (void) XMapRaised(display,windows->info.id);
6687  windows->info.mapped=MagickTrue;
6688  }
6689  /*
6690  Initialize Info matte information.
6691  */
6692  height=(unsigned int) (font_info->ascent+font_info->descent);
6693  XGetWidgetInfo(activity,&monitor_info);
6694  monitor_info.bevel_width--;
6695  margin=monitor_info.bevel_width+((windows->info.height-height) >> 1)-2;
6696  monitor_info.center=MagickFalse;
6697  monitor_info.x=(int) margin;
6698  monitor_info.y=(int) margin;
6699  monitor_info.width=windows->info.width-(margin << 1);
6700  monitor_info.height=windows->info.height-(margin << 1)+1;
6701  /*
6702  Draw Info widget.
6703  */
6704  monitor_info.raised=MagickFalse;
6705  XDrawBeveledMatte(display,&windows->info,&monitor_info);
6706  monitor_info.raised=MagickTrue;
6707  XDrawWidgetText(display,&windows->info,&monitor_info);
6708 }
6709 
6710 /*
6711 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6712 % %
6713 % %
6714 % %
6715 % X L i s t B r o w s e r W i d g e t %
6716 % %
6717 % %
6718 % %
6719 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6720 %
6721 % XListBrowserWidget() displays a List Browser widget with a query to the
6722 % user. The user keys a reply or select a reply from the list. Finally, the
6723 % user presses the Action or Cancel button to exit. The typed text is
6724 % returned as the reply function parameter.
6725 %
6726 % The format of the XListBrowserWidget method is:
6727 %
6728 % void XListBrowserWidget(Display *display,XWindows *windows,
6729 % XWindowInfo *window_info,const char *const *list,const char *action,
6730 % const char *query,char *reply)
6731 %
6732 % A description of each parameter follows:
6733 %
6734 % o display: Specifies a connection to an X server; returned from
6735 % XOpenDisplay.
6736 %
6737 % o window: Specifies a pointer to a XWindows structure.
6738 %
6739 % o list: Specifies a pointer to an array of strings. The user can
6740 % select from these strings as a possible reply value.
6741 %
6742 % o action: Specifies a pointer to the action of this widget.
6743 %
6744 % o query: Specifies a pointer to the query to present to the user.
6745 %
6746 % o reply: the response from the user is returned in this parameter.
6747 %
6748 */
6749 MagickExport void XListBrowserWidget(Display *display,XWindows *windows,
6750  XWindowInfo *window_info,const char *const *list,const char *action,
6751  const char *query,char *reply)
6752 {
6753 #define CancelButtonText "Cancel"
6754 
6755  char
6756  primary_selection[MaxTextExtent];
6757 
6758  int
6759  x;
6760 
6761  int
6762  i;
6763 
6764  static MagickStatusType
6765  mask = (MagickStatusType) (CWWidth | CWHeight | CWX | CWY);
6766 
6767  Status
6768  status;
6769 
6770  unsigned int
6771  entries,
6772  height,
6773  text_width,
6774  visible_entries,
6775  width;
6776 
6777  size_t
6778  delay,
6779  state;
6780 
6781  XEvent
6782  event;
6783 
6784  XFontStruct
6785  *font_info;
6786 
6787  XTextProperty
6788  window_name;
6789 
6790  XWidgetInfo
6791  action_info,
6792  cancel_info,
6793  expose_info,
6794  list_info,
6795  north_info,
6796  reply_info,
6797  scroll_info,
6798  selection_info,
6799  slider_info,
6800  south_info,
6801  text_info;
6802 
6803  XWindowChanges
6804  window_changes;
6805 
6806  /*
6807  Count the number of entries in the list.
6808  */
6809  assert(display != (Display *) NULL);
6810  assert(windows != (XWindows *) NULL);
6811  assert(window_info != (XWindowInfo *) NULL);
6812  assert(list != (const char **) NULL);
6813  assert(action != (char *) NULL);
6814  assert(query != (char *) NULL);
6815  assert(reply != (char *) NULL);
6816  if (IsEventLogging() != MagickFalse)
6817  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",action);
6818  XSetCursorState(display,windows,MagickTrue);
6819  XCheckRefreshWindows(display,windows);
6820  if (list == (const char **) NULL)
6821  {
6822  XNoticeWidget(display,windows,"No text to browse:",(char *) NULL);
6823  return;
6824  }
6825  for (entries=0; ; entries++)
6826  if (list[entries] == (char *) NULL)
6827  break;
6828  /*
6829  Determine Font Browser widget attributes.
6830  */
6831  font_info=window_info->font_info;
6832  text_width=WidgetTextWidth(font_info,(char *) query);
6833  for (i=0; i < (int) entries; i++)
6834  if (WidgetTextWidth(font_info,(char *) list[i]) > text_width)
6835  text_width=WidgetTextWidth(font_info,(char *) list[i]);
6836  width=WidgetTextWidth(font_info,(char *) action);
6837  if (WidgetTextWidth(font_info,CancelButtonText) > width)
6838  width=WidgetTextWidth(font_info,CancelButtonText);
6839  width+=QuantumMargin;
6840  height=(unsigned int) (font_info->ascent+font_info->descent);
6841  /*
6842  Position List Browser widget.
6843  */
6844  window_info->width=(unsigned int) MagickMin((int) text_width,(int)
6845  MaxTextWidth)+((9*QuantumMargin) >> 1);
6846  window_info->min_width=(unsigned int) (MinTextWidth+4*QuantumMargin);
6847  if (window_info->width < window_info->min_width)
6848  window_info->width=window_info->min_width;
6849  window_info->height=(unsigned int)
6850  (((81*height) >> 2)+((13*QuantumMargin) >> 1)+4);
6851  window_info->min_height=(unsigned int)
6852  (((23*height) >> 1)+((13*QuantumMargin) >> 1)+4);
6853  if (window_info->height < window_info->min_height)
6854  window_info->height=window_info->min_height;
6855  XConstrainWindowPosition(display,window_info);
6856  /*
6857  Map List Browser widget.
6858  */
6859  (void) CopyMagickString(window_info->name,"Browse",MaxTextExtent);
6860  status=XStringListToTextProperty(&window_info->name,1,&window_name);
6861  if (status != False)
6862  {
6863  XSetWMName(display,window_info->id,&window_name);
6864  XSetWMIconName(display,windows->widget.id,&window_name);
6865  (void) XFree((void *) window_name.value);
6866  }
6867  window_changes.width=(int) window_info->width;
6868  window_changes.height=(int) window_info->height;
6869  window_changes.x=window_info->x;
6870  window_changes.y=window_info->y;
6871  (void) XReconfigureWMWindow(display,window_info->id,window_info->screen,mask,
6872  &window_changes);
6873  (void) XMapRaised(display,window_info->id);
6874  window_info->mapped=MagickFalse;
6875  /*
6876  Respond to X events.
6877  */
6878  XGetWidgetInfo((char *) NULL,&slider_info);
6879  XGetWidgetInfo((char *) NULL,&north_info);
6880  XGetWidgetInfo((char *) NULL,&south_info);
6881  XGetWidgetInfo((char *) NULL,&expose_info);
6882  XGetWidgetInfo((char *) NULL,&selection_info);
6883  visible_entries=0;
6884  delay=SuspendTime << 2;
6885  state=UpdateConfigurationState;
6886  do
6887  {
6888  if (state & UpdateConfigurationState)
6889  {
6890  int
6891  id;
6892 
6893  /*
6894  Initialize button information.
6895  */
6896  XGetWidgetInfo(CancelButtonText,&cancel_info);
6897  cancel_info.width=width;
6898  cancel_info.height=(unsigned int) ((3*height) >> 1);
6899  cancel_info.x=(int)
6900  (window_info->width-cancel_info.width-QuantumMargin-2);
6901  cancel_info.y=(int)
6902  (window_info->height-cancel_info.height-QuantumMargin);
6903  XGetWidgetInfo(action,&action_info);
6904  action_info.width=width;
6905  action_info.height=(unsigned int) ((3*height) >> 1);
6906  action_info.x=cancel_info.x-(cancel_info.width+(QuantumMargin >> 1)+
6907  (action_info.bevel_width << 1));
6908  action_info.y=cancel_info.y;
6909  /*
6910  Initialize reply information.
6911  */
6912  XGetWidgetInfo(reply,&reply_info);
6913  reply_info.raised=MagickFalse;
6914  reply_info.bevel_width--;
6915  reply_info.width=window_info->width-((4*QuantumMargin) >> 1);
6916  reply_info.height=height << 1;
6917  reply_info.x=QuantumMargin;
6918  reply_info.y=action_info.y-reply_info.height-QuantumMargin;
6919  /*
6920  Initialize scroll information.
6921  */
6922  XGetWidgetInfo((char *) NULL,&scroll_info);
6923  scroll_info.bevel_width--;
6924  scroll_info.width=height;
6925  scroll_info.height=(unsigned int)
6926  (reply_info.y-((6*QuantumMargin) >> 1)-height);
6927  scroll_info.x=reply_info.x+(reply_info.width-scroll_info.width);
6928  scroll_info.y=((5*QuantumMargin) >> 1)+height-reply_info.bevel_width;
6929  scroll_info.raised=MagickFalse;
6930  scroll_info.trough=MagickTrue;
6931  north_info=scroll_info;
6932  north_info.raised=MagickTrue;
6933  north_info.width-=(north_info.bevel_width << 1);
6934  north_info.height=north_info.width-1;
6935  north_info.x+=north_info.bevel_width;
6936  north_info.y+=north_info.bevel_width;
6937  south_info=north_info;
6938  south_info.y=scroll_info.y+scroll_info.height-scroll_info.bevel_width-
6939  south_info.height;
6940  id=slider_info.id;
6941  slider_info=north_info;
6942  slider_info.id=id;
6943  slider_info.width-=2;
6944  slider_info.min_y=north_info.y+north_info.height+north_info.bevel_width+
6945  slider_info.bevel_width+2;
6946  slider_info.height=scroll_info.height-((slider_info.min_y-
6947  scroll_info.y+1) << 1)+4;
6948  visible_entries=(unsigned int) (scroll_info.height*
6949  PerceptibleReciprocal((double) height+(height >> 3)));
6950  if (entries > visible_entries)
6951  slider_info.height=(visible_entries*slider_info.height)/entries;
6952  slider_info.max_y=south_info.y-south_info.bevel_width-
6953  slider_info.bevel_width-2;
6954  slider_info.x=scroll_info.x+slider_info.bevel_width+1;
6955  slider_info.y=slider_info.min_y;
6956  expose_info=scroll_info;
6957  expose_info.y=slider_info.y;
6958  /*
6959  Initialize list information.
6960  */
6961  XGetWidgetInfo((char *) NULL,&list_info);
6962  list_info.raised=MagickFalse;
6963  list_info.bevel_width--;
6964  list_info.width=(unsigned int)
6965  (scroll_info.x-reply_info.x-(QuantumMargin >> 1));
6966  list_info.height=scroll_info.height;
6967  list_info.x=reply_info.x;
6968  list_info.y=scroll_info.y;
6969  if (window_info->mapped == MagickFalse)
6970  for (i=0; i < (int) entries; i++)
6971  if (LocaleCompare(list[i],reply) == 0)
6972  {
6973  list_info.id=i;
6974  slider_info.id=i-(visible_entries >> 1);
6975  if (slider_info.id < 0)
6976  slider_info.id=0;
6977  }
6978  /*
6979  Initialize text information.
6980  */
6981  XGetWidgetInfo(query,&text_info);
6982  text_info.width=reply_info.width;
6983  text_info.height=height;
6984  text_info.x=list_info.x-(QuantumMargin >> 1);
6985  text_info.y=QuantumMargin;
6986  /*
6987  Initialize selection information.
6988  */
6989  XGetWidgetInfo((char *) NULL,&selection_info);
6990  selection_info.center=MagickFalse;
6991  selection_info.width=list_info.width;
6992  selection_info.height=(unsigned int) ((9*height) >> 3);
6993  selection_info.x=list_info.x;
6994  state&=(~UpdateConfigurationState);
6995  }
6996  if (state & RedrawWidgetState)
6997  {
6998  /*
6999  Redraw List Browser window.
7000  */
7001  XDrawWidgetText(display,window_info,&text_info);
7002  XDrawBeveledMatte(display,window_info,&list_info);
7003  XDrawBeveledMatte(display,window_info,&scroll_info);
7004  XDrawTriangleNorth(display,window_info,&north_info);
7005  XDrawBeveledButton(display,window_info,&slider_info);
7006  XDrawTriangleSouth(display,window_info,&south_info);
7007  XDrawBeveledMatte(display,window_info,&reply_info);
7008  XDrawMatteText(display,window_info,&reply_info);
7009  XDrawBeveledButton(display,window_info,&action_info);
7010  XDrawBeveledButton(display,window_info,&cancel_info);
7011  XHighlightWidget(display,window_info,BorderOffset,BorderOffset);
7012  selection_info.id=(~0);
7013  state|=RedrawActionState;
7014  state|=RedrawListState;
7015  state&=(~RedrawWidgetState);
7016  }
7017  if (state & RedrawListState)
7018  {
7019  /*
7020  Determine slider id and position.
7021  */
7022  if (slider_info.id >= (int) (entries-visible_entries))
7023  slider_info.id=(int) (entries-visible_entries);
7024  if ((slider_info.id < 0) || (entries <= visible_entries))
7025  slider_info.id=0;
7026  slider_info.y=slider_info.min_y;
7027  if (entries > 0)
7028  slider_info.y+=
7029  slider_info.id*(slider_info.max_y-slider_info.min_y+1)/entries;
7030  if (slider_info.id != selection_info.id)
7031  {
7032  /*
7033  Redraw scroll bar and file names.
7034  */
7035  selection_info.id=slider_info.id;
7036  selection_info.y=list_info.y+(height >> 3)+2;
7037  for (i=0; i < (int) visible_entries; i++)
7038  {
7039  selection_info.raised=(slider_info.id+i) != list_info.id ?
7040  MagickTrue : MagickFalse;
7041  selection_info.text=(char *) NULL;
7042  if ((slider_info.id+i) < (int) entries)
7043  selection_info.text=(char *) list[slider_info.id+i];
7044  XDrawWidgetText(display,window_info,&selection_info);
7045  selection_info.y+=(int) selection_info.height;
7046  }
7047  /*
7048  Update slider.
7049  */
7050  if (slider_info.y > expose_info.y)
7051  {
7052  expose_info.height=(unsigned int) slider_info.y-expose_info.y;
7053  expose_info.y=slider_info.y-expose_info.height-
7054  slider_info.bevel_width-1;
7055  }
7056  else
7057  {
7058  expose_info.height=(unsigned int) expose_info.y-slider_info.y;
7059  expose_info.y=slider_info.y+slider_info.height+
7060  slider_info.bevel_width+1;
7061  }
7062  XDrawTriangleNorth(display,window_info,&north_info);
7063  XDrawMatte(display,window_info,&expose_info);
7064  XDrawBeveledButton(display,window_info,&slider_info);
7065  XDrawTriangleSouth(display,window_info,&south_info);
7066  expose_info.y=slider_info.y;
7067  }
7068  state&=(~RedrawListState);
7069  }
7070  /*
7071  Wait for next event.
7072  */
7073  if (north_info.raised && south_info.raised)
7074  (void) XIfEvent(display,&event,XScreenEvent,(char *) windows);
7075  else
7076  {
7077  /*
7078  Brief delay before advancing scroll bar.
7079  */
7080  XDelay(display,delay);
7081  delay=SuspendTime;
7082  (void) XCheckIfEvent(display,&event,XScreenEvent,(char *) windows);
7083  if (north_info.raised == MagickFalse)
7084  if (slider_info.id > 0)
7085  {
7086  /*
7087  Move slider up.
7088  */
7089  slider_info.id--;
7090  state|=RedrawListState;
7091  }
7092  if (south_info.raised == MagickFalse)
7093  if (slider_info.id < (int) entries)
7094  {
7095  /*
7096  Move slider down.
7097  */
7098  slider_info.id++;
7099  state|=RedrawListState;
7100  }
7101  if (event.type != ButtonRelease)
7102  continue;
7103  }
7104  switch (event.type)
7105  {
7106  case ButtonPress:
7107  {
7108  if (MatteIsActive(slider_info,event.xbutton))
7109  {
7110  /*
7111  Track slider.
7112  */
7113  slider_info.active=MagickTrue;
7114  break;
7115  }
7116  if (MatteIsActive(north_info,event.xbutton))
7117  if (slider_info.id > 0)
7118  {
7119  /*
7120  Move slider up.
7121  */
7122  north_info.raised=MagickFalse;
7123  slider_info.id--;
7124  state|=RedrawListState;
7125  break;
7126  }
7127  if (MatteIsActive(south_info,event.xbutton))
7128  if (slider_info.id < (int) entries)
7129  {
7130  /*
7131  Move slider down.
7132  */
7133  south_info.raised=MagickFalse;
7134  slider_info.id++;
7135  state|=RedrawListState;
7136  break;
7137  }
7138  if (MatteIsActive(scroll_info,event.xbutton))
7139  {
7140  /*
7141  Move slider.
7142  */
7143  if (event.xbutton.y < slider_info.y)
7144  slider_info.id-=(visible_entries-1);
7145  else
7146  slider_info.id+=(visible_entries-1);
7147  state|=RedrawListState;
7148  break;
7149  }
7150  if (MatteIsActive(list_info,event.xbutton))
7151  {
7152  int
7153  id;
7154 
7155  /*
7156  User pressed list matte.
7157  */
7158  id=slider_info.id+(event.xbutton.y-(list_info.y+(height >> 1))+1)/
7159  selection_info.height;
7160  if (id >= (int) entries)
7161  break;
7162  (void) CopyMagickString(reply_info.text,list[id],MaxTextExtent);
7163  reply_info.highlight=MagickFalse;
7164  reply_info.marker=reply_info.text;
7165  reply_info.cursor=reply_info.text+Extent(reply_info.text);
7166  XDrawMatteText(display,window_info,&reply_info);
7167  selection_info.id=(~0);
7168  if (id == list_info.id)
7169  {
7170  action_info.raised=MagickFalse;
7171  XDrawBeveledButton(display,window_info,&action_info);
7172  state|=ExitState;
7173  }
7174  list_info.id=id;
7175  state|=RedrawListState;
7176  break;
7177  }
7178  if (MatteIsActive(action_info,event.xbutton))
7179  {
7180  /*
7181  User pressed action button.
7182  */
7183  action_info.raised=MagickFalse;
7184  XDrawBeveledButton(display,window_info,&action_info);
7185  break;
7186  }
7187  if (MatteIsActive(cancel_info,event.xbutton))
7188  {
7189  /*
7190  User pressed Cancel button.
7191  */
7192  cancel_info.raised=MagickFalse;
7193  XDrawBeveledButton(display,window_info,&cancel_info);
7194  break;
7195  }
7196  if (MatteIsActive(reply_info,event.xbutton) == MagickFalse)
7197  break;
7198  if (event.xbutton.button != Button2)
7199  {
7200  static Time
7201  click_time;
7202 
7203  /*
7204  Move text cursor to position of button press.
7205  */
7206  x=event.xbutton.x-reply_info.x-(QuantumMargin >> 2);
7207  for (i=1; i <= Extent(reply_info.marker); i++)
7208  if (XTextWidth(font_info,reply_info.marker,i) > x)
7209  break;
7210  reply_info.cursor=reply_info.marker+i-1;
7211  if (event.xbutton.time > (click_time+DoubleClick))
7212  reply_info.highlight=MagickFalse;
7213  else
7214  {
7215  /*
7216  Become the XA_PRIMARY selection owner.
7217  */
7218  (void) CopyMagickString(primary_selection,reply_info.text,
7219  MaxTextExtent);
7220  (void) XSetSelectionOwner(display,XA_PRIMARY,window_info->id,
7221  event.xbutton.time);
7222  reply_info.highlight=XGetSelectionOwner(display,XA_PRIMARY) ==
7223  window_info->id ? MagickTrue : MagickFalse;
7224  }
7225  XDrawMatteText(display,window_info,&reply_info);
7226  click_time=event.xbutton.time;
7227  break;
7228  }
7229  /*
7230  Request primary selection.
7231  */
7232  (void) XConvertSelection(display,XA_PRIMARY,XA_STRING,XA_STRING,
7233  window_info->id,event.xbutton.time);
7234  break;
7235  }
7236  case ButtonRelease:
7237  {
7238  if (window_info->mapped == MagickFalse)
7239  break;
7240  if (north_info.raised == MagickFalse)
7241  {
7242  /*
7243  User released up button.
7244  */
7245  delay=SuspendTime << 2;
7246  north_info.raised=MagickTrue;
7247  XDrawTriangleNorth(display,window_info,&north_info);
7248  }
7249  if (south_info.raised == MagickFalse)
7250  {
7251  /*
7252  User released down button.
7253  */
7254  delay=SuspendTime << 2;
7255  south_info.raised=MagickTrue;
7256  XDrawTriangleSouth(display,window_info,&south_info);
7257  }
7258  if (slider_info.active)
7259  {
7260  /*
7261  Stop tracking slider.
7262  */
7263  slider_info.active=MagickFalse;
7264  break;
7265  }
7266  if (action_info.raised == MagickFalse)
7267  {
7268  if (event.xbutton.window == window_info->id)
7269  {
7270  if (MatteIsActive(action_info,event.xbutton))
7271  {
7272  if (*reply_info.text == '\0')
7273  (void) XBell(display,0);
7274  else
7275  state|=ExitState;
7276  }
7277  }
7278  action_info.raised=MagickTrue;
7279  XDrawBeveledButton(display,window_info,&action_info);
7280  }
7281  if (cancel_info.raised == MagickFalse)
7282  {
7283  if (event.xbutton.window == window_info->id)
7284  if (MatteIsActive(cancel_info,event.xbutton))
7285  {
7286  *reply_info.text='\0';
7287  state|=ExitState;
7288  }
7289  cancel_info.raised=MagickTrue;
7290  XDrawBeveledButton(display,window_info,&cancel_info);
7291  }
7292  if (MatteIsActive(reply_info,event.xbutton) == MagickFalse)
7293  break;
7294  break;
7295  }
7296  case ClientMessage:
7297  {
7298  /*
7299  If client window delete message, exit.
7300  */
7301  if (event.xclient.message_type != windows->wm_protocols)
7302  break;
7303  if (*event.xclient.data.l == (int) windows->wm_take_focus)
7304  {
7305  (void) XSetInputFocus(display,event.xclient.window,RevertToParent,
7306  (Time) event.xclient.data.l[1]);
7307  break;
7308  }
7309  if (*event.xclient.data.l != (int) windows->wm_delete_window)
7310  break;
7311  if (event.xclient.window == window_info->id)
7312  {
7313  *reply_info.text='\0';
7314  state|=ExitState;
7315  break;
7316  }
7317  break;
7318  }
7319  case ConfigureNotify:
7320  {
7321  /*
7322  Update widget configuration.
7323  */
7324  if (event.xconfigure.window != window_info->id)
7325  break;
7326  if ((event.xconfigure.width == (int) window_info->width) &&
7327  (event.xconfigure.height == (int) window_info->height))
7328  break;
7329  window_info->width=(unsigned int)
7330  MagickMax(event.xconfigure.width,(int) window_info->min_width);
7331  window_info->height=(unsigned int)
7332  MagickMax(event.xconfigure.height,(int) window_info->min_height);
7333  state|=UpdateConfigurationState;
7334  break;
7335  }
7336  case EnterNotify:
7337  {
7338  if (event.xcrossing.window != window_info->id)
7339  break;
7340  state&=(~InactiveWidgetState);
7341  break;
7342  }
7343  case Expose:
7344  {
7345  if (event.xexpose.window != window_info->id)
7346  break;
7347  if (event.xexpose.count != 0)
7348  break;
7349  state|=RedrawWidgetState;
7350  break;
7351  }
7352  case KeyPress:
7353  {
7354  static char
7355  command[MaxTextExtent];
7356 
7357  static int
7358  length;
7359 
7360  static KeySym
7361  key_symbol;
7362 
7363  /*
7364  Respond to a user key press.
7365  */
7366  if (event.xkey.window != window_info->id)
7367  break;
7368  length=XLookupString((XKeyEvent *) &event.xkey,command,
7369  (int) sizeof(command),&key_symbol,(XComposeStatus *) NULL);
7370  *(command+length)='\0';
7371  if (AreaIsActive(scroll_info,event.xkey))
7372  {
7373  /*
7374  Move slider.
7375  */
7376  switch ((int) key_symbol)
7377  {
7378  case XK_Home:
7379  case XK_KP_Home:
7380  {
7381  slider_info.id=0;
7382  break;
7383  }
7384  case XK_Up:
7385  case XK_KP_Up:
7386  {
7387  slider_info.id--;
7388  break;
7389  }
7390  case XK_Down:
7391  case XK_KP_Down:
7392  {
7393  slider_info.id++;
7394  break;
7395  }
7396  case XK_Prior:
7397  case XK_KP_Prior:
7398  {
7399  slider_info.id-=visible_entries;
7400  break;
7401  }
7402  case XK_Next:
7403  case XK_KP_Next:
7404  {
7405  slider_info.id+=visible_entries;
7406  break;
7407  }
7408  case XK_End:
7409  case XK_KP_End:
7410  {
7411  slider_info.id=(int) entries;
7412  break;
7413  }
7414  }
7415  state|=RedrawListState;
7416  break;
7417  }
7418  if ((key_symbol == XK_Return) || (key_symbol == XK_KP_Enter))
7419  {
7420  /*
7421  Read new entry.
7422  */
7423  if (*reply_info.text == '\0')
7424  break;
7425  action_info.raised=MagickFalse;
7426  XDrawBeveledButton(display,window_info,&action_info);
7427  state|=ExitState;
7428  break;
7429  }
7430  if (key_symbol == XK_Control_L)
7431  {
7432  state|=ControlState;
7433  break;
7434  }
7435  if (state & ControlState)
7436  switch ((int) key_symbol)
7437  {
7438  case XK_u:
7439  case XK_U:
7440  {
7441  /*
7442  Erase the entire line of text.
7443  */
7444  *reply_info.text='\0';
7445  reply_info.cursor=reply_info.text;
7446  reply_info.marker=reply_info.text;
7447  reply_info.highlight=MagickFalse;
7448  break;
7449  }
7450  default:
7451  break;
7452  }
7453  XEditText(display,&reply_info,key_symbol,command,state);
7454  XDrawMatteText(display,window_info,&reply_info);
7455  break;
7456  }
7457  case KeyRelease:
7458  {
7459  static char
7460  command[MaxTextExtent];
7461 
7462  static KeySym
7463  key_symbol;
7464 
7465  /*
7466  Respond to a user key release.
7467  */
7468  if (event.xkey.window != window_info->id)
7469  break;
7470  (void) XLookupString((XKeyEvent *) &event.xkey,command,
7471  (int) sizeof(command),&key_symbol,(XComposeStatus *) NULL);
7472  if (key_symbol == XK_Control_L)
7473  state&=(~ControlState);
7474  break;
7475  }
7476  case LeaveNotify:
7477  {
7478  if (event.xcrossing.window != window_info->id)
7479  break;
7480  state|=InactiveWidgetState;
7481  break;
7482  }
7483  case MapNotify:
7484  {
7485  mask&=(~CWX);
7486  mask&=(~CWY);
7487  break;
7488  }
7489  case MotionNotify:
7490  {
7491  /*
7492  Discard pending button motion events.
7493  */
7494  while (XCheckMaskEvent(display,ButtonMotionMask,&event)) ;
7495  if (slider_info.active)
7496  {
7497  /*
7498  Move slider matte.
7499  */
7500  slider_info.y=event.xmotion.y-
7501  ((slider_info.height+slider_info.bevel_width) >> 1)+1;
7502  if (slider_info.y < slider_info.min_y)
7503  slider_info.y=slider_info.min_y;
7504  if (slider_info.y > slider_info.max_y)
7505  slider_info.y=slider_info.max_y;
7506  slider_info.id=0;
7507  if (slider_info.y != slider_info.min_y)
7508  slider_info.id=(int) ((entries*(slider_info.y-
7509  slider_info.min_y+1))/(slider_info.max_y-slider_info.min_y+1));
7510  state|=RedrawListState;
7511  break;
7512  }
7513  if (state & InactiveWidgetState)
7514  break;
7515  if (action_info.raised == MatteIsActive(action_info,event.xmotion))
7516  {
7517  /*
7518  Action button status changed.
7519  */
7520  action_info.raised=action_info.raised == MagickFalse ?
7521  MagickTrue : MagickFalse;
7522  XDrawBeveledButton(display,window_info,&action_info);
7523  break;
7524  }
7525  if (cancel_info.raised == MatteIsActive(cancel_info,event.xmotion))
7526  {
7527  /*
7528  Cancel button status changed.
7529  */
7530  cancel_info.raised=cancel_info.raised == MagickFalse ?
7531  MagickTrue : MagickFalse;
7532  XDrawBeveledButton(display,window_info,&cancel_info);
7533  break;
7534  }
7535  break;
7536  }
7537  case SelectionClear:
7538  {
7539  reply_info.highlight=MagickFalse;
7540  XDrawMatteText(display,window_info,&reply_info);
7541  break;
7542  }
7543  case SelectionNotify:
7544  {
7545  Atom
7546  type;
7547 
7548  int
7549  format;
7550 
7551  unsigned char
7552  *data;
7553 
7554  unsigned long
7555  after,
7556  length;
7557 
7558  /*
7559  Obtain response from primary selection.
7560  */
7561  if (event.xselection.property == (Atom) None)
7562  break;
7563  status=XGetWindowProperty(display,
7564  event.xselection.requestor,event.xselection.property,0L,2047L,
7565  MagickTrue,XA_STRING,&type,&format,&length,&after,&data);
7566  if ((status != Success) || (type != XA_STRING) || (format == 32) ||
7567  (length == 0))
7568  break;
7569  if ((Extent(reply_info.text)+length) >= (MaxTextExtent-1))
7570  (void) XBell(display,0);
7571  else
7572  {
7573  /*
7574  Insert primary selection in reply text.
7575  */
7576  *(data+length)='\0';
7577  XEditText(display,&reply_info,(KeySym) XK_Insert,(char *) data,
7578  state);
7579  XDrawMatteText(display,window_info,&reply_info);
7580  state|=RedrawActionState;
7581  }
7582  (void) XFree((void *) data);
7583  break;
7584  }
7585  case SelectionRequest:
7586  {
7587  XSelectionEvent
7588  notify;
7589 
7590  XSelectionRequestEvent
7591  *request;
7592 
7593  if (reply_info.highlight == MagickFalse)
7594  break;
7595  /*
7596  Set primary selection.
7597  */
7598  request=(&(event.xselectionrequest));
7599  (void) XChangeProperty(request->display,request->requestor,
7600  request->property,request->target,8,PropModeReplace,
7601  (unsigned char *) primary_selection,Extent(primary_selection));
7602  notify.type=SelectionNotify;
7603  notify.send_event=MagickTrue;
7604  notify.display=request->display;
7605  notify.requestor=request->requestor;
7606  notify.selection=request->selection;
7607  notify.target=request->target;
7608  notify.time=request->time;
7609  if (request->property == None)
7610  notify.property=request->target;
7611  else
7612  notify.property=request->property;
7613  (void) XSendEvent(request->display,request->requestor,False,NoEventMask,
7614  (XEvent *) &notify);
7615  }
7616  default:
7617  break;
7618  }
7619  } while ((state & ExitState) == 0);
7620  XSetCursorState(display,windows,MagickFalse);
7621  (void) XWithdrawWindow(display,window_info->id,window_info->screen);
7622  XCheckRefreshWindows(display,windows);
7623 }
7624 
7625 /*
7626 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7627 % %
7628 % %
7629 % %
7630 % X M e n u W i d g e t %
7631 % %
7632 % %
7633 % %
7634 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7635 %
7636 % XMenuWidget() maps a menu and returns the command pointed to by the user
7637 % when the button is released.
7638 %
7639 % The format of the XMenuWidget method is:
7640 %
7641 % int XMenuWidget(Display *display,XWindows *windows,const char *title,
7642 % const char *const *selections,char *item)
7643 %
7644 % A description of each parameter follows:
7645 %
7646 % o selection_number: Specifies the number of the selection that the
7647 % user choose.
7648 %
7649 % o display: Specifies a connection to an X server; returned from
7650 % XOpenDisplay.
7651 %
7652 % o window: Specifies a pointer to a XWindows structure.
7653 %
7654 % o title: Specifies a character string that describes the menu selections.
7655 %
7656 % o selections: Specifies a pointer to one or more strings that comprise
7657 % the choices in the menu.
7658 %
7659 % o item: Specifies a character array. The item selected from the menu
7660 % is returned here.
7661 %
7662 */
7663 MagickExport int XMenuWidget(Display *display,XWindows *windows,
7664  const char *title,const char *const *selections,char *item)
7665 {
7666  Cursor
7667  cursor;
7668 
7669  int
7670  id,
7671  x,
7672  y;
7673 
7674  unsigned int
7675  height,
7676  number_selections,
7677  title_height,
7678  top_offset,
7679  width;
7680 
7681  size_t
7682  state;
7683 
7684  XEvent
7685  event;
7686 
7687  XFontStruct
7688  *font_info;
7689 
7690  XSetWindowAttributes
7691  window_attributes;
7692 
7693  XWidgetInfo
7694  highlight_info,
7695  menu_info,
7696  selection_info;
7697 
7698  XWindowChanges
7699  window_changes;
7700 
7701  /*
7702  Determine Menu widget attributes.
7703  */
7704  assert(display != (Display *) NULL);
7705  assert(windows != (XWindows *) NULL);
7706  assert(title != (char *) NULL);
7707  assert(selections != (const char **) NULL);
7708  assert(item != (char *) NULL);
7709  if (IsEventLogging() != MagickFalse)
7710  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",title);
7711  font_info=windows->widget.font_info;
7712  windows->widget.width=submenu_info.active == 0 ?
7713  WidgetTextWidth(font_info,(char *) title) : 0;
7714  for (id=0; selections[id] != (char *) NULL; id++)
7715  {
7716  width=WidgetTextWidth(font_info,(char *) selections[id]);
7717  if (width > windows->widget.width)
7718  windows->widget.width=width;
7719  }
7720  number_selections=(unsigned int) id;
7721  XGetWidgetInfo((char *) NULL,&menu_info);
7722  title_height=(unsigned int) (submenu_info.active == 0 ?
7723  (3*(font_info->descent+font_info->ascent) >> 1)+5 : 2);
7724  width=WidgetTextWidth(font_info,(char *) title);
7725  height=(unsigned int) ((3*(font_info->ascent+font_info->descent)) >> 1);
7726  /*
7727  Position Menu widget.
7728  */
7729  windows->widget.width+=QuantumMargin+(menu_info.bevel_width << 1);
7730  top_offset=title_height+menu_info.bevel_width-1;
7731  windows->widget.height=top_offset+number_selections*height+4;
7732  windows->widget.min_width=windows->widget.width;
7733  windows->widget.min_height=windows->widget.height;
7734  XQueryPosition(display,windows->widget.root,&x,&y);
7735  windows->widget.x=x-(QuantumMargin >> 1);
7736  if (submenu_info.active != 0)
7737  {
7738  windows->widget.x=
7739  windows->command.x+windows->command.width-QuantumMargin;
7740  toggle_info.raised=MagickTrue;
7741  XDrawTriangleEast(display,&windows->command,&toggle_info);
7742  }
7743  windows->widget.y=submenu_info.active == 0 ? y-(int)
7744  ((3*title_height) >> 2) : y;
7745  if (submenu_info.active != 0)
7746  windows->widget.y=windows->command.y+submenu_info.y;
7747  XConstrainWindowPosition(display,&windows->widget);
7748  /*
7749  Map Menu widget.
7750  */
7751  window_attributes.override_redirect=MagickTrue;
7752  (void) XChangeWindowAttributes(display,windows->widget.id,
7753  (size_t) CWOverrideRedirect,&window_attributes);
7754  window_changes.width=(int) windows->widget.width;
7755  window_changes.height=(int) windows->widget.height;
7756  window_changes.x=windows->widget.x;
7757  window_changes.y=windows->widget.y;
7758  (void) XReconfigureWMWindow(display,windows->widget.id,windows->widget.screen,
7759  (unsigned int) (CWWidth | CWHeight | CWX | CWY),&window_changes);
7760  (void) XMapRaised(display,windows->widget.id);
7761  windows->widget.mapped=MagickFalse;
7762  /*
7763  Respond to X events.
7764  */
7765  selection_info.height=height;
7766  cursor=XCreateFontCursor(display,XC_right_ptr);
7767  (void) XCheckDefineCursor(display,windows->image.id,cursor);
7768  (void) XCheckDefineCursor(display,windows->command.id,cursor);
7769  (void) XCheckDefineCursor(display,windows->widget.id,cursor);
7770  state=UpdateConfigurationState;
7771  do
7772  {
7773  if (state & UpdateConfigurationState)
7774  {
7775  /*
7776  Initialize selection information.
7777  */
7778  XGetWidgetInfo((char *) NULL,&menu_info);
7779  menu_info.bevel_width--;
7780  menu_info.width=windows->widget.width-((menu_info.bevel_width) << 1);
7781  menu_info.height=windows->widget.height-((menu_info.bevel_width) << 1);
7782  menu_info.x=(int) menu_info.bevel_width;
7783  menu_info.y=(int) menu_info.bevel_width;
7784  XGetWidgetInfo((char *) NULL,&selection_info);
7785  selection_info.center=MagickFalse;
7786  selection_info.width=menu_info.width;
7787  selection_info.height=height;
7788  selection_info.x=menu_info.x;
7789  highlight_info=selection_info;
7790  highlight_info.bevel_width--;
7791  highlight_info.width-=(highlight_info.bevel_width << 1);
7792  highlight_info.height-=(highlight_info.bevel_width << 1);
7793  highlight_info.x+=highlight_info.bevel_width;
7794  state&=(~UpdateConfigurationState);
7795  }
7796  if (state & RedrawWidgetState)
7797  {
7798  /*
7799  Redraw Menu widget.
7800  */
7801  if (submenu_info.active == 0)
7802  {
7803  y=(int) title_height;
7804  XSetBevelColor(display,&windows->widget,MagickFalse);
7805  (void) XDrawLine(display,windows->widget.id,
7806  windows->widget.widget_context,selection_info.x,y-1,
7807  (int) selection_info.width,y-1);
7808  XSetBevelColor(display,&windows->widget,MagickTrue);
7809  (void) XDrawLine(display,windows->widget.id,
7810  windows->widget.widget_context,selection_info.x,y,
7811  (int) selection_info.width,y);
7812  (void) XSetFillStyle(display,windows->widget.widget_context,
7813  FillSolid);
7814  }
7815  /*
7816  Draw menu selections.
7817  */
7818  selection_info.center=MagickTrue;
7819  selection_info.y=(int) menu_info.bevel_width;
7820  selection_info.text=(char *) title;
7821  if (submenu_info.active == 0)
7822  XDrawWidgetText(display,&windows->widget,&selection_info);
7823  selection_info.center=MagickFalse;
7824  selection_info.y=(int) top_offset;
7825  for (id=0; id < (int) number_selections; id++)
7826  {
7827  selection_info.text=(char *) selections[id];
7828  XDrawWidgetText(display,&windows->widget,&selection_info);
7829  highlight_info.y=selection_info.y+highlight_info.bevel_width;
7830  if (id == selection_info.id)
7831  XDrawBevel(display,&windows->widget,&highlight_info);
7832  selection_info.y+=(int) selection_info.height;
7833  }
7834  XDrawBevel(display,&windows->widget,&menu_info);
7835  state&=(~RedrawWidgetState);
7836  }
7837  if (number_selections > 2)
7838  {
7839  /*
7840  Redraw Menu line.
7841  */
7842  y=(int) (top_offset+selection_info.height*(number_selections-1));
7843  XSetBevelColor(display,&windows->widget,MagickFalse);
7844  (void) XDrawLine(display,windows->widget.id,
7845  windows->widget.widget_context,selection_info.x,y-1,
7846  (int) selection_info.width,y-1);
7847  XSetBevelColor(display,&windows->widget,MagickTrue);
7848  (void) XDrawLine(display,windows->widget.id,
7849  windows->widget.widget_context,selection_info.x,y,
7850  (int) selection_info.width,y);
7851  (void) XSetFillStyle(display,windows->widget.widget_context,FillSolid);
7852  }
7853  /*
7854  Wait for next event.
7855  */
7856  (void) XIfEvent(display,&event,XScreenEvent,(char *) windows);
7857  switch (event.type)
7858  {
7859  case ButtonPress:
7860  {
7861  if (event.xbutton.window != windows->widget.id)
7862  {
7863  /*
7864  exit menu.
7865  */
7866  if (event.xbutton.window == windows->command.id)
7867  (void) XPutBackEvent(display,&event);
7868  selection_info.id=(~0);
7869  *item='\0';
7870  state|=ExitState;
7871  break;
7872  }
7873  state&=(~InactiveWidgetState);
7874  if (selection_info.height == 0)
7875  break;
7876  id=(event.xbutton.y-top_offset)/(int) selection_info.height;
7877  selection_info.id=id;
7878  if ((id < 0) || (id >= (int) number_selections))
7879  break;
7880  /*
7881  Highlight this selection.
7882  */
7883  selection_info.y=(int) (top_offset+id*selection_info.height);
7884  selection_info.text=(char *) selections[id];
7885  XDrawWidgetText(display,&windows->widget,&selection_info);
7886  highlight_info.y=selection_info.y+highlight_info.bevel_width;
7887  XDrawBevel(display,&windows->widget,&highlight_info);
7888  break;
7889  }
7890  case ButtonRelease:
7891  {
7892  if (windows->widget.mapped == MagickFalse)
7893  break;
7894  if (event.xbutton.window == windows->command.id)
7895  if ((state & InactiveWidgetState) == 0)
7896  break;
7897  /*
7898  exit menu.
7899  */
7900  XSetCursorState(display,windows,MagickFalse);
7901  *item='\0';
7902  state|=ExitState;
7903  break;
7904  }
7905  case ConfigureNotify:
7906  {
7907  /*
7908  Update widget configuration.
7909  */
7910  if (event.xconfigure.window != windows->widget.id)
7911  break;
7912  if ((event.xconfigure.width == (int) windows->widget.width) &&
7913  (event.xconfigure.height == (int) windows->widget.height))
7914  break;
7915  windows->widget.width=(unsigned int)
7916  MagickMax(event.xconfigure.width,(int) windows->widget.min_width);
7917  windows->widget.height=(unsigned int)
7918  MagickMax(event.xconfigure.height,(int) windows->widget.min_height);
7919  state|=UpdateConfigurationState;
7920  break;
7921  }
7922  case EnterNotify:
7923  {
7924  if (event.xcrossing.window != windows->widget.id)
7925  break;
7926  if (event.xcrossing.state == 0)
7927  break;
7928  state&=(~InactiveWidgetState);
7929  if (selection_info.height == 0)
7930  break;
7931  id=((event.xcrossing.y-top_offset)/(int) selection_info.height);
7932  if ((selection_info.id >= 0) &&
7933  (selection_info.id < (int) number_selections))
7934  {
7935  /*
7936  Unhighlight last selection.
7937  */
7938  if (id == selection_info.id)
7939  break;
7940  selection_info.y=(int)
7941  (top_offset+selection_info.id*selection_info.height);
7942  selection_info.text=(char *) selections[selection_info.id];
7943  XDrawWidgetText(display,&windows->widget,&selection_info);
7944  }
7945  if ((id < 0) || (id >= (int) number_selections))
7946  break;
7947  /*
7948  Highlight this selection.
7949  */
7950  selection_info.id=id;
7951  selection_info.y=(int)
7952  (top_offset+selection_info.id*selection_info.height);
7953  selection_info.text=(char *) selections[selection_info.id];
7954  XDrawWidgetText(display,&windows->widget,&selection_info);
7955  highlight_info.y=selection_info.y+highlight_info.bevel_width;
7956  XDrawBevel(display,&windows->widget,&highlight_info);
7957  break;
7958  }
7959  case Expose:
7960  {
7961  if (event.xexpose.window != windows->widget.id)
7962  break;
7963  if (event.xexpose.count != 0)
7964  break;
7965  state|=RedrawWidgetState;
7966  break;
7967  }
7968  case LeaveNotify:
7969  {
7970  if (event.xcrossing.window != windows->widget.id)
7971  break;
7972  state|=InactiveWidgetState;
7973  id=selection_info.id;
7974  if ((id < 0) || (id >= (int) number_selections))
7975  break;
7976  /*
7977  Unhighlight last selection.
7978  */
7979  selection_info.y=(int) (top_offset+id*selection_info.height);
7980  selection_info.id=(~0);
7981  selection_info.text=(char *) selections[id];
7982  XDrawWidgetText(display,&windows->widget,&selection_info);
7983  break;
7984  }
7985  case MotionNotify:
7986  {
7987  /*
7988  Discard pending button motion events.
7989  */
7990  while (XCheckMaskEvent(display,ButtonMotionMask,&event)) ;
7991  if (submenu_info.active != 0)
7992  if (event.xmotion.window == windows->command.id)
7993  {
7994  if ((state & InactiveWidgetState) == 0)
7995  {
7996  if (MatteIsActive(submenu_info,event.xmotion) == MagickFalse)
7997  {
7998  selection_info.id=(~0);
7999  *item='\0';
8000  state|=ExitState;
8001  break;
8002  }
8003  }
8004  else
8005  if (WindowIsActive(windows->command,event.xmotion))
8006  {
8007  selection_info.id=(~0);
8008  *item='\0';
8009  state|=ExitState;
8010  break;
8011  }
8012  }
8013  if (event.xmotion.window != windows->widget.id)
8014  break;
8015  if (state & InactiveWidgetState)
8016  break;
8017  if (selection_info.height == 0)
8018  break;
8019  id=(event.xmotion.y-top_offset)/(int) selection_info.height;
8020  if ((selection_info.id >= 0) &&
8021  (selection_info.id < (int) number_selections))
8022  {
8023  /*
8024  Unhighlight last selection.
8025  */
8026  if (id == selection_info.id)
8027  break;
8028  selection_info.y=(int)
8029  (top_offset+selection_info.id*selection_info.height);
8030  selection_info.text=(char *) selections[selection_info.id];
8031  XDrawWidgetText(display,&windows->widget,&selection_info);
8032  }
8033  selection_info.id=id;
8034  if ((id < 0) || (id >= (int) number_selections))
8035  break;
8036  /*
8037  Highlight this selection.
8038  */
8039  selection_info.y=(int) (top_offset+id*selection_info.height);
8040  selection_info.text=(char *) selections[id];
8041  XDrawWidgetText(display,&windows->widget,&selection_info);
8042  highlight_info.y=selection_info.y+highlight_info.bevel_width;
8043  XDrawBevel(display,&windows->widget,&highlight_info);
8044  break;
8045  }
8046  default:
8047  break;
8048  }
8049  } while ((state & ExitState) == 0);
8050  (void) XFreeCursor(display,cursor);
8051  window_attributes.override_redirect=MagickFalse;
8052  (void) XChangeWindowAttributes(display,windows->widget.id,
8053  (size_t) CWOverrideRedirect,&window_attributes);
8054  (void) XWithdrawWindow(display,windows->widget.id,windows->widget.screen);
8055  XCheckRefreshWindows(display,windows);
8056  if (submenu_info.active != 0)
8057  {
8058  submenu_info.active=MagickFalse;
8059  toggle_info.raised=MagickFalse;
8060  XDrawTriangleEast(display,&windows->command,&toggle_info);
8061  }
8062  if ((selection_info.id < 0) || (selection_info.id >= (int) number_selections))
8063  return(~0);
8064  (void) CopyMagickString(item,selections[selection_info.id],MaxTextExtent);
8065  return(selection_info.id);
8066 }
8067 
8068 /*
8069 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8070 % %
8071 % %
8072 % %
8073 % X N o t i c e W i d g e t %
8074 % %
8075 % %
8076 % %
8077 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8078 %
8079 % XNoticeWidget() displays a Notice widget with a notice to the user. The
8080 % function returns when the user presses the "Dismiss" button.
8081 %
8082 % The format of the XNoticeWidget method is:
8083 %
8084 % void XNoticeWidget(Display *display,XWindows *windows,
8085 % const char *reason,const char *description)
8086 %
8087 % A description of each parameter follows:
8088 %
8089 % o display: Specifies a connection to an X server; returned from
8090 % XOpenDisplay.
8091 %
8092 % o window: Specifies a pointer to a XWindows structure.
8093 %
8094 % o reason: Specifies the message to display before terminating the
8095 % program.
8096 %
8097 % o description: Specifies any description to the message.
8098 %
8099 */
8100 MagickExport void XNoticeWidget(Display *display,XWindows *windows,
8101  const char *reason,const char *description)
8102 {
8103 #define DismissButtonText "Dismiss"
8104 #define Timeout 8
8105 
8106  const char
8107  *text;
8108 
8109  int
8110  x,
8111  y;
8112 
8113  Status
8114  status;
8115 
8116  time_t
8117  timer;
8118 
8119  unsigned int
8120  height,
8121  width;
8122 
8123  size_t
8124  state;
8125 
8126  XEvent
8127  event;
8128 
8129  XFontStruct
8130  *font_info;
8131 
8132  XTextProperty
8133  window_name;
8134 
8135  XWidgetInfo
8136  dismiss_info;
8137 
8138  XWindowChanges
8139  window_changes;
8140 
8141  /*
8142  Determine Notice widget attributes.
8143  */
8144  assert(display != (Display *) NULL);
8145  assert(windows != (XWindows *) NULL);
8146  assert(reason != (char *) NULL);
8147  if (IsEventLogging() != MagickFalse)
8148  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",reason);
8149  XDelay(display,SuspendTime << 3); /* avoid surprise with delay */
8150  XSetCursorState(display,windows,MagickTrue);
8151  XCheckRefreshWindows(display,windows);
8152  font_info=windows->widget.font_info;
8153  width=WidgetTextWidth(font_info,DismissButtonText);
8154  text=GetLocaleExceptionMessage(XServerError,reason);
8155  if (text != (char *) NULL)
8156  if (WidgetTextWidth(font_info,(char *) text) > width)
8157  width=WidgetTextWidth(font_info,(char *) text);
8158  if (description != (char *) NULL)
8159  {
8160  text=GetLocaleExceptionMessage(XServerError,description);
8161  if (text != (char *) NULL)
8162  if (WidgetTextWidth(font_info,(char *) text) > width)
8163  width=WidgetTextWidth(font_info,(char *) text);
8164  }
8165  height=(unsigned int) (font_info->ascent+font_info->descent);
8166  /*
8167  Position Notice widget.
8168  */
8169  windows->widget.width=width+4*QuantumMargin;
8170  windows->widget.min_width=width+QuantumMargin;
8171  if (windows->widget.width < windows->widget.min_width)
8172  windows->widget.width=windows->widget.min_width;
8173  windows->widget.height=(unsigned int) (12*height);
8174  windows->widget.min_height=(unsigned int) (7*height);
8175  if (windows->widget.height < windows->widget.min_height)
8176  windows->widget.height=windows->widget.min_height;
8177  XConstrainWindowPosition(display,&windows->widget);
8178  /*
8179  Map Notice widget.
8180  */
8181  (void) CopyMagickString(windows->widget.name,"Notice",MaxTextExtent);
8182  status=XStringListToTextProperty(&windows->widget.name,1,&window_name);
8183  if (status != False)
8184  {
8185  XSetWMName(display,windows->widget.id,&window_name);
8186  XSetWMIconName(display,windows->widget.id,&window_name);
8187  (void) XFree((void *) window_name.value);
8188  }
8189  window_changes.width=(int) windows->widget.width;
8190  window_changes.height=(int) windows->widget.height;
8191  window_changes.x=windows->widget.x;
8192  window_changes.y=windows->widget.y;
8193  (void) XReconfigureWMWindow(display,windows->widget.id,windows->widget.screen,
8194  (unsigned int) (CWWidth | CWHeight | CWX | CWY),&window_changes);
8195  (void) XMapRaised(display,windows->widget.id);
8196  windows->widget.mapped=MagickFalse;
8197  (void) XBell(display,0);
8198  /*
8199  Respond to X events.
8200  */
8201  timer=GetMagickTime()+Timeout;
8202  state=UpdateConfigurationState;
8203  do
8204  {
8205  if (GetMagickTime() > timer)
8206  break;
8207  if (state & UpdateConfigurationState)
8208  {
8209  /*
8210  Initialize Dismiss button information.
8211  */
8212  XGetWidgetInfo(DismissButtonText,&dismiss_info);
8213  dismiss_info.width=(unsigned int) QuantumMargin+
8214  WidgetTextWidth(font_info,DismissButtonText);
8215  dismiss_info.height=(unsigned int) ((3*height) >> 1);
8216  dismiss_info.x=(int)
8217  ((windows->widget.width >> 1)-(dismiss_info.width >> 1));
8218  dismiss_info.y=(int)
8219  (windows->widget.height-(dismiss_info.height << 1));
8220  state&=(~UpdateConfigurationState);
8221  }
8222  if (state & RedrawWidgetState)
8223  {
8224  /*
8225  Redraw Notice widget.
8226  */
8227  width=WidgetTextWidth(font_info,(char *) reason);
8228  x=(int) ((windows->widget.width >> 1)-(width >> 1));
8229  y=(int) ((windows->widget.height >> 1)-(height << 1));
8230  (void) XDrawString(display,windows->widget.id,
8231  windows->widget.annotate_context,x,y,(char *) reason,Extent(reason));
8232  if (description != (char *) NULL)
8233  {
8234  width=WidgetTextWidth(font_info,(char *) description);
8235  x=(int) ((windows->widget.width >> 1)-(width >> 1));
8236  y+=height;
8237  (void) XDrawString(display,windows->widget.id,
8238  windows->widget.annotate_context,x,y,(char *) description,
8239  Extent(description));
8240  }
8241  XDrawBeveledButton(display,&windows->widget,&dismiss_info);
8242  XHighlightWidget(display,&windows->widget,BorderOffset,BorderOffset);
8243  state&=(~RedrawWidgetState);
8244  }
8245  /*
8246  Wait for next event.
8247  */
8248  if (XCheckIfEvent(display,&event,XScreenEvent,(char *) windows) == MagickFalse)
8249  {
8250  /*
8251  Do not block if delay > 0.
8252  */
8253  XDelay(display,SuspendTime << 2);
8254  continue;
8255  }
8256  switch (event.type)
8257  {
8258  case ButtonPress:
8259  {
8260  if (MatteIsActive(dismiss_info,event.xbutton))
8261  {
8262  /*
8263  User pressed Dismiss button.
8264  */
8265  dismiss_info.raised=MagickFalse;
8266  XDrawBeveledButton(display,&windows->widget,&dismiss_info);
8267  break;
8268  }
8269  break;
8270  }
8271  case ButtonRelease:
8272  {
8273  if (windows->widget.mapped == MagickFalse)
8274  break;
8275  if (dismiss_info.raised == MagickFalse)
8276  {
8277  if (event.xbutton.window == windows->widget.id)
8278  if (MatteIsActive(dismiss_info,event.xbutton))
8279  state|=ExitState;
8280  dismiss_info.raised=MagickTrue;
8281  XDrawBeveledButton(display,&windows->widget,&dismiss_info);
8282  }
8283  break;
8284  }
8285  case ClientMessage:
8286  {
8287  /*
8288  If client window delete message, exit.
8289  */
8290  if (event.xclient.message_type != windows->wm_protocols)
8291  break;
8292  if (*event.xclient.data.l == (int) windows->wm_take_focus)
8293  {
8294  (void) XSetInputFocus(display,event.xclient.window,RevertToParent,
8295  (Time) event.xclient.data.l[1]);
8296  break;
8297  }
8298  if (*event.xclient.data.l != (int) windows->wm_delete_window)
8299  break;
8300  if (event.xclient.window == windows->widget.id)
8301  {
8302  state|=ExitState;
8303  break;
8304  }
8305  break;
8306  }
8307  case ConfigureNotify:
8308  {
8309  /*
8310  Update widget configuration.
8311  */
8312  if (event.xconfigure.window != windows->widget.id)
8313  break;
8314  if ((event.xconfigure.width == (int) windows->widget.width) &&
8315  (event.xconfigure.height == (int) windows->widget.height))
8316  break;
8317  windows->widget.width=(unsigned int)
8318  MagickMax(event.xconfigure.width,(int) windows->widget.min_width);
8319  windows->widget.height=(unsigned int)
8320  MagickMax(event.xconfigure.height,(int) windows->widget.min_height);
8321  state|=UpdateConfigurationState;
8322  break;
8323  }
8324  case EnterNotify:
8325  {
8326  if (event.xcrossing.window != windows->widget.id)
8327  break;
8328  state&=(~InactiveWidgetState);
8329  break;
8330  }
8331  case Expose:
8332  {
8333  if (event.xexpose.window != windows->widget.id)
8334  break;
8335  if (event.xexpose.count != 0)
8336  break;
8337  state|=RedrawWidgetState;
8338  break;
8339  }
8340  case KeyPress:
8341  {
8342  static char
8343  command[MaxTextExtent];
8344 
8345  static KeySym
8346  key_symbol;
8347 
8348  /*
8349  Respond to a user key press.
8350  */
8351  if (event.xkey.window != windows->widget.id)
8352  break;
8353  (void) XLookupString((XKeyEvent *) &event.xkey,command,
8354  (int) sizeof(command),&key_symbol,(XComposeStatus *) NULL);
8355  if ((key_symbol == XK_Return) || (key_symbol == XK_KP_Enter))
8356  {
8357  dismiss_info.raised=MagickFalse;
8358  XDrawBeveledButton(display,&windows->widget,&dismiss_info);
8359  state|=ExitState;
8360  break;
8361  }
8362  break;
8363  }
8364  case LeaveNotify:
8365  {
8366  if (event.xcrossing.window != windows->widget.id)
8367  break;
8368  state|=InactiveWidgetState;
8369  break;
8370  }
8371  case MotionNotify:
8372  {
8373  /*
8374  Discard pending button motion events.
8375  */
8376  while (XCheckMaskEvent(display,ButtonMotionMask,&event)) ;
8377  if (state & InactiveWidgetState)
8378  break;
8379  if (dismiss_info.raised == MatteIsActive(dismiss_info,event.xmotion))
8380  {
8381  /*
8382  Dismiss button status changed.
8383  */
8384  dismiss_info.raised=
8385  dismiss_info.raised == MagickFalse ? MagickTrue : MagickFalse;
8386  XDrawBeveledButton(display,&windows->widget,&dismiss_info);
8387  break;
8388  }
8389  break;
8390  }
8391  default:
8392  break;
8393  }
8394  } while ((state & ExitState) == 0);
8395  XSetCursorState(display,windows,MagickFalse);
8396  (void) XWithdrawWindow(display,windows->widget.id,windows->widget.screen);
8397  XCheckRefreshWindows(display,windows);
8398 }
8399 
8400 /*
8401 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8402 % %
8403 % %
8404 % %
8405 % X P r e f e r e n c e s W i d g e t %
8406 % %
8407 % %
8408 % %
8409 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8410 %
8411 % XPreferencesWidget() displays a Preferences widget with program preferences.
8412 % If the user presses the Apply button, the preferences are stored in a
8413 % configuration file in the users' home directory.
8414 %
8415 % The format of the XPreferencesWidget method is:
8416 %
8417 % MagickBooleanType XPreferencesWidget(Display *display,
8418 % XResourceInfo *resource_info,XWindows *windows)
8419 %
8420 % A description of each parameter follows:
8421 %
8422 % o display: Specifies a connection to an X server; returned from
8423 % XOpenDisplay.
8424 %
8425 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
8426 %
8427 % o window: Specifies a pointer to a XWindows structure.
8428 %
8429 */
8430 MagickExport MagickBooleanType XPreferencesWidget(Display *display,
8431  XResourceInfo *resource_info,XWindows *windows)
8432 {
8433 #define ApplyButtonText "Apply"
8434 #define CacheButtonText "%lu mega-bytes of memory in the undo edit cache "
8435 #define CancelButtonText "Cancel"
8436 #define NumberPreferences 8
8437 
8438  static const char
8439  *Preferences[] =
8440  {
8441  "display image centered on a backdrop",
8442  "confirm on program exit",
8443  "confirm on image edits",
8444  "correct image for display gamma",
8445  "display warning messages",
8446  "apply Floyd/Steinberg error diffusion to image",
8447  "use a shared colormap for colormapped X visuals",
8448  "display images as an X server pixmap"
8449  };
8450 
8451  char
8452  cache[MaxTextExtent];
8453 
8454  int
8455  x,
8456  y;
8457 
8458  int
8459  i;
8460 
8461  Status
8462  status;
8463 
8464  unsigned int
8465  height,
8466  text_width,
8467  width;
8468 
8469  size_t
8470  state;
8471 
8472  XEvent
8473  event;
8474 
8475  XFontStruct
8476  *font_info;
8477 
8478  XTextProperty
8479  window_name;
8480 
8481  XWidgetInfo
8482  apply_info,
8483  cache_info,
8484  cancel_info,
8485  preferences_info[NumberPreferences];
8486 
8487  XWindowChanges
8488  window_changes;
8489 
8490  /*
8491  Determine Preferences widget attributes.
8492  */
8493  assert(display != (Display *) NULL);
8494  assert(resource_info != (XResourceInfo *) NULL);
8495  assert(windows != (XWindows *) NULL);
8496  if (IsEventLogging() != MagickFalse)
8497  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
8498  XCheckRefreshWindows(display,windows);
8499  font_info=windows->widget.font_info;
8500  text_width=WidgetTextWidth(font_info,CacheButtonText);
8501  for (i=0; i < NumberPreferences; i++)
8502  if (WidgetTextWidth(font_info,(char *) Preferences[i]) > text_width)
8503  text_width=WidgetTextWidth(font_info,(char *) Preferences[i]);
8504  width=WidgetTextWidth(font_info,ApplyButtonText);
8505  if (WidgetTextWidth(font_info,CancelButtonText) > width)
8506  width=WidgetTextWidth(font_info,CancelButtonText);
8507  width+=(unsigned int) QuantumMargin;
8508  height=(unsigned int) (font_info->ascent+font_info->descent);
8509  /*
8510  Position Preferences widget.
8511  */
8512  windows->widget.width=(unsigned int) (MagickMax((int) (width << 1),
8513  (int) text_width)+6*QuantumMargin);
8514  windows->widget.min_width=(width << 1)+QuantumMargin;
8515  if (windows->widget.width < windows->widget.min_width)
8516  windows->widget.width=windows->widget.min_width;
8517  windows->widget.height=(unsigned int)
8518  (7*height+NumberPreferences*(height+(QuantumMargin >> 1)));
8519  windows->widget.min_height=(unsigned int)
8520  (7*height+NumberPreferences*(height+(QuantumMargin >> 1)));
8521  if (windows->widget.height < windows->widget.min_height)
8522  windows->widget.height=windows->widget.min_height;
8523  XConstrainWindowPosition(display,&windows->widget);
8524  /*
8525  Map Preferences widget.
8526  */
8527  (void) CopyMagickString(windows->widget.name,"Preferences",MaxTextExtent);
8528  status=XStringListToTextProperty(&windows->widget.name,1,&window_name);
8529  if (status != False)
8530  {
8531  XSetWMName(display,windows->widget.id,&window_name);
8532  XSetWMIconName(display,windows->widget.id,&window_name);
8533  (void) XFree((void *) window_name.value);
8534  }
8535  window_changes.width=(int) windows->widget.width;
8536  window_changes.height=(int) windows->widget.height;
8537  window_changes.x=windows->widget.x;
8538  window_changes.y=windows->widget.y;
8539  (void) XReconfigureWMWindow(display,windows->widget.id,windows->widget.screen,
8540  (unsigned int) (CWWidth | CWHeight | CWX | CWY),&window_changes);
8541  (void) XMapRaised(display,windows->widget.id);
8542  windows->widget.mapped=MagickFalse;
8543  /*
8544  Respond to X events.
8545  */
8546  state=UpdateConfigurationState;
8547  XSetCursorState(display,windows,MagickTrue);
8548  do
8549  {
8550  if (state & UpdateConfigurationState)
8551  {
8552  /*
8553  Initialize button information.
8554  */
8555  XGetWidgetInfo(CancelButtonText,&cancel_info);
8556  cancel_info.width=width;
8557  cancel_info.height=(unsigned int) (3*height) >> 1;
8558  cancel_info.x=(int) windows->widget.width-cancel_info.width-
8559  (QuantumMargin << 1);
8560  cancel_info.y=(int) windows->widget.height-
8561  cancel_info.height-QuantumMargin;
8562  XGetWidgetInfo(ApplyButtonText,&apply_info);
8563  apply_info.width=width;
8564  apply_info.height=(unsigned int) (3*height) >> 1;
8565  apply_info.x=QuantumMargin << 1;
8566  apply_info.y=cancel_info.y;
8567  y=(int) (height << 1);
8568  for (i=0; i < NumberPreferences; i++)
8569  {
8570  XGetWidgetInfo(Preferences[i],&preferences_info[i]);
8571  preferences_info[i].bevel_width--;
8572  preferences_info[i].width=(unsigned int) QuantumMargin >> 1;
8573  preferences_info[i].height=(unsigned int) QuantumMargin >> 1;
8574  preferences_info[i].x=QuantumMargin << 1;
8575  preferences_info[i].y=y;
8576  y+=height+(QuantumMargin >> 1);
8577  }
8578  preferences_info[0].raised=resource_info->backdrop ==
8579  MagickFalse ? MagickTrue : MagickFalse;
8580  preferences_info[1].raised=resource_info->confirm_exit ==
8581  MagickFalse ? MagickTrue : MagickFalse;
8582  preferences_info[2].raised=resource_info->confirm_edit ==
8583  MagickFalse ? MagickTrue : MagickFalse;
8584  preferences_info[3].raised=resource_info->gamma_correct ==
8585  MagickFalse ? MagickTrue : MagickFalse;
8586  preferences_info[4].raised=resource_info->display_warnings ==
8587  MagickFalse ? MagickTrue : MagickFalse;
8588  preferences_info[5].raised=resource_info->quantize_info->dither ==
8589  MagickFalse ? MagickTrue : MagickFalse;
8590  preferences_info[6].raised=resource_info->colormap !=
8591  SharedColormap ? MagickTrue : MagickFalse;
8592  preferences_info[7].raised=resource_info->use_pixmap ==
8593  MagickFalse ? MagickTrue : MagickFalse;
8594  (void) FormatLocaleString(cache,MaxTextExtent,CacheButtonText,
8595  (unsigned long) resource_info->undo_cache);
8596  XGetWidgetInfo(cache,&cache_info);
8597  cache_info.bevel_width--;
8598  cache_info.width=(unsigned int) QuantumMargin >> 1;
8599  cache_info.height=(unsigned int) QuantumMargin >> 1;
8600  cache_info.x=QuantumMargin << 1;
8601  cache_info.y=y;
8602  state&=(~UpdateConfigurationState);
8603  }
8604  if (state & RedrawWidgetState)
8605  {
8606  /*
8607  Redraw Preferences widget.
8608  */
8609  XDrawBeveledButton(display,&windows->widget,&apply_info);
8610  XDrawBeveledButton(display,&windows->widget,&cancel_info);
8611  for (i=0; i < NumberPreferences; i++)
8612  XDrawBeveledButton(display,&windows->widget,&preferences_info[i]);
8613  XDrawTriangleEast(display,&windows->widget,&cache_info);
8614  XHighlightWidget(display,&windows->widget,BorderOffset,BorderOffset);
8615  state&=(~RedrawWidgetState);
8616  }
8617  /*
8618  Wait for next event.
8619  */
8620  (void) XIfEvent(display,&event,XScreenEvent,(char *) windows);
8621  switch (event.type)
8622  {
8623  case ButtonPress:
8624  {
8625  if (MatteIsActive(apply_info,event.xbutton))
8626  {
8627  /*
8628  User pressed Apply button.
8629  */
8630  apply_info.raised=MagickFalse;
8631  XDrawBeveledButton(display,&windows->widget,&apply_info);
8632  break;
8633  }
8634  if (MatteIsActive(cancel_info,event.xbutton))
8635  {
8636  /*
8637  User pressed Cancel button.
8638  */
8639  cancel_info.raised=MagickFalse;
8640  XDrawBeveledButton(display,&windows->widget,&cancel_info);
8641  break;
8642  }
8643  for (i=0; i < NumberPreferences; i++)
8644  if (MatteIsActive(preferences_info[i],event.xbutton))
8645  {
8646  /*
8647  User pressed a Preferences button.
8648  */
8649  preferences_info[i].raised=preferences_info[i].raised ==
8650  MagickFalse ? MagickTrue : MagickFalse;
8651  XDrawBeveledButton(display,&windows->widget,&preferences_info[i]);
8652  break;
8653  }
8654  if (MatteIsActive(cache_info,event.xbutton))
8655  {
8656  /*
8657  User pressed Cache button.
8658  */
8659  x=cache_info.x+cache_info.width+cache_info.bevel_width+
8660  (QuantumMargin >> 1);
8661  y=cache_info.y+((cache_info.height-height) >> 1);
8662  width=WidgetTextWidth(font_info,cache);
8663  (void) XClearArea(display,windows->widget.id,x,y,width,height,
8664  False);
8665  resource_info->undo_cache<<=1;
8666  if (resource_info->undo_cache > 256)
8667  resource_info->undo_cache=1;
8668  (void) FormatLocaleString(cache,MaxTextExtent,CacheButtonText,
8669  (unsigned long) resource_info->undo_cache);
8670  cache_info.raised=MagickFalse;
8671  XDrawTriangleEast(display,&windows->widget,&cache_info);
8672  break;
8673  }
8674  break;
8675  }
8676  case ButtonRelease:
8677  {
8678  if (windows->widget.mapped == MagickFalse)
8679  break;
8680  if (apply_info.raised == MagickFalse)
8681  {
8682  if (event.xbutton.window == windows->widget.id)
8683  if (MatteIsActive(apply_info,event.xbutton))
8684  state|=ExitState;
8685  apply_info.raised=MagickTrue;
8686  XDrawBeveledButton(display,&windows->widget,&apply_info);
8687  apply_info.raised=MagickFalse;
8688  }
8689  if (cancel_info.raised == MagickFalse)
8690  {
8691  if (event.xbutton.window == windows->widget.id)
8692  if (MatteIsActive(cancel_info,event.xbutton))
8693  state|=ExitState;
8694  cancel_info.raised=MagickTrue;
8695  XDrawBeveledButton(display,&windows->widget,&cancel_info);
8696  }
8697  if (cache_info.raised == MagickFalse)
8698  {
8699  cache_info.raised=MagickTrue;
8700  XDrawTriangleEast(display,&windows->widget,&cache_info);
8701  }
8702  break;
8703  }
8704  case ClientMessage:
8705  {
8706  /*
8707  If client window delete message, exit.
8708  */
8709  if (event.xclient.message_type != windows->wm_protocols)
8710  break;
8711  if (*event.xclient.data.l == (int) windows->wm_take_focus)
8712  {
8713  (void) XSetInputFocus(display,event.xclient.window,RevertToParent,
8714  (Time) event.xclient.data.l[1]);
8715  break;
8716  }
8717  if (*event.xclient.data.l != (int) windows->wm_delete_window)
8718  break;
8719  if (event.xclient.window == windows->widget.id)
8720  {
8721  state|=ExitState;
8722  break;
8723  }
8724  break;
8725  }
8726  case ConfigureNotify:
8727  {
8728  /*
8729  Update widget configuration.
8730  */
8731  if (event.xconfigure.window != windows->widget.id)
8732  break;
8733  if ((event.xconfigure.width == (int) windows->widget.width) &&
8734  (event.xconfigure.height == (int) windows->widget.height))
8735  break;
8736  windows->widget.width=(unsigned int)
8737  MagickMax(event.xconfigure.width,(int) windows->widget.min_width);
8738  windows->widget.height=(unsigned int)
8739  MagickMax(event.xconfigure.height,(int) windows->widget.min_height);
8740  state|=UpdateConfigurationState;
8741  break;
8742  }
8743  case EnterNotify:
8744  {
8745  if (event.xcrossing.window != windows->widget.id)
8746  break;
8747  state&=(~InactiveWidgetState);
8748  break;
8749  }
8750  case Expose:
8751  {
8752  if (event.xexpose.window != windows->widget.id)
8753  break;
8754  if (event.xexpose.count != 0)
8755  break;
8756  state|=RedrawWidgetState;
8757  break;
8758  }
8759  case KeyPress:
8760  {
8761  static char
8762  command[MaxTextExtent];
8763 
8764  static KeySym
8765  key_symbol;
8766 
8767  /*
8768  Respond to a user key press.
8769  */
8770  if (event.xkey.window != windows->widget.id)
8771  break;
8772  (void) XLookupString((XKeyEvent *) &event.xkey,command,
8773  (int) sizeof(command),&key_symbol,(XComposeStatus *) NULL);
8774  if ((key_symbol == XK_Return) || (key_symbol == XK_KP_Enter))
8775  {
8776  apply_info.raised=MagickFalse;
8777  XDrawBeveledButton(display,&windows->widget,&apply_info);
8778  state|=ExitState;
8779  break;
8780  }
8781  break;
8782  }
8783  case LeaveNotify:
8784  {
8785  if (event.xcrossing.window != windows->widget.id)
8786  break;
8787  state|=InactiveWidgetState;
8788  break;
8789  }
8790  case MotionNotify:
8791  {
8792  /*
8793  Discard pending button motion events.
8794  */
8795  while (XCheckMaskEvent(display,ButtonMotionMask,&event)) ;
8796  if (state & InactiveWidgetState)
8797  break;
8798  if (apply_info.raised == MatteIsActive(apply_info,event.xmotion))
8799  {
8800  /*
8801  Apply button status changed.
8802  */
8803  apply_info.raised=
8804  apply_info.raised == MagickFalse ? MagickTrue : MagickFalse;
8805  XDrawBeveledButton(display,&windows->widget,&apply_info);
8806  break;
8807  }
8808  if (cancel_info.raised == MatteIsActive(cancel_info,event.xmotion))
8809  {
8810  /*
8811  Cancel button status changed.
8812  */
8813  cancel_info.raised=
8814  cancel_info.raised == MagickFalse ? MagickTrue : MagickFalse;
8815  XDrawBeveledButton(display,&windows->widget,&cancel_info);
8816  break;
8817  }
8818  break;
8819  }
8820  default:
8821  break;
8822  }
8823  } while ((state & ExitState) == 0);
8824  XSetCursorState(display,windows,MagickFalse);
8825  (void) XWithdrawWindow(display,windows->widget.id,windows->widget.screen);
8826  XCheckRefreshWindows(display,windows);
8827  if (apply_info.raised)
8828  return(MagickFalse);
8829  /*
8830  Save user preferences to the client configuration file.
8831  */
8832  resource_info->backdrop=
8833  preferences_info[0].raised == MagickFalse ? MagickTrue : MagickFalse;
8834  resource_info->confirm_exit=
8835  preferences_info[1].raised == MagickFalse ? MagickTrue : MagickFalse;
8836  resource_info->confirm_edit=
8837  preferences_info[2].raised == MagickFalse ? MagickTrue : MagickFalse;
8838  resource_info->gamma_correct=
8839  preferences_info[3].raised == MagickFalse ? MagickTrue : MagickFalse;
8840  resource_info->display_warnings=
8841  preferences_info[4].raised == MagickFalse ? MagickTrue : MagickFalse;
8842  resource_info->quantize_info->dither=
8843  preferences_info[5].raised == MagickFalse ? MagickTrue : MagickFalse;
8844  resource_info->colormap=SharedColormap;
8845  if (preferences_info[6].raised)
8846  resource_info->colormap=PrivateColormap;
8847  resource_info->use_pixmap=
8848  preferences_info[7].raised == MagickFalse ? MagickTrue : MagickFalse;
8849  XUserPreferences(resource_info);
8850  return(MagickTrue);
8851 }
8852 
8853 /*
8854 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8855 % %
8856 % %
8857 % %
8858 % X P r o g r e s s M o n i t o r W i d g e t %
8859 % %
8860 % %
8861 % %
8862 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8863 %
8864 % XProgressMonitorWidget() displays the progress a task is making in
8865 % completing a task. A span of zero toggles the active status. An inactive
8866 % state disables the progress monitor.
8867 %
8868 % The format of the XProgressMonitorWidget method is:
8869 %
8870 % void XProgressMonitorWidget(Display *display,XWindows *windows,
8871 % const char *task,const MagickOffsetType offset,
8872 % const MagickSizeType span)
8873 %
8874 % A description of each parameter follows:
8875 %
8876 % o display: Specifies a connection to an X server; returned from
8877 % XOpenDisplay.
8878 %
8879 % o window: Specifies a pointer to a XWindows structure.
8880 %
8881 % o task: Identifies the task in progress.
8882 %
8883 % o offset: Specifies the offset position within the span which represents
8884 % how much progress has been made in completing a task.
8885 %
8886 % o span: Specifies the span relative to completing a task.
8887 %
8888 */
8889 MagickExport void XProgressMonitorWidget(Display *display,XWindows *windows,
8890  const char *task,const MagickOffsetType offset,const MagickSizeType span)
8891 {
8892  unsigned int
8893  width;
8894 
8895  XEvent
8896  event;
8897 
8898  assert(display != (Display *) NULL);
8899  assert(windows != (XWindows *) NULL);
8900  assert(task != (const char *) NULL);
8901  if (IsEventLogging() != MagickFalse)
8902  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",task);
8903  if (span == 0)
8904  return;
8905  /*
8906  Update image windows if there is a pending expose event.
8907  */
8908  while (XCheckTypedWindowEvent(display,windows->command.id,Expose,&event))
8909  (void) XCommandWidget(display,windows,(const char *const *) NULL,&event);
8910  while (XCheckTypedWindowEvent(display,windows->image.id,Expose,&event))
8911  XRefreshWindow(display,&windows->image,&event);
8912  while (XCheckTypedWindowEvent(display,windows->info.id,Expose,&event))
8913  if (monitor_info.text != (char *) NULL)
8914  XInfoWidget(display,windows,monitor_info.text);
8915  /*
8916  Draw progress monitor bar to represent percent completion of a task.
8917  */
8918  if ((windows->info.mapped == MagickFalse) || (task != monitor_info.text))
8919  XInfoWidget(display,windows,task);
8920  width=(unsigned int) (((offset+1)*(windows->info.width-
8921  (2*monitor_info.x)))/span);
8922  if (width < monitor_info.width)
8923  {
8924  monitor_info.raised=MagickTrue;
8925  XDrawWidgetText(display,&windows->info,&monitor_info);
8926  monitor_info.raised=MagickFalse;
8927  }
8928  monitor_info.width=width;
8929  XDrawWidgetText(display,&windows->info,&monitor_info);
8930  (void) XFlush(display);
8931 }
8932 
8933 /*
8934 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8935 % %
8936 % %
8937 % %
8938 % X T e x t V i e w W i d g e t %
8939 % %
8940 % %
8941 % %
8942 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8943 %
8944 % XTextViewWidget() displays text in a Text View widget.
8945 %
8946 % The format of the XTextViewWidget method is:
8947 %
8948 % void XTextViewWidget(Display *display,const XResourceInfo *resource_info,
8949 % XWindows *windows,const MagickBooleanType mono,const char *title,
8950 % const char **textlist)
8951 %
8952 % A description of each parameter follows:
8953 %
8954 % o display: Specifies a connection to an X server; returned from
8955 % XOpenDisplay.
8956 %
8957 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
8958 %
8959 % o window: Specifies a pointer to a XWindows structure.
8960 %
8961 % o mono: Use mono-spaced font when displaying text.
8962 %
8963 % o title: This character string is displayed at the top of the widget
8964 % window.
8965 %
8966 % o textlist: This string list is displayed within the Text View widget.
8967 %
8968 */
8969 MagickExport void XTextViewWidget(Display *display,
8970  const XResourceInfo *resource_info,XWindows *windows,
8971  const MagickBooleanType mono,const char *title,const char **textlist)
8972 {
8973 #define DismissButtonText "Dismiss"
8974 
8975  char
8976  primary_selection[MaxTextExtent];
8977 
8978  int
8979  i;
8980 
8981  static MagickStatusType
8982  mask = (MagickStatusType) (CWWidth | CWHeight | CWX | CWY);
8983 
8984  Status
8985  status;
8986 
8987  unsigned int
8988  height,
8989  lines,
8990  text_width,
8991  visible_lines,
8992  width;
8993 
8994  size_t
8995  delay,
8996  state;
8997 
8998  XEvent
8999  event;
9000 
9001  XFontStruct
9002  *font_info,
9003  *text_info;
9004 
9005  XTextProperty
9006  window_name;
9007 
9008  XWidgetInfo
9009  dismiss_info,
9010  expose_info,
9011  list_info,
9012  north_info,
9013  scroll_info,
9014  selection_info,
9015  slider_info,
9016  south_info;
9017 
9018  XWindowChanges
9019  window_changes;
9020 
9021  /*
9022  Convert text string to a text list.
9023  */
9024  assert(display != (Display *) NULL);
9025  assert(resource_info != (XResourceInfo *) NULL);
9026  assert(windows != (XWindows *) NULL);
9027  assert(title != (const char *) NULL);
9028  assert(textlist != (const char **) NULL);
9029  if (IsEventLogging() != MagickFalse)
9030  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",title);
9031  XSetCursorState(display,windows,MagickTrue);
9032  XCheckRefreshWindows(display,windows);
9033  if (textlist == (const char **) NULL)
9034  {
9035  XNoticeWidget(display,windows,"No text to view:",(char *) NULL);
9036  return;
9037  }
9038  /*
9039  Determine Text View widget attributes.
9040  */
9041  font_info=windows->widget.font_info;
9042  text_info=(XFontStruct *) NULL;
9043  if (mono != MagickFalse)
9044  text_info=XBestFont(display,resource_info,MagickTrue);
9045  if (text_info == (XFontStruct *) NULL)
9046  text_info=windows->widget.font_info;
9047  text_width=0;
9048  for (i=0; textlist[i] != (char *) NULL; i++)
9049  if (WidgetTextWidth(text_info,(char *) textlist[i]) > text_width)
9050  text_width=(unsigned int) XTextWidth(text_info,(char *) textlist[i],
9051  MagickMin(Extent(textlist[i]),160));
9052  lines=(unsigned int) i;
9053  width=WidgetTextWidth(font_info,DismissButtonText);
9054  width+=QuantumMargin;
9055  height=(unsigned int) (text_info->ascent+text_info->descent);
9056  /*
9057  Position Text View widget.
9058  */
9059  windows->widget.width=(unsigned int) (MagickMin((int) text_width,
9060  (int) MaxTextWidth)+5*QuantumMargin);
9061  windows->widget.min_width=(unsigned int) (MinTextWidth+4*QuantumMargin);
9062  if (windows->widget.width < windows->widget.min_width)
9063  windows->widget.width=windows->widget.min_width;
9064  windows->widget.height=(unsigned int) (MagickMin(MagickMax((int) lines,3),32)*
9065  height+((13*height) >> 1)+((9*QuantumMargin) >> 1));
9066  windows->widget.min_height=(unsigned int) (3*height+((13*height) >> 1)+((9*
9067  QuantumMargin) >> 1));
9068  if (windows->widget.height < windows->widget.min_height)
9069  windows->widget.height=windows->widget.min_height;
9070  XConstrainWindowPosition(display,&windows->widget);
9071  /*
9072  Map Text View widget.
9073  */
9074  (void) CopyMagickString(windows->widget.name,title,MaxTextExtent);
9075  status=XStringListToTextProperty(&windows->widget.name,1,&window_name);
9076  if (status != False)
9077  {
9078  XSetWMName(display,windows->widget.id,&window_name);
9079  XSetWMIconName(display,windows->widget.id,&window_name);
9080  (void) XFree((void *) window_name.value);
9081  }
9082  window_changes.width=(int) windows->widget.width;
9083  window_changes.height=(int) windows->widget.height;
9084  window_changes.x=windows->widget.x;
9085  window_changes.y=windows->widget.y;
9086  (void) XReconfigureWMWindow(display,windows->widget.id,
9087  windows->widget.screen,(unsigned int) mask,&window_changes);
9088  (void) XMapRaised(display,windows->widget.id);
9089  windows->widget.mapped=MagickFalse;
9090  /*
9091  Respond to X events.
9092  */
9093  XGetWidgetInfo((char *) NULL,&slider_info);
9094  XGetWidgetInfo((char *) NULL,&north_info);
9095  XGetWidgetInfo((char *) NULL,&south_info);
9096  XGetWidgetInfo((char *) NULL,&expose_info);
9097  XGetWidgetInfo((char *) NULL,&selection_info);
9098  visible_lines=0;
9099  delay=SuspendTime << 2;
9100  height=(unsigned int) (font_info->ascent+font_info->descent);
9101  state=UpdateConfigurationState;
9102  do
9103  {
9104  if (state & UpdateConfigurationState)
9105  {
9106  int
9107  id;
9108 
9109  /*
9110  Initialize button information.
9111  */
9112  XGetWidgetInfo(DismissButtonText,&dismiss_info);
9113  dismiss_info.width=width;
9114  dismiss_info.height=(unsigned int) ((3*height) >> 1);
9115  dismiss_info.x=(int) windows->widget.width-dismiss_info.width-
9116  QuantumMargin-2;
9117  dismiss_info.y=(int) windows->widget.height-dismiss_info.height-
9118  QuantumMargin;
9119  /*
9120  Initialize scroll information.
9121  */
9122  XGetWidgetInfo((char *) NULL,&scroll_info);
9123  scroll_info.bevel_width--;
9124  scroll_info.width=height;
9125  scroll_info.height=(unsigned int) (dismiss_info.y-((5*QuantumMargin) >>
9126  1));
9127  scroll_info.x=(int) windows->widget.width-QuantumMargin-
9128  scroll_info.width;
9129  scroll_info.y=(3*QuantumMargin) >> 1;
9130  scroll_info.raised=MagickFalse;
9131  scroll_info.trough=MagickTrue;
9132  north_info=scroll_info;
9133  north_info.raised=MagickTrue;
9134  north_info.width-=(north_info.bevel_width << 1);
9135  north_info.height=north_info.width-1;
9136  north_info.x+=north_info.bevel_width;
9137  north_info.y+=north_info.bevel_width;
9138  south_info=north_info;
9139  south_info.y=scroll_info.y+scroll_info.height-scroll_info.bevel_width-
9140  south_info.height;
9141  id=slider_info.id;
9142  slider_info=north_info;
9143  slider_info.id=id;
9144  slider_info.width-=2;
9145  slider_info.min_y=north_info.y+north_info.height+north_info.bevel_width+
9146  slider_info.bevel_width+2;
9147  slider_info.height=scroll_info.height-((slider_info.min_y-
9148  scroll_info.y+1) << 1)+4;
9149  visible_lines=(unsigned int) (scroll_info.height*PerceptibleReciprocal(
9150  (double) text_info->ascent+text_info->descent+((text_info->ascent+
9151  text_info->descent) >> 3)));
9152  if (lines > visible_lines)
9153  slider_info.height=(unsigned int) (visible_lines*slider_info.height)/
9154  lines;
9155  slider_info.max_y=south_info.y-south_info.bevel_width-
9156  slider_info.bevel_width-2;
9157  slider_info.x=scroll_info.x+slider_info.bevel_width+1;
9158  slider_info.y=slider_info.min_y;
9159  expose_info=scroll_info;
9160  expose_info.y=slider_info.y;
9161  /*
9162  Initialize list information.
9163  */
9164  XGetWidgetInfo((char *) NULL,&list_info);
9165  list_info.raised=MagickFalse;
9166  list_info.bevel_width--;
9167  list_info.width=(unsigned int) scroll_info.x-((3*QuantumMargin) >> 1);
9168  list_info.height=scroll_info.height;
9169  list_info.x=QuantumMargin;
9170  list_info.y=scroll_info.y;
9171  /*
9172  Initialize selection information.
9173  */
9174  XGetWidgetInfo((char *) NULL,&selection_info);
9175  selection_info.center=MagickFalse;
9176  selection_info.width=list_info.width;
9177  selection_info.height=(unsigned int)
9178  (9*(text_info->ascent+text_info->descent)) >> 3;
9179  selection_info.x=list_info.x;
9180  state&=(~UpdateConfigurationState);
9181  }
9182  if (state & RedrawWidgetState)
9183  {
9184  /*
9185  Redraw Text View window.
9186  */
9187  XDrawBeveledMatte(display,&windows->widget,&list_info);
9188  XDrawBeveledMatte(display,&windows->widget,&scroll_info);
9189  XDrawTriangleNorth(display,&windows->widget,&north_info);
9190  XDrawBeveledButton(display,&windows->widget,&slider_info);
9191  XDrawTriangleSouth(display,&windows->widget,&south_info);
9192  XDrawBeveledButton(display,&windows->widget,&dismiss_info);
9193  XHighlightWidget(display,&windows->widget,BorderOffset,BorderOffset);
9194  selection_info.id=(~0);
9195  state|=RedrawListState;
9196  state&=(~RedrawWidgetState);
9197  }
9198  if (state & RedrawListState)
9199  {
9200  /*
9201  Determine slider id and position.
9202  */
9203  if (slider_info.id >= (int) (lines-visible_lines))
9204  slider_info.id=(int) lines-visible_lines;
9205  if ((slider_info.id < 0) || (lines <= visible_lines))
9206  slider_info.id=0;
9207  slider_info.y=slider_info.min_y;
9208  if (lines != 0)
9209  slider_info.y+=
9210  slider_info.id*(slider_info.max_y-slider_info.min_y+1)/lines;
9211  if (slider_info.id != selection_info.id)
9212  {
9213  /*
9214  Redraw scroll bar and text.
9215  */
9216  windows->widget.font_info=text_info;
9217  (void) XSetFont(display,windows->widget.annotate_context,
9218  text_info->fid);
9219  (void) XSetFont(display,windows->widget.highlight_context,
9220  text_info->fid);
9221  selection_info.id=slider_info.id;
9222  selection_info.y=list_info.y+(height >> 3)+2;
9223  for (i=0; i < (int) visible_lines; i++)
9224  {
9225  selection_info.raised=
9226  (slider_info.id+i) != list_info.id ? MagickTrue : MagickFalse;
9227  selection_info.text=(char *) NULL;
9228  if ((slider_info.id+i) < (int) lines)
9229  selection_info.text=(char *) textlist[slider_info.id+i];
9230  XDrawWidgetText(display,&windows->widget,&selection_info);
9231  selection_info.y+=(int) selection_info.height;
9232  }
9233  windows->widget.font_info=font_info;
9234  (void) XSetFont(display,windows->widget.annotate_context,
9235  font_info->fid);
9236  (void) XSetFont(display,windows->widget.highlight_context,
9237  font_info->fid);
9238  /*
9239  Update slider.
9240  */
9241  if (slider_info.y > expose_info.y)
9242  {
9243  expose_info.height=(unsigned int) slider_info.y-expose_info.y;
9244  expose_info.y=slider_info.y-expose_info.height-
9245  slider_info.bevel_width-1;
9246  }
9247  else
9248  {
9249  expose_info.height=(unsigned int) expose_info.y-slider_info.y;
9250  expose_info.y=slider_info.y+slider_info.height+
9251  slider_info.bevel_width+1;
9252  }
9253  XDrawTriangleNorth(display,&windows->widget,&north_info);
9254  XDrawMatte(display,&windows->widget,&expose_info);
9255  XDrawBeveledButton(display,&windows->widget,&slider_info);
9256  XDrawTriangleSouth(display,&windows->widget,&south_info);
9257  expose_info.y=slider_info.y;
9258  }
9259  state&=(~RedrawListState);
9260  }
9261  /*
9262  Wait for next event.
9263  */
9264  if (north_info.raised && south_info.raised)
9265  (void) XIfEvent(display,&event,XScreenEvent,(char *) windows);
9266  else
9267  {
9268  /*
9269  Brief delay before advancing scroll bar.
9270  */
9271  XDelay(display,delay);
9272  delay=SuspendTime;
9273  (void) XCheckIfEvent(display,&event,XScreenEvent,(char *) windows);
9274  if (north_info.raised == MagickFalse)
9275  if (slider_info.id > 0)
9276  {
9277  /*
9278  Move slider up.
9279  */
9280  slider_info.id--;
9281  state|=RedrawListState;
9282  }
9283  if (south_info.raised == MagickFalse)
9284  if (slider_info.id < (int) lines)
9285  {
9286  /*
9287  Move slider down.
9288  */
9289  slider_info.id++;
9290  state|=RedrawListState;
9291  }
9292  if (event.type != ButtonRelease)
9293  continue;
9294  }
9295  switch (event.type)
9296  {
9297  case ButtonPress:
9298  {
9299  if (MatteIsActive(slider_info,event.xbutton))
9300  {
9301  /*
9302  Track slider.
9303  */
9304  slider_info.active=MagickTrue;
9305  break;
9306  }
9307  if (MatteIsActive(north_info,event.xbutton))
9308  if (slider_info.id > 0)
9309  {
9310  /*
9311  Move slider up.
9312  */
9313  north_info.raised=MagickFalse;
9314  slider_info.id--;
9315  state|=RedrawListState;
9316  break;
9317  }
9318  if (MatteIsActive(south_info,event.xbutton))
9319  if (slider_info.id < (int) lines)
9320  {
9321  /*
9322  Move slider down.
9323  */
9324  south_info.raised=MagickFalse;
9325  slider_info.id++;
9326  state|=RedrawListState;
9327  break;
9328  }
9329  if (MatteIsActive(scroll_info,event.xbutton))
9330  {
9331  /*
9332  Move slider.
9333  */
9334  if (event.xbutton.y < slider_info.y)
9335  slider_info.id-=(visible_lines-1);
9336  else
9337  slider_info.id+=(visible_lines-1);
9338  state|=RedrawListState;
9339  break;
9340  }
9341  if (MatteIsActive(dismiss_info,event.xbutton))
9342  {
9343  /*
9344  User pressed Dismiss button.
9345  */
9346  dismiss_info.raised=MagickFalse;
9347  XDrawBeveledButton(display,&windows->widget,&dismiss_info);
9348  break;
9349  }
9350  if (MatteIsActive(list_info,event.xbutton))
9351  {
9352  int
9353  id;
9354 
9355  static Time
9356  click_time;
9357 
9358  /*
9359  User pressed list matte.
9360  */
9361  id=slider_info.id+(event.xbutton.y-(list_info.y+(height >> 1))+1)/
9362  selection_info.height;
9363  if (id >= (int) lines)
9364  break;
9365  if (id != list_info.id)
9366  {
9367  list_info.id=id;
9368  click_time=event.xbutton.time;
9369  break;
9370  }
9371  list_info.id=id;
9372  if (event.xbutton.time >= (click_time+DoubleClick))
9373  {
9374  click_time=event.xbutton.time;
9375  break;
9376  }
9377  click_time=event.xbutton.time;
9378  /*
9379  Become the XA_PRIMARY selection owner.
9380  */
9381  (void) CopyMagickString(primary_selection,textlist[list_info.id],
9382  MaxTextExtent);
9383  (void) XSetSelectionOwner(display,XA_PRIMARY,windows->widget.id,
9384  event.xbutton.time);
9385  if (XGetSelectionOwner(display,XA_PRIMARY) != windows->widget.id)
9386  break;
9387  selection_info.id=(~0);
9388  list_info.id=id;
9389  state|=RedrawListState;
9390  break;
9391  }
9392  break;
9393  }
9394  case ButtonRelease:
9395  {
9396  if (windows->widget.mapped == MagickFalse)
9397  break;
9398  if (north_info.raised == MagickFalse)
9399  {
9400  /*
9401  User released up button.
9402  */
9403  delay=SuspendTime << 2;
9404  north_info.raised=MagickTrue;
9405  XDrawTriangleNorth(display,&windows->widget,&north_info);
9406  }
9407  if (south_info.raised == MagickFalse)
9408  {
9409  /*
9410  User released down button.
9411  */
9412  delay=SuspendTime << 2;
9413  south_info.raised=MagickTrue;
9414  XDrawTriangleSouth(display,&windows->widget,&south_info);
9415  }
9416  if (slider_info.active)
9417  {
9418  /*
9419  Stop tracking slider.
9420  */
9421  slider_info.active=MagickFalse;
9422  break;
9423  }
9424  if (dismiss_info.raised == MagickFalse)
9425  {
9426  if (event.xbutton.window == windows->widget.id)
9427  if (MatteIsActive(dismiss_info,event.xbutton))
9428  state|=ExitState;
9429  dismiss_info.raised=MagickTrue;
9430  XDrawBeveledButton(display,&windows->widget,&dismiss_info);
9431  }
9432  break;
9433  }
9434  case ClientMessage:
9435  {
9436  /*
9437  If client window delete message, exit.
9438  */
9439  if (event.xclient.message_type != windows->wm_protocols)
9440  break;
9441  if (*event.xclient.data.l == (int) windows->wm_take_focus)
9442  {
9443  (void) XSetInputFocus(display,event.xclient.window,RevertToParent,
9444  (Time) event.xclient.data.l[1]);
9445  break;
9446  }
9447  if (*event.xclient.data.l != (int) windows->wm_delete_window)
9448  break;
9449  if (event.xclient.window == windows->widget.id)
9450  {
9451  state|=ExitState;
9452  break;
9453  }
9454  break;
9455  }
9456  case ConfigureNotify:
9457  {
9458  /*
9459  Update widget configuration.
9460  */
9461  if (event.xconfigure.window != windows->widget.id)
9462  break;
9463  if ((event.xconfigure.width == (int) windows->widget.width) &&
9464  (event.xconfigure.height == (int) windows->widget.height))
9465  break;
9466  windows->widget.width=(unsigned int)
9467  MagickMax(event.xconfigure.width,(int) windows->widget.min_width);
9468  windows->widget.height=(unsigned int)
9469  MagickMax(event.xconfigure.height,(int) windows->widget.min_height);
9470  state|=UpdateConfigurationState;
9471  break;
9472  }
9473  case EnterNotify:
9474  {
9475  if (event.xcrossing.window != windows->widget.id)
9476  break;
9477  state&=(~InactiveWidgetState);
9478  break;
9479  }
9480  case Expose:
9481  {
9482  if (event.xexpose.window != windows->widget.id)
9483  break;
9484  if (event.xexpose.count != 0)
9485  break;
9486  state|=RedrawWidgetState;
9487  break;
9488  }
9489  case KeyPress:
9490  {
9491  static char
9492  command[MaxTextExtent];
9493 
9494  static int
9495  length;
9496 
9497  static KeySym
9498  key_symbol;
9499 
9500  /*
9501  Respond to a user key press.
9502  */
9503  if (event.xkey.window != windows->widget.id)
9504  break;
9505  length=XLookupString((XKeyEvent *) &event.xkey,command,
9506  (int) sizeof(command),&key_symbol,(XComposeStatus *) NULL);
9507  *(command+length)='\0';
9508  if ((key_symbol == XK_Return) || (key_symbol == XK_KP_Enter))
9509  {
9510  dismiss_info.raised=MagickFalse;
9511  XDrawBeveledButton(display,&windows->widget,&dismiss_info);
9512  state|=ExitState;
9513  break;
9514  }
9515  if (AreaIsActive(scroll_info,event.xkey))
9516  {
9517  /*
9518  Move slider.
9519  */
9520  switch ((int) key_symbol)
9521  {
9522  case XK_Home:
9523  case XK_KP_Home:
9524  {
9525  slider_info.id=0;
9526  break;
9527  }
9528  case XK_Up:
9529  case XK_KP_Up:
9530  {
9531  slider_info.id--;
9532  break;
9533  }
9534  case XK_Down:
9535  case XK_KP_Down:
9536  {
9537  slider_info.id++;
9538  break;
9539  }
9540  case XK_Prior:
9541  case XK_KP_Prior:
9542  {
9543  slider_info.id-=visible_lines;
9544  break;
9545  }
9546  case XK_Next:
9547  case XK_KP_Next:
9548  {
9549  slider_info.id+=visible_lines;
9550  break;
9551  }
9552  case XK_End:
9553  case XK_KP_End:
9554  {
9555  slider_info.id=(int) lines;
9556  break;
9557  }
9558  }
9559  state|=RedrawListState;
9560  break;
9561  }
9562  break;
9563  }
9564  case KeyRelease:
9565  break;
9566  case LeaveNotify:
9567  {
9568  if (event.xcrossing.window != windows->widget.id)
9569  break;
9570  state|=InactiveWidgetState;
9571  break;
9572  }
9573  case MapNotify:
9574  {
9575  mask&=(~CWX);
9576  mask&=(~CWY);
9577  break;
9578  }
9579  case MotionNotify:
9580  {
9581  /*
9582  Discard pending button motion events.
9583  */
9584  while (XCheckMaskEvent(display,ButtonMotionMask,&event)) ;
9585  if (slider_info.active)
9586  {
9587  /*
9588  Move slider matte.
9589  */
9590  slider_info.y=event.xmotion.y-
9591  ((slider_info.height+slider_info.bevel_width) >> 1)+1;
9592  if (slider_info.y < slider_info.min_y)
9593  slider_info.y=slider_info.min_y;
9594  if (slider_info.y > slider_info.max_y)
9595  slider_info.y=slider_info.max_y;
9596  slider_info.id=0;
9597  if (slider_info.y != slider_info.min_y)
9598  slider_info.id=(int) (lines*(slider_info.y-slider_info.min_y+1))/
9599  (slider_info.max_y-slider_info.min_y+1);
9600  state|=RedrawListState;
9601  break;
9602  }
9603  if (state & InactiveWidgetState)
9604  break;
9605  if (dismiss_info.raised == MatteIsActive(dismiss_info,event.xmotion))
9606  {
9607  /*
9608  Dismiss button status changed.
9609  */
9610  dismiss_info.raised=
9611  dismiss_info.raised == MagickFalse ? MagickTrue : MagickFalse;
9612  XDrawBeveledButton(display,&windows->widget,&dismiss_info);
9613  break;
9614  }
9615  break;
9616  }
9617  case SelectionClear:
9618  {
9619  list_info.id=(~0);
9620  selection_info.id=(~0);
9621  state|=RedrawListState;
9622  break;
9623  }
9624  case SelectionRequest:
9625  {
9626  XSelectionEvent
9627  notify;
9628 
9629  XSelectionRequestEvent
9630  *request;
9631 
9632  if (list_info.id == (~0))
9633  break;
9634  /*
9635  Set primary selection.
9636  */
9637  request=(&(event.xselectionrequest));
9638  (void) XChangeProperty(request->display,request->requestor,
9639  request->property,request->target,8,PropModeReplace,
9640  (unsigned char *) primary_selection,Extent(primary_selection));
9641  notify.type=SelectionNotify;
9642  notify.send_event=MagickTrue;
9643  notify.display=request->display;
9644  notify.requestor=request->requestor;
9645  notify.selection=request->selection;
9646  notify.target=request->target;
9647  notify.time=request->time;
9648  if (request->property == None)
9649  notify.property=request->target;
9650  else
9651  notify.property=request->property;
9652  (void) XSendEvent(request->display,request->requestor,False,NoEventMask,
9653  (XEvent *) &notify);
9654  }
9655  default:
9656  break;
9657  }
9658  } while ((state & ExitState) == 0);
9659  if (text_info != windows->widget.font_info)
9660  (void) XFreeFont(display,text_info);
9661  XSetCursorState(display,windows,MagickFalse);
9662  (void) XWithdrawWindow(display,windows->widget.id,windows->widget.screen);
9663  XCheckRefreshWindows(display,windows);
9664 }
9665 RestoreMSCWarning
9666 RestoreMSCWarning
9667 #endif