]> granicus.if.org Git - imagemagick/blob - MagickCore/display.c
cleanup identical conditions (#1339)
[imagemagick] / MagickCore / display.c
1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 %                                                                             %
4 %                                                                             %
5 %                                                                             %
6 %               DDDD   IIIII  SSSSS  PPPP   L       AAA   Y   Y               %
7 %               D   D    I    SS     P   P  L      A   A   Y Y                %
8 %               D   D    I     SSS   PPPP   L      AAAAA    Y                 %
9 %               D   D    I       SS  P      L      A   A    Y                 %
10 %               DDDD   IIIII  SSSSS  P      LLLLL  A   A    Y                 %
11 %                                                                             %
12 %                                                                             %
13 %        MagickCore Methods to Interactively Display and Edit an Image        %
14 %                                                                             %
15 %                             Software Design                                 %
16 %                                  Cristy                                     %
17 %                                July 1992                                    %
18 %                                                                             %
19 %                                                                             %
20 %  Copyright 1999-2018 ImageMagick Studio LLC, a non-profit organization      %
21 %  dedicated to making software imaging solutions freely available.           %
22 %                                                                             %
23 %  You may not use this file except in compliance with the License.  You may  %
24 %  obtain a copy of the License at                                            %
25 %                                                                             %
26 %    https://imagemagick.org/script/license.php                               %
27 %                                                                             %
28 %  Unless required by applicable law or agreed to in writing, software        %
29 %  distributed under the License is distributed on an "AS IS" BASIS,          %
30 %  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
31 %  See the License for the specific language governing permissions and        %
32 %  limitations under the License.                                             %
33 %                                                                             %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35 %
36 %
37 */
38 \f
39 /*
40   Include declarations.
41 */
42 #include "MagickCore/studio.h"
43 #include "MagickCore/artifact.h"
44 #include "MagickCore/attribute.h"
45 #include "MagickCore/blob.h"
46 #include "MagickCore/cache.h"
47 #include "MagickCore/cache-private.h"
48 #include "MagickCore/channel.h"
49 #include "MagickCore/client.h"
50 #include "MagickCore/color.h"
51 #include "MagickCore/colorspace.h"
52 #include "MagickCore/composite.h"
53 #include "MagickCore/constitute.h"
54 #include "MagickCore/decorate.h"
55 #include "MagickCore/delegate.h"
56 #include "MagickCore/display.h"
57 #include "MagickCore/display-private.h"
58 #include "MagickCore/distort.h"
59 #include "MagickCore/draw.h"
60 #include "MagickCore/effect.h"
61 #include "MagickCore/enhance.h"
62 #include "MagickCore/exception.h"
63 #include "MagickCore/exception-private.h"
64 #include "MagickCore/fx.h"
65 #include "MagickCore/geometry.h"
66 #include "MagickCore/image.h"
67 #include "MagickCore/image-private.h"
68 #include "MagickCore/list.h"
69 #include "MagickCore/log.h"
70 #include "MagickCore/magick.h"
71 #include "MagickCore/memory_.h"
72 #include "MagickCore/monitor.h"
73 #include "MagickCore/monitor-private.h"
74 #include "MagickCore/montage.h"
75 #include "MagickCore/nt-base-private.h"
76 #include "MagickCore/option.h"
77 #include "MagickCore/paint.h"
78 #include "MagickCore/pixel.h"
79 #include "MagickCore/pixel-accessor.h"
80 #include "MagickCore/property.h"
81 #include "MagickCore/quantum.h"
82 #include "MagickCore/quantum-private.h"
83 #include "MagickCore/resize.h"
84 #include "MagickCore/resource_.h"
85 #include "MagickCore/shear.h"
86 #include "MagickCore/segment.h"
87 #include "MagickCore/statistic.h"
88 #include "MagickCore/string_.h"
89 #include "MagickCore/string-private.h"
90 #include "MagickCore/transform.h"
91 #include "MagickCore/transform-private.h"
92 #include "MagickCore/threshold.h"
93 #include "MagickCore/utility.h"
94 #include "MagickCore/utility-private.h"
95 #include "MagickCore/version.h"
96 #include "MagickCore/widget.h"
97 #include "MagickCore/widget-private.h"
98 #include "MagickCore/xwindow.h"
99 #include "MagickCore/xwindow-private.h"
100 \f
101 #if defined(MAGICKCORE_X11_DELEGATE)
102 /*
103   Define declarations.
104 */
105 #define MaxColors  MagickMin((ssize_t) windows->visual_info->colormap_size,256L)
106 \f
107 /*
108   Constant declarations.
109 */
110 static const unsigned char
111   HighlightBitmap[8] =
112   {
113     0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55
114   },
115   OpaqueBitmap[8] =
116   {
117     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
118   },
119   ShadowBitmap[8] =
120   {
121     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
122   };
123
124 static const char
125   *PageSizes[] =
126   {
127     "Letter",
128     "Tabloid",
129     "Ledger",
130     "Legal",
131     "Statement",
132     "Executive",
133     "A3",
134     "A4",
135     "A5",
136     "B4",
137     "B5",
138     "Folio",
139     "Quarto",
140     "10x14",
141     (char *) NULL
142   };
143 \f
144 /*
145   Help widget declarations.
146 */
147 static const char
148   *ImageAnnotateHelp[] =
149   {
150     "In annotate mode, the Command widget has these options:",
151     "",
152     "    Font Name",
153     "      fixed",
154     "      variable",
155     "      5x8",
156     "      6x10",
157     "      7x13bold",
158     "      8x13bold",
159     "      9x15bold",
160     "      10x20",
161     "      12x24",
162     "      Browser...",
163     "    Font Color",
164     "      black",
165     "      blue",
166     "      cyan",
167     "      green",
168     "      gray",
169     "      red",
170     "      magenta",
171     "      yellow",
172     "      white",
173     "      transparent",
174     "      Browser...",
175     "    Font Color",
176     "      black",
177     "      blue",
178     "      cyan",
179     "      green",
180     "      gray",
181     "      red",
182     "      magenta",
183     "      yellow",
184     "      white",
185     "      transparent",
186     "      Browser...",
187     "    Rotate Text",
188     "      -90",
189     "      -45",
190     "      -30",
191     "      0",
192     "      30",
193     "      45",
194     "      90",
195     "      180",
196     "      Dialog...",
197     "    Help",
198     "    Dismiss",
199     "",
200     "Choose a font name from the Font Name sub-menu.  Additional",
201     "font names can be specified with the font browser.  You can",
202     "change the menu names by setting the X resources font1",
203     "through font9.",
204     "",
205     "Choose a font color from the Font Color sub-menu.",
206     "Additional font colors can be specified with the color",
207     "browser.  You can change the menu colors by setting the X",
208     "resources pen1 through pen9.",
209     "",
210     "If you select the color browser and press Grab, you can",
211     "choose the font color by moving the pointer to the desired",
212     "color on the screen and press any button.",
213     "",
214     "If you choose to rotate the text, choose Rotate Text from the",
215     "menu and select an angle.  Typically you will only want to",
216     "rotate one line of text at a time.  Depending on the angle you",
217     "choose, subsequent lines may end up overwriting each other.",
218     "",
219     "Choosing a font and its color is optional.  The default font",
220     "is fixed and the default color is black.  However, you must",
221     "choose a location to begin entering text and press button 1.",
222     "An underscore character will appear at the location of the",
223     "pointer.  The cursor changes to a pencil to indicate you are",
224     "in text mode.  To exit immediately, press Dismiss.",
225     "",
226     "In text mode, any key presses will display the character at",
227     "the location of the underscore and advance the underscore",
228     "cursor.  Enter your text and once completed press Apply to",
229     "finish your image annotation.  To correct errors press BACK",
230     "SPACE.  To delete an entire line of text, press DELETE.  Any",
231     "text that exceeds the boundaries of the image window is",
232     "automagically continued onto the next line.",
233     "",
234     "The actual color you request for the font is saved in the",
235     "image.  However, the color that appears in your image window",
236     "may be different.  For example, on a monochrome screen the",
237     "text will appear black or white even if you choose the color",
238     "red as the font color.  However, the image saved to a file",
239     "with -write is written with red lettering.  To assure the",
240     "correct color text in the final image, any PseudoClass image",
241     "is promoted to DirectClass (see miff(5)).  To force a",
242     "PseudoClass image to remain PseudoClass, use -colors.",
243     (char *) NULL,
244   },
245   *ImageChopHelp[] =
246   {
247     "In chop mode, the Command widget has these options:",
248     "",
249     "    Direction",
250     "      horizontal",
251     "      vertical",
252     "    Help",
253     "    Dismiss",
254     "",
255     "If the you choose the horizontal direction (this the",
256     "default), the area of the image between the two horizontal",
257     "endpoints of the chop line is removed.  Otherwise, the area",
258     "of the image between the two vertical endpoints of the chop",
259     "line is removed.",
260     "",
261     "Select a location within the image window to begin your chop,",
262     "press and hold any button.  Next, move the pointer to",
263     "another location in the image.  As you move a line will",
264     "connect the initial location and the pointer.  When you",
265     "release the button, the area within the image to chop is",
266     "determined by which direction you choose from the Command",
267     "widget.",
268     "",
269     "To cancel the image chopping, move the pointer back to the",
270     "starting point of the line and release the button.",
271     (char *) NULL,
272   },
273   *ImageColorEditHelp[] =
274   {
275     "In color edit mode, the Command widget has these options:",
276     "",
277     "    Method",
278     "      point",
279     "      replace",
280     "      floodfill",
281     "      filltoborder",
282     "      reset",
283     "    Pixel Color",
284     "      black",
285     "      blue",
286     "      cyan",
287     "      green",
288     "      gray",
289     "      red",
290     "      magenta",
291     "      yellow",
292     "      white",
293     "      Browser...",
294     "    Border Color",
295     "      black",
296     "      blue",
297     "      cyan",
298     "      green",
299     "      gray",
300     "      red",
301     "      magenta",
302     "      yellow",
303     "      white",
304     "      Browser...",
305     "    Fuzz",
306     "      0%",
307     "      2%",
308     "      5%",
309     "      10%",
310     "      15%",
311     "      Dialog...",
312     "    Undo",
313     "    Help",
314     "    Dismiss",
315     "",
316     "Choose a color editing method from the Method sub-menu",
317     "of the Command widget.  The point method recolors any pixel",
318     "selected with the pointer until the button is released.  The",
319     "replace method recolors any pixel that matches the color of",
320     "the pixel you select with a button press.  Floodfill recolors",
321     "any pixel that matches the color of the pixel you select with",
322     "a button press and is a neighbor.  Whereas filltoborder recolors",
323     "any neighbor pixel that is not the border color.  Finally reset",
324     "changes the entire image to the designated color.",
325     "",
326     "Next, choose a pixel color from the Pixel Color sub-menu.",
327     "Additional pixel colors can be specified with the color",
328     "browser.  You can change the menu colors by setting the X",
329     "resources pen1 through pen9.",
330     "",
331     "Now press button 1 to select a pixel within the image window",
332     "to change its color.  Additional pixels may be recolored as",
333     "prescribed by the method you choose.",
334     "",
335     "If the Magnify widget is mapped, it can be helpful in positioning",
336     "your pointer within the image (refer to button 2).",
337     "",
338     "The actual color you request for the pixels is saved in the",
339     "image.  However, the color that appears in your image window",
340     "may be different.  For example, on a monochrome screen the",
341     "pixel will appear black or white even if you choose the",
342     "color red as the pixel color.  However, the image saved to a",
343     "file with -write is written with red pixels.  To assure the",
344     "correct color text in the final image, any PseudoClass image",
345     "is promoted to DirectClass (see miff(5)).  To force a",
346     "PseudoClass image to remain PseudoClass, use -colors.",
347     (char *) NULL,
348   },
349   *ImageCompositeHelp[] =
350   {
351     "First a widget window is displayed requesting you to enter an",
352     "image name. Press Composite, Grab or type a file name.",
353     "Press Cancel if you choose not to create a composite image.",
354     "When you choose Grab, move the pointer to the desired window",
355     "and press any button.",
356     "",
357     "If the Composite image does not have any matte information,",
358     "you are informed and the file browser is displayed again.",
359     "Enter the name of a mask image.  The image is typically",
360     "grayscale and the same size as the composite image.  If the",
361     "image is not grayscale, it is converted to grayscale and the",
362     "resulting intensities are used as matte information.",
363     "",
364     "A small window appears showing the location of the cursor in",
365     "the image window. You are now in composite mode.  To exit",
366     "immediately, press Dismiss.  In composite mode, the Command",
367     "widget has these options:",
368     "",
369     "    Operators",
370     "      Over",
371     "      In",
372     "      Out",
373     "      Atop",
374     "      Xor",
375     "      Plus",
376     "      Minus",
377     "      Add",
378     "      Subtract",
379     "      Difference",
380     "      Multiply",
381     "      Bumpmap",
382     "      Copy",
383     "      CopyRed",
384     "      CopyGreen",
385     "      CopyBlue",
386     "      CopyOpacity",
387     "      Clear",
388     "    Dissolve",
389     "    Displace",
390     "    Help",
391     "    Dismiss",
392     "",
393     "Choose a composite operation from the Operators sub-menu of",
394     "the Command widget.  How each operator behaves is described",
395     "below.  Image window is the image currently displayed on",
396     "your X server and image is the image obtained with the File",
397     "Browser widget.",
398     "",
399     "Over     The result is the union of the two image shapes,",
400     "         with image obscuring image window in the region of",
401     "         overlap.",
402     "",
403     "In       The result is simply image cut by the shape of",
404     "         image window.  None of the image data of image",
405     "         window is in the result.",
406     "",
407     "Out      The resulting image is image with the shape of",
408     "         image window cut out.",
409     "",
410     "Atop     The result is the same shape as image image window,",
411     "         with image obscuring image window where the image",
412     "         shapes overlap.  Note this differs from over",
413     "         because the portion of image outside image window's",
414     "         shape does not appear in the result.",
415     "",
416     "Xor      The result is the image data from both image and",
417     "         image window that is outside the overlap region.",
418     "         The overlap region is blank.",
419     "",
420     "Plus     The result is just the sum of the image data.",
421     "         Output values are cropped to QuantumRange (no overflow).",
422     "",
423     "Minus    The result of image - image window, with underflow",
424     "         cropped to zero.",
425     "",
426     "Add      The result of image + image window, with overflow",
427     "         wrapping around (mod 256).",
428     "",
429     "Subtract The result of image - image window, with underflow",
430     "         wrapping around (mod 256).  The add and subtract",
431     "         operators can be used to perform reversible",
432     "         transformations.",
433     "",
434     "Difference",
435     "         The result of abs(image - image window).  This",
436     "         useful for comparing two very similar images.",
437     "",
438     "Multiply",
439     "         The result of image * image window.  This",
440     "         useful for the creation of drop-shadows.",
441     "",
442     "Bumpmap  The result of surface normals from image * image",
443     "         window.",
444     "",
445     "Copy     The resulting image is image window replaced with",
446     "         image.  Here the matte information is ignored.",
447     "",
448     "CopyRed  The red layer of the image window is replace with",
449     "         the red layer of the image.  The other layers are",
450     "         untouched.",
451     "",
452     "CopyGreen",
453     "         The green layer of the image window is replace with",
454     "         the green layer of the image.  The other layers are",
455     "         untouched.",
456     "",
457     "CopyBlue The blue layer of the image window is replace with",
458     "         the blue layer of the image.  The other layers are",
459     "         untouched.",
460     "",
461     "CopyOpacity",
462     "         The matte layer of the image window is replace with",
463     "         the matte layer of the image.  The other layers are",
464     "         untouched.",
465     "",
466     "The image compositor requires a matte, or alpha channel in",
467     "the image for some operations.  This extra channel usually",
468     "defines a mask which represents a sort of a cookie-cutter",
469     "for the image.  This the case when matte is opaque (full",
470     "coverage) for pixels inside the shape, zero outside, and",
471     "between 0 and QuantumRange on the boundary.  If image does not",
472     "have a matte channel, it is initialized with 0 for any pixel",
473     "matching in color to pixel location (0,0), otherwise QuantumRange.",
474     "",
475     "If you choose Dissolve, the composite operator becomes Over.  The",
476     "image matte channel percent transparency is initialized to factor.",
477     "The image window is initialized to (100-factor). Where factor is the",
478     "value you specify in the Dialog widget.",
479     "",
480     "Displace shifts the image pixels as defined by a displacement",
481     "map.  With this option, image is used as a displacement map.",
482     "Black, within the displacement map, is a maximum positive",
483     "displacement.  White is a maximum negative displacement and",
484     "middle gray is neutral.  The displacement is scaled to determine",
485     "the pixel shift.  By default, the displacement applies in both the",
486     "horizontal and vertical directions.  However, if you specify a mask,",
487     "image is the horizontal X displacement and mask the vertical Y",
488     "displacement.",
489     "",
490     "Note that matte information for image window is not retained",
491     "for colormapped X server visuals (e.g. StaticColor,",
492     "StaticColor, GrayScale, PseudoColor).  Correct compositing",
493     "behavior may require a TrueColor or DirectColor visual or a",
494     "Standard Colormap.",
495     "",
496     "Choosing a composite operator is optional.  The default",
497     "operator is replace.  However, you must choose a location to",
498     "composite your image and press button 1.  Press and hold the",
499     "button before releasing and an outline of the image will",
500     "appear to help you identify your location.",
501     "",
502     "The actual colors of the composite image is saved.  However,",
503     "the color that appears in image window may be different.",
504     "For example, on a monochrome screen image window will appear",
505     "black or white even though your composited image may have",
506     "many colors.  If the image is saved to a file it is written",
507     "with the correct colors.  To assure the correct colors are",
508     "saved in the final image, any PseudoClass image is promoted",
509     "to DirectClass (see miff(5)).  To force a PseudoClass image",
510     "to remain PseudoClass, use -colors.",
511     (char *) NULL,
512   },
513   *ImageCutHelp[] =
514   {
515     "In cut mode, the Command widget has these options:",
516     "",
517     "    Help",
518     "    Dismiss",
519     "",
520     "To define a cut region, press button 1 and drag.  The",
521     "cut region is defined by a highlighted rectangle that",
522     "expands or contracts as it follows the pointer.  Once you",
523     "are satisfied with the cut region, release the button.",
524     "You are now in rectify mode.  In rectify mode, the Command",
525     "widget has these options:",
526     "",
527     "    Cut",
528     "    Help",
529     "    Dismiss",
530     "",
531     "You can make adjustments by moving the pointer to one of the",
532     "cut rectangle corners, pressing a button, and dragging.",
533     "Finally, press Cut to commit your copy region.  To",
534     "exit without cutting the image, press Dismiss.",
535     (char *) NULL,
536   },
537   *ImageCopyHelp[] =
538   {
539     "In copy mode, the Command widget has these options:",
540     "",
541     "    Help",
542     "    Dismiss",
543     "",
544     "To define a copy region, press button 1 and drag.  The",
545     "copy region is defined by a highlighted rectangle that",
546     "expands or contracts as it follows the pointer.  Once you",
547     "are satisfied with the copy region, release the button.",
548     "You are now in rectify mode.  In rectify mode, the Command",
549     "widget has these options:",
550     "",
551     "    Copy",
552     "    Help",
553     "    Dismiss",
554     "",
555     "You can make adjustments by moving the pointer to one of the",
556     "copy rectangle corners, pressing a button, and dragging.",
557     "Finally, press Copy to commit your copy region.  To",
558     "exit without copying the image, press Dismiss.",
559     (char *) NULL,
560   },
561   *ImageCropHelp[] =
562   {
563     "In crop mode, the Command widget has these options:",
564     "",
565     "    Help",
566     "    Dismiss",
567     "",
568     "To define a cropping region, press button 1 and drag.  The",
569     "cropping region is defined by a highlighted rectangle that",
570     "expands or contracts as it follows the pointer.  Once you",
571     "are satisfied with the cropping region, release the button.",
572     "You are now in rectify mode.  In rectify mode, the Command",
573     "widget has these options:",
574     "",
575     "    Crop",
576     "    Help",
577     "    Dismiss",
578     "",
579     "You can make adjustments by moving the pointer to one of the",
580     "cropping rectangle corners, pressing a button, and dragging.",
581     "Finally, press Crop to commit your cropping region.  To",
582     "exit without cropping the image, press Dismiss.",
583     (char *) NULL,
584   },
585   *ImageDrawHelp[] =
586   {
587     "The cursor changes to a crosshair to indicate you are in",
588     "draw mode.  To exit immediately, press Dismiss.  In draw mode,",
589     "the Command widget has these options:",
590     "",
591     "    Element",
592     "      point",
593     "      line",
594     "      rectangle",
595     "      fill rectangle",
596     "      circle",
597     "      fill circle",
598     "      ellipse",
599     "      fill ellipse",
600     "      polygon",
601     "      fill polygon",
602     "    Color",
603     "      black",
604     "      blue",
605     "      cyan",
606     "      green",
607     "      gray",
608     "      red",
609     "      magenta",
610     "      yellow",
611     "      white",
612     "      transparent",
613     "      Browser...",
614     "    Stipple",
615     "      Brick",
616     "      Diagonal",
617     "      Scales",
618     "      Vertical",
619     "      Wavy",
620     "      Translucent",
621     "      Opaque",
622     "      Open...",
623     "    Width",
624     "      1",
625     "      2",
626     "      4",
627     "      8",
628     "      16",
629     "      Dialog...",
630     "    Undo",
631     "    Help",
632     "    Dismiss",
633     "",
634     "Choose a drawing primitive from the Element sub-menu.",
635     "",
636     "Choose a color from the Color sub-menu.  Additional",
637     "colors can be specified with the color browser.",
638     "",
639     "If you choose the color browser and press Grab, you can",
640     "select the color by moving the pointer to the desired",
641     "color on the screen and press any button.  The transparent",
642     "color updates the image matte channel and is useful for",
643     "image compositing.",
644     "",
645     "Choose a stipple, if appropriate, from the Stipple sub-menu.",
646     "Additional stipples can be specified with the file browser.",
647     "Stipples obtained from the file browser must be on disk in the",
648     "X11 bitmap format.",
649     "",
650     "Choose a width, if appropriate, from the Width sub-menu.  To",
651     "choose a specific width select the Dialog widget.",
652     "",
653     "Choose a point in the Image window and press button 1 and",
654     "hold.  Next, move the pointer to another location in the",
655     "image.  As you move, a line connects the initial location and",
656     "the pointer.  When you release the button, the image is",
657     "updated with the primitive you just drew.  For polygons, the",
658     "image is updated when you press and release the button without",
659     "moving the pointer.",
660     "",
661     "To cancel image drawing, move the pointer back to the",
662     "starting point of the line and release the button.",
663     (char *) NULL,
664   },
665   *DisplayHelp[] =
666   {
667     "BUTTONS",
668     "  The effects of each button press is described below.  Three",
669     "  buttons are required.  If you have a two button mouse,",
670     "  button 1 and 3 are returned.  Press ALT and button 3 to",
671     "  simulate button 2.",
672     "",
673     "  1    Press this button to map or unmap the Command widget.",
674     "",
675     "  2    Press and drag to define a region of the image to",
676     "       magnify.",
677     "",
678     "  3    Press and drag to choose from a select set of commands.",
679     "       This button behaves differently if the image being",
680     "       displayed is a visual image directory.  Here, choose a",
681     "       particular tile of the directory and press this button and",
682     "       drag to select a command from a pop-up menu.  Choose from",
683     "       these menu items:",
684     "",
685     "           Open",
686     "           Next",
687     "           Former",
688     "           Delete",
689     "           Update",
690     "",
691     "       If you choose Open, the image represented by the tile is",
692     "       displayed.  To return to the visual image directory, choose",
693     "       Next from the Command widget.  Next and Former moves to the",
694     "       next or former image respectively.  Choose Delete to delete",
695     "       a particular image tile.  Finally, choose Update to",
696     "       synchronize all the image tiles with their respective",
697     "       images.",
698     "",
699     "COMMAND WIDGET",
700     "  The Command widget lists a number of sub-menus and commands.",
701     "  They are",
702     "",
703     "      File",
704     "        Open...",
705     "        Next",
706     "        Former",
707     "        Select...",
708     "        Save...",
709     "        Print...",
710     "        Delete...",
711     "        New...",
712     "        Visual Directory...",
713     "        Quit",
714     "      Edit",
715     "        Undo",
716     "        Redo",
717     "        Cut",
718     "        Copy",
719     "        Paste",
720     "      View",
721     "        Half Size",
722     "        Original Size",
723     "        Double Size",
724     "        Resize...",
725     "        Apply",
726     "        Refresh",
727     "        Restore",
728     "      Transform",
729     "        Crop",
730     "        Chop",
731     "        Flop",
732     "        Flip",
733     "        Rotate Right",
734     "        Rotate Left",
735     "        Rotate...",
736     "        Shear...",
737     "        Roll...",
738     "        Trim Edges",
739     "      Enhance",
740     "        Brightness...",
741     "        Saturation...",
742     "        Hue...",
743     "        Gamma...",
744     "        Sharpen...",
745     "        Dull",
746     "        Contrast Stretch...",
747     "        Sigmoidal Contrast...",
748     "        Normalize",
749     "        Equalize",
750     "        Negate",
751     "        Grayscale",
752     "        Map...",
753     "        Quantize...",
754     "      Effects",
755     "        Despeckle",
756     "        Emboss",
757     "        Reduce Noise",
758     "        Add Noise",
759     "        Sharpen...",
760     "        Blur...",
761     "        Threshold...",
762     "        Edge Detect...",
763     "        Spread...",
764     "        Shade...",
765     "        Painting...",
766     "        Segment...",
767     "      F/X",
768     "        Solarize...",
769     "        Sepia Tone...",
770     "        Swirl...",
771     "        Implode...",
772     "        Vignette...",
773     "        Wave...",
774     "        Oil Painting...",
775     "        Charcoal Drawing...",
776     "      Image Edit",
777     "        Annotate...",
778     "        Draw...",
779     "        Color...",
780     "        Matte...",
781     "        Composite...",
782     "        Add Border...",
783     "        Add Frame...",
784     "        Comment...",
785     "        Launch...",
786     "        Region of Interest...",
787     "      Miscellany",
788     "        Image Info",
789     "        Zoom Image",
790     "        Show Preview...",
791     "        Show Histogram",
792     "        Show Matte",
793     "        Background...",
794     "        Slide Show",
795     "        Preferences...",
796     "      Help",
797     "        Overview",
798     "        Browse Documentation",
799     "        About Display",
800     "",
801     "  Menu items with a indented triangle have a sub-menu.  They",
802     "  are represented above as the indented items.  To access a",
803     "  sub-menu item, move the pointer to the appropriate menu and",
804     "  press a button and drag.  When you find the desired sub-menu",
805     "  item, release the button and the command is executed.  Move",
806     "  the pointer away from the sub-menu if you decide not to",
807     "  execute a particular command.",
808     "",
809     "KEYBOARD ACCELERATORS",
810     "  Accelerators are one or two key presses that effect a",
811     "  particular command.  The keyboard accelerators that",
812     "  display(1) understands is:",
813     "",
814     "  Ctl+O     Press to open an image from a file.",
815     "",
816     "  space     Press to display the next image.",
817     "",
818     "            If the image is a multi-paged document such as a Postscript",
819     "            document, you can skip ahead several pages by preceding",
820     "            this command with a number.  For example to display the",
821     "            third page beyond the current page, press 3<space>.",
822     "",
823     "  backspace Press to display the former image.",
824     "",
825     "            If the image is a multi-paged document such as a Postscript",
826     "            document, you can skip behind several pages by preceding",
827     "            this command with a number.  For example to display the",
828     "            third page preceding the current page, press 3<backspace>.",
829     "",
830     "  Ctl+S     Press to write the image to a file.",
831     "",
832     "  Ctl+P     Press to print the image to a Postscript printer.",
833     "",
834     "  Ctl+D     Press to delete an image file.",
835     "",
836     "  Ctl+N     Press to create a blank canvas.",
837     "",
838     "  Ctl+Q     Press to discard all images and exit program.",
839     "",
840     "  Ctl+Z     Press to undo last image transformation.",
841     "",
842     "  Ctl+R     Press to redo last image transformation.",
843     "",
844     "  Ctl+X     Press to cut a region of the image.",
845     "",
846     "  Ctl+C     Press to copy a region of the image.",
847     "",
848     "  Ctl+V     Press to paste a region to the image.",
849     "",
850     "  <         Press to half the image size.",
851     "",
852     "  -         Press to return to the original image size.",
853     "",
854     "  >         Press to double the image size.",
855     "",
856     "  %         Press to resize the image to a width and height you",
857     "            specify.",
858     "",
859     "Cmd-A       Press to make any image transformations permanent."
860     "",
861     "            By default, any image size transformations are applied",
862     "            to the original image to create the image displayed on",
863     "            the X server.  However, the transformations are not",
864     "            permanent (i.e. the original image does not change",
865     "            size only the X image does).  For example, if you",
866     "            press > the X image will appear to double in size,",
867     "            but the original image will in fact remain the same size.",
868     "            To force the original image to double in size, press >",
869     "            followed by Cmd-A.",
870     "",
871     "  @         Press to refresh the image window.",
872     "",
873     "  C         Press to cut out a rectangular region of the image.",
874     "",
875     "  [         Press to chop the image.",
876     "",
877     "  H         Press to flop image in the horizontal direction.",
878     "",
879     "  V         Press to flip image in the vertical direction.",
880     "",
881     "  /         Press to rotate the image 90 degrees clockwise.",
882     "",
883     " \\         Press to rotate the image 90 degrees counter-clockwise.",
884     "",
885     "  *         Press to rotate the image the number of degrees you",
886     "            specify.",
887     "",
888     "  S         Press to shear the image the number of degrees you",
889     "            specify.",
890     "",
891     "  R         Press to roll the image.",
892     "",
893     "  T         Press to trim the image edges.",
894     "",
895     "  Shft-H    Press to vary the image hue.",
896     "",
897     "  Shft-S    Press to vary the color saturation.",
898     "",
899     "  Shft-L    Press to vary the color brightness.",
900     "",
901     "  Shft-G    Press to gamma correct the image.",
902     "",
903     "  Shft-C    Press to sharpen the image contrast.",
904     "",
905     "  Shft-Z    Press to dull the image contrast.",
906     "",
907     "  =         Press to perform histogram equalization on the image.",
908     "",
909     "  Shft-N    Press to perform histogram normalization on the image.",
910     "",
911     "  Shft-~    Press to negate the colors of the image.",
912     "",
913     "  .         Press to convert the image colors to gray.",
914     "",
915     "  Shft-#    Press to set the maximum number of unique colors in the",
916     "            image.",
917     "",
918     "  F2        Press to reduce the speckles in an image.",
919     "",
920     "  F3        Press to eliminate peak noise from an image.",
921     "",
922     "  F4        Press to add noise to an image.",
923     "",
924     "  F5        Press to sharpen an image.",
925     "",
926     "  F6        Press to delete an image file.",
927     "",
928     "  F7        Press to threshold the image.",
929     "",
930     "  F8        Press to detect edges within an image.",
931     "",
932     "  F9        Press to emboss an image.",
933     "",
934     "  F10       Press to displace pixels by a random amount.",
935     "",
936     "  F11       Press to negate all pixels above the threshold level.",
937     "",
938     "  F12       Press to shade the image using a distant light source.",
939     "",
940     "  F13       Press to lighten or darken image edges to create a 3-D effect.",
941     "",
942     "  F14       Press to segment the image by color.",
943     "",
944     "  Meta-S    Press to swirl image pixels about the center.",
945     "",
946     "  Meta-I    Press to implode image pixels about the center.",
947     "",
948     "  Meta-W    Press to alter an image along a sine wave.",
949     "",
950     "  Meta-P    Press to simulate an oil painting.",
951     "",
952     "  Meta-C    Press to simulate a charcoal drawing.",
953     "",
954     "  Alt-A     Press to annotate the image with text.",
955     "",
956     "  Alt-D     Press to draw on an image.",
957     "",
958     "  Alt-P     Press to edit an image pixel color.",
959     "",
960     "  Alt-M     Press to edit the image matte information.",
961     "",
962     "  Alt-V     Press to composite the image with another.",
963     "",
964     "  Alt-B     Press to add a border to the image.",
965     "",
966     "  Alt-F     Press to add an ornamental border to the image.",
967     "",
968     "  Alt-Shft-!",
969     "            Press to add an image comment.",
970     "",
971     "  Ctl-A     Press to apply image processing techniques to a region",
972     "            of interest.",
973     "",
974     "  Shft-?    Press to display information about the image.",
975     "",
976     "  Shft-+    Press to map the zoom image window.",
977     "",
978     "  Shft-P    Press to preview an image enhancement, effect, or f/x.",
979     "",
980     "  F1        Press to display helpful information about display(1).",
981     "",
982     "  Find      Press to browse documentation about ImageMagick.",
983     "",
984     "  1-9       Press to change the level of magnification.",
985     "",
986     "  Use the arrow keys to move the image one pixel up, down,",
987     "  left, or right within the magnify window.  Be sure to first",
988     "  map the magnify window by pressing button 2.",
989     "",
990     "  Press ALT and one of the arrow keys to trim off one pixel",
991     "  from any side of the image.",
992     (char *) NULL,
993   },
994   *ImageMatteEditHelp[] =
995   {
996     "Matte information within an image is useful for some",
997     "operations such as image compositing (See IMAGE",
998     "COMPOSITING).  This extra channel usually defines a mask",
999     "which represents a sort of a cookie-cutter for the image.",
1000     "This the case when matte is opaque (full coverage) for",
1001     "pixels inside the shape, zero outside, and between 0 and",
1002     "QuantumRange on the boundary.",
1003     "",
1004     "A small window appears showing the location of the cursor in",
1005     "the image window. You are now in matte edit mode.  To exit",
1006     "immediately, press Dismiss.  In matte edit mode, the Command",
1007     "widget has these options:",
1008     "",
1009     "    Method",
1010     "      point",
1011     "      replace",
1012     "      floodfill",
1013     "      filltoborder",
1014     "      reset",
1015     "    Border Color",
1016     "      black",
1017     "      blue",
1018     "      cyan",
1019     "      green",
1020     "      gray",
1021     "      red",
1022     "      magenta",
1023     "      yellow",
1024     "      white",
1025     "      Browser...",
1026     "    Fuzz",
1027     "      0%",
1028     "      2%",
1029     "      5%",
1030     "      10%",
1031     "      15%",
1032     "      Dialog...",
1033     "    Matte",
1034     "      Opaque",
1035     "      Transparent",
1036     "      Dialog...",
1037     "    Undo",
1038     "    Help",
1039     "    Dismiss",
1040     "",
1041     "Choose a matte editing method from the Method sub-menu of",
1042     "the Command widget.  The point method changes the matte value",
1043     "of any pixel selected with the pointer until the button is",
1044     "is released.  The replace method changes the matte value of",
1045     "any pixel that matches the color of the pixel you select with",
1046     "a button press.  Floodfill changes the matte value of any pixel",
1047     "that matches the color of the pixel you select with a button",
1048     "press and is a neighbor.  Whereas filltoborder changes the matte",
1049     "value any neighbor pixel that is not the border color.  Finally",
1050     "reset changes the entire image to the designated matte value.",
1051     "",
1052     "Choose Matte Value and pick Opaque or Transarent.  For other values",
1053     "select the Dialog entry.  Here a dialog appears requesting a matte",
1054     "value.  The value you select is assigned as the opacity value of the",
1055     "selected pixel or pixels.",
1056     "",
1057     "Now, press any button to select a pixel within the image",
1058     "window to change its matte value.",
1059     "",
1060     "If the Magnify widget is mapped, it can be helpful in positioning",
1061     "your pointer within the image (refer to button 2).",
1062     "",
1063     "Matte information is only valid in a DirectClass image.",
1064     "Therefore, any PseudoClass image is promoted to DirectClass",
1065     "(see miff(5)).  Note that matte information for PseudoClass",
1066     "is not retained for colormapped X server visuals (e.g.",
1067     "StaticColor, StaticColor, GrayScale, PseudoColor) unless you",
1068     "immediately save your image to a file (refer to Write).",
1069     "Correct matte editing behavior may require a TrueColor or",
1070     "DirectColor visual or a Standard Colormap.",
1071     (char *) NULL,
1072   },
1073   *ImagePanHelp[] =
1074   {
1075     "When an image exceeds the width or height of the X server",
1076     "screen, display maps a small panning icon.  The rectangle",
1077     "within the panning icon shows the area that is currently",
1078     "displayed in the image window.  To pan about the image,",
1079     "press any button and drag the pointer within the panning",
1080     "icon.  The pan rectangle moves with the pointer and the",
1081     "image window is updated to reflect the location of the",
1082     "rectangle within the panning icon.  When you have selected",
1083     "the area of the image you wish to view, release the button.",
1084     "",
1085     "Use the arrow keys to pan the image one pixel up, down,",
1086     "left, or right within the image window.",
1087     "",
1088     "The panning icon is withdrawn if the image becomes smaller",
1089     "than the dimensions of the X server screen.",
1090     (char *) NULL,
1091   },
1092   *ImagePasteHelp[] =
1093   {
1094     "A small window appears showing the location of the cursor in",
1095     "the image window. You are now in paste mode.  To exit",
1096     "immediately, press Dismiss.  In paste mode, the Command",
1097     "widget has these options:",
1098     "",
1099     "    Operators",
1100     "      over",
1101     "      in",
1102     "      out",
1103     "      atop",
1104     "      xor",
1105     "      plus",
1106     "      minus",
1107     "      add",
1108     "      subtract",
1109     "      difference",
1110     "      replace",
1111     "    Help",
1112     "    Dismiss",
1113     "",
1114     "Choose a composite operation from the Operators sub-menu of",
1115     "the Command widget.  How each operator behaves is described",
1116     "below.  Image window is the image currently displayed on",
1117     "your X server and image is the image obtained with the File",
1118     "Browser widget.",
1119     "",
1120     "Over     The result is the union of the two image shapes,",
1121     "         with image obscuring image window in the region of",
1122     "         overlap.",
1123     "",
1124     "In       The result is simply image cut by the shape of",
1125     "         image window.  None of the image data of image",
1126     "         window is in the result.",
1127     "",
1128     "Out      The resulting image is image with the shape of",
1129     "         image window cut out.",
1130     "",
1131     "Atop     The result is the same shape as image image window,",
1132     "         with image obscuring image window where the image",
1133     "         shapes overlap.  Note this differs from over",
1134     "         because the portion of image outside image window's",
1135     "         shape does not appear in the result.",
1136     "",
1137     "Xor      The result is the image data from both image and",
1138     "         image window that is outside the overlap region.",
1139     "         The overlap region is blank.",
1140     "",
1141     "Plus     The result is just the sum of the image data.",
1142     "         Output values are cropped to QuantumRange (no overflow).",
1143     "         This operation is independent of the matte",
1144     "         channels.",
1145     "",
1146     "Minus    The result of image - image window, with underflow",
1147     "         cropped to zero.",
1148     "",
1149     "Add      The result of image + image window, with overflow",
1150     "         wrapping around (mod 256).",
1151     "",
1152     "Subtract The result of image - image window, with underflow",
1153     "         wrapping around (mod 256).  The add and subtract",
1154     "         operators can be used to perform reversible",
1155     "         transformations.",
1156     "",
1157     "Difference",
1158     "         The result of abs(image - image window).  This",
1159     "         useful for comparing two very similar images.",
1160     "",
1161     "Copy     The resulting image is image window replaced with",
1162     "         image.  Here the matte information is ignored.",
1163     "",
1164     "CopyRed  The red layer of the image window is replace with",
1165     "         the red layer of the image.  The other layers are",
1166     "         untouched.",
1167     "",
1168     "CopyGreen",
1169     "         The green layer of the image window is replace with",
1170     "         the green layer of the image.  The other layers are",
1171     "         untouched.",
1172     "",
1173     "CopyBlue The blue layer of the image window is replace with",
1174     "         the blue layer of the image.  The other layers are",
1175     "         untouched.",
1176     "",
1177     "CopyOpacity",
1178     "         The matte layer of the image window is replace with",
1179     "         the matte layer of the image.  The other layers are",
1180     "         untouched.",
1181     "",
1182     "The image compositor requires a matte, or alpha channel in",
1183     "the image for some operations.  This extra channel usually",
1184     "defines a mask which represents a sort of a cookie-cutter",
1185     "for the image.  This the case when matte is opaque (full",
1186     "coverage) for pixels inside the shape, zero outside, and",
1187     "between 0 and QuantumRange on the boundary.  If image does not",
1188     "have a matte channel, it is initialized with 0 for any pixel",
1189     "matching in color to pixel location (0,0), otherwise QuantumRange.",
1190     "",
1191     "Note that matte information for image window is not retained",
1192     "for colormapped X server visuals (e.g. StaticColor,",
1193     "StaticColor, GrayScale, PseudoColor).  Correct compositing",
1194     "behavior may require a TrueColor or DirectColor visual or a",
1195     "Standard Colormap.",
1196     "",
1197     "Choosing a composite operator is optional.  The default",
1198     "operator is replace.  However, you must choose a location to",
1199     "paste your image and press button 1.  Press and hold the",
1200     "button before releasing and an outline of the image will",
1201     "appear to help you identify your location.",
1202     "",
1203     "The actual colors of the pasted image is saved.  However,",
1204     "the color that appears in image window may be different.",
1205     "For example, on a monochrome screen image window will appear",
1206     "black or white even though your pasted image may have",
1207     "many colors.  If the image is saved to a file it is written",
1208     "with the correct colors.  To assure the correct colors are",
1209     "saved in the final image, any PseudoClass image is promoted",
1210     "to DirectClass (see miff(5)).  To force a PseudoClass image",
1211     "to remain PseudoClass, use -colors.",
1212     (char *) NULL,
1213   },
1214   *ImageROIHelp[] =
1215   {
1216     "In region of interest mode, the Command widget has these",
1217     "options:",
1218     "",
1219     "    Help",
1220     "    Dismiss",
1221     "",
1222     "To define a region of interest, press button 1 and drag.",
1223     "The region of interest is defined by a highlighted rectangle",
1224     "that expands or contracts as it follows the pointer.  Once",
1225     "you are satisfied with the region of interest, release the",
1226     "button.  You are now in apply mode.  In apply mode the",
1227     "Command widget has these options:",
1228     "",
1229     "      File",
1230     "        Save...",
1231     "        Print...",
1232     "      Edit",
1233     "        Undo",
1234     "        Redo",
1235     "      Transform",
1236     "        Flop",
1237     "        Flip",
1238     "        Rotate Right",
1239     "        Rotate Left",
1240     "      Enhance",
1241     "        Hue...",
1242     "        Saturation...",
1243     "        Brightness...",
1244     "        Gamma...",
1245     "        Spiff",
1246     "        Dull",
1247     "        Contrast Stretch",
1248     "        Sigmoidal Contrast...",
1249     "        Normalize",
1250     "        Equalize",
1251     "        Negate",
1252     "        Grayscale",
1253     "        Map...",
1254     "        Quantize...",
1255     "      Effects",
1256     "        Despeckle",
1257     "        Emboss",
1258     "        Reduce Noise",
1259     "        Sharpen...",
1260     "        Blur...",
1261     "        Threshold...",
1262     "        Edge Detect...",
1263     "        Spread...",
1264     "        Shade...",
1265     "        Raise...",
1266     "        Segment...",
1267     "      F/X",
1268     "        Solarize...",
1269     "        Sepia Tone...",
1270     "        Swirl...",
1271     "        Implode...",
1272     "        Vignette...",
1273     "        Wave...",
1274     "        Oil Painting...",
1275     "        Charcoal Drawing...",
1276     "      Miscellany",
1277     "        Image Info",
1278     "        Zoom Image",
1279     "        Show Preview...",
1280     "        Show Histogram",
1281     "        Show Matte",
1282     "      Help",
1283     "      Dismiss",
1284     "",
1285     "You can make adjustments to the region of interest by moving",
1286     "the pointer to one of the rectangle corners, pressing a",
1287     "button, and dragging.  Finally, choose an image processing",
1288     "technique from the Command widget.  You can choose more than",
1289     "one image processing technique to apply to an area.",
1290     "Alternatively, you can move the region of interest before",
1291     "applying another image processing technique.  To exit, press",
1292     "Dismiss.",
1293     (char *) NULL,
1294   },
1295   *ImageRotateHelp[] =
1296   {
1297     "In rotate mode, the Command widget has these options:",
1298     "",
1299     "    Pixel Color",
1300     "      black",
1301     "      blue",
1302     "      cyan",
1303     "      green",
1304     "      gray",
1305     "      red",
1306     "      magenta",
1307     "      yellow",
1308     "      white",
1309     "      Browser...",
1310     "    Direction",
1311     "      horizontal",
1312     "      vertical",
1313     "    Help",
1314     "    Dismiss",
1315     "",
1316     "Choose a background color from the Pixel Color sub-menu.",
1317     "Additional background colors can be specified with the color",
1318     "browser.  You can change the menu colors by setting the X",
1319     "resources pen1 through pen9.",
1320     "",
1321     "If you choose the color browser and press Grab, you can",
1322     "select the background color by moving the pointer to the",
1323     "desired color on the screen and press any button.",
1324     "",
1325     "Choose a point in the image window and press this button and",
1326     "hold.  Next, move the pointer to another location in the",
1327     "image.  As you move a line connects the initial location and",
1328     "the pointer.  When you release the button, the degree of",
1329     "image rotation is determined by the slope of the line you",
1330     "just drew.  The slope is relative to the direction you",
1331     "choose from the Direction sub-menu of the Command widget.",
1332     "",
1333     "To cancel the image rotation, move the pointer back to the",
1334     "starting point of the line and release the button.",
1335     (char *) NULL,
1336   };
1337 \f
1338 /*
1339   Enumeration declarations.
1340 */
1341 typedef enum
1342 {
1343   CopyMode,
1344   CropMode,
1345   CutMode
1346 } ClipboardMode;
1347
1348 typedef enum
1349 {
1350   OpenCommand,
1351   NextCommand,
1352   FormerCommand,
1353   SelectCommand,
1354   SaveCommand,
1355   PrintCommand,
1356   DeleteCommand,
1357   NewCommand,
1358   VisualDirectoryCommand,
1359   QuitCommand,
1360   UndoCommand,
1361   RedoCommand,
1362   CutCommand,
1363   CopyCommand,
1364   PasteCommand,
1365   HalfSizeCommand,
1366   OriginalSizeCommand,
1367   DoubleSizeCommand,
1368   ResizeCommand,
1369   ApplyCommand,
1370   RefreshCommand,
1371   RestoreCommand,
1372   CropCommand,
1373   ChopCommand,
1374   FlopCommand,
1375   FlipCommand,
1376   RotateRightCommand,
1377   RotateLeftCommand,
1378   RotateCommand,
1379   ShearCommand,
1380   RollCommand,
1381   TrimCommand,
1382   HueCommand,
1383   SaturationCommand,
1384   BrightnessCommand,
1385   GammaCommand,
1386   SpiffCommand,
1387   DullCommand,
1388   ContrastStretchCommand,
1389   SigmoidalContrastCommand,
1390   NormalizeCommand,
1391   EqualizeCommand,
1392   NegateCommand,
1393   GrayscaleCommand,
1394   MapCommand,
1395   QuantizeCommand,
1396   DespeckleCommand,
1397   EmbossCommand,
1398   ReduceNoiseCommand,
1399   AddNoiseCommand,
1400   SharpenCommand,
1401   BlurCommand,
1402   ThresholdCommand,
1403   EdgeDetectCommand,
1404   SpreadCommand,
1405   ShadeCommand,
1406   RaiseCommand,
1407   SegmentCommand,
1408   SolarizeCommand,
1409   SepiaToneCommand,
1410   SwirlCommand,
1411   ImplodeCommand,
1412   VignetteCommand,
1413   WaveCommand,
1414   OilPaintCommand,
1415   CharcoalDrawCommand,
1416   AnnotateCommand,
1417   DrawCommand,
1418   ColorCommand,
1419   MatteCommand,
1420   CompositeCommand,
1421   AddBorderCommand,
1422   AddFrameCommand,
1423   CommentCommand,
1424   LaunchCommand,
1425   RegionofInterestCommand,
1426   ROIHelpCommand,
1427   ROIDismissCommand,
1428   InfoCommand,
1429   ZoomCommand,
1430   ShowPreviewCommand,
1431   ShowHistogramCommand,
1432   ShowMatteCommand,
1433   BackgroundCommand,
1434   SlideShowCommand,
1435   PreferencesCommand,
1436   HelpCommand,
1437   BrowseDocumentationCommand,
1438   VersionCommand,
1439   SaveToUndoBufferCommand,
1440   FreeBuffersCommand,
1441   NullCommand
1442 } CommandType;
1443
1444 typedef enum
1445 {
1446   AnnotateNameCommand,
1447   AnnotateFontColorCommand,
1448   AnnotateBackgroundColorCommand,
1449   AnnotateRotateCommand,
1450   AnnotateHelpCommand,
1451   AnnotateDismissCommand,
1452   TextHelpCommand,
1453   TextApplyCommand,
1454   ChopDirectionCommand,
1455   ChopHelpCommand,
1456   ChopDismissCommand,
1457   HorizontalChopCommand,
1458   VerticalChopCommand,
1459   ColorEditMethodCommand,
1460   ColorEditColorCommand,
1461   ColorEditBorderCommand,
1462   ColorEditFuzzCommand,
1463   ColorEditUndoCommand,
1464   ColorEditHelpCommand,
1465   ColorEditDismissCommand,
1466   CompositeOperatorsCommand,
1467   CompositeDissolveCommand,
1468   CompositeDisplaceCommand,
1469   CompositeHelpCommand,
1470   CompositeDismissCommand,
1471   CropHelpCommand,
1472   CropDismissCommand,
1473   RectifyCopyCommand,
1474   RectifyHelpCommand,
1475   RectifyDismissCommand,
1476   DrawElementCommand,
1477   DrawColorCommand,
1478   DrawStippleCommand,
1479   DrawWidthCommand,
1480   DrawUndoCommand,
1481   DrawHelpCommand,
1482   DrawDismissCommand,
1483   MatteEditMethod,
1484   MatteEditBorderCommand,
1485   MatteEditFuzzCommand,
1486   MatteEditValueCommand,
1487   MatteEditUndoCommand,
1488   MatteEditHelpCommand,
1489   MatteEditDismissCommand,
1490   PasteOperatorsCommand,
1491   PasteHelpCommand,
1492   PasteDismissCommand,
1493   RotateColorCommand,
1494   RotateDirectionCommand,
1495   RotateCropCommand,
1496   RotateSharpenCommand,
1497   RotateHelpCommand,
1498   RotateDismissCommand,
1499   HorizontalRotateCommand,
1500   VerticalRotateCommand,
1501   TileLoadCommand,
1502   TileNextCommand,
1503   TileFormerCommand,
1504   TileDeleteCommand,
1505   TileUpdateCommand
1506 } ModeType;
1507 \f
1508 /*
1509   Stipples.
1510 */
1511 #define BricksWidth  20
1512 #define BricksHeight  20
1513 #define DiagonalWidth  16
1514 #define DiagonalHeight  16
1515 #define HighlightWidth  8
1516 #define HighlightHeight  8
1517 #define OpaqueWidth  8
1518 #define OpaqueHeight  8
1519 #define ScalesWidth  16
1520 #define ScalesHeight  16
1521 #define ShadowWidth  8
1522 #define ShadowHeight  8
1523 #define VerticalWidth  16
1524 #define VerticalHeight  16
1525 #define WavyWidth  16
1526 #define WavyHeight  16
1527 \f
1528 /*
1529   Constant declaration.
1530 */
1531 static const int
1532   RoiDelta = 8;
1533
1534 static const unsigned char
1535   BricksBitmap[] =
1536   {
1537     0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00,
1538     0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01,
1539     0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00,
1540     0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f,
1541     0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01
1542   },
1543   DiagonalBitmap[] =
1544   {
1545     0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88,
1546     0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22,
1547     0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22
1548   },
1549   ScalesBitmap[] =
1550   {
1551     0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3, 0x80, 0x80, 0x80, 0x80,
1552     0x41, 0x41, 0x3e, 0x3e, 0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3,
1553     0x80, 0x80, 0x80, 0x80, 0x41, 0x41, 0x3e, 0x3e
1554   },
1555   VerticalBitmap[] =
1556   {
1557     0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1558     0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1559     0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11
1560   },
1561   WavyBitmap[] =
1562   {
1563     0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfb, 0xff,
1564     0xe7, 0xff, 0x1f, 0xff, 0xff, 0xf8, 0xff, 0xe7, 0xff, 0xdf, 0xff, 0xbf,
1565     0xff, 0xbf, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f
1566   };
1567 \f
1568 /*
1569   Function prototypes.
1570 */
1571 static CommandType
1572   XImageWindowCommand(Display *,XResourceInfo *,XWindows *,
1573     const MagickStatusType,KeySym,Image **,ExceptionInfo *);
1574
1575 static Image
1576   *XMagickCommand(Display *,XResourceInfo *,XWindows *,const CommandType,
1577     Image **,ExceptionInfo *),
1578   *XOpenImage(Display *,XResourceInfo *,XWindows *,const MagickBooleanType),
1579   *XTileImage(Display *,XResourceInfo *,XWindows *,Image *,XEvent *,
1580     ExceptionInfo *),
1581   *XVisualDirectoryImage(Display *,XResourceInfo *,XWindows *,
1582     ExceptionInfo *);
1583
1584 static MagickBooleanType
1585   XAnnotateEditImage(Display *,XResourceInfo *,XWindows *,Image *,
1586     ExceptionInfo *),
1587   XBackgroundImage(Display *,XResourceInfo *,XWindows *,Image **,
1588     ExceptionInfo *),
1589   XChopImage(Display *,XResourceInfo *,XWindows *,Image **,
1590     ExceptionInfo *),
1591   XCropImage(Display *,XResourceInfo *,XWindows *,Image *,const ClipboardMode,
1592     ExceptionInfo *),
1593   XColorEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1594     ExceptionInfo *),
1595   XCompositeImage(Display *,XResourceInfo *,XWindows *,Image *,
1596     ExceptionInfo *),
1597   XConfigureImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1598   XDrawEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1599     ExceptionInfo *),
1600   XMatteEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1601     ExceptionInfo *),
1602   XPasteImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1603   XPrintImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1604   XRotateImage(Display *,XResourceInfo *,XWindows *,double,Image **,
1605     ExceptionInfo *),
1606   XROIImage(Display *,XResourceInfo *,XWindows *,Image **,ExceptionInfo *),
1607   XSaveImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1608   XTrimImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *);
1609
1610 static void
1611   XDrawPanRectangle(Display *,XWindows *),
1612   XImageCache(Display *,XResourceInfo *,XWindows *,const CommandType,Image **,
1613     ExceptionInfo *),
1614   XMagnifyImage(Display *,XWindows *,XEvent *,ExceptionInfo *),
1615   XMakePanImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1616   XPanImage(Display *,XWindows *,XEvent *,ExceptionInfo *),
1617   XMagnifyWindowCommand(Display *,XWindows *,const MagickStatusType,
1618     const KeySym,ExceptionInfo *),
1619   XSetCropGeometry(Display *,XWindows *,RectangleInfo *,Image *),
1620   XScreenEvent(Display *,XWindows *,XEvent *,ExceptionInfo *),
1621   XTranslateImage(Display *,XWindows *,Image *,const KeySym);
1622 \f
1623 /*
1624 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1625 %                                                                             %
1626 %                                                                             %
1627 %                                                                             %
1628 %   D i s p l a y I m a g e s                                                 %
1629 %                                                                             %
1630 %                                                                             %
1631 %                                                                             %
1632 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1633 %
1634 %  DisplayImages() displays an image sequence to any X window screen.  It
1635 %  returns a value other than 0 if successful.  Check the exception member
1636 %  of image to determine the reason for any failure.
1637 %
1638 %  The format of the DisplayImages method is:
1639 %
1640 %      MagickBooleanType DisplayImages(const ImageInfo *image_info,
1641 %        Image *images,ExceptionInfo *exception)
1642 %
1643 %  A description of each parameter follows:
1644 %
1645 %    o image_info: the image info.
1646 %
1647 %    o image: the image.
1648 %
1649 %    o exception: return any errors or warnings in this structure.
1650 %
1651 */
1652 MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
1653   Image *images,ExceptionInfo *exception)
1654 {
1655   char
1656     *argv[1];
1657
1658   Display
1659     *display;
1660
1661   Image
1662     *image;
1663
1664   register ssize_t
1665     i;
1666
1667   size_t
1668     state;
1669
1670   XrmDatabase
1671     resource_database;
1672
1673   XResourceInfo
1674     resource_info;
1675
1676   assert(image_info != (const ImageInfo *) NULL);
1677   assert(image_info->signature == MagickCoreSignature);
1678   assert(images != (Image *) NULL);
1679   assert(images->signature == MagickCoreSignature);
1680   if (images->debug != MagickFalse )
1681     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
1682   display=XOpenDisplay(image_info->server_name);
1683   if (display == (Display *) NULL)
1684     {
1685       (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
1686         "UnableToOpenXServer","`%s'",XDisplayName(image_info->server_name));
1687       return(MagickFalse);
1688     }
1689   if (exception->severity != UndefinedException)
1690     CatchException(exception);
1691   (void) XSetErrorHandler(XError);
1692   resource_database=XGetResourceDatabase(display,GetClientName());
1693   (void) memset(&resource_info,0,sizeof(resource_info));
1694   XGetResourceInfo(image_info,resource_database,GetClientName(),&resource_info);
1695   if (image_info->page != (char *) NULL)
1696     resource_info.image_geometry=AcquireString(image_info->page);
1697   resource_info.immutable=MagickTrue;
1698   argv[0]=AcquireString(GetClientName());
1699   state=DefaultState;
1700   for (i=0; (state & ExitState) == 0; i++)
1701   {
1702     if ((images->iterations != 0) && (i >= (ssize_t) images->iterations))
1703       break;
1704     image=GetImageFromList(images,i % GetImageListLength(images));
1705     (void) XDisplayImage(display,&resource_info,argv,1,&image,&state,exception);
1706   }
1707   (void) SetErrorHandler((ErrorHandler) NULL);
1708   (void) SetWarningHandler((WarningHandler) NULL);
1709   argv[0]=DestroyString(argv[0]);
1710   (void) XCloseDisplay(display);
1711   XDestroyResourceInfo(&resource_info);
1712   if (exception->severity != UndefinedException)
1713     return(MagickFalse);
1714   return(MagickTrue);
1715 }
1716 \f
1717 /*
1718 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1719 %                                                                             %
1720 %                                                                             %
1721 %                                                                             %
1722 %   R e m o t e D i s p l a y C o m m a n d                                   %
1723 %                                                                             %
1724 %                                                                             %
1725 %                                                                             %
1726 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1727 %
1728 %  RemoteDisplayCommand() encourages a remote display program to display the
1729 %  specified image filename.
1730 %
1731 %  The format of the RemoteDisplayCommand method is:
1732 %
1733 %      MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1734 %        const char *window,const char *filename,ExceptionInfo *exception)
1735 %
1736 %  A description of each parameter follows:
1737 %
1738 %    o image_info: the image info.
1739 %
1740 %    o window: Specifies the name or id of an X window.
1741 %
1742 %    o filename: the name of the image filename to display.
1743 %
1744 %    o exception: return any errors or warnings in this structure.
1745 %
1746 */
1747 MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1748   const char *window,const char *filename,ExceptionInfo *exception)
1749 {
1750   Display
1751     *display;
1752
1753   MagickStatusType
1754     status;
1755
1756   assert(image_info != (const ImageInfo *) NULL);
1757   assert(image_info->signature == MagickCoreSignature);
1758   assert(filename != (char *) NULL);
1759   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
1760   display=XOpenDisplay(image_info->server_name);
1761   if (display == (Display *) NULL)
1762     {
1763       (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
1764         "UnableToOpenXServer","`%s'",XDisplayName(image_info->server_name));
1765       return(MagickFalse);
1766     }
1767   (void) XSetErrorHandler(XError);
1768   status=XRemoteCommand(display,window,filename);
1769   (void) XCloseDisplay(display);
1770   return(status != 0 ? MagickTrue : MagickFalse);
1771 }
1772 \f
1773 /*
1774 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1775 %                                                                             %
1776 %                                                                             %
1777 %                                                                             %
1778 +   X A n n o t a t e E d i t I m a g e                                       %
1779 %                                                                             %
1780 %                                                                             %
1781 %                                                                             %
1782 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1783 %
1784 %  XAnnotateEditImage() annotates the image with text.
1785 %
1786 %  The format of the XAnnotateEditImage method is:
1787 %
1788 %      MagickBooleanType XAnnotateEditImage(Display *display,
1789 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
1790 %        ExceptionInfo *exception)
1791 %
1792 %  A description of each parameter follows:
1793 %
1794 %    o display: Specifies a connection to an X server;  returned from
1795 %      XOpenDisplay.
1796 %
1797 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
1798 %
1799 %    o windows: Specifies a pointer to a XWindows structure.
1800 %
1801 %    o image: the image; returned from ReadImage.
1802 %
1803 */
1804
1805 static MagickBooleanType XAnnotateEditImage(Display *display,
1806   XResourceInfo *resource_info,XWindows *windows,Image *image,
1807   ExceptionInfo *exception)
1808 {
1809   static const char
1810     *AnnotateMenu[] =
1811     {
1812       "Font Name",
1813       "Font Color",
1814       "Box Color",
1815       "Rotate Text",
1816       "Help",
1817       "Dismiss",
1818       (char *) NULL
1819     },
1820     *TextMenu[] =
1821     {
1822       "Help",
1823       "Apply",
1824       (char *) NULL
1825     };
1826
1827   static const ModeType
1828     AnnotateCommands[] =
1829     {
1830       AnnotateNameCommand,
1831       AnnotateFontColorCommand,
1832       AnnotateBackgroundColorCommand,
1833       AnnotateRotateCommand,
1834       AnnotateHelpCommand,
1835       AnnotateDismissCommand
1836     },
1837     TextCommands[] =
1838     {
1839       TextHelpCommand,
1840       TextApplyCommand
1841     };
1842
1843   static MagickBooleanType
1844     transparent_box = MagickTrue,
1845     transparent_pen = MagickFalse;
1846
1847   static double
1848     degrees = 0.0;
1849
1850   static unsigned int
1851     box_id = MaxNumberPens-2,
1852     font_id = 0,
1853     pen_id = 0;
1854
1855   char
1856     command[MagickPathExtent],
1857     text[MagickPathExtent];
1858
1859   const char
1860     *ColorMenu[MaxNumberPens+1];
1861
1862   Cursor
1863     cursor;
1864
1865   GC
1866     annotate_context;
1867
1868   int
1869     id,
1870     pen_number,
1871     status,
1872     x,
1873     y;
1874
1875   KeySym
1876     key_symbol;
1877
1878   register char
1879     *p;
1880
1881   register ssize_t
1882     i;
1883
1884   unsigned int
1885     height,
1886     width;
1887
1888   size_t
1889     state;
1890
1891   XAnnotateInfo
1892     *annotate_info,
1893     *previous_info;
1894
1895   XColor
1896     color;
1897
1898   XFontStruct
1899     *font_info;
1900
1901   XEvent
1902     event,
1903     text_event;
1904
1905   /*
1906     Map Command widget.
1907   */
1908   (void) CloneString(&windows->command.name,"Annotate");
1909   windows->command.data=4;
1910   (void) XCommandWidget(display,windows,AnnotateMenu,(XEvent *) NULL);
1911   (void) XMapRaised(display,windows->command.id);
1912   XClientMessage(display,windows->image.id,windows->im_protocols,
1913     windows->im_update_widget,CurrentTime);
1914   /*
1915     Track pointer until button 1 is pressed.
1916   */
1917   XQueryPosition(display,windows->image.id,&x,&y);
1918   (void) XSelectInput(display,windows->image.id,
1919     windows->image.attributes.event_mask | PointerMotionMask);
1920   cursor=XCreateFontCursor(display,XC_left_side);
1921   (void) XCheckDefineCursor(display,windows->image.id,cursor);
1922   state=DefaultState;
1923   do
1924   {
1925     if (windows->info.mapped != MagickFalse )
1926       {
1927         /*
1928           Display pointer position.
1929         */
1930         (void) FormatLocaleString(text,MagickPathExtent," %+d%+d ",
1931           x+windows->image.x,y+windows->image.y);
1932         XInfoWidget(display,windows,text);
1933       }
1934     /*
1935       Wait for next event.
1936     */
1937     XScreenEvent(display,windows,&event,exception);
1938     if (event.xany.window == windows->command.id)
1939       {
1940         /*
1941           Select a command from the Command widget.
1942         */
1943         id=XCommandWidget(display,windows,AnnotateMenu,&event);
1944         (void) XCheckDefineCursor(display,windows->image.id,cursor);
1945         if (id < 0)
1946           continue;
1947         switch (AnnotateCommands[id])
1948         {
1949           case AnnotateNameCommand:
1950           {
1951             const char
1952               *FontMenu[MaxNumberFonts];
1953
1954             int
1955               font_number;
1956
1957             /*
1958               Initialize menu selections.
1959             */
1960             for (i=0; i < MaxNumberFonts; i++)
1961               FontMenu[i]=resource_info->font_name[i];
1962             FontMenu[MaxNumberFonts-2]="Browser...";
1963             FontMenu[MaxNumberFonts-1]=(const char *) NULL;
1964             /*
1965               Select a font name from the pop-up menu.
1966             */
1967             font_number=XMenuWidget(display,windows,AnnotateMenu[id],
1968               (const char **) FontMenu,command);
1969             if (font_number < 0)
1970               break;
1971             if (font_number == (MaxNumberFonts-2))
1972               {
1973                 static char
1974                   font_name[MagickPathExtent] = "fixed";
1975
1976                 /*
1977                   Select a font name from a browser.
1978                 */
1979                 resource_info->font_name[font_number]=font_name;
1980                 XFontBrowserWidget(display,windows,"Select",font_name);
1981                 if (*font_name == '\0')
1982                   break;
1983               }
1984             /*
1985               Initialize font info.
1986             */
1987             font_info=XLoadQueryFont(display,resource_info->font_name[
1988               font_number]);
1989             if (font_info == (XFontStruct *) NULL)
1990               {
1991                 XNoticeWidget(display,windows,"Unable to load font:",
1992                   resource_info->font_name[font_number]);
1993                 break;
1994               }
1995             font_id=(unsigned int) font_number;
1996             (void) XFreeFont(display,font_info);
1997             break;
1998           }
1999           case AnnotateFontColorCommand:
2000           {
2001             /*
2002               Initialize menu selections.
2003             */
2004             for (i=0; i < (int) (MaxNumberPens-2); i++)
2005               ColorMenu[i]=resource_info->pen_colors[i];
2006             ColorMenu[MaxNumberPens-2]="transparent";
2007             ColorMenu[MaxNumberPens-1]="Browser...";
2008             ColorMenu[MaxNumberPens]=(const char *) NULL;
2009             /*
2010               Select a pen color from the pop-up menu.
2011             */
2012             pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
2013               (const char **) ColorMenu,command);
2014             if (pen_number < 0)
2015               break;
2016             transparent_pen=pen_number == (MaxNumberPens-2) ? MagickTrue :
2017               MagickFalse;
2018             if (transparent_pen != MagickFalse )
2019               break;
2020             if (pen_number == (MaxNumberPens-1))
2021               {
2022                 static char
2023                   color_name[MagickPathExtent] = "gray";
2024
2025                 /*
2026                   Select a pen color from a dialog.
2027                 */
2028                 resource_info->pen_colors[pen_number]=color_name;
2029                 XColorBrowserWidget(display,windows,"Select",color_name);
2030                 if (*color_name == '\0')
2031                   break;
2032               }
2033             /*
2034               Set pen color.
2035             */
2036             (void) XParseColor(display,windows->map_info->colormap,
2037               resource_info->pen_colors[pen_number],&color);
2038             XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
2039               (unsigned int) MaxColors,&color);
2040             windows->pixel_info->pen_colors[pen_number]=color;
2041             pen_id=(unsigned int) pen_number;
2042             break;
2043           }
2044           case AnnotateBackgroundColorCommand:
2045           {
2046             /*
2047               Initialize menu selections.
2048             */
2049             for (i=0; i < (int) (MaxNumberPens-2); i++)
2050               ColorMenu[i]=resource_info->pen_colors[i];
2051             ColorMenu[MaxNumberPens-2]="transparent";
2052             ColorMenu[MaxNumberPens-1]="Browser...";
2053             ColorMenu[MaxNumberPens]=(const char *) NULL;
2054             /*
2055               Select a pen color from the pop-up menu.
2056             */
2057             pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
2058               (const char **) ColorMenu,command);
2059             if (pen_number < 0)
2060               break;
2061             transparent_box=pen_number == (MaxNumberPens-2) ? MagickTrue :
2062               MagickFalse;
2063             if (transparent_box != MagickFalse )
2064               break;
2065             if (pen_number == (MaxNumberPens-1))
2066               {
2067                 static char
2068                   color_name[MagickPathExtent] = "gray";
2069
2070                 /*
2071                   Select a pen color from a dialog.
2072                 */
2073                 resource_info->pen_colors[pen_number]=color_name;
2074                 XColorBrowserWidget(display,windows,"Select",color_name);
2075                 if (*color_name == '\0')
2076                   break;
2077               }
2078             /*
2079               Set pen color.
2080             */
2081             (void) XParseColor(display,windows->map_info->colormap,
2082               resource_info->pen_colors[pen_number],&color);
2083             XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
2084               (unsigned int) MaxColors,&color);
2085             windows->pixel_info->pen_colors[pen_number]=color;
2086             box_id=(unsigned int) pen_number;
2087             break;
2088           }
2089           case AnnotateRotateCommand:
2090           {
2091             int
2092               entry;
2093
2094             static char
2095               angle[MagickPathExtent] = "30.0";
2096
2097             static const char
2098               *RotateMenu[] =
2099               {
2100                 "-90",
2101                 "-45",
2102                 "-30",
2103                 "0",
2104                 "30",
2105                 "45",
2106                 "90",
2107                 "180",
2108                 "Dialog...",
2109                 (char *) NULL,
2110               };
2111
2112             /*
2113               Select a command from the pop-up menu.
2114             */
2115             entry=XMenuWidget(display,windows,AnnotateMenu[id],RotateMenu,
2116               command);
2117             if (entry < 0)
2118               break;
2119             if (entry != 8)
2120               {
2121                 degrees=StringToDouble(RotateMenu[entry],(char **) NULL);
2122                 break;
2123               }
2124             (void) XDialogWidget(display,windows,"OK","Enter rotation angle:",
2125               angle);
2126             if (*angle == '\0')
2127               break;
2128             degrees=StringToDouble(angle,(char **) NULL);
2129             break;
2130           }
2131           case AnnotateHelpCommand:
2132           {
2133             XTextViewWidget(display,resource_info,windows,MagickFalse,
2134               "Help Viewer - Image Annotation",ImageAnnotateHelp);
2135             break;
2136           }
2137           case AnnotateDismissCommand:
2138           {
2139             /*
2140               Prematurely exit.
2141             */
2142             state|=EscapeState;
2143             state|=ExitState;
2144             break;
2145           }
2146           default:
2147             break;
2148         }
2149         continue;
2150       }
2151     switch (event.type)
2152     {
2153       case ButtonPress:
2154       {
2155         if (event.xbutton.button != Button1)
2156           break;
2157         if (event.xbutton.window != windows->image.id)
2158           break;
2159         /*
2160           Change to text entering mode.
2161         */
2162         x=event.xbutton.x;
2163         y=event.xbutton.y;
2164         state|=ExitState;
2165         break;
2166       }
2167       case ButtonRelease:
2168         break;
2169       case Expose:
2170         break;
2171       case KeyPress:
2172       {
2173         if (event.xkey.window != windows->image.id)
2174           break;
2175         /*
2176           Respond to a user key press.
2177         */
2178         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2179           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2180         switch ((int) key_symbol)
2181         {
2182           case XK_Escape:
2183           case XK_F20:
2184           {
2185             /*
2186               Prematurely exit.
2187             */
2188             state|=EscapeState;
2189             state|=ExitState;
2190             break;
2191           }
2192           case XK_F1:
2193           case XK_Help:
2194           {
2195             XTextViewWidget(display,resource_info,windows,MagickFalse,
2196               "Help Viewer - Image Annotation",ImageAnnotateHelp);
2197             break;
2198           }
2199           default:
2200           {
2201             (void) XBell(display,0);
2202             break;
2203           }
2204         }
2205         break;
2206       }
2207       case MotionNotify:
2208       {
2209         /*
2210           Map and unmap Info widget as cursor crosses its boundaries.
2211         */
2212         x=event.xmotion.x;
2213         y=event.xmotion.y;
2214         if (windows->info.mapped != MagickFalse )
2215           {
2216             if ((x < (int) (windows->info.x+windows->info.width)) &&
2217                 (y < (int) (windows->info.y+windows->info.height)))
2218               (void) XWithdrawWindow(display,windows->info.id,
2219                 windows->info.screen);
2220           }
2221         else
2222           if ((x > (int) (windows->info.x+windows->info.width)) ||
2223               (y > (int) (windows->info.y+windows->info.height)))
2224             (void) XMapWindow(display,windows->info.id);
2225         break;
2226       }
2227       default:
2228         break;
2229     }
2230   } while ((state & ExitState) == 0);
2231   (void) XSelectInput(display,windows->image.id,
2232     windows->image.attributes.event_mask);
2233   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
2234   if ((state & EscapeState) != 0)
2235     return(MagickTrue);
2236   /*
2237     Set font info and check boundary conditions.
2238   */
2239   font_info=XLoadQueryFont(display,resource_info->font_name[font_id]);
2240   if (font_info == (XFontStruct *) NULL)
2241     {
2242       XNoticeWidget(display,windows,"Unable to load font:",
2243         resource_info->font_name[font_id]);
2244       font_info=windows->font_info;
2245     }
2246   if ((x+font_info->max_bounds.width) >= (int) windows->image.width)
2247     x=(int) windows->image.width-font_info->max_bounds.width;
2248   if (y < (int) (font_info->ascent+font_info->descent))
2249     y=(int) font_info->ascent+font_info->descent;
2250   if (((int) font_info->max_bounds.width > (int) windows->image.width) ||
2251       ((font_info->ascent+font_info->descent) >= (int) windows->image.height))
2252     return(MagickFalse);
2253   /*
2254     Initialize annotate structure.
2255   */
2256   annotate_info=(XAnnotateInfo *) AcquireMagickMemory(sizeof(*annotate_info));
2257   if (annotate_info == (XAnnotateInfo *) NULL)
2258     return(MagickFalse);
2259   XGetAnnotateInfo(annotate_info);
2260   annotate_info->x=x;
2261   annotate_info->y=y;
2262   if ((transparent_box == MagickFalse) && (transparent_pen == MagickFalse))
2263     annotate_info->stencil=OpaqueStencil;
2264   else
2265     if (transparent_box == MagickFalse)
2266       annotate_info->stencil=BackgroundStencil;
2267     else
2268       annotate_info->stencil=ForegroundStencil;
2269   annotate_info->height=(unsigned int) font_info->ascent+font_info->descent;
2270   annotate_info->degrees=degrees;
2271   annotate_info->font_info=font_info;
2272   annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2273     windows->image.width/MagickMax((ssize_t) font_info->min_bounds.width,1)+2UL,
2274     sizeof(*annotate_info->text));
2275   if (annotate_info->text == (char *) NULL)
2276     return(MagickFalse);
2277   /*
2278     Create cursor and set graphic context.
2279   */
2280   cursor=XCreateFontCursor(display,XC_pencil);
2281   (void) XCheckDefineCursor(display,windows->image.id,cursor);
2282   annotate_context=windows->image.annotate_context;
2283   (void) XSetFont(display,annotate_context,font_info->fid);
2284   (void) XSetBackground(display,annotate_context,
2285     windows->pixel_info->pen_colors[box_id].pixel);
2286   (void) XSetForeground(display,annotate_context,
2287     windows->pixel_info->pen_colors[pen_id].pixel);
2288   /*
2289     Begin annotating the image with text.
2290   */
2291   (void) CloneString(&windows->command.name,"Text");
2292   windows->command.data=0;
2293   (void) XCommandWidget(display,windows,TextMenu,(XEvent *) NULL);
2294   state=DefaultState;
2295   (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2296   text_event.xexpose.width=(int) font_info->max_bounds.width;
2297   text_event.xexpose.height=font_info->max_bounds.ascent+
2298     font_info->max_bounds.descent;
2299   p=annotate_info->text;
2300   do
2301   {
2302     /*
2303       Display text cursor.
2304     */
2305     *p='\0';
2306     (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2307     /*
2308       Wait for next event.
2309     */
2310     XScreenEvent(display,windows,&event,exception);
2311     if (event.xany.window == windows->command.id)
2312       {
2313         /*
2314           Select a command from the Command widget.
2315         */
2316         (void) XSetBackground(display,annotate_context,
2317           windows->pixel_info->background_color.pixel);
2318         (void) XSetForeground(display,annotate_context,
2319           windows->pixel_info->foreground_color.pixel);
2320         id=XCommandWidget(display,windows,AnnotateMenu,&event);
2321         (void) XSetBackground(display,annotate_context,
2322           windows->pixel_info->pen_colors[box_id].pixel);
2323         (void) XSetForeground(display,annotate_context,
2324           windows->pixel_info->pen_colors[pen_id].pixel);
2325         if (id < 0)
2326           continue;
2327         switch (TextCommands[id])
2328         {
2329           case TextHelpCommand:
2330           {
2331             XTextViewWidget(display,resource_info,windows,MagickFalse,
2332               "Help Viewer - Image Annotation",ImageAnnotateHelp);
2333             (void) XCheckDefineCursor(display,windows->image.id,cursor);
2334             break;
2335           }
2336           case TextApplyCommand:
2337           {
2338             /*
2339               Finished annotating.
2340             */
2341             annotate_info->width=(unsigned int) XTextWidth(font_info,
2342               annotate_info->text,(int) strlen(annotate_info->text));
2343             XRefreshWindow(display,&windows->image,&text_event);
2344             state|=ExitState;
2345             break;
2346           }
2347           default:
2348             break;
2349         }
2350         continue;
2351       }
2352     /*
2353       Erase text cursor.
2354     */
2355     text_event.xexpose.x=x;
2356     text_event.xexpose.y=y-font_info->max_bounds.ascent;
2357     (void) XClearArea(display,windows->image.id,x,text_event.xexpose.y,
2358       (unsigned int) text_event.xexpose.width,(unsigned int)
2359       text_event.xexpose.height,MagickFalse);
2360     XRefreshWindow(display,&windows->image,&text_event);
2361     switch (event.type)
2362     {
2363       case ButtonPress:
2364       {
2365         if (event.xbutton.window != windows->image.id)
2366           break;
2367         if (event.xbutton.button == Button2)
2368           {
2369             /*
2370               Request primary selection.
2371             */
2372             (void) XConvertSelection(display,XA_PRIMARY,XA_STRING,XA_STRING,
2373               windows->image.id,CurrentTime);
2374             break;
2375           }
2376         break;
2377       }
2378       case Expose:
2379       {
2380         if (event.xexpose.count == 0)
2381           {
2382             XAnnotateInfo
2383               *text_info;
2384
2385             /*
2386               Refresh Image window.
2387             */
2388             XRefreshWindow(display,&windows->image,(XEvent *) NULL);
2389             text_info=annotate_info;
2390             while (text_info != (XAnnotateInfo *) NULL)
2391             {
2392               if (annotate_info->stencil == ForegroundStencil)
2393                 (void) XDrawString(display,windows->image.id,annotate_context,
2394                   text_info->x,text_info->y,text_info->text,
2395                   (int) strlen(text_info->text));
2396               else
2397                 (void) XDrawImageString(display,windows->image.id,
2398                   annotate_context,text_info->x,text_info->y,text_info->text,
2399                   (int) strlen(text_info->text));
2400               text_info=text_info->previous;
2401             }
2402             (void) XDrawString(display,windows->image.id,annotate_context,
2403               x,y,"_",1);
2404           }
2405         break;
2406       }
2407       case KeyPress:
2408       {
2409         int
2410           length;
2411
2412         if (event.xkey.window != windows->image.id)
2413           break;
2414         /*
2415           Respond to a user key press.
2416         */
2417         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
2418           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2419         *(command+length)='\0';
2420         if (((event.xkey.state & ControlMask) != 0) ||
2421             ((event.xkey.state & Mod1Mask) != 0))
2422           state|=ModifierState;
2423         if ((state & ModifierState) != 0)
2424           switch ((int) key_symbol)
2425           {
2426             case XK_u:
2427             case XK_U:
2428             {
2429               key_symbol=DeleteCommand;
2430               break;
2431             }
2432             default:
2433               break;
2434           }
2435         switch ((int) key_symbol)
2436         {
2437           case XK_BackSpace:
2438           {
2439             /*
2440               Erase one character.
2441             */
2442             if (p == annotate_info->text)
2443               {
2444                 if (annotate_info->previous == (XAnnotateInfo *) NULL)
2445                   break;
2446                 else
2447                   {
2448                     /*
2449                       Go to end of the previous line of text.
2450                     */
2451                     annotate_info=annotate_info->previous;
2452                     p=annotate_info->text;
2453                     x=annotate_info->x+annotate_info->width;
2454                     y=annotate_info->y;
2455                     if (annotate_info->width != 0)
2456                       p+=strlen(annotate_info->text);
2457                     break;
2458                   }
2459               }
2460             p--;
2461             x-=XTextWidth(font_info,p,1);
2462             text_event.xexpose.x=x;
2463             text_event.xexpose.y=y-font_info->max_bounds.ascent;
2464             XRefreshWindow(display,&windows->image,&text_event);
2465             break;
2466           }
2467           case XK_bracketleft:
2468           {
2469             key_symbol=XK_Escape;
2470             break;
2471           }
2472           case DeleteCommand:
2473           {
2474             /*
2475               Erase the entire line of text.
2476             */
2477             while (p != annotate_info->text)
2478             {
2479               p--;
2480               x-=XTextWidth(font_info,p,1);
2481               text_event.xexpose.x=x;
2482               XRefreshWindow(display,&windows->image,&text_event);
2483             }
2484             break;
2485           }
2486           case XK_Escape:
2487           case XK_F20:
2488           {
2489             /*
2490               Finished annotating.
2491             */
2492             annotate_info->width=(unsigned int) XTextWidth(font_info,
2493               annotate_info->text,(int) strlen(annotate_info->text));
2494             XRefreshWindow(display,&windows->image,&text_event);
2495             state|=ExitState;
2496             break;
2497           }
2498           default:
2499           {
2500             /*
2501               Draw a single character on the Image window.
2502             */
2503             if ((state & ModifierState) != 0)
2504               break;
2505             if (*command == '\0')
2506               break;
2507             *p=(*command);
2508             if (annotate_info->stencil == ForegroundStencil)
2509               (void) XDrawString(display,windows->image.id,annotate_context,
2510                 x,y,p,1);
2511             else
2512               (void) XDrawImageString(display,windows->image.id,
2513                 annotate_context,x,y,p,1);
2514             x+=XTextWidth(font_info,p,1);
2515             p++;
2516             if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2517               break;
2518           }
2519           case XK_Return:
2520           case XK_KP_Enter:
2521           {
2522             /*
2523               Advance to the next line of text.
2524             */
2525             *p='\0';
2526             annotate_info->width=(unsigned int) XTextWidth(font_info,
2527               annotate_info->text,(int) strlen(annotate_info->text));
2528             if (annotate_info->next != (XAnnotateInfo *) NULL)
2529               {
2530                 /*
2531                   Line of text already exists.
2532                 */
2533                 annotate_info=annotate_info->next;
2534                 x=annotate_info->x;
2535                 y=annotate_info->y;
2536                 p=annotate_info->text;
2537                 break;
2538               }
2539             annotate_info->next=(XAnnotateInfo *) AcquireMagickMemory(
2540               sizeof(*annotate_info->next));
2541             if (annotate_info->next == (XAnnotateInfo *) NULL)
2542               return(MagickFalse);
2543             *annotate_info->next=(*annotate_info);
2544             annotate_info->next->previous=annotate_info;
2545             annotate_info=annotate_info->next;
2546             annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2547               windows->image.width/MagickMax((ssize_t)
2548               font_info->min_bounds.width,1)+2UL,sizeof(*annotate_info->text));
2549             if (annotate_info->text == (char *) NULL)
2550               return(MagickFalse);
2551             annotate_info->y+=annotate_info->height;
2552             if (annotate_info->y > (int) windows->image.height)
2553               annotate_info->y=(int) annotate_info->height;
2554             annotate_info->next=(XAnnotateInfo *) NULL;
2555             x=annotate_info->x;
2556             y=annotate_info->y;
2557             p=annotate_info->text;
2558             break;
2559           }
2560         }
2561         break;
2562       }
2563       case KeyRelease:
2564       {
2565         /*
2566           Respond to a user key release.
2567         */
2568         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2569           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2570         state&=(~ModifierState);
2571         break;
2572       }
2573       case SelectionNotify:
2574       {
2575         Atom
2576           type;
2577
2578         int
2579           format;
2580
2581         unsigned char
2582           *data;
2583
2584         unsigned long
2585           after,
2586           length;
2587
2588         /*
2589           Obtain response from primary selection.
2590         */
2591         if (event.xselection.property == (Atom) None)
2592           break;
2593         status=XGetWindowProperty(display,event.xselection.requestor,
2594           event.xselection.property,0L,(long) MagickPathExtent,True,XA_STRING,
2595           &type,&format,&length,&after,&data);
2596         if ((status != Success) || (type != XA_STRING) || (format == 32) ||
2597             (length == 0))
2598           break;
2599         /*
2600           Annotate Image window with primary selection.
2601         */
2602         for (i=0; i < (ssize_t) length; i++)
2603         {
2604           if ((char) data[i] != '\n')
2605             {
2606               /*
2607                 Draw a single character on the Image window.
2608               */
2609               *p=(char) data[i];
2610               (void) XDrawString(display,windows->image.id,annotate_context,
2611                 x,y,p,1);
2612               x+=XTextWidth(font_info,p,1);
2613               p++;
2614               if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2615                 continue;
2616             }
2617           /*
2618             Advance to the next line of text.
2619           */
2620           *p='\0';
2621           annotate_info->width=(unsigned int) XTextWidth(font_info,
2622             annotate_info->text,(int) strlen(annotate_info->text));
2623           if (annotate_info->next != (XAnnotateInfo *) NULL)
2624             {
2625               /*
2626                 Line of text already exists.
2627               */
2628               annotate_info=annotate_info->next;
2629               x=annotate_info->x;
2630               y=annotate_info->y;
2631               p=annotate_info->text;
2632               continue;
2633             }
2634           annotate_info->next=(XAnnotateInfo *) AcquireMagickMemory(
2635             sizeof(*annotate_info->next));
2636           if (annotate_info->next == (XAnnotateInfo *) NULL)
2637             return(MagickFalse);
2638           *annotate_info->next=(*annotate_info);
2639           annotate_info->next->previous=annotate_info;
2640           annotate_info=annotate_info->next;
2641           annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2642             windows->image.width/MagickMax((ssize_t)
2643             font_info->min_bounds.width,1)+2UL,sizeof(*annotate_info->text));
2644           if (annotate_info->text == (char *) NULL)
2645             return(MagickFalse);
2646           annotate_info->y+=annotate_info->height;
2647           if (annotate_info->y > (int) windows->image.height)
2648             annotate_info->y=(int) annotate_info->height;
2649           annotate_info->next=(XAnnotateInfo *) NULL;
2650           x=annotate_info->x;
2651           y=annotate_info->y;
2652           p=annotate_info->text;
2653         }
2654         (void) XFree((void *) data);
2655         break;
2656       }
2657       default:
2658         break;
2659     }
2660   } while ((state & ExitState) == 0);
2661   (void) XFreeCursor(display,cursor);
2662   /*
2663     Annotation is relative to image configuration.
2664   */
2665   width=(unsigned int) image->columns;
2666   height=(unsigned int) image->rows;
2667   x=0;
2668   y=0;
2669   if (windows->image.crop_geometry != (char *) NULL)
2670     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
2671   /*
2672     Initialize annotated image.
2673   */
2674   XSetCursorState(display,windows,MagickTrue);
2675   XCheckRefreshWindows(display,windows);
2676   while (annotate_info != (XAnnotateInfo *) NULL)
2677   {
2678     if (annotate_info->width == 0)
2679       {
2680         /*
2681           No text on this line--  go to the next line of text.
2682         */
2683         previous_info=annotate_info->previous;
2684         annotate_info->text=(char *)
2685           RelinquishMagickMemory(annotate_info->text);
2686         annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2687         annotate_info=previous_info;
2688         continue;
2689       }
2690     /*
2691       Determine pixel index for box and pen color.
2692     */
2693     windows->pixel_info->box_color=windows->pixel_info->pen_colors[box_id];
2694     if (windows->pixel_info->colors != 0)
2695       for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
2696         if (windows->pixel_info->pixels[i] ==
2697             windows->pixel_info->pen_colors[box_id].pixel)
2698           {
2699             windows->pixel_info->box_index=(unsigned short) i;
2700             break;
2701           }
2702     windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
2703     if (windows->pixel_info->colors != 0)
2704       for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
2705         if (windows->pixel_info->pixels[i] ==
2706             windows->pixel_info->pen_colors[pen_id].pixel)
2707           {
2708             windows->pixel_info->pen_index=(unsigned short) i;
2709             break;
2710           }
2711     /*
2712       Define the annotate geometry string.
2713     */
2714     annotate_info->x=(int)
2715       width*(annotate_info->x+windows->image.x)/windows->image.ximage->width;
2716     annotate_info->y=(int) height*(annotate_info->y-font_info->ascent+
2717       windows->image.y)/windows->image.ximage->height;
2718     (void) FormatLocaleString(annotate_info->geometry,MagickPathExtent,
2719       "%ux%u%+d%+d",width*annotate_info->width/windows->image.ximage->width,
2720       height*annotate_info->height/windows->image.ximage->height,
2721       annotate_info->x+x,annotate_info->y+y);
2722     /*
2723       Annotate image with text.
2724     */
2725     status=XAnnotateImage(display,windows->pixel_info,annotate_info,image,
2726       exception);
2727     if (status == 0)
2728       return(MagickFalse);
2729     /*
2730       Free up memory.
2731     */
2732     previous_info=annotate_info->previous;
2733     annotate_info->text=DestroyString(annotate_info->text);
2734     annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2735     annotate_info=previous_info;
2736   }
2737   (void) XSetForeground(display,annotate_context,
2738     windows->pixel_info->foreground_color.pixel);
2739   (void) XSetBackground(display,annotate_context,
2740     windows->pixel_info->background_color.pixel);
2741   (void) XSetFont(display,annotate_context,windows->font_info->fid);
2742   XSetCursorState(display,windows,MagickFalse);
2743   (void) XFreeFont(display,font_info);
2744   /*
2745     Update image configuration.
2746   */
2747   XConfigureImageColormap(display,resource_info,windows,image,exception);
2748   (void) XConfigureImage(display,resource_info,windows,image,exception);
2749   return(MagickTrue);
2750 }
2751 \f
2752 /*
2753 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2754 %                                                                             %
2755 %                                                                             %
2756 %                                                                             %
2757 +   X B a c k g r o u n d I m a g e                                           %
2758 %                                                                             %
2759 %                                                                             %
2760 %                                                                             %
2761 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2762 %
2763 %  XBackgroundImage() displays the image in the background of a window.
2764 %
2765 %  The format of the XBackgroundImage method is:
2766 %
2767 %      MagickBooleanType XBackgroundImage(Display *display,
2768 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
2769 %        ExceptionInfo *exception)
2770 %
2771 %  A description of each parameter follows:
2772 %
2773 %    o display: Specifies a connection to an X server; returned from
2774 %      XOpenDisplay.
2775 %
2776 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2777 %
2778 %    o windows: Specifies a pointer to a XWindows structure.
2779 %
2780 %    o image: the image.
2781 %
2782 %    o exception: return any errors or warnings in this structure.
2783 %
2784 */
2785 static MagickBooleanType XBackgroundImage(Display *display,
2786   XResourceInfo *resource_info,XWindows *windows,Image **image,
2787   ExceptionInfo *exception)
2788 {
2789 #define BackgroundImageTag  "Background/Image"
2790
2791   int
2792     status;
2793
2794   static char
2795     window_id[MagickPathExtent] = "root";
2796
2797   XResourceInfo
2798     background_resources;
2799
2800   /*
2801     Put image in background.
2802   */
2803   status=XDialogWidget(display,windows,"Background",
2804     "Enter window id (id 0x00 selects window with pointer):",window_id);
2805   if (*window_id == '\0')
2806     return(MagickFalse);
2807   (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
2808     exception);
2809   XInfoWidget(display,windows,BackgroundImageTag);
2810   XSetCursorState(display,windows,MagickTrue);
2811   XCheckRefreshWindows(display,windows);
2812   background_resources=(*resource_info);
2813   background_resources.window_id=window_id;
2814   background_resources.backdrop=status != 0 ? MagickTrue : MagickFalse;
2815   status=XDisplayBackgroundImage(display,&background_resources,*image,
2816     exception);
2817   if (status != MagickFalse)
2818     XClientMessage(display,windows->image.id,windows->im_protocols,
2819       windows->im_retain_colors,CurrentTime);
2820   XSetCursorState(display,windows,MagickFalse);
2821   (void) XMagickCommand(display,resource_info,windows,UndoCommand,image,
2822     exception);
2823   return(MagickTrue);
2824 }
2825 \f
2826 /*
2827 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2828 %                                                                             %
2829 %                                                                             %
2830 %                                                                             %
2831 +   X C h o p I m a g e                                                       %
2832 %                                                                             %
2833 %                                                                             %
2834 %                                                                             %
2835 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2836 %
2837 %  XChopImage() chops the X image.
2838 %
2839 %  The format of the XChopImage method is:
2840 %
2841 %    MagickBooleanType XChopImage(Display *display,XResourceInfo *resource_info,
2842 %      XWindows *windows,Image **image,ExceptionInfo *exception)
2843 %
2844 %  A description of each parameter follows:
2845 %
2846 %    o display: Specifies a connection to an X server; returned from
2847 %      XOpenDisplay.
2848 %
2849 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2850 %
2851 %    o windows: Specifies a pointer to a XWindows structure.
2852 %
2853 %    o image: the image.
2854 %
2855 %    o exception: return any errors or warnings in this structure.
2856 %
2857 */
2858 static MagickBooleanType XChopImage(Display *display,
2859   XResourceInfo *resource_info,XWindows *windows,Image **image,
2860   ExceptionInfo *exception)
2861 {
2862   static const char
2863     *ChopMenu[] =
2864     {
2865       "Direction",
2866       "Help",
2867       "Dismiss",
2868       (char *) NULL
2869     };
2870
2871   static ModeType
2872     direction = HorizontalChopCommand;
2873
2874   static const ModeType
2875     ChopCommands[] =
2876     {
2877       ChopDirectionCommand,
2878       ChopHelpCommand,
2879       ChopDismissCommand
2880     },
2881     DirectionCommands[] =
2882     {
2883       HorizontalChopCommand,
2884       VerticalChopCommand
2885     };
2886
2887   char
2888     text[MagickPathExtent];
2889
2890   Image
2891     *chop_image;
2892
2893   int
2894     id,
2895     x,
2896     y;
2897
2898   double
2899     scale_factor;
2900
2901   RectangleInfo
2902     chop_info;
2903
2904   unsigned int
2905     distance,
2906     height,
2907     width;
2908
2909   size_t
2910     state;
2911
2912   XEvent
2913     event;
2914
2915   XSegment
2916     segment_info;
2917
2918   /*
2919     Map Command widget.
2920   */
2921   (void) CloneString(&windows->command.name,"Chop");
2922   windows->command.data=1;
2923   (void) XCommandWidget(display,windows,ChopMenu,(XEvent *) NULL);
2924   (void) XMapRaised(display,windows->command.id);
2925   XClientMessage(display,windows->image.id,windows->im_protocols,
2926     windows->im_update_widget,CurrentTime);
2927   /*
2928     Track pointer until button 1 is pressed.
2929   */
2930   XQueryPosition(display,windows->image.id,&x,&y);
2931   (void) XSelectInput(display,windows->image.id,
2932     windows->image.attributes.event_mask | PointerMotionMask);
2933   state=DefaultState;
2934   (void) memset(&segment_info,0,sizeof(segment_info));
2935   do
2936   {
2937     if (windows->info.mapped != MagickFalse )
2938       {
2939         /*
2940           Display pointer position.
2941         */
2942         (void) FormatLocaleString(text,MagickPathExtent," %+d%+d ",
2943           x+windows->image.x,y+windows->image.y);
2944         XInfoWidget(display,windows,text);
2945       }
2946     /*
2947       Wait for next event.
2948     */
2949     XScreenEvent(display,windows,&event,exception);
2950     if (event.xany.window == windows->command.id)
2951       {
2952         /*
2953           Select a command from the Command widget.
2954         */
2955         id=XCommandWidget(display,windows,ChopMenu,&event);
2956         if (id < 0)
2957           continue;
2958         switch (ChopCommands[id])
2959         {
2960           case ChopDirectionCommand:
2961           {
2962             char
2963               command[MagickPathExtent];
2964
2965             static const char
2966               *Directions[] =
2967               {
2968                 "horizontal",
2969                 "vertical",
2970                 (char *) NULL,
2971               };
2972
2973             /*
2974               Select a command from the pop-up menu.
2975             */
2976             id=XMenuWidget(display,windows,ChopMenu[id],Directions,command);
2977             if (id >= 0)
2978               direction=DirectionCommands[id];
2979             break;
2980           }
2981           case ChopHelpCommand:
2982           {
2983             XTextViewWidget(display,resource_info,windows,MagickFalse,
2984               "Help Viewer - Image Chop",ImageChopHelp);
2985             break;
2986           }
2987           case ChopDismissCommand:
2988           {
2989             /*
2990               Prematurely exit.
2991             */
2992             state|=EscapeState;
2993             state|=ExitState;
2994             break;
2995           }
2996           default:
2997             break;
2998         }
2999         continue;
3000       }
3001     switch (event.type)
3002     {
3003       case ButtonPress:
3004       {
3005         if (event.xbutton.button != Button1)
3006           break;
3007         if (event.xbutton.window != windows->image.id)
3008           break;
3009         /*
3010           User has committed to start point of chopping line.
3011         */
3012         segment_info.x1=(short int) event.xbutton.x;
3013         segment_info.x2=(short int) event.xbutton.x;
3014         segment_info.y1=(short int) event.xbutton.y;
3015         segment_info.y2=(short int) event.xbutton.y;
3016         state|=ExitState;
3017         break;
3018       }
3019       case ButtonRelease:
3020         break;
3021       case Expose:
3022         break;
3023       case KeyPress:
3024       {
3025         char
3026           command[MagickPathExtent];
3027
3028         KeySym
3029           key_symbol;
3030
3031         if (event.xkey.window != windows->image.id)
3032           break;
3033         /*
3034           Respond to a user key press.
3035         */
3036         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3037           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3038         switch ((int) key_symbol)
3039         {
3040           case XK_Escape:
3041           case XK_F20:
3042           {
3043             /*
3044               Prematurely exit.
3045             */
3046             state|=EscapeState;
3047             state|=ExitState;
3048             break;
3049           }
3050           case XK_F1:
3051           case XK_Help:
3052           {
3053             (void) XSetFunction(display,windows->image.highlight_context,
3054               GXcopy);
3055             XTextViewWidget(display,resource_info,windows,MagickFalse,
3056               "Help Viewer - Image Chop",ImageChopHelp);
3057             (void) XSetFunction(display,windows->image.highlight_context,
3058               GXinvert);
3059             break;
3060           }
3061           default:
3062           {
3063             (void) XBell(display,0);
3064             break;
3065           }
3066         }
3067         break;
3068       }
3069       case MotionNotify:
3070       {
3071         /*
3072           Map and unmap Info widget as text cursor crosses its boundaries.
3073         */
3074         x=event.xmotion.x;
3075         y=event.xmotion.y;
3076         if (windows->info.mapped != MagickFalse )
3077           {
3078             if ((x < (int) (windows->info.x+windows->info.width)) &&
3079                 (y < (int) (windows->info.y+windows->info.height)))
3080               (void) XWithdrawWindow(display,windows->info.id,
3081                 windows->info.screen);
3082           }
3083         else
3084           if ((x > (int) (windows->info.x+windows->info.width)) ||
3085               (y > (int) (windows->info.y+windows->info.height)))
3086             (void) XMapWindow(display,windows->info.id);
3087       }
3088     }
3089   } while ((state & ExitState) == 0);
3090   (void) XSelectInput(display,windows->image.id,
3091     windows->image.attributes.event_mask);
3092   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3093   if ((state & EscapeState) != 0)
3094     return(MagickTrue);
3095   /*
3096     Draw line as pointer moves until the mouse button is released.
3097   */
3098   chop_info.width=0;
3099   chop_info.height=0;
3100   chop_info.x=0;
3101   chop_info.y=0;
3102   distance=0;
3103   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
3104   state=DefaultState;
3105   do
3106   {
3107     if (distance > 9)
3108       {
3109         /*
3110           Display info and draw chopping line.
3111         */
3112         if (windows->info.mapped == MagickFalse)
3113           (void) XMapWindow(display,windows->info.id);
3114         (void) FormatLocaleString(text,MagickPathExtent,
3115           " %.20gx%.20g%+.20g%+.20g",(double) chop_info.width,(double)
3116           chop_info.height,(double) chop_info.x,(double) chop_info.y);
3117         XInfoWidget(display,windows,text);
3118         XHighlightLine(display,windows->image.id,
3119           windows->image.highlight_context,&segment_info);
3120       }
3121     else
3122       if (windows->info.mapped != MagickFalse )
3123         (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3124     /*
3125       Wait for next event.
3126     */
3127     XScreenEvent(display,windows,&event,exception);
3128     if (distance > 9)
3129       XHighlightLine(display,windows->image.id,
3130         windows->image.highlight_context,&segment_info);
3131     switch (event.type)
3132     {
3133       case ButtonPress:
3134       {
3135         segment_info.x2=(short int) event.xmotion.x;
3136         segment_info.y2=(short int) event.xmotion.y;
3137         break;
3138       }
3139       case ButtonRelease:
3140       {
3141         /*
3142           User has committed to chopping line.
3143         */
3144         segment_info.x2=(short int) event.xbutton.x;
3145         segment_info.y2=(short int) event.xbutton.y;
3146         state|=ExitState;
3147         break;
3148       }
3149       case Expose:
3150         break;
3151       case MotionNotify:
3152       {
3153         segment_info.x2=(short int) event.xmotion.x;
3154         segment_info.y2=(short int) event.xmotion.y;
3155       }
3156       default:
3157         break;
3158     }
3159     /*
3160       Check boundary conditions.
3161     */
3162     if (segment_info.x2 < 0)
3163       segment_info.x2=0;
3164     else
3165       if (segment_info.x2 > windows->image.ximage->width)
3166         segment_info.x2=windows->image.ximage->width;
3167     if (segment_info.y2 < 0)
3168       segment_info.y2=0;
3169     else
3170       if (segment_info.y2 > windows->image.ximage->height)
3171         segment_info.y2=windows->image.ximage->height;
3172     distance=(unsigned int)
3173       (((segment_info.x2-segment_info.x1)*(segment_info.x2-segment_info.x1))+
3174        ((segment_info.y2-segment_info.y1)*(segment_info.y2-segment_info.y1)));
3175     /*
3176       Compute chopping geometry.
3177     */
3178     if (direction == HorizontalChopCommand)
3179       {
3180         chop_info.width=(size_t) (segment_info.x2-segment_info.x1+1);
3181         chop_info.x=(ssize_t) windows->image.x+segment_info.x1;
3182         chop_info.height=0;
3183         chop_info.y=0;
3184         if (segment_info.x1 > (int) segment_info.x2)
3185           {
3186             chop_info.width=(size_t) (segment_info.x1-segment_info.x2+1);
3187             chop_info.x=(ssize_t) windows->image.x+segment_info.x2;
3188           }
3189       }
3190     else
3191       {
3192         chop_info.width=0;
3193         chop_info.height=(size_t) (segment_info.y2-segment_info.y1+1);
3194         chop_info.x=0;
3195         chop_info.y=(ssize_t) windows->image.y+segment_info.y1;
3196         if (segment_info.y1 > segment_info.y2)
3197           {
3198             chop_info.height=(size_t) (segment_info.y1-segment_info.y2+1);
3199             chop_info.y=(ssize_t) windows->image.y+segment_info.y2;
3200           }
3201       }
3202   } while ((state & ExitState) == 0);
3203   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
3204   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3205   if (distance <= 9)
3206     return(MagickTrue);
3207   /*
3208     Image chopping is relative to image configuration.
3209   */
3210   (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
3211     exception);
3212   XSetCursorState(display,windows,MagickTrue);
3213   XCheckRefreshWindows(display,windows);
3214   windows->image.window_changes.width=windows->image.ximage->width-
3215     (unsigned int) chop_info.width;
3216   windows->image.window_changes.height=windows->image.ximage->height-
3217     (unsigned int) chop_info.height;
3218   width=(unsigned int) (*image)->columns;
3219   height=(unsigned int) (*image)->rows;
3220   x=0;
3221   y=0;
3222   if (windows->image.crop_geometry != (char *) NULL)
3223     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
3224   scale_factor=(double) width/windows->image.ximage->width;
3225   chop_info.x+=x;
3226   chop_info.x=(ssize_t) (scale_factor*chop_info.x+0.5);
3227   chop_info.width=(unsigned int) (scale_factor*chop_info.width+0.5);
3228   scale_factor=(double) height/windows->image.ximage->height;
3229   chop_info.y+=y;
3230   chop_info.y=(ssize_t) (scale_factor*chop_info.y+0.5);
3231   chop_info.height=(unsigned int) (scale_factor*chop_info.height+0.5);
3232   /*
3233     Chop image.
3234   */
3235   chop_image=ChopImage(*image,&chop_info,exception);
3236   XSetCursorState(display,windows,MagickFalse);
3237   if (chop_image == (Image *) NULL)
3238     return(MagickFalse);
3239   *image=DestroyImage(*image);
3240   *image=chop_image;
3241   /*
3242     Update image configuration.
3243   */
3244   XConfigureImageColormap(display,resource_info,windows,*image,exception);
3245   (void) XConfigureImage(display,resource_info,windows,*image,exception);
3246   return(MagickTrue);
3247 }
3248 \f
3249 /*
3250 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3251 %                                                                             %
3252 %                                                                             %
3253 %                                                                             %
3254 +   X C o l o r E d i t I m a g e                                             %
3255 %                                                                             %
3256 %                                                                             %
3257 %                                                                             %
3258 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3259 %
3260 %  XColorEditImage() allows the user to interactively change the color of one
3261 %  pixel for a DirectColor image or one colormap entry for a PseudoClass image.
3262 %
3263 %  The format of the XColorEditImage method is:
3264 %
3265 %      MagickBooleanType XColorEditImage(Display *display,
3266 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
3267 %          ExceptionInfo *exception)
3268 %
3269 %  A description of each parameter follows:
3270 %
3271 %    o display: Specifies a connection to an X server;  returned from
3272 %      XOpenDisplay.
3273 %
3274 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3275 %
3276 %    o windows: Specifies a pointer to a XWindows structure.
3277 %
3278 %    o image: the image; returned from ReadImage.
3279 %
3280 %    o exception: return any errors or warnings in this structure.
3281 %
3282 */
3283 static MagickBooleanType XColorEditImage(Display *display,
3284   XResourceInfo *resource_info,XWindows *windows,Image **image,
3285   ExceptionInfo *exception)
3286 {
3287   static const char
3288     *ColorEditMenu[] =
3289     {
3290       "Method",
3291       "Pixel Color",
3292       "Border Color",
3293       "Fuzz",
3294       "Undo",
3295       "Help",
3296       "Dismiss",
3297       (char *) NULL
3298     };
3299
3300   static const ModeType
3301     ColorEditCommands[] =
3302     {
3303       ColorEditMethodCommand,
3304       ColorEditColorCommand,
3305       ColorEditBorderCommand,
3306       ColorEditFuzzCommand,
3307       ColorEditUndoCommand,
3308       ColorEditHelpCommand,
3309       ColorEditDismissCommand
3310     };
3311
3312   static PaintMethod
3313     method = PointMethod;
3314
3315   static unsigned int
3316     pen_id = 0;
3317
3318   static XColor
3319     border_color = { 0, 0, 0, 0, 0, 0 };
3320
3321   char
3322     command[MagickPathExtent],
3323     text[MagickPathExtent];
3324
3325   Cursor
3326     cursor;
3327
3328   int
3329     entry,
3330     id,
3331     x,
3332     x_offset,
3333     y,
3334     y_offset;
3335
3336   register Quantum
3337     *q;
3338
3339   register ssize_t
3340     i;
3341
3342   unsigned int
3343     height,
3344     width;
3345
3346   size_t
3347     state;
3348
3349   XColor
3350     color;
3351
3352   XEvent
3353     event;
3354
3355   /*
3356     Map Command widget.
3357   */
3358   (void) CloneString(&windows->command.name,"Color Edit");
3359   windows->command.data=4;
3360   (void) XCommandWidget(display,windows,ColorEditMenu,(XEvent *) NULL);
3361   (void) XMapRaised(display,windows->command.id);
3362   XClientMessage(display,windows->image.id,windows->im_protocols,
3363     windows->im_update_widget,CurrentTime);
3364   /*
3365     Make cursor.
3366   */
3367   cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
3368     resource_info->background_color,resource_info->foreground_color);
3369   (void) XCheckDefineCursor(display,windows->image.id,cursor);
3370   /*
3371     Track pointer until button 1 is pressed.
3372   */
3373   XQueryPosition(display,windows->image.id,&x,&y);
3374   (void) XSelectInput(display,windows->image.id,
3375     windows->image.attributes.event_mask | PointerMotionMask);
3376   state=DefaultState;
3377   do
3378   {
3379     if (windows->info.mapped != MagickFalse )
3380       {
3381         /*
3382           Display pointer position.
3383         */
3384         (void) FormatLocaleString(text,MagickPathExtent," %+d%+d ",
3385           x+windows->image.x,y+windows->image.y);
3386         XInfoWidget(display,windows,text);
3387       }
3388     /*
3389       Wait for next event.
3390     */
3391     XScreenEvent(display,windows,&event,exception);
3392     if (event.xany.window == windows->command.id)
3393       {
3394         /*
3395           Select a command from the Command widget.
3396         */
3397         id=XCommandWidget(display,windows,ColorEditMenu,&event);
3398         if (id < 0)
3399           {
3400             (void) XCheckDefineCursor(display,windows->image.id,cursor);
3401             continue;
3402           }
3403         switch (ColorEditCommands[id])
3404         {
3405           case ColorEditMethodCommand:
3406           {
3407             char
3408               **methods;
3409
3410             /*
3411               Select a method from the pop-up menu.
3412             */
3413             methods=(char **) GetCommandOptions(MagickMethodOptions);
3414             if (methods == (char **) NULL)
3415               break;
3416             entry=XMenuWidget(display,windows,ColorEditMenu[id],
3417               (const char **) methods,command);
3418             if (entry >= 0)
3419               method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
3420                 MagickFalse,methods[entry]);
3421             methods=DestroyStringList(methods);
3422             break;
3423           }
3424           case ColorEditColorCommand:
3425           {
3426             const char
3427               *ColorMenu[MaxNumberPens];
3428
3429             int
3430               pen_number;
3431
3432             /*
3433               Initialize menu selections.
3434             */
3435             for (i=0; i < (int) (MaxNumberPens-2); i++)
3436               ColorMenu[i]=resource_info->pen_colors[i];
3437             ColorMenu[MaxNumberPens-2]="Browser...";
3438             ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3439             /*
3440               Select a pen color from the pop-up menu.
3441             */
3442             pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3443               (const char **) ColorMenu,command);
3444             if (pen_number < 0)
3445               break;
3446             if (pen_number == (MaxNumberPens-2))
3447               {
3448                 static char
3449                   color_name[MagickPathExtent] = "gray";
3450
3451                 /*
3452                   Select a pen color from a dialog.
3453                 */
3454                 resource_info->pen_colors[pen_number]=color_name;
3455                 XColorBrowserWidget(display,windows,"Select",color_name);
3456                 if (*color_name == '\0')
3457                   break;
3458               }
3459             /*
3460               Set pen color.
3461             */
3462             (void) XParseColor(display,windows->map_info->colormap,
3463               resource_info->pen_colors[pen_number],&color);
3464             XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
3465               (unsigned int) MaxColors,&color);
3466             windows->pixel_info->pen_colors[pen_number]=color;
3467             pen_id=(unsigned int) pen_number;
3468             break;
3469           }
3470           case ColorEditBorderCommand:
3471           {
3472             const char
3473               *ColorMenu[MaxNumberPens];
3474
3475             int
3476               pen_number;
3477
3478             /*
3479               Initialize menu selections.
3480             */
3481             for (i=0; i < (int) (MaxNumberPens-2); i++)
3482               ColorMenu[i]=resource_info->pen_colors[i];
3483             ColorMenu[MaxNumberPens-2]="Browser...";
3484             ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3485             /*
3486               Select a pen color from the pop-up menu.
3487             */
3488             pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3489               (const char **) ColorMenu,command);
3490             if (pen_number < 0)
3491               break;
3492             if (pen_number == (MaxNumberPens-2))
3493               {
3494                 static char
3495                   color_name[MagickPathExtent] = "gray";
3496
3497                 /*
3498                   Select a pen color from a dialog.
3499                 */
3500                 resource_info->pen_colors[pen_number]=color_name;
3501                 XColorBrowserWidget(display,windows,"Select",color_name);
3502                 if (*color_name == '\0')
3503                   break;
3504               }
3505             /*
3506               Set border color.
3507             */
3508             (void) XParseColor(display,windows->map_info->colormap,
3509               resource_info->pen_colors[pen_number],&border_color);
3510             break;
3511           }
3512           case ColorEditFuzzCommand:
3513           {
3514             static char
3515               fuzz[MagickPathExtent];
3516
3517             static const char
3518               *FuzzMenu[] =
3519               {
3520                 "0%",
3521                 "2%",
3522                 "5%",
3523                 "10%",
3524                 "15%",
3525                 "Dialog...",
3526                 (char *) NULL,
3527               };
3528
3529             /*
3530               Select a command from the pop-up menu.
3531             */
3532             entry=XMenuWidget(display,windows,ColorEditMenu[id],FuzzMenu,
3533               command);
3534             if (entry < 0)
3535               break;
3536             if (entry != 5)
3537               {
3538                 (*image)->fuzz=StringToDoubleInterval(FuzzMenu[entry],(double)
3539                   QuantumRange+1.0);
3540                 break;
3541               }
3542             (void) (void) CopyMagickString(fuzz,"20%",MagickPathExtent);
3543             (void) XDialogWidget(display,windows,"Ok",
3544               "Enter fuzz factor (0.0 - 99.9%):",fuzz);
3545             if (*fuzz == '\0')
3546               break;
3547             (void) ConcatenateMagickString(fuzz,"%",MagickPathExtent);
3548             (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+
3549               1.0);
3550             break;
3551           }
3552           case ColorEditUndoCommand:
3553           {
3554             (void) XMagickCommand(display,resource_info,windows,UndoCommand,
3555               image,exception);
3556             break;
3557           }
3558           case ColorEditHelpCommand:
3559           default:
3560           {
3561             XTextViewWidget(display,resource_info,windows,MagickFalse,
3562               "Help Viewer - Image Annotation",ImageColorEditHelp);
3563             break;
3564           }
3565           case ColorEditDismissCommand:
3566           {
3567             /*
3568               Prematurely exit.
3569             */
3570             state|=EscapeState;
3571             state|=ExitState;
3572             break;
3573           }
3574         }
3575         (void) XCheckDefineCursor(display,windows->image.id,cursor);
3576         continue;
3577       }
3578     switch (event.type)
3579     {
3580       case ButtonPress:
3581       {
3582         if (event.xbutton.button != Button1)
3583           break;
3584         if ((event.xbutton.window != windows->image.id) &&
3585             (event.xbutton.window != windows->magnify.id))
3586           break;
3587         /*
3588           exit loop.
3589         */
3590         x=event.xbutton.x;
3591         y=event.xbutton.y;
3592         (void) XMagickCommand(display,resource_info,windows,
3593           SaveToUndoBufferCommand,image,exception);
3594         state|=UpdateConfigurationState;
3595         break;
3596       }
3597       case ButtonRelease:
3598       {
3599         if (event.xbutton.button != Button1)
3600           break;
3601         if ((event.xbutton.window != windows->image.id) &&
3602             (event.xbutton.window != windows->magnify.id))
3603           break;
3604         /*
3605           Update colormap information.
3606         */
3607         x=event.xbutton.x;
3608         y=event.xbutton.y;
3609         XConfigureImageColormap(display,resource_info,windows,*image,exception);
3610         (void) XConfigureImage(display,resource_info,windows,*image,exception);
3611         XInfoWidget(display,windows,text);
3612         (void) XCheckDefineCursor(display,windows->image.id,cursor);
3613         state&=(~UpdateConfigurationState);
3614         break;
3615       }
3616       case Expose:
3617         break;
3618       case KeyPress:
3619       {
3620         KeySym
3621           key_symbol;
3622
3623         if (event.xkey.window == windows->magnify.id)
3624           {
3625             Window
3626               window;
3627
3628             window=windows->magnify.id;
3629             while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
3630           }
3631         if (event.xkey.window != windows->image.id)
3632           break;
3633         /*
3634           Respond to a user key press.
3635         */
3636         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3637           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3638         switch ((int) key_symbol)
3639         {
3640           case XK_Escape:
3641           case XK_F20:
3642           {
3643             /*
3644               Prematurely exit.
3645             */
3646             state|=ExitState;
3647             break;
3648           }
3649           case XK_F1:
3650           case XK_Help:
3651           {
3652             XTextViewWidget(display,resource_info,windows,MagickFalse,
3653               "Help Viewer - Image Annotation",ImageColorEditHelp);
3654             break;
3655           }
3656           default:
3657           {
3658             (void) XBell(display,0);
3659             break;
3660           }
3661         }
3662         break;
3663       }
3664       case MotionNotify:
3665       {
3666         /*
3667           Map and unmap Info widget as cursor crosses its boundaries.
3668         */
3669         x=event.xmotion.x;
3670         y=event.xmotion.y;
3671         if (windows->info.mapped != MagickFalse )
3672           {
3673             if ((x < (int) (windows->info.x+windows->info.width)) &&
3674                 (y < (int) (windows->info.y+windows->info.height)))
3675               (void) XWithdrawWindow(display,windows->info.id,
3676                 windows->info.screen);
3677           }
3678         else
3679           if ((x > (int) (windows->info.x+windows->info.width)) ||
3680               (y > (int) (windows->info.y+windows->info.height)))
3681             (void) XMapWindow(display,windows->info.id);
3682         break;
3683       }
3684       default:
3685         break;
3686     }
3687     if (event.xany.window == windows->magnify.id)
3688       {
3689         x=windows->magnify.x-windows->image.x;
3690         y=windows->magnify.y-windows->image.y;
3691       }
3692     x_offset=x;
3693     y_offset=y;
3694     if ((state & UpdateConfigurationState) != 0)
3695       {
3696         CacheView
3697           *image_view;
3698
3699         int
3700           x,
3701           y;
3702
3703         /*
3704           Pixel edit is relative to image configuration.
3705         */
3706         (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
3707           MagickTrue);
3708         color=windows->pixel_info->pen_colors[pen_id];
3709         XPutPixel(windows->image.ximage,x_offset,y_offset,color.pixel);
3710         width=(unsigned int) (*image)->columns;
3711         height=(unsigned int) (*image)->rows;
3712         x=0;
3713         y=0;
3714         if (windows->image.crop_geometry != (char *) NULL)
3715           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
3716             &width,&height);
3717         x_offset=(int)
3718           (width*(windows->image.x+x_offset)/windows->image.ximage->width+x);
3719         y_offset=(int)
3720           (height*(windows->image.y+y_offset)/windows->image.ximage->height+y);
3721         if ((x_offset < 0) || (y_offset < 0))
3722           continue;
3723         if ((x_offset >= (int) (*image)->columns) ||
3724             (y_offset >= (int) (*image)->rows))
3725           continue;
3726         image_view=AcquireAuthenticCacheView(*image,exception);
3727         switch (method)
3728         {
3729           case PointMethod:
3730           default:
3731           {
3732             /*
3733               Update color information using point algorithm.
3734             */
3735             if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
3736               return(MagickFalse);
3737             q=GetCacheViewAuthenticPixels(image_view,(ssize_t)x_offset,
3738               (ssize_t) y_offset,1,1,exception);
3739             if (q == (Quantum *) NULL)
3740               break;
3741             SetPixelRed(*image,ScaleShortToQuantum(color.red),q);
3742             SetPixelGreen(*image,ScaleShortToQuantum(color.green),q);
3743             SetPixelBlue(*image,ScaleShortToQuantum(color.blue),q);
3744             (void) SyncCacheViewAuthenticPixels(image_view,exception);
3745             break;
3746           }
3747           case ReplaceMethod:
3748           {
3749             PixelInfo
3750               pixel,
3751               target;
3752
3753             /*
3754               Update color information using replace algorithm.
3755             */
3756             (void) GetOneCacheViewVirtualPixelInfo(image_view,(ssize_t)
3757               x_offset,(ssize_t) y_offset,&target,exception);
3758             if ((*image)->storage_class == DirectClass)
3759               {
3760                 for (y=0; y < (int) (*image)->rows; y++)
3761                 {
3762                   q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3763                     (*image)->columns,1,exception);
3764                   if (q == (Quantum *) NULL)
3765                     break;
3766                   for (x=0; x < (int) (*image)->columns; x++)
3767                   {
3768                     GetPixelInfoPixel(*image,q,&pixel);
3769                     if (IsFuzzyEquivalencePixelInfo(&pixel,&target))
3770                       {
3771                         SetPixelRed(*image,ScaleShortToQuantum(
3772                           color.red),q);
3773                         SetPixelGreen(*image,ScaleShortToQuantum(
3774                           color.green),q);
3775                         SetPixelBlue(*image,ScaleShortToQuantum(
3776                           color.blue),q);
3777                       }
3778                     q+=GetPixelChannels(*image);
3779                   }
3780                   if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3781                     break;
3782                 }
3783               }
3784             else
3785               {
3786                 for (i=0; i < (ssize_t) (*image)->colors; i++)
3787                   if (IsFuzzyEquivalencePixelInfo((*image)->colormap+i,&target))
3788                     {
3789                       (*image)->colormap[i].red=(double) ScaleShortToQuantum(
3790                         color.red);
3791                       (*image)->colormap[i].green=(double) ScaleShortToQuantum(
3792                         color.green);
3793                       (*image)->colormap[i].blue=(double) ScaleShortToQuantum(
3794                         color.blue);
3795                     }
3796                 (void) SyncImage(*image,exception);
3797               }
3798             break;
3799           }
3800           case FloodfillMethod:
3801           case FillToBorderMethod:
3802           {
3803             DrawInfo
3804               *draw_info;
3805
3806             PixelInfo
3807               target;
3808
3809             /*
3810               Update color information using floodfill algorithm.
3811             */
3812             (void) GetOneVirtualPixelInfo(*image,
3813               GetPixelCacheVirtualMethod(*image),(ssize_t) x_offset,(ssize_t)
3814               y_offset,&target,exception);
3815             if (method == FillToBorderMethod)
3816               {
3817                 target.red=(double)
3818                   ScaleShortToQuantum(border_color.red);
3819                 target.green=(double)
3820                   ScaleShortToQuantum(border_color.green);
3821                 target.blue=(double)
3822                   ScaleShortToQuantum(border_color.blue);
3823               }
3824             draw_info=CloneDrawInfo(resource_info->image_info,
3825               (DrawInfo *) NULL);
3826             (void) QueryColorCompliance(resource_info->pen_colors[pen_id],
3827               AllCompliance,&draw_info->fill,exception);
3828             (void) FloodfillPaintImage(*image,draw_info,&target,
3829               (ssize_t)x_offset,(ssize_t)y_offset,
3830               method != FloodfillMethod ? MagickTrue : MagickFalse,exception);
3831             draw_info=DestroyDrawInfo(draw_info);
3832             break;
3833           }
3834           case ResetMethod:
3835           {
3836             /*
3837               Update color information using reset algorithm.
3838             */
3839             if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
3840               return(MagickFalse);
3841             for (y=0; y < (int) (*image)->rows; y++)
3842             {
3843               q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3844                 (*image)->columns,1,exception);
3845               if (q == (Quantum *) NULL)
3846                 break;
3847               for (x=0; x < (int) (*image)->columns; x++)
3848               {
3849                 SetPixelRed(*image,ScaleShortToQuantum(color.red),q);
3850                 SetPixelGreen(*image,ScaleShortToQuantum(color.green),q);
3851                 SetPixelBlue(*image,ScaleShortToQuantum(color.blue),q);
3852                 q+=GetPixelChannels(*image);
3853               }
3854               if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3855                 break;
3856             }
3857             break;
3858           }
3859         }
3860         image_view=DestroyCacheView(image_view);
3861         state&=(~UpdateConfigurationState);
3862       }
3863   } while ((state & ExitState) == 0);
3864   (void) XSelectInput(display,windows->image.id,
3865     windows->image.attributes.event_mask);
3866   XSetCursorState(display,windows,MagickFalse);
3867   (void) XFreeCursor(display,cursor);
3868   return(MagickTrue);
3869 }
3870 \f
3871 /*
3872 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3873 %                                                                             %
3874 %                                                                             %
3875 %                                                                             %
3876 +   X C o m p o s i t e I m a g e                                             %
3877 %                                                                             %
3878 %                                                                             %
3879 %                                                                             %
3880 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3881 %
3882 %  XCompositeImage() requests an image name from the user, reads the image and
3883 %  composites it with the X window image at a location the user chooses with
3884 %  the pointer.
3885 %
3886 %  The format of the XCompositeImage method is:
3887 %
3888 %      MagickBooleanType XCompositeImage(Display *display,
3889 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
3890 %        ExceptionInfo *exception)
3891 %
3892 %  A description of each parameter follows:
3893 %
3894 %    o display: Specifies a connection to an X server;  returned from
3895 %      XOpenDisplay.
3896 %
3897 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3898 %
3899 %    o windows: Specifies a pointer to a XWindows structure.
3900 %
3901 %    o image: the image; returned from ReadImage.
3902 %
3903 %    o exception: return any errors or warnings in this structure.
3904 %
3905 */
3906 static MagickBooleanType XCompositeImage(Display *display,
3907   XResourceInfo *resource_info,XWindows *windows,Image *image,
3908   ExceptionInfo *exception)
3909 {
3910   static char
3911     displacement_geometry[MagickPathExtent] = "30x30",
3912     filename[MagickPathExtent] = "\0";
3913
3914   static const char
3915     *CompositeMenu[] =
3916     {
3917       "Operators",
3918       "Dissolve",
3919       "Displace",
3920       "Help",
3921       "Dismiss",
3922       (char *) NULL
3923     };
3924
3925   static CompositeOperator
3926     compose = CopyCompositeOp;
3927
3928   static const ModeType
3929     CompositeCommands[] =
3930     {
3931       CompositeOperatorsCommand,
3932       CompositeDissolveCommand,
3933       CompositeDisplaceCommand,
3934       CompositeHelpCommand,
3935       CompositeDismissCommand
3936     };
3937
3938   char
3939     text[MagickPathExtent];
3940
3941   Cursor
3942     cursor;
3943
3944   Image
3945     *composite_image;
3946
3947   int
3948     entry,
3949     id,
3950     x,
3951     y;
3952
3953   double
3954     blend,
3955     scale_factor;
3956
3957   RectangleInfo
3958     highlight_info,
3959     composite_info;
3960
3961   unsigned int
3962     height,
3963     width;
3964
3965   size_t
3966     state;
3967
3968   XEvent
3969     event;
3970
3971   /*
3972     Request image file name from user.
3973   */
3974   XFileBrowserWidget(display,windows,"Composite",filename);
3975   if (*filename == '\0')
3976     return(MagickTrue);
3977   /*
3978     Read image.
3979   */
3980   XSetCursorState(display,windows,MagickTrue);
3981   XCheckRefreshWindows(display,windows);
3982   (void) CopyMagickString(resource_info->image_info->filename,filename,
3983     MagickPathExtent);
3984   composite_image=ReadImage(resource_info->image_info,exception);
3985   CatchException(exception);
3986   XSetCursorState(display,windows,MagickFalse);
3987   if (composite_image == (Image *) NULL)
3988     return(MagickFalse);
3989   /*
3990     Map Command widget.
3991   */
3992   (void) CloneString(&windows->command.name,"Composite");
3993   windows->command.data=1;
3994   (void) XCommandWidget(display,windows,CompositeMenu,(XEvent *) NULL);
3995   (void) XMapRaised(display,windows->command.id);
3996   XClientMessage(display,windows->image.id,windows->im_protocols,
3997     windows->im_update_widget,CurrentTime);
3998   /*
3999     Track pointer until button 1 is pressed.
4000   */
4001   XQueryPosition(display,windows->image.id,&x,&y);
4002   (void) XSelectInput(display,windows->image.id,
4003     windows->image.attributes.event_mask | PointerMotionMask);
4004   composite_info.x=(ssize_t) windows->image.x+x;
4005   composite_info.y=(ssize_t) windows->image.y+y;
4006   composite_info.width=0;
4007   composite_info.height=0;
4008   cursor=XCreateFontCursor(display,XC_ul_angle);
4009   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
4010   blend=0.0;
4011   state=DefaultState;
4012   do
4013   {
4014     if (windows->info.mapped != MagickFalse )
4015       {
4016         /*
4017           Display pointer position.
4018         */
4019         (void) FormatLocaleString(text,MagickPathExtent," %+ld%+ld ",
4020           (long) composite_info.x,(long) composite_info.y);
4021         XInfoWidget(display,windows,text);
4022       }
4023     highlight_info=composite_info;
4024     highlight_info.x=composite_info.x-windows->image.x;
4025     highlight_info.y=composite_info.y-windows->image.y;
4026     XHighlightRectangle(display,windows->image.id,
4027       windows->image.highlight_context,&highlight_info);
4028     /*
4029       Wait for next event.
4030     */
4031     XScreenEvent(display,windows,&event,exception);
4032     XHighlightRectangle(display,windows->image.id,
4033       windows->image.highlight_context,&highlight_info);
4034     if (event.xany.window == windows->command.id)
4035       {
4036         /*
4037           Select a command from the Command widget.
4038         */
4039         id=XCommandWidget(display,windows,CompositeMenu,&event);
4040         if (id < 0)
4041           continue;
4042         switch (CompositeCommands[id])
4043         {
4044           case CompositeOperatorsCommand:
4045           {
4046             char
4047               command[MagickPathExtent],
4048               **operators;
4049
4050             /*
4051               Select a command from the pop-up menu.
4052             */
4053             operators=GetCommandOptions(MagickComposeOptions);
4054             if (operators == (char **) NULL)
4055               break;
4056             entry=XMenuWidget(display,windows,CompositeMenu[id],
4057               (const char **) operators,command);
4058             if (entry >= 0)
4059               compose=(CompositeOperator) ParseCommandOption(
4060                 MagickComposeOptions,MagickFalse,operators[entry]);
4061             operators=DestroyStringList(operators);
4062             break;
4063           }
4064           case CompositeDissolveCommand:
4065           {
4066             static char
4067               factor[MagickPathExtent] = "20.0";
4068
4069             /*
4070               Dissolve the two images a given percent.
4071             */
4072             (void) XSetFunction(display,windows->image.highlight_context,
4073               GXcopy);
4074             (void) XDialogWidget(display,windows,"Dissolve",
4075               "Enter the blend factor (0.0 - 99.9%):",factor);
4076             (void) XSetFunction(display,windows->image.highlight_context,
4077               GXinvert);
4078             if (*factor == '\0')
4079               break;
4080             blend=StringToDouble(factor,(char **) NULL);
4081             compose=DissolveCompositeOp;
4082             break;
4083           }
4084           case CompositeDisplaceCommand:
4085           {
4086             /*
4087               Get horizontal and vertical scale displacement geometry.
4088             */
4089             (void) XSetFunction(display,windows->image.highlight_context,
4090               GXcopy);
4091             (void) XDialogWidget(display,windows,"Displace",
4092               "Enter the horizontal and vertical scale:",displacement_geometry);
4093             (void) XSetFunction(display,windows->image.highlight_context,
4094               GXinvert);
4095             if (*displacement_geometry == '\0')
4096               break;
4097             compose=DisplaceCompositeOp;
4098             break;
4099           }
4100           case CompositeHelpCommand:
4101           {
4102             (void) XSetFunction(display,windows->image.highlight_context,
4103               GXcopy);
4104             XTextViewWidget(display,resource_info,windows,MagickFalse,
4105               "Help Viewer - Image Composite",ImageCompositeHelp);
4106             (void) XSetFunction(display,windows->image.highlight_context,
4107               GXinvert);
4108             break;
4109           }
4110           case CompositeDismissCommand:
4111           {
4112             /*
4113               Prematurely exit.
4114             */
4115             state|=EscapeState;
4116             state|=ExitState;
4117             break;
4118           }
4119           default:
4120             break;
4121         }
4122         continue;
4123       }
4124     switch (event.type)
4125     {
4126       case ButtonPress:
4127       {
4128         if (image->debug != MagickFalse )
4129           (void) LogMagickEvent(X11Event,GetMagickModule(),
4130             "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
4131             event.xbutton.button,event.xbutton.x,event.xbutton.y);
4132         if (event.xbutton.button != Button1)
4133           break;
4134         if (event.xbutton.window != windows->image.id)
4135           break;
4136         /*
4137           Change cursor.
4138         */
4139         composite_info.width=composite_image->columns;
4140         composite_info.height=composite_image->rows;
4141         (void) XCheckDefineCursor(display,windows->image.id,cursor);
4142         composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4143         composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4144         break;
4145       }
4146       case ButtonRelease:
4147       {
4148         if (image->debug != MagickFalse )
4149           (void) LogMagickEvent(X11Event,GetMagickModule(),
4150             "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
4151             event.xbutton.button,event.xbutton.x,event.xbutton.y);
4152         if (event.xbutton.button != Button1)
4153           break;
4154         if (event.xbutton.window != windows->image.id)
4155           break;
4156         if ((composite_info.width != 0) && (composite_info.height != 0))
4157           {
4158             /*
4159               User has selected the location of the composite image.
4160             */
4161             composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4162             composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4163             state|=ExitState;
4164           }
4165         break;
4166       }
4167       case Expose:
4168         break;
4169       case KeyPress:
4170       {
4171         char
4172           command[MagickPathExtent];
4173
4174         KeySym
4175           key_symbol;
4176
4177         int
4178           length;
4179
4180         if (event.xkey.window != windows->image.id)
4181           break;
4182         /*
4183           Respond to a user key press.
4184         */
4185         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
4186           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4187         *(command+length)='\0';
4188         if (image->debug != MagickFalse )
4189           (void) LogMagickEvent(X11Event,GetMagickModule(),
4190             "Key press: 0x%lx (%s)",(unsigned long) key_symbol,command);
4191         switch ((int) key_symbol)
4192         {
4193           case XK_Escape:
4194           case XK_F20:
4195           {
4196             /*
4197               Prematurely exit.
4198             */
4199             composite_image=DestroyImage(composite_image);
4200             state|=EscapeState;
4201             state|=ExitState;
4202             break;
4203           }
4204           case XK_F1:
4205           case XK_Help:
4206           {
4207             (void) XSetFunction(display,windows->image.highlight_context,
4208               GXcopy);
4209             XTextViewWidget(display,resource_info,windows,MagickFalse,
4210               "Help Viewer - Image Composite",ImageCompositeHelp);
4211             (void) XSetFunction(display,windows->image.highlight_context,
4212               GXinvert);
4213             break;
4214           }
4215           default:
4216           {
4217             (void) XBell(display,0);
4218             break;
4219           }
4220         }
4221         break;
4222       }
4223       case MotionNotify:
4224       {
4225         /*
4226           Map and unmap Info widget as text cursor crosses its boundaries.
4227         */
4228         x=event.xmotion.x;
4229         y=event.xmotion.y;
4230         if (windows->info.mapped != MagickFalse )
4231           {
4232             if ((x < (int) (windows->info.x+windows->info.width)) &&
4233                 (y < (int) (windows->info.y+windows->info.height)))
4234               (void) XWithdrawWindow(display,windows->info.id,
4235                 windows->info.screen);
4236           }
4237         else
4238           if ((x > (int) (windows->info.x+windows->info.width)) ||
4239               (y > (int) (windows->info.y+windows->info.height)))
4240             (void) XMapWindow(display,windows->info.id);
4241         composite_info.x=(ssize_t) windows->image.x+x;
4242         composite_info.y=(ssize_t) windows->image.y+y;
4243         break;
4244       }
4245       default:
4246       {
4247         if (image->debug != MagickFalse )
4248           (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
4249             event.type);
4250         break;
4251       }
4252     }
4253   } while ((state & ExitState) == 0);
4254   (void) XSelectInput(display,windows->image.id,
4255     windows->image.attributes.event_mask);
4256   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
4257   XSetCursorState(display,windows,MagickFalse);
4258   (void) XFreeCursor(display,cursor);
4259   if ((state & EscapeState) != 0)
4260     return(MagickTrue);
4261   /*
4262     Image compositing is relative to image configuration.
4263   */
4264   XSetCursorState(display,windows,MagickTrue);
4265   XCheckRefreshWindows(display,windows);
4266   width=(unsigned int) image->columns;
4267   height=(unsigned int) image->rows;
4268   x=0;
4269   y=0;
4270   if (windows->image.crop_geometry != (char *) NULL)
4271     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
4272   scale_factor=(double) width/windows->image.ximage->width;
4273   composite_info.x+=x;
4274   composite_info.x=(ssize_t) (scale_factor*composite_info.x+0.5);
4275   composite_info.width=(unsigned int) (scale_factor*composite_info.width+0.5);
4276   scale_factor=(double) height/windows->image.ximage->height;
4277   composite_info.y+=y;
4278   composite_info.y=(ssize_t) (scale_factor*composite_info.y+0.5);
4279   composite_info.height=(unsigned int) (scale_factor*composite_info.height+0.5);
4280   if ((composite_info.width != composite_image->columns) ||
4281       (composite_info.height != composite_image->rows))
4282     {
4283       Image
4284         *resize_image;
4285
4286       /*
4287         Scale composite image.
4288       */
4289       resize_image=ResizeImage(composite_image,composite_info.width,
4290         composite_info.height,composite_image->filter,exception);
4291       composite_image=DestroyImage(composite_image);
4292       if (resize_image == (Image *) NULL)
4293         {
4294           XSetCursorState(display,windows,MagickFalse);
4295           return(MagickFalse);
4296         }
4297       composite_image=resize_image;
4298     }
4299   if (compose == DisplaceCompositeOp)
4300     (void) SetImageArtifact(composite_image,"compose:args",
4301       displacement_geometry);
4302   if (blend != 0.0)
4303     {
4304       CacheView
4305         *image_view;
4306
4307       int
4308         y;
4309
4310       Quantum
4311         opacity;
4312
4313       register int
4314         x;
4315
4316       register Quantum
4317         *q;
4318
4319       /*
4320         Create mattes for blending.
4321       */
4322       (void) SetImageAlphaChannel(composite_image,OpaqueAlphaChannel,exception);
4323       opacity=(Quantum) (ScaleQuantumToChar(QuantumRange)-
4324         ((ssize_t) ScaleQuantumToChar(QuantumRange)*blend)/100);
4325       if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
4326         return(MagickFalse);
4327       image->alpha_trait=BlendPixelTrait;
4328       image_view=AcquireAuthenticCacheView(image,exception);
4329       for (y=0; y < (int) image->rows; y++)
4330       {
4331         q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,image->columns,1,
4332           exception);
4333         if (q == (Quantum *) NULL)
4334           break;
4335         for (x=0; x < (int) image->columns; x++)
4336         {
4337           SetPixelAlpha(image,opacity,q);
4338           q+=GetPixelChannels(image);
4339         }
4340         if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4341           break;
4342       }
4343       image_view=DestroyCacheView(image_view);
4344     }
4345   /*
4346     Composite image with X Image window.
4347   */
4348   (void) CompositeImage(image,composite_image,compose,MagickTrue,
4349     composite_info.x,composite_info.y,exception);
4350   composite_image=DestroyImage(composite_image);
4351   XSetCursorState(display,windows,MagickFalse);
4352   /*
4353     Update image configuration.
4354   */
4355   XConfigureImageColormap(display,resource_info,windows,image,exception);
4356   (void) XConfigureImage(display,resource_info,windows,image,exception);
4357   return(MagickTrue);
4358 }
4359 \f
4360 /*
4361 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4362 %                                                                             %
4363 %                                                                             %
4364 %                                                                             %
4365 +   X C o n f i g u r e I m a g e                                             %
4366 %                                                                             %
4367 %                                                                             %
4368 %                                                                             %
4369 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4370 %
4371 %  XConfigureImage() creates a new X image.  It also notifies the window
4372 %  manager of the new image size and configures the transient widows.
4373 %
4374 %  The format of the XConfigureImage method is:
4375 %
4376 %      MagickBooleanType XConfigureImage(Display *display,
4377 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
4378 %        ExceptionInfo *exception)
4379 %
4380 %  A description of each parameter follows:
4381 %
4382 %    o display: Specifies a connection to an X server; returned from
4383 %      XOpenDisplay.
4384 %
4385 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4386 %
4387 %    o windows: Specifies a pointer to a XWindows structure.
4388 %
4389 %    o image: the image.
4390 %
4391 %    o exception: return any errors or warnings in this structure.
4392 %
4393 %    o exception: return any errors or warnings in this structure.
4394 %
4395 */
4396 static MagickBooleanType XConfigureImage(Display *display,
4397   XResourceInfo *resource_info,XWindows *windows,Image *image,
4398   ExceptionInfo *exception)
4399 {
4400   char
4401     geometry[MagickPathExtent];
4402
4403   MagickStatusType
4404     status;
4405
4406   size_t
4407     mask,
4408     height,
4409     width;
4410
4411   ssize_t
4412     x,
4413     y;
4414
4415   XSizeHints
4416     *size_hints;
4417
4418   XWindowChanges
4419     window_changes;
4420
4421   /*
4422     Dismiss if window dimensions are zero.
4423   */
4424   width=(unsigned int) windows->image.window_changes.width;
4425   height=(unsigned int) windows->image.window_changes.height;
4426   if (image->debug != MagickFalse )
4427     (void) LogMagickEvent(X11Event,GetMagickModule(),
4428       "Configure Image: %dx%d=>%.20gx%.20g",windows->image.ximage->width,
4429       windows->image.ximage->height,(double) width,(double) height);
4430   if ((width*height) == 0)
4431     return(MagickTrue);
4432   x=0;
4433   y=0;
4434   /*
4435     Resize image to fit Image window dimensions.
4436   */
4437   XSetCursorState(display,windows,MagickTrue);
4438   (void) XFlush(display);
4439   if (((int) width != windows->image.ximage->width) ||
4440       ((int) height != windows->image.ximage->height))
4441     image->taint=MagickTrue;
4442   windows->magnify.x=(int)
4443     width*windows->magnify.x/windows->image.ximage->width;
4444   windows->magnify.y=(int)
4445     height*windows->magnify.y/windows->image.ximage->height;
4446   windows->image.x=(int) (width*windows->image.x/windows->image.ximage->width);
4447   windows->image.y=(int)
4448     (height*windows->image.y/windows->image.ximage->height);
4449   status=XMakeImage(display,resource_info,&windows->image,image,
4450     (unsigned int) width,(unsigned int) height,exception);
4451   if (status == MagickFalse)
4452     XNoticeWidget(display,windows,"Unable to configure X image:",
4453       windows->image.name);
4454   /*
4455     Notify window manager of the new configuration.
4456   */
4457   if (resource_info->image_geometry != (char *) NULL)
4458     (void) FormatLocaleString(geometry,MagickPathExtent,"%s>!",
4459       resource_info->image_geometry);
4460   else
4461     (void) FormatLocaleString(geometry,MagickPathExtent,"%ux%u+0+0>!",
4462       XDisplayWidth(display,windows->image.screen),
4463       XDisplayHeight(display,windows->image.screen));
4464   (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
4465   window_changes.width=(int) width;
4466   if (window_changes.width > XDisplayWidth(display,windows->image.screen))
4467     window_changes.width=XDisplayWidth(display,windows->image.screen);
4468   window_changes.height=(int) height;
4469   if (window_changes.height > XDisplayHeight(display,windows->image.screen))
4470     window_changes.height=XDisplayHeight(display,windows->image.screen);
4471   mask=(size_t) (CWWidth | CWHeight);
4472   if (resource_info->backdrop)
4473     {
4474       mask|=CWX | CWY;
4475       window_changes.x=(int)
4476         ((XDisplayWidth(display,windows->image.screen)/2)-(width/2));
4477       window_changes.y=(int)
4478         ((XDisplayHeight(display,windows->image.screen)/2)-(height/2));
4479     }
4480   (void) XReconfigureWMWindow(display,windows->image.id,windows->image.screen,
4481     (unsigned int) mask,&window_changes);
4482   (void) XClearWindow(display,windows->image.id);
4483   XRefreshWindow(display,&windows->image,(XEvent *) NULL);
4484   /*
4485     Update Magnify window configuration.
4486   */
4487   if (windows->magnify.mapped != MagickFalse )
4488     XMakeMagnifyImage(display,windows,exception);
4489   windows->pan.crop_geometry=windows->image.crop_geometry;
4490   XBestIconSize(display,&windows->pan,image);
4491   while (((windows->pan.width << 1) < MaxIconSize) &&
4492          ((windows->pan.height << 1) < MaxIconSize))
4493   {
4494     windows->pan.width<<=1;
4495     windows->pan.height<<=1;
4496   }
4497   if (windows->pan.geometry != (char *) NULL)
4498     (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
4499       &windows->pan.width,&windows->pan.height);
4500   window_changes.width=(int) windows->pan.width;
4501   window_changes.height=(int) windows->pan.height;
4502   size_hints=XAllocSizeHints();
4503   if (size_hints != (XSizeHints *) NULL)
4504     {
4505       /*
4506         Set new size hints.
4507       */
4508       size_hints->flags=PSize | PMinSize | PMaxSize;
4509       size_hints->width=window_changes.width;
4510       size_hints->height=window_changes.height;
4511       size_hints->min_width=size_hints->width;
4512       size_hints->min_height=size_hints->height;
4513       size_hints->max_width=size_hints->width;
4514       size_hints->max_height=size_hints->height;
4515       (void) XSetNormalHints(display,windows->pan.id,size_hints);
4516       (void) XFree((void *) size_hints);
4517     }
4518   (void) XReconfigureWMWindow(display,windows->pan.id,windows->pan.screen,
4519     (unsigned int) (CWWidth | CWHeight),&window_changes);
4520   /*
4521     Update icon window configuration.
4522   */
4523   windows->icon.crop_geometry=windows->image.crop_geometry;
4524   XBestIconSize(display,&windows->icon,image);
4525   window_changes.width=(int) windows->icon.width;
4526   window_changes.height=(int) windows->icon.height;
4527   (void) XReconfigureWMWindow(display,windows->icon.id,windows->icon.screen,
4528     (unsigned int) (CWWidth | CWHeight),&window_changes);
4529   XSetCursorState(display,windows,MagickFalse);
4530   return(status != 0 ? MagickTrue : MagickFalse);
4531 }
4532 \f
4533 /*
4534 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4535 %                                                                             %
4536 %                                                                             %
4537 %                                                                             %
4538 +   X C r o p I m a g e                                                       %
4539 %                                                                             %
4540 %                                                                             %
4541 %                                                                             %
4542 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4543 %
4544 %  XCropImage() allows the user to select a region of the image and crop, copy,
4545 %  or cut it.  For copy or cut, the image can subsequently be composited onto
4546 %  the image with XPasteImage.
4547 %
4548 %  The format of the XCropImage method is:
4549 %
4550 %      MagickBooleanType XCropImage(Display *display,
4551 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
4552 %        const ClipboardMode mode,ExceptionInfo *exception)
4553 %
4554 %  A description of each parameter follows:
4555 %
4556 %    o display: Specifies a connection to an X server; returned from
4557 %      XOpenDisplay.
4558 %
4559 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4560 %
4561 %    o windows: Specifies a pointer to a XWindows structure.
4562 %
4563 %    o image: the image; returned from ReadImage.
4564 %
4565 %    o mode: This unsigned value specified whether the image should be
4566 %      cropped, copied, or cut.
4567 %
4568 %    o exception: return any errors or warnings in this structure.
4569 %
4570 */
4571 static MagickBooleanType XCropImage(Display *display,
4572   XResourceInfo *resource_info,XWindows *windows,Image *image,
4573   const ClipboardMode mode,ExceptionInfo *exception)
4574 {
4575   static const char
4576     *CropModeMenu[] =
4577     {
4578       "Help",
4579       "Dismiss",
4580       (char *) NULL
4581     },
4582     *RectifyModeMenu[] =
4583     {
4584       "Crop",
4585       "Help",
4586       "Dismiss",
4587       (char *) NULL
4588     };
4589
4590   static const ModeType
4591     CropCommands[] =
4592     {
4593       CropHelpCommand,
4594       CropDismissCommand
4595     },
4596     RectifyCommands[] =
4597     {
4598       RectifyCopyCommand,
4599       RectifyHelpCommand,
4600       RectifyDismissCommand
4601     };
4602
4603   CacheView
4604     *image_view;
4605
4606   char
4607     command[MagickPathExtent],
4608     text[MagickPathExtent];
4609
4610   Cursor
4611     cursor;
4612
4613   int
4614     id,
4615     x,
4616     y;
4617
4618   KeySym
4619     key_symbol;
4620
4621   Image
4622     *crop_image;
4623
4624   double
4625     scale_factor;
4626
4627   RectangleInfo
4628     crop_info,
4629     highlight_info;
4630
4631   register Quantum
4632     *q;
4633
4634   unsigned int
4635     height,
4636     width;
4637
4638   size_t
4639     state;
4640
4641   XEvent
4642     event;
4643
4644   /*
4645     Map Command widget.
4646   */
4647   switch (mode)
4648   {
4649     case CopyMode:
4650     {
4651       (void) CloneString(&windows->command.name,"Copy");
4652       break;
4653     }
4654     case CropMode:
4655     {
4656       (void) CloneString(&windows->command.name,"Crop");
4657       break;
4658     }
4659     case CutMode:
4660     {
4661       (void) CloneString(&windows->command.name,"Cut");
4662       break;
4663     }
4664   }
4665   RectifyModeMenu[0]=windows->command.name;
4666   windows->command.data=0;
4667   (void) XCommandWidget(display,windows,CropModeMenu,(XEvent *) NULL);
4668   (void) XMapRaised(display,windows->command.id);
4669   XClientMessage(display,windows->image.id,windows->im_protocols,
4670     windows->im_update_widget,CurrentTime);
4671   /*
4672     Track pointer until button 1 is pressed.
4673   */
4674   XQueryPosition(display,windows->image.id,&x,&y);
4675   (void) XSelectInput(display,windows->image.id,
4676     windows->image.attributes.event_mask | PointerMotionMask);
4677   crop_info.x=(ssize_t) windows->image.x+x;
4678   crop_info.y=(ssize_t) windows->image.y+y;
4679   crop_info.width=0;
4680   crop_info.height=0;
4681   cursor=XCreateFontCursor(display,XC_fleur);
4682   state=DefaultState;
4683   do
4684   {
4685     if (windows->info.mapped != MagickFalse )
4686       {
4687         /*
4688           Display pointer position.
4689         */
4690         (void) FormatLocaleString(text,MagickPathExtent," %+ld%+ld ",
4691           (long) crop_info.x,(long) crop_info.y);
4692         XInfoWidget(display,windows,text);
4693       }
4694     /*
4695       Wait for next event.
4696     */
4697     XScreenEvent(display,windows,&event,exception);
4698     if (event.xany.window == windows->command.id)
4699       {
4700         /*
4701           Select a command from the Command widget.
4702         */
4703         id=XCommandWidget(display,windows,CropModeMenu,&event);
4704         if (id < 0)
4705           continue;
4706         switch (CropCommands[id])
4707         {
4708           case CropHelpCommand:
4709           {
4710             switch (mode)
4711             {
4712               case CopyMode:
4713               {
4714                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4715                   "Help Viewer - Image Copy",ImageCopyHelp);
4716                 break;
4717               }
4718               case CropMode:
4719               {
4720                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4721                   "Help Viewer - Image Crop",ImageCropHelp);
4722                 break;
4723               }
4724               case CutMode:
4725               {
4726                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4727                   "Help Viewer - Image Cut",ImageCutHelp);
4728                 break;
4729               }
4730             }
4731             break;
4732           }
4733           case CropDismissCommand:
4734           {
4735             /*
4736               Prematurely exit.
4737             */
4738             state|=EscapeState;
4739             state|=ExitState;
4740             break;
4741           }
4742           default:
4743             break;
4744         }
4745         continue;
4746       }
4747     switch (event.type)
4748     {
4749       case ButtonPress:
4750       {
4751         if (event.xbutton.button != Button1)
4752           break;
4753         if (event.xbutton.window != windows->image.id)
4754           break;
4755         /*
4756           Note first corner of cropping rectangle-- exit loop.
4757         */
4758         (void) XCheckDefineCursor(display,windows->image.id,cursor);
4759         crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4760         crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4761         state|=ExitState;
4762         break;
4763       }
4764       case ButtonRelease:
4765         break;
4766       case Expose:
4767         break;
4768       case KeyPress:
4769       {
4770         if (event.xkey.window != windows->image.id)
4771           break;
4772         /*
4773           Respond to a user key press.
4774         */
4775         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
4776           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4777         switch ((int) key_symbol)
4778         {
4779           case XK_Escape:
4780           case XK_F20:
4781           {
4782             /*
4783               Prematurely exit.
4784             */
4785             state|=EscapeState;
4786             state|=ExitState;
4787             break;
4788           }
4789           case XK_F1:
4790           case XK_Help:
4791           {
4792             switch (mode)
4793             {
4794               case CopyMode:
4795               {
4796                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4797                   "Help Viewer - Image Copy",ImageCopyHelp);
4798                 break;
4799               }
4800               case CropMode:
4801               {
4802                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4803                   "Help Viewer - Image Crop",ImageCropHelp);
4804                 break;
4805               }
4806               case CutMode:
4807               {
4808                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4809                   "Help Viewer - Image Cut",ImageCutHelp);
4810                 break;
4811               }
4812             }
4813             break;
4814           }
4815           default:
4816           {
4817             (void) XBell(display,0);
4818             break;
4819           }
4820         }
4821         break;
4822       }
4823       case MotionNotify:
4824       {
4825         if (event.xmotion.window != windows->image.id)
4826           break;
4827         /*
4828           Map and unmap Info widget as text cursor crosses its boundaries.
4829         */
4830         x=event.xmotion.x;
4831         y=event.xmotion.y;
4832         if (windows->info.mapped != MagickFalse )
4833           {
4834             if ((x < (int) (windows->info.x+windows->info.width)) &&
4835                 (y < (int) (windows->info.y+windows->info.height)))
4836               (void) XWithdrawWindow(display,windows->info.id,
4837                 windows->info.screen);
4838           }
4839         else
4840           if ((x > (int) (windows->info.x+windows->info.width)) ||
4841               (y > (int) (windows->info.y+windows->info.height)))
4842             (void) XMapWindow(display,windows->info.id);
4843         crop_info.x=(ssize_t) windows->image.x+x;
4844         crop_info.y=(ssize_t) windows->image.y+y;
4845         break;
4846       }
4847       default:
4848         break;
4849     }
4850   } while ((state & ExitState) == 0);
4851   (void) XSelectInput(display,windows->image.id,
4852     windows->image.attributes.event_mask);
4853   if ((state & EscapeState) != 0)
4854     {
4855       /*
4856         User want to exit without cropping.
4857       */
4858       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4859       (void) XFreeCursor(display,cursor);
4860       return(MagickTrue);
4861     }
4862   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
4863   do
4864   {
4865     /*
4866       Size rectangle as pointer moves until the mouse button is released.
4867     */
4868     x=(int) crop_info.x;
4869     y=(int) crop_info.y;
4870     crop_info.width=0;
4871     crop_info.height=0;
4872     state=DefaultState;
4873     do
4874     {
4875       highlight_info=crop_info;
4876       highlight_info.x=crop_info.x-windows->image.x;
4877       highlight_info.y=crop_info.y-windows->image.y;
4878       if ((highlight_info.width > 3) && (highlight_info.height > 3))
4879         {
4880           /*
4881             Display info and draw cropping rectangle.
4882           */
4883           if (windows->info.mapped == MagickFalse)
4884             (void) XMapWindow(display,windows->info.id);
4885           (void) FormatLocaleString(text,MagickPathExtent,
4886             " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
4887             crop_info.height,(double) crop_info.x,(double) crop_info.y);
4888           XInfoWidget(display,windows,text);
4889           XHighlightRectangle(display,windows->image.id,
4890             windows->image.highlight_context,&highlight_info);
4891         }
4892       else
4893         if (windows->info.mapped != MagickFalse )
4894           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4895       /*
4896         Wait for next event.
4897       */
4898       XScreenEvent(display,windows,&event,exception);
4899       if ((highlight_info.width > 3) && (highlight_info.height > 3))
4900         XHighlightRectangle(display,windows->image.id,
4901           windows->image.highlight_context,&highlight_info);
4902       switch (event.type)
4903       {
4904         case ButtonPress:
4905         {
4906           crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4907           crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4908           break;
4909         }
4910         case ButtonRelease:
4911         {
4912           /*
4913             User has committed to cropping rectangle.
4914           */
4915           crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4916           crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4917           XSetCursorState(display,windows,MagickFalse);
4918           state|=ExitState;
4919           windows->command.data=0;
4920           (void) XCommandWidget(display,windows,RectifyModeMenu,
4921             (XEvent *) NULL);
4922           break;
4923         }
4924         case Expose:
4925           break;
4926         case MotionNotify:
4927         {
4928           crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
4929           crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
4930         }
4931         default:
4932           break;
4933       }
4934       if ((((int) crop_info.x != x) && ((int) crop_info.y != y)) ||
4935           ((state & ExitState) != 0))
4936         {
4937           /*
4938             Check boundary conditions.
4939           */
4940           if (crop_info.x < 0)
4941             crop_info.x=0;
4942           else
4943             if (crop_info.x > (ssize_t) windows->image.ximage->width)
4944               crop_info.x=(ssize_t) windows->image.ximage->width;
4945           if ((int) crop_info.x < x)
4946             crop_info.width=(unsigned int) (x-crop_info.x);
4947           else
4948             {
4949               crop_info.width=(unsigned int) (crop_info.x-x);
4950               crop_info.x=(ssize_t) x;
4951             }
4952           if (crop_info.y < 0)
4953             crop_info.y=0;
4954           else
4955             if (crop_info.y > (ssize_t) windows->image.ximage->height)
4956               crop_info.y=(ssize_t) windows->image.ximage->height;
4957           if ((int) crop_info.y < y)
4958             crop_info.height=(unsigned int) (y-crop_info.y);
4959           else
4960             {
4961               crop_info.height=(unsigned int) (crop_info.y-y);
4962               crop_info.y=(ssize_t) y;
4963             }
4964         }
4965     } while ((state & ExitState) == 0);
4966     /*
4967       Wait for user to grab a corner of the rectangle or press return.
4968     */
4969     state=DefaultState;
4970     (void) XMapWindow(display,windows->info.id);
4971     do
4972     {
4973       if (windows->info.mapped != MagickFalse )
4974         {
4975           /*
4976             Display pointer position.
4977           */
4978           (void) FormatLocaleString(text,MagickPathExtent,
4979             " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
4980             crop_info.height,(double) crop_info.x,(double) crop_info.y);
4981           XInfoWidget(display,windows,text);
4982         }
4983       highlight_info=crop_info;
4984       highlight_info.x=crop_info.x-windows->image.x;
4985       highlight_info.y=crop_info.y-windows->image.y;
4986       if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
4987         {
4988           state|=EscapeState;
4989           state|=ExitState;
4990           break;
4991         }
4992       XHighlightRectangle(display,windows->image.id,
4993         windows->image.highlight_context,&highlight_info);
4994       XScreenEvent(display,windows,&event,exception);
4995       if (event.xany.window == windows->command.id)
4996         {
4997           /*
4998             Select a command from the Command widget.
4999           */
5000           (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
5001           id=XCommandWidget(display,windows,RectifyModeMenu,&event);
5002           (void) XSetFunction(display,windows->image.highlight_context,
5003             GXinvert);
5004           XHighlightRectangle(display,windows->image.id,
5005             windows->image.highlight_context,&highlight_info);
5006           if (id >= 0)
5007             switch (RectifyCommands[id])
5008             {
5009               case RectifyCopyCommand:
5010               {
5011                 state|=ExitState;
5012                 break;
5013               }
5014               case RectifyHelpCommand:
5015               {
5016                 (void) XSetFunction(display,windows->image.highlight_context,
5017                   GXcopy);
5018                 switch (mode)
5019                 {
5020                   case CopyMode:
5021                   {
5022                     XTextViewWidget(display,resource_info,windows,MagickFalse,
5023                       "Help Viewer - Image Copy",ImageCopyHelp);
5024                     break;
5025                   }
5026                   case CropMode:
5027                   {
5028                     XTextViewWidget(display,resource_info,windows,MagickFalse,
5029                       "Help Viewer - Image Crop",ImageCropHelp);
5030                     break;
5031                   }
5032                   case CutMode:
5033                   {
5034                     XTextViewWidget(display,resource_info,windows,MagickFalse,
5035                       "Help Viewer - Image Cut",ImageCutHelp);
5036                     break;
5037                   }
5038                 }
5039                 (void) XSetFunction(display,windows->image.highlight_context,
5040                   GXinvert);
5041                 break;
5042               }
5043               case RectifyDismissCommand:
5044               {
5045                 /*
5046                   Prematurely exit.
5047                 */
5048                 state|=EscapeState;
5049                 state|=ExitState;
5050                 break;
5051               }
5052               default:
5053                 break;
5054             }
5055           continue;
5056         }
5057       XHighlightRectangle(display,windows->image.id,
5058         windows->image.highlight_context,&highlight_info);
5059       switch (event.type)
5060       {
5061         case ButtonPress:
5062         {
5063           if (event.xbutton.button != Button1)
5064             break;
5065           if (event.xbutton.window != windows->image.id)
5066             break;
5067           x=windows->image.x+event.xbutton.x;
5068           y=windows->image.y+event.xbutton.y;
5069           if ((x < (int) (crop_info.x+RoiDelta)) &&
5070               (x > (int) (crop_info.x-RoiDelta)) &&
5071               (y < (int) (crop_info.y+RoiDelta)) &&
5072               (y > (int) (crop_info.y-RoiDelta)))
5073             {
5074               crop_info.x=(ssize_t) (crop_info.x+crop_info.width);
5075               crop_info.y=(ssize_t) (crop_info.y+crop_info.height);
5076               state|=UpdateConfigurationState;
5077               break;
5078             }
5079           if ((x < (int) (crop_info.x+RoiDelta)) &&
5080               (x > (int) (crop_info.x-RoiDelta)) &&
5081               (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5082               (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5083             {
5084               crop_info.x=(ssize_t) (crop_info.x+crop_info.width);
5085               state|=UpdateConfigurationState;
5086               break;
5087             }
5088           if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5089               (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5090               (y < (int) (crop_info.y+RoiDelta)) &&
5091               (y > (int) (crop_info.y-RoiDelta)))
5092             {
5093               crop_info.y=(ssize_t) (crop_info.y+crop_info.height);
5094               state|=UpdateConfigurationState;
5095               break;
5096             }
5097           if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5098               (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5099               (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5100               (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5101             {
5102               state|=UpdateConfigurationState;
5103               break;
5104             }
5105         }
5106         case ButtonRelease:
5107         {
5108           if (event.xbutton.window == windows->pan.id)
5109             if ((highlight_info.x != crop_info.x-windows->image.x) ||
5110                 (highlight_info.y != crop_info.y-windows->image.y))
5111               XHighlightRectangle(display,windows->image.id,
5112                 windows->image.highlight_context,&highlight_info);
5113           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5114             event.xbutton.time);
5115           break;
5116         }
5117         case Expose:
5118         {
5119           if (event.xexpose.window == windows->image.id)
5120             if (event.xexpose.count == 0)
5121               {
5122                 event.xexpose.x=(int) highlight_info.x;
5123                 event.xexpose.y=(int) highlight_info.y;
5124                 event.xexpose.width=(int) highlight_info.width;
5125                 event.xexpose.height=(int) highlight_info.height;
5126                 XRefreshWindow(display,&windows->image,&event);
5127               }
5128           if (event.xexpose.window == windows->info.id)
5129             if (event.xexpose.count == 0)
5130               XInfoWidget(display,windows,text);
5131           break;
5132         }
5133         case KeyPress:
5134         {
5135           if (event.xkey.window != windows->image.id)
5136             break;
5137           /*
5138             Respond to a user key press.
5139           */
5140           (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5141             sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5142           switch ((int) key_symbol)
5143           {
5144             case XK_Escape:
5145             case XK_F20:
5146               state|=EscapeState;
5147             case XK_Return:
5148             {
5149               state|=ExitState;
5150               break;
5151             }
5152             case XK_Home:
5153             case XK_KP_Home:
5154             {
5155               crop_info.x=(ssize_t) (windows->image.width/2L-crop_info.width/
5156                 2L);
5157               crop_info.y=(ssize_t) (windows->image.height/2L-crop_info.height/
5158                 2L);
5159               break;
5160             }
5161             case XK_Left:
5162             case XK_KP_Left:
5163             {
5164               crop_info.x--;
5165               break;
5166             }
5167             case XK_Up:
5168             case XK_KP_Up:
5169             case XK_Next:
5170             {
5171               crop_info.y--;
5172               break;
5173             }
5174             case XK_Right:
5175             case XK_KP_Right:
5176             {
5177               crop_info.x++;
5178               break;
5179             }
5180             case XK_Prior:
5181             case XK_Down:
5182             case XK_KP_Down:
5183             {
5184               crop_info.y++;
5185               break;
5186             }
5187             case XK_F1:
5188             case XK_Help:
5189             {
5190               (void) XSetFunction(display,windows->image.highlight_context,
5191                 GXcopy);
5192               switch (mode)
5193               {
5194                 case CopyMode:
5195                 {
5196                   XTextViewWidget(display,resource_info,windows,MagickFalse,
5197                     "Help Viewer - Image Copy",ImageCopyHelp);
5198                   break;
5199                 }
5200                 case CropMode:
5201                 {
5202                   XTextViewWidget(display,resource_info,windows,MagickFalse,
5203                     "Help Viewer - Image Cropg",ImageCropHelp);
5204                   break;
5205                 }
5206                 case CutMode:
5207                 {
5208                   XTextViewWidget(display,resource_info,windows,MagickFalse,
5209                     "Help Viewer - Image Cutg",ImageCutHelp);
5210                   break;
5211                 }
5212               }
5213               (void) XSetFunction(display,windows->image.highlight_context,
5214                 GXinvert);
5215               break;
5216             }
5217             default:
5218             {
5219               (void) XBell(display,0);
5220               break;
5221             }
5222           }
5223           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5224             event.xkey.time);
5225           break;
5226         }
5227         case KeyRelease:
5228           break;
5229         case MotionNotify:
5230         {
5231           if (event.xmotion.window != windows->image.id)
5232             break;
5233           /*
5234             Map and unmap Info widget as text cursor crosses its boundaries.
5235           */
5236           x=event.xmotion.x;
5237           y=event.xmotion.y;
5238           if (windows->info.mapped != MagickFalse )
5239             {
5240               if ((x < (int) (windows->info.x+windows->info.width)) &&
5241                   (y < (int) (windows->info.y+windows->info.height)))
5242                 (void) XWithdrawWindow(display,windows->info.id,
5243                   windows->info.screen);
5244             }
5245           else
5246             if ((x > (int) (windows->info.x+windows->info.width)) ||
5247                 (y > (int) (windows->info.y+windows->info.height)))
5248               (void) XMapWindow(display,windows->info.id);
5249           crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
5250           crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
5251           break;
5252         }
5253         case SelectionRequest:
5254         {
5255           XSelectionEvent
5256             notify;
5257
5258           XSelectionRequestEvent
5259             *request;
5260
5261           /*
5262             Set primary selection.
5263           */
5264           (void) FormatLocaleString(text,MagickPathExtent,
5265             "%.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
5266             crop_info.height,(double) crop_info.x,(double) crop_info.y);
5267           request=(&(event.xselectionrequest));
5268           (void) XChangeProperty(request->display,request->requestor,
5269             request->property,request->target,8,PropModeReplace,
5270             (unsigned char *) text,(int) strlen(text));
5271           notify.type=SelectionNotify;
5272           notify.display=request->display;
5273           notify.requestor=request->requestor;
5274           notify.selection=request->selection;
5275           notify.target=request->target;
5276           notify.time=request->time;
5277           if (request->property == None)
5278             notify.property=request->target;
5279           else
5280             notify.property=request->property;
5281           (void) XSendEvent(request->display,request->requestor,False,0,
5282             (XEvent *) &notify);
5283         }
5284         default:
5285           break;
5286       }
5287       if ((state & UpdateConfigurationState) != 0)
5288         {
5289           (void) XPutBackEvent(display,&event);
5290           (void) XCheckDefineCursor(display,windows->image.id,cursor);
5291           break;
5292         }
5293     } while ((state & ExitState) == 0);
5294   } while ((state & ExitState) == 0);
5295   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
5296   XSetCursorState(display,windows,MagickFalse);
5297   if ((state & EscapeState) != 0)
5298     return(MagickTrue);
5299   if (mode == CropMode)
5300     if (((int) crop_info.width != windows->image.ximage->width) ||
5301         ((int) crop_info.height != windows->image.ximage->height))
5302       {
5303         /*
5304           Reconfigure Image window as defined by cropping rectangle.
5305         */
5306         XSetCropGeometry(display,windows,&crop_info,image);
5307         windows->image.window_changes.width=(int) crop_info.width;
5308         windows->image.window_changes.height=(int) crop_info.height;
5309         (void) XConfigureImage(display,resource_info,windows,image,exception);
5310         return(MagickTrue);
5311       }
5312   /*
5313     Copy image before applying image transforms.
5314   */
5315   XSetCursorState(display,windows,MagickTrue);
5316   XCheckRefreshWindows(display,windows);
5317   width=(unsigned int) image->columns;
5318   height=(unsigned int) image->rows;
5319   x=0;
5320   y=0;
5321   if (windows->image.crop_geometry != (char *) NULL)
5322     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
5323   scale_factor=(double) width/windows->image.ximage->width;
5324   crop_info.x+=x;
5325   crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
5326   crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
5327   scale_factor=(double) height/windows->image.ximage->height;
5328   crop_info.y+=y;
5329   crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
5330   crop_info.height=(unsigned int) (scale_factor*crop_info.height+0.5);
5331   crop_image=CropImage(image,&crop_info,exception);
5332   XSetCursorState(display,windows,MagickFalse);
5333   if (crop_image == (Image *) NULL)
5334     return(MagickFalse);
5335   if (resource_info->copy_image != (Image *) NULL)
5336     resource_info->copy_image=DestroyImage(resource_info->copy_image);
5337   resource_info->copy_image=crop_image;
5338   if (mode == CopyMode)
5339     {
5340       (void) XConfigureImage(display,resource_info,windows,image,exception);
5341       return(MagickTrue);
5342     }
5343   /*
5344     Cut image.
5345   */
5346   if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
5347     return(MagickFalse);
5348   image->alpha_trait=BlendPixelTrait;
5349   image_view=AcquireAuthenticCacheView(image,exception);
5350   for (y=0; y < (int) crop_info.height; y++)
5351   {
5352     q=GetCacheViewAuthenticPixels(image_view,crop_info.x,y+crop_info.y,
5353       crop_info.width,1,exception);
5354     if (q == (Quantum *) NULL)
5355       break;
5356     for (x=0; x < (int) crop_info.width; x++)
5357     {
5358       SetPixelAlpha(image,TransparentAlpha,q);
5359       q+=GetPixelChannels(image);
5360     }
5361     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
5362       break;
5363   }
5364   image_view=DestroyCacheView(image_view);
5365   /*
5366     Update image configuration.
5367   */
5368   XConfigureImageColormap(display,resource_info,windows,image,exception);
5369   (void) XConfigureImage(display,resource_info,windows,image,exception);
5370   return(MagickTrue);
5371 }
5372 \f
5373 /*
5374 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5375 %                                                                             %
5376 %                                                                             %
5377 %                                                                             %
5378 +   X D r a w I m a g e                                                       %
5379 %                                                                             %
5380 %                                                                             %
5381 %                                                                             %
5382 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5383 %
5384 %  XDrawEditImage() draws a graphic element (point, line, rectangle, etc.) on
5385 %  the image.
5386 %
5387 %  The format of the XDrawEditImage method is:
5388 %
5389 %      MagickBooleanType XDrawEditImage(Display *display,
5390 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
5391 %        ExceptionInfo *exception)
5392 %
5393 %  A description of each parameter follows:
5394 %
5395 %    o display: Specifies a connection to an X server; returned from
5396 %      XOpenDisplay.
5397 %
5398 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
5399 %
5400 %    o windows: Specifies a pointer to a XWindows structure.
5401 %
5402 %    o image: the image.
5403 %
5404 %    o exception: return any errors or warnings in this structure.
5405 %
5406 */
5407 static MagickBooleanType XDrawEditImage(Display *display,
5408   XResourceInfo *resource_info,XWindows *windows,Image **image,
5409   ExceptionInfo *exception)
5410 {
5411   static const char
5412     *DrawMenu[] =
5413     {
5414       "Element",
5415       "Color",
5416       "Stipple",
5417       "Width",
5418       "Undo",
5419       "Help",
5420       "Dismiss",
5421       (char *) NULL
5422     };
5423
5424   static ElementType
5425     element = PointElement;
5426
5427   static const ModeType
5428     DrawCommands[] =
5429     {
5430       DrawElementCommand,
5431       DrawColorCommand,
5432       DrawStippleCommand,
5433       DrawWidthCommand,
5434       DrawUndoCommand,
5435       DrawHelpCommand,
5436       DrawDismissCommand
5437     };
5438
5439   static Pixmap
5440     stipple = (Pixmap) NULL;
5441
5442   static unsigned int
5443     pen_id = 0,
5444     line_width = 1;
5445
5446   char
5447     command[MagickPathExtent],
5448     text[MagickPathExtent];
5449
5450   Cursor
5451     cursor;
5452
5453   int
5454     entry,
5455     id,
5456     number_coordinates,
5457     x,
5458     y;
5459
5460   double
5461     degrees;
5462
5463   MagickStatusType
5464     status;
5465
5466   RectangleInfo
5467     rectangle_info;
5468
5469   register int
5470     i;
5471
5472   unsigned int
5473     distance,
5474     height,
5475     max_coordinates,
5476     width;
5477
5478   size_t
5479     state;
5480
5481   Window
5482     root_window;
5483
5484   XDrawInfo
5485     draw_info;
5486
5487   XEvent
5488     event;
5489
5490   XPoint
5491     *coordinate_info;
5492
5493   XSegment
5494     line_info;
5495
5496   /*
5497     Allocate polygon info.
5498   */
5499   max_coordinates=2048;
5500   coordinate_info=(XPoint *) AcquireQuantumMemory((size_t) max_coordinates,
5501     sizeof(*coordinate_info));
5502   if (coordinate_info == (XPoint *) NULL)
5503     {
5504       (void) ThrowMagickException(exception,GetMagickModule(),
5505         ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
5506       return(MagickFalse);
5507     }
5508   /*
5509     Map Command widget.
5510   */
5511   (void) CloneString(&windows->command.name,"Draw");
5512   windows->command.data=4;
5513   (void) XCommandWidget(display,windows,DrawMenu,(XEvent *) NULL);
5514   (void) XMapRaised(display,windows->command.id);
5515   XClientMessage(display,windows->image.id,windows->im_protocols,
5516     windows->im_update_widget,CurrentTime);
5517   /*
5518     Wait for first button press.
5519   */
5520   root_window=XRootWindow(display,XDefaultScreen(display));
5521   draw_info.stencil=OpaqueStencil;
5522   status=MagickTrue;
5523   cursor=XCreateFontCursor(display,XC_tcross);
5524   for ( ; ; )
5525   {
5526     XQueryPosition(display,windows->image.id,&x,&y);
5527     (void) XSelectInput(display,windows->image.id,
5528       windows->image.attributes.event_mask | PointerMotionMask);
5529     (void) XCheckDefineCursor(display,windows->image.id,cursor);
5530     state=DefaultState;
5531     do
5532     {
5533       if (windows->info.mapped != MagickFalse )
5534         {
5535           /*
5536             Display pointer position.
5537           */
5538           (void) FormatLocaleString(text,MagickPathExtent," %+d%+d ",
5539             x+windows->image.x,y+windows->image.y);
5540           XInfoWidget(display,windows,text);
5541         }
5542       /*
5543         Wait for next event.
5544       */
5545       XScreenEvent(display,windows,&event,exception);
5546       if (event.xany.window == windows->command.id)
5547         {
5548           /*
5549             Select a command from the Command widget.
5550           */
5551           id=XCommandWidget(display,windows,DrawMenu,&event);
5552           if (id < 0)
5553             continue;
5554           switch (DrawCommands[id])
5555           {
5556             case DrawElementCommand:
5557             {
5558               static const char
5559                 *Elements[] =
5560                 {
5561                   "point",
5562                   "line",
5563                   "rectangle",
5564                   "fill rectangle",
5565                   "circle",
5566                   "fill circle",
5567                   "ellipse",
5568                   "fill ellipse",
5569                   "polygon",
5570                   "fill polygon",
5571                   (char *) NULL,
5572                 };
5573
5574               /*
5575                 Select a command from the pop-up menu.
5576               */
5577               element=(ElementType) (XMenuWidget(display,windows,
5578                 DrawMenu[id],Elements,command)+1);
5579               break;
5580             }
5581             case DrawColorCommand:
5582             {
5583               const char
5584                 *ColorMenu[MaxNumberPens+1];
5585
5586               int
5587                 pen_number;
5588
5589               MagickBooleanType
5590                 transparent;
5591
5592               XColor
5593                 color;
5594
5595               /*
5596                 Initialize menu selections.
5597               */
5598               for (i=0; i < (int) (MaxNumberPens-2); i++)
5599                 ColorMenu[i]=resource_info->pen_colors[i];
5600               ColorMenu[MaxNumberPens-2]="transparent";
5601               ColorMenu[MaxNumberPens-1]="Browser...";
5602               ColorMenu[MaxNumberPens]=(char *) NULL;
5603               /*
5604                 Select a pen color from the pop-up menu.
5605               */
5606               pen_number=XMenuWidget(display,windows,DrawMenu[id],
5607                 (const char **) ColorMenu,command);
5608               if (pen_number < 0)
5609                 break;
5610               transparent=pen_number == (MaxNumberPens-2) ? MagickTrue :
5611                 MagickFalse;
5612               if (transparent != MagickFalse )
5613                 {
5614                   draw_info.stencil=TransparentStencil;
5615                   break;
5616                 }
5617               if (pen_number == (MaxNumberPens-1))
5618                 {
5619                   static char
5620                     color_name[MagickPathExtent] = "gray";
5621
5622                   /*
5623                     Select a pen color from a dialog.
5624                   */
5625                   resource_info->pen_colors[pen_number]=color_name;
5626                   XColorBrowserWidget(display,windows,"Select",color_name);
5627                   if (*color_name == '\0')
5628                     break;
5629                 }
5630               /*
5631                 Set pen color.
5632               */
5633               (void) XParseColor(display,windows->map_info->colormap,
5634                 resource_info->pen_colors[pen_number],&color);
5635               XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
5636                 (unsigned int) MaxColors,&color);
5637               windows->pixel_info->pen_colors[pen_number]=color;
5638               pen_id=(unsigned int) pen_number;
5639               draw_info.stencil=OpaqueStencil;
5640               break;
5641             }
5642             case DrawStippleCommand:
5643             {
5644               Image
5645                 *stipple_image;
5646
5647               ImageInfo
5648                 *image_info;
5649
5650               int
5651                 status;
5652
5653               static char
5654                 filename[MagickPathExtent] = "\0";
5655
5656               static const char
5657                 *StipplesMenu[] =
5658                 {
5659                   "Brick",
5660                   "Diagonal",
5661                   "Scales",
5662                   "Vertical",
5663                   "Wavy",
5664                   "Translucent",
5665                   "Opaque",
5666                   (char *) NULL,
5667                   (char *) NULL,
5668                 };
5669
5670               /*
5671                 Select a command from the pop-up menu.
5672               */
5673               StipplesMenu[7]="Open...";
5674               entry=XMenuWidget(display,windows,DrawMenu[id],StipplesMenu,
5675                 command);
5676               if (entry < 0)
5677                 break;
5678               if (stipple != (Pixmap) NULL)
5679                 (void) XFreePixmap(display,stipple);
5680               stipple=(Pixmap) NULL;
5681               if (entry != 7)
5682                 {
5683                   switch (entry)
5684                   {
5685                     case 0:
5686                     {
5687                       stipple=XCreateBitmapFromData(display,root_window,
5688                         (char *) BricksBitmap,BricksWidth,BricksHeight);
5689                       break;
5690                     }
5691                     case 1:
5692                     {
5693                       stipple=XCreateBitmapFromData(display,root_window,
5694                         (char *) DiagonalBitmap,DiagonalWidth,DiagonalHeight);
5695                       break;
5696                     }
5697                     case 2:
5698                     {
5699                       stipple=XCreateBitmapFromData(display,root_window,
5700                         (char *) ScalesBitmap,ScalesWidth,ScalesHeight);
5701                       break;
5702                     }
5703                     case 3:
5704                     {
5705                       stipple=XCreateBitmapFromData(display,root_window,
5706                         (char *) VerticalBitmap,VerticalWidth,VerticalHeight);
5707                       break;
5708                     }
5709                     case 4:
5710                     {
5711                       stipple=XCreateBitmapFromData(display,root_window,
5712                         (char *) WavyBitmap,WavyWidth,WavyHeight);
5713                       break;
5714                     }
5715                     case 5:
5716                     {
5717                       stipple=XCreateBitmapFromData(display,root_window,
5718                         (char *) HighlightBitmap,HighlightWidth,
5719                         HighlightHeight);
5720                       break;
5721                     }
5722                     case 6:
5723                     default:
5724                     {
5725                       stipple=XCreateBitmapFromData(display,root_window,
5726                         (char *) OpaqueBitmap,OpaqueWidth,OpaqueHeight);
5727                       break;
5728                     }
5729                   }
5730                   break;
5731                 }
5732               XFileBrowserWidget(display,windows,"Stipple",filename);
5733               if (*filename == '\0')
5734                 break;
5735               /*
5736                 Read image.
5737               */
5738               XSetCursorState(display,windows,MagickTrue);
5739               XCheckRefreshWindows(display,windows);
5740               image_info=AcquireImageInfo();
5741               (void) CopyMagickString(image_info->filename,filename,
5742                 MagickPathExtent);
5743               stipple_image=ReadImage(image_info,exception);
5744               CatchException(exception);
5745               XSetCursorState(display,windows,MagickFalse);
5746               if (stipple_image == (Image *) NULL)
5747                 break;
5748               (void) AcquireUniqueFileResource(filename);
5749               (void) FormatLocaleString(stipple_image->filename,
5750                 MagickPathExtent,"xbm:%s",filename);
5751               (void) WriteImage(image_info,stipple_image,exception);
5752               stipple_image=DestroyImage(stipple_image);
5753               image_info=DestroyImageInfo(image_info);
5754               status=XReadBitmapFile(display,root_window,filename,&width,
5755                 &height,&stipple,&x,&y);
5756               (void) RelinquishUniqueFileResource(filename);
5757               if ((status != BitmapSuccess) != 0)
5758                 XNoticeWidget(display,windows,"Unable to read X bitmap image:",
5759                   filename);
5760               break;
5761             }
5762             case DrawWidthCommand:
5763             {
5764               static char
5765                 width[MagickPathExtent] = "0";
5766
5767               static const char
5768                 *WidthsMenu[] =
5769                 {
5770                   "1",
5771                   "2",
5772                   "4",
5773                   "8",
5774                   "16",
5775                   "Dialog...",
5776                   (char *) NULL,
5777                 };
5778
5779               /*
5780                 Select a command from the pop-up menu.
5781               */
5782               entry=XMenuWidget(display,windows,DrawMenu[id],WidthsMenu,
5783                 command);
5784               if (entry < 0)
5785                 break;
5786               if (entry != 5)
5787                 {
5788                   line_width=(unsigned int) StringToUnsignedLong(
5789                     WidthsMenu[entry]);
5790                   break;
5791                 }
5792               (void) XDialogWidget(display,windows,"Ok","Enter line width:",
5793                 width);
5794               if (*width == '\0')
5795                 break;
5796               line_width=(unsigned int) StringToUnsignedLong(width);
5797               break;
5798             }
5799             case DrawUndoCommand:
5800             {
5801               (void) XMagickCommand(display,resource_info,windows,UndoCommand,
5802                 image,exception);
5803               break;
5804             }
5805             case DrawHelpCommand:
5806             {
5807               XTextViewWidget(display,resource_info,windows,MagickFalse,
5808                 "Help Viewer - Image Rotation",ImageDrawHelp);
5809               (void) XCheckDefineCursor(display,windows->image.id,cursor);
5810               break;
5811             }
5812             case DrawDismissCommand:
5813             {
5814               /*
5815                 Prematurely exit.
5816               */
5817               state|=EscapeState;
5818               state|=ExitState;
5819               break;
5820             }
5821             default:
5822               break;
5823           }
5824           (void) XCheckDefineCursor(display,windows->image.id,cursor);
5825           continue;
5826         }
5827       switch (event.type)
5828       {
5829         case ButtonPress:
5830         {
5831           if (event.xbutton.button != Button1)
5832             break;
5833           if (event.xbutton.window != windows->image.id)
5834             break;
5835           /*
5836             exit loop.
5837           */
5838           x=event.xbutton.x;
5839           y=event.xbutton.y;
5840           state|=ExitState;
5841           break;
5842         }
5843         case ButtonRelease:
5844           break;
5845         case Expose:
5846           break;
5847         case KeyPress:
5848         {
5849           KeySym
5850             key_symbol;
5851
5852           if (event.xkey.window != windows->image.id)
5853             break;
5854           /*
5855             Respond to a user key press.
5856           */
5857           (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5858             sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5859           switch ((int) key_symbol)
5860           {
5861             case XK_Escape:
5862             case XK_F20:
5863             {
5864               /*
5865                 Prematurely exit.
5866               */
5867               state|=EscapeState;
5868               state|=ExitState;
5869               break;
5870             }
5871             case XK_F1:
5872             case XK_Help:
5873             {
5874               XTextViewWidget(display,resource_info,windows,MagickFalse,
5875                 "Help Viewer - Image Rotation",ImageDrawHelp);
5876               break;
5877             }
5878             default:
5879             {
5880               (void) XBell(display,0);
5881               break;
5882             }
5883           }
5884           break;
5885         }
5886         case MotionNotify:
5887         {
5888           /*
5889             Map and unmap Info widget as text cursor crosses its boundaries.
5890           */
5891           x=event.xmotion.x;
5892           y=event.xmotion.y;
5893           if (windows->info.mapped != MagickFalse )
5894             {
5895               if ((x < (int) (windows->info.x+windows->info.width)) &&
5896                   (y < (int) (windows->info.y+windows->info.height)))
5897                 (void) XWithdrawWindow(display,windows->info.id,
5898                   windows->info.screen);
5899             }
5900           else
5901             if ((x > (int) (windows->info.x+windows->info.width)) ||
5902                 (y > (int) (windows->info.y+windows->info.height)))
5903               (void) XMapWindow(display,windows->info.id);
5904           break;
5905         }
5906       }
5907     } while ((state & ExitState) == 0);
5908     (void) XSelectInput(display,windows->image.id,
5909       windows->image.attributes.event_mask);
5910     (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
5911     if ((state & EscapeState) != 0)
5912       break;
5913     /*
5914       Draw element as pointer moves until the button is released.
5915     */
5916     distance=0;
5917     degrees=0.0;
5918     line_info.x1=x;
5919     line_info.y1=y;
5920     line_info.x2=x;
5921     line_info.y2=y;
5922     rectangle_info.x=(ssize_t) x;
5923     rectangle_info.y=(ssize_t) y;
5924     rectangle_info.width=0;
5925     rectangle_info.height=0;
5926     number_coordinates=1;
5927     coordinate_info->x=x;
5928     coordinate_info->y=y;
5929     (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
5930     state=DefaultState;
5931     do
5932     {
5933       switch (element)
5934       {
5935         case PointElement:
5936         default:
5937         {
5938           if (number_coordinates > 1)
5939             {
5940               (void) XDrawLines(display,windows->image.id,
5941                 windows->image.highlight_context,coordinate_info,
5942                 number_coordinates,CoordModeOrigin);
5943               (void) FormatLocaleString(text,MagickPathExtent," %+d%+d",
5944                 coordinate_info[number_coordinates-1].x,
5945                 coordinate_info[number_coordinates-1].y);
5946               XInfoWidget(display,windows,text);
5947             }
5948           break;
5949         }
5950         case LineElement:
5951         {
5952           if (distance > 9)
5953             {
5954               /*
5955                 Display angle of the line.
5956               */
5957               degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
5958                 line_info.y1),(double) (line_info.x2-line_info.x1)));
5959               (void) FormatLocaleString(text,MagickPathExtent," %g",
5960                 (double) degrees);
5961               XInfoWidget(display,windows,text);
5962               XHighlightLine(display,windows->image.id,
5963                 windows->image.highlight_context,&line_info);
5964             }
5965           else
5966             if (windows->info.mapped != MagickFalse )
5967               (void) XWithdrawWindow(display,windows->info.id,
5968                 windows->info.screen);
5969           break;
5970         }
5971         case RectangleElement:
5972         case FillRectangleElement:
5973         {
5974           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
5975             {
5976               /*
5977                 Display info and draw drawing rectangle.
5978               */
5979               (void) FormatLocaleString(text,MagickPathExtent,
5980                 " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
5981                 (double) rectangle_info.height,(double) rectangle_info.x,
5982                 (double) rectangle_info.y);
5983               XInfoWidget(display,windows,text);
5984               XHighlightRectangle(display,windows->image.id,
5985                 windows->image.highlight_context,&rectangle_info);
5986             }
5987           else
5988             if (windows->info.mapped != MagickFalse )
5989               (void) XWithdrawWindow(display,windows->info.id,
5990                 windows->info.screen);
5991           break;
5992         }
5993         case CircleElement:
5994         case FillCircleElement:
5995         case EllipseElement:
5996         case FillEllipseElement:
5997         {
5998           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
5999             {
6000               /*
6001                 Display info and draw drawing rectangle.
6002               */
6003               (void) FormatLocaleString(text,MagickPathExtent,
6004                 " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
6005                 (double) rectangle_info.height,(double) rectangle_info.x,
6006                 (double) rectangle_info.y);
6007               XInfoWidget(display,windows,text);
6008               XHighlightEllipse(display,windows->image.id,
6009                 windows->image.highlight_context,&rectangle_info);
6010             }
6011           else
6012             if (windows->info.mapped != MagickFalse )
6013               (void) XWithdrawWindow(display,windows->info.id,
6014                 windows->info.screen);
6015           break;
6016         }
6017         case PolygonElement:
6018         case FillPolygonElement:
6019         {
6020           if (number_coordinates > 1)
6021             (void) XDrawLines(display,windows->image.id,
6022               windows->image.highlight_context,coordinate_info,
6023               number_coordinates,CoordModeOrigin);
6024           if (distance > 9)
6025             {
6026               /*
6027                 Display angle of the line.
6028               */
6029               degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
6030                 line_info.y1),(double) (line_info.x2-line_info.x1)));
6031               (void) FormatLocaleString(text,MagickPathExtent," %g",
6032                 (double) degrees);
6033               XInfoWidget(display,windows,text);
6034               XHighlightLine(display,windows->image.id,
6035                 windows->image.highlight_context,&line_info);
6036             }
6037           else
6038             if (windows->info.mapped != MagickFalse )
6039               (void) XWithdrawWindow(display,windows->info.id,
6040                 windows->info.screen);
6041           break;
6042         }
6043       }
6044       /*
6045         Wait for next event.
6046       */
6047       XScreenEvent(display,windows,&event,exception);
6048       switch (element)
6049       {
6050         case PointElement:
6051         default:
6052         {
6053           if (number_coordinates > 1)
6054             (void) XDrawLines(display,windows->image.id,
6055               windows->image.highlight_context,coordinate_info,
6056               number_coordinates,CoordModeOrigin);
6057           break;
6058         }
6059         case LineElement:
6060         {
6061           if (distance > 9)
6062             XHighlightLine(display,windows->image.id,
6063               windows->image.highlight_context,&line_info);
6064           break;
6065         }
6066         case RectangleElement:
6067         case FillRectangleElement:
6068         {
6069           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6070             XHighlightRectangle(display,windows->image.id,
6071               windows->image.highlight_context,&rectangle_info);
6072           break;
6073         }
6074         case CircleElement:
6075         case FillCircleElement:
6076         case EllipseElement:
6077         case FillEllipseElement:
6078         {
6079           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6080             XHighlightEllipse(display,windows->image.id,
6081               windows->image.highlight_context,&rectangle_info);
6082           break;
6083         }
6084         case PolygonElement:
6085         case FillPolygonElement:
6086         {
6087           if (number_coordinates > 1)
6088             (void) XDrawLines(display,windows->image.id,
6089               windows->image.highlight_context,coordinate_info,
6090               number_coordinates,CoordModeOrigin);
6091           if (distance > 9)
6092             XHighlightLine(display,windows->image.id,
6093               windows->image.highlight_context,&line_info);
6094           break;
6095         }
6096       }
6097       switch (event.type)
6098       {
6099         case ButtonPress:
6100           break;
6101         case ButtonRelease:
6102         {
6103           /*
6104             User has committed to element.
6105           */
6106           line_info.x2=event.xbutton.x;
6107           line_info.y2=event.xbutton.y;
6108           rectangle_info.x=(ssize_t) event.xbutton.x;
6109           rectangle_info.y=(ssize_t) event.xbutton.y;
6110           coordinate_info[number_coordinates].x=event.xbutton.x;
6111           coordinate_info[number_coordinates].y=event.xbutton.y;
6112           if (((element != PolygonElement) &&
6113                (element != FillPolygonElement)) || (distance <= 9))
6114             {
6115               state|=ExitState;
6116               break;
6117             }
6118           number_coordinates++;
6119           if (number_coordinates < (int) max_coordinates)
6120             {
6121               line_info.x1=event.xbutton.x;
6122               line_info.y1=event.xbutton.y;
6123               break;
6124             }
6125           max_coordinates<<=1;
6126           coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6127             max_coordinates,sizeof(*coordinate_info));
6128           if (coordinate_info == (XPoint *) NULL)
6129             (void) ThrowMagickException(exception,GetMagickModule(),
6130               ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6131           break;
6132         }
6133         case Expose:
6134           break;
6135         case MotionNotify:
6136         {
6137           if (event.xmotion.window != windows->image.id)
6138             break;
6139           if (element != PointElement)
6140             {
6141               line_info.x2=event.xmotion.x;
6142               line_info.y2=event.xmotion.y;
6143               rectangle_info.x=(ssize_t) event.xmotion.x;
6144               rectangle_info.y=(ssize_t) event.xmotion.y;
6145               break;
6146             }
6147           coordinate_info[number_coordinates].x=event.xbutton.x;
6148           coordinate_info[number_coordinates].y=event.xbutton.y;
6149           number_coordinates++;
6150           if (number_coordinates < (int) max_coordinates)
6151             break;
6152           max_coordinates<<=1;
6153           coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6154             max_coordinates,sizeof(*coordinate_info));
6155           if (coordinate_info == (XPoint *) NULL)
6156             (void) ThrowMagickException(exception,GetMagickModule(),
6157               ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6158           break;
6159         }
6160         default:
6161           break;
6162       }
6163       /*
6164         Check boundary conditions.
6165       */
6166       if (line_info.x2 < 0)
6167         line_info.x2=0;
6168       else
6169         if (line_info.x2 > (int) windows->image.width)
6170           line_info.x2=(short) windows->image.width;
6171       if (line_info.y2 < 0)
6172         line_info.y2=0;
6173       else
6174         if (line_info.y2 > (int) windows->image.height)
6175           line_info.y2=(short) windows->image.height;
6176       distance=(unsigned int)
6177         (((line_info.x2-line_info.x1+1)*(line_info.x2-line_info.x1+1))+
6178          ((line_info.y2-line_info.y1+1)*(line_info.y2-line_info.y1+1)));
6179       if ((((int) rectangle_info.x != x) && ((int) rectangle_info.y != y)) ||
6180           ((state & ExitState) != 0))
6181         {
6182           if (rectangle_info.x < 0)
6183             rectangle_info.x=0;
6184           else
6185             if (rectangle_info.x > (ssize_t) windows->image.width)
6186               rectangle_info.x=(ssize_t) windows->image.width;
6187           if ((int) rectangle_info.x < x)
6188             rectangle_info.width=(unsigned int) (x-rectangle_info.x);
6189           else
6190             {
6191               rectangle_info.width=(unsigned int) (rectangle_info.x-x);
6192               rectangle_info.x=(ssize_t) x;
6193             }
6194           if (rectangle_info.y < 0)
6195             rectangle_info.y=0;
6196           else
6197             if (rectangle_info.y > (ssize_t) windows->image.height)
6198               rectangle_info.y=(ssize_t) windows->image.height;
6199           if ((int) rectangle_info.y < y)
6200             rectangle_info.height=(unsigned int) (y-rectangle_info.y);
6201           else
6202             {
6203               rectangle_info.height=(unsigned int) (rectangle_info.y-y);
6204               rectangle_info.y=(ssize_t) y;
6205             }
6206         }
6207     } while ((state & ExitState) == 0);
6208     (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
6209     if ((element == PointElement) || (element == PolygonElement) ||
6210         (element == FillPolygonElement))
6211       {
6212         /*
6213           Determine polygon bounding box.
6214         */
6215         rectangle_info.x=(ssize_t) coordinate_info->x;
6216         rectangle_info.y=(ssize_t) coordinate_info->y;
6217         x=coordinate_info->x;
6218         y=coordinate_info->y;
6219         for (i=1; i < number_coordinates; i++)
6220         {
6221           if (coordinate_info[i].x > x)
6222             x=coordinate_info[i].x;
6223           if (coordinate_info[i].y > y)
6224             y=coordinate_info[i].y;
6225           if ((ssize_t) coordinate_info[i].x < rectangle_info.x)
6226             rectangle_info.x=MagickMax((ssize_t) coordinate_info[i].x,0);
6227           if ((ssize_t) coordinate_info[i].y < rectangle_info.y)
6228             rectangle_info.y=MagickMax((ssize_t) coordinate_info[i].y,0);
6229         }
6230         rectangle_info.width=(size_t) (x-rectangle_info.x);
6231         rectangle_info.height=(size_t) (y-rectangle_info.y);
6232         for (i=0; i < number_coordinates; i++)
6233         {
6234           coordinate_info[i].x-=rectangle_info.x;
6235           coordinate_info[i].y-=rectangle_info.y;
6236         }
6237       }
6238     else
6239       if (distance <= 9)
6240         continue;
6241       else
6242         if ((element == RectangleElement) ||
6243             (element == CircleElement) || (element == EllipseElement))
6244           {
6245             rectangle_info.width--;
6246             rectangle_info.height--;
6247           }
6248     /*
6249       Drawing is relative to image configuration.
6250     */
6251     draw_info.x=(int) rectangle_info.x;
6252     draw_info.y=(int) rectangle_info.y;
6253     (void) XMagickCommand(display,resource_info,windows,SaveToUndoBufferCommand,
6254       image,exception);
6255     width=(unsigned int) (*image)->columns;
6256     height=(unsigned int) (*image)->rows;
6257     x=0;
6258     y=0;
6259     if (windows->image.crop_geometry != (char *) NULL)
6260       (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
6261     draw_info.x+=windows->image.x-(line_width/2);
6262     if (draw_info.x < 0)
6263       draw_info.x=0;
6264     draw_info.x=(int) (width*draw_info.x/windows->image.ximage->width);
6265     draw_info.y+=windows->image.y-(line_width/2);
6266     if (draw_info.y < 0)
6267       draw_info.y=0;
6268     draw_info.y=(int) height*draw_info.y/windows->image.ximage->height;
6269     draw_info.width=(unsigned int) rectangle_info.width+(line_width << 1);
6270     if (draw_info.width > (unsigned int) (*image)->columns)
6271       draw_info.width=(unsigned int) (*image)->columns;
6272     draw_info.height=(unsigned int) rectangle_info.height+(line_width << 1);
6273     if (draw_info.height > (unsigned int) (*image)->rows)
6274       draw_info.height=(unsigned int) (*image)->rows;
6275     (void) FormatLocaleString(draw_info.geometry,MagickPathExtent,"%ux%u%+d%+d",
6276       width*draw_info.width/windows->image.ximage->width,
6277       height*draw_info.height/windows->image.ximage->height,
6278       draw_info.x+x,draw_info.y+y);
6279     /*
6280       Initialize drawing attributes.
6281     */
6282     draw_info.degrees=0.0;
6283     draw_info.element=element;
6284     draw_info.stipple=stipple;
6285     draw_info.line_width=line_width;
6286     draw_info.line_info=line_info;
6287     if (line_info.x1 > (int) (line_width/2))
6288       draw_info.line_info.x1=(short) line_width/2;
6289     if (line_info.y1 > (int) (line_width/2))
6290       draw_info.line_info.y1=(short) line_width/2;
6291     draw_info.line_info.x2=(short) (line_info.x2-line_info.x1+(line_width/2));
6292     draw_info.line_info.y2=(short) (line_info.y2-line_info.y1+(line_width/2));
6293     if ((draw_info.line_info.x2 < 0) && (draw_info.line_info.y2 < 0))
6294       {
6295         draw_info.line_info.x2=(-draw_info.line_info.x2);
6296         draw_info.line_info.y2=(-draw_info.line_info.y2);
6297       }
6298     if (draw_info.line_info.x2 < 0)
6299       {
6300         draw_info.line_info.x2=(-draw_info.line_info.x2);
6301         Swap(draw_info.line_info.x1,draw_info.line_info.x2);
6302       }
6303     if (draw_info.line_info.y2 < 0)
6304       {
6305         draw_info.line_info.y2=(-draw_info.line_info.y2);
6306         Swap(draw_info.line_info.y1,draw_info.line_info.y2);
6307       }
6308     draw_info.rectangle_info=rectangle_info;
6309     if (draw_info.rectangle_info.x > (ssize_t) (line_width/2))
6310       draw_info.rectangle_info.x=(ssize_t) line_width/2;
6311     if (draw_info.rectangle_info.y > (ssize_t) (line_width/2))
6312       draw_info.rectangle_info.y=(ssize_t) line_width/2;
6313     draw_info.number_coordinates=(unsigned int) number_coordinates;
6314     draw_info.coordinate_info=coordinate_info;
6315     windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
6316     /*
6317       Draw element on image.
6318     */
6319     XSetCursorState(display,windows,MagickTrue);
6320     XCheckRefreshWindows(display,windows);
6321     status=XDrawImage(display,windows->pixel_info,&draw_info,*image,exception);
6322     XSetCursorState(display,windows,MagickFalse);
6323     /*
6324       Update image colormap and return to image drawing.
6325     */
6326     XConfigureImageColormap(display,resource_info,windows,*image,exception);
6327     (void) XConfigureImage(display,resource_info,windows,*image,exception);
6328   }
6329   XSetCursorState(display,windows,MagickFalse);
6330   coordinate_info=(XPoint *) RelinquishMagickMemory(coordinate_info);
6331   return(status != 0 ? MagickTrue : MagickFalse);
6332 }
6333 \f
6334 /*
6335 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6336 %                                                                             %
6337 %                                                                             %
6338 %                                                                             %
6339 +   X D r a w P a n R e c t a n g l e                                         %
6340 %                                                                             %
6341 %                                                                             %
6342 %                                                                             %
6343 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6344 %
6345 %  XDrawPanRectangle() draws a rectangle in the pan window.  The pan window
6346 %  displays a zoom image and the rectangle shows which portion of the image is
6347 %  displayed in the Image window.
6348 %
6349 %  The format of the XDrawPanRectangle method is:
6350 %
6351 %      XDrawPanRectangle(Display *display,XWindows *windows)
6352 %
6353 %  A description of each parameter follows:
6354 %
6355 %    o display: Specifies a connection to an X server;  returned from
6356 %      XOpenDisplay.
6357 %
6358 %    o windows: Specifies a pointer to a XWindows structure.
6359 %
6360 */
6361 static void XDrawPanRectangle(Display *display,XWindows *windows)
6362 {
6363   double
6364     scale_factor;
6365
6366   RectangleInfo
6367     highlight_info;
6368
6369   /*
6370     Determine dimensions of the panning rectangle.
6371   */
6372   scale_factor=(double) windows->pan.width/windows->image.ximage->width;
6373   highlight_info.x=(ssize_t) (scale_factor*windows->image.x+0.5);
6374   highlight_info.width=(unsigned int) (scale_factor*windows->image.width+0.5);
6375   scale_factor=(double)
6376     windows->pan.height/windows->image.ximage->height;
6377   highlight_info.y=(ssize_t) (scale_factor*windows->image.y+0.5);
6378   highlight_info.height=(unsigned int) (scale_factor*windows->image.height+0.5);
6379   /*
6380     Display the panning rectangle.
6381   */
6382   (void) XClearWindow(display,windows->pan.id);
6383   XHighlightRectangle(display,windows->pan.id,windows->pan.annotate_context,
6384     &highlight_info);
6385 }
6386 \f
6387 /*
6388 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6389 %                                                                             %
6390 %                                                                             %
6391 %                                                                             %
6392 +   X I m a g e C a c h e                                                     %
6393 %                                                                             %
6394 %                                                                             %
6395 %                                                                             %
6396 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6397 %
6398 %  XImageCache() handles the creation, manipulation, and destruction of the
6399 %  image cache (undo and redo buffers).
6400 %
6401 %  The format of the XImageCache method is:
6402 %
6403 %      void XImageCache(Display *display,XResourceInfo *resource_info,
6404 %        XWindows *windows,const CommandType command,Image **image,
6405 %        ExceptionInfo *exception)
6406 %
6407 %  A description of each parameter follows:
6408 %
6409 %    o display: Specifies a connection to an X server; returned from
6410 %      XOpenDisplay.
6411 %
6412 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6413 %
6414 %    o windows: Specifies a pointer to a XWindows structure.
6415 %
6416 %    o command: Specifies a command to perform.
6417 %
6418 %    o image: the image;  XImageCache may transform the image and return a new
6419 %      image pointer.
6420 %
6421 %    o exception: return any errors or warnings in this structure.
6422 %
6423 */
6424 static void XImageCache(Display *display,XResourceInfo *resource_info,
6425   XWindows *windows,const CommandType command,Image **image,
6426   ExceptionInfo *exception)
6427 {
6428   Image
6429     *cache_image;
6430
6431   static Image
6432     *redo_image = (Image *) NULL,
6433     *undo_image = (Image *) NULL;
6434
6435   switch (command)
6436   {
6437     case FreeBuffersCommand:
6438     {
6439       /*
6440         Free memory from the undo and redo cache.
6441       */
6442       while (undo_image != (Image *) NULL)
6443       {
6444         cache_image=undo_image;
6445         undo_image=GetPreviousImageInList(undo_image);
6446         cache_image->list=DestroyImage(cache_image->list);
6447         cache_image=DestroyImage(cache_image);
6448       }
6449       undo_image=NewImageList();
6450       if (redo_image != (Image *) NULL)
6451         redo_image=DestroyImage(redo_image);
6452       redo_image=NewImageList();
6453       return;
6454     }
6455     case UndoCommand:
6456     {
6457       char
6458         image_geometry[MagickPathExtent];
6459
6460       /*
6461         Undo the last image transformation.
6462       */
6463       if (undo_image == (Image *) NULL)
6464         {
6465           (void) XBell(display,0);
6466           return;
6467         }
6468       cache_image=undo_image;
6469       undo_image=GetPreviousImageInList(undo_image);
6470       windows->image.window_changes.width=(int) cache_image->columns;
6471       windows->image.window_changes.height=(int) cache_image->rows;
6472       (void) FormatLocaleString(image_geometry,MagickPathExtent,"%dx%d!",
6473         windows->image.ximage->width,windows->image.ximage->height);
6474       (void) TransformImage(image,windows->image.crop_geometry,image_geometry,
6475         exception);
6476       if (windows->image.crop_geometry != (char *) NULL)
6477         windows->image.crop_geometry=(char *) RelinquishMagickMemory(
6478           windows->image.crop_geometry);
6479       windows->image.crop_geometry=cache_image->geometry;
6480       if (redo_image != (Image *) NULL)
6481         redo_image=DestroyImage(redo_image);
6482       redo_image=(*image);
6483       *image=cache_image->list;
6484       cache_image=DestroyImage(cache_image);
6485       if (windows->image.orphan != MagickFalse )
6486         return;
6487       XConfigureImageColormap(display,resource_info,windows,*image,exception);
6488       (void) XConfigureImage(display,resource_info,windows,*image,exception);
6489       return;
6490     }
6491     case CutCommand:
6492     case PasteCommand:
6493     case ApplyCommand:
6494     case HalfSizeCommand:
6495     case OriginalSizeCommand:
6496     case DoubleSizeCommand:
6497     case ResizeCommand:
6498     case TrimCommand:
6499     case CropCommand:
6500     case ChopCommand:
6501     case FlipCommand:
6502     case FlopCommand:
6503     case RotateRightCommand:
6504     case RotateLeftCommand:
6505     case RotateCommand:
6506     case ShearCommand:
6507     case RollCommand:
6508     case NegateCommand:
6509     case ContrastStretchCommand:
6510     case SigmoidalContrastCommand:
6511     case NormalizeCommand:
6512     case EqualizeCommand:
6513     case HueCommand:
6514     case SaturationCommand:
6515     case BrightnessCommand:
6516     case GammaCommand:
6517     case SpiffCommand:
6518     case DullCommand:
6519     case GrayscaleCommand:
6520     case MapCommand:
6521     case QuantizeCommand:
6522     case DespeckleCommand:
6523     case EmbossCommand:
6524     case ReduceNoiseCommand:
6525     case AddNoiseCommand:
6526     case SharpenCommand:
6527     case BlurCommand:
6528     case ThresholdCommand:
6529     case EdgeDetectCommand:
6530     case SpreadCommand:
6531     case ShadeCommand:
6532     case RaiseCommand:
6533     case SegmentCommand:
6534     case SolarizeCommand:
6535     case SepiaToneCommand:
6536     case SwirlCommand:
6537     case ImplodeCommand:
6538     case VignetteCommand:
6539     case WaveCommand:
6540     case OilPaintCommand:
6541     case CharcoalDrawCommand:
6542     case AnnotateCommand:
6543     case AddBorderCommand:
6544     case AddFrameCommand:
6545     case CompositeCommand:
6546     case CommentCommand:
6547     case LaunchCommand:
6548     case RegionofInterestCommand:
6549     case SaveToUndoBufferCommand:
6550     case RedoCommand:
6551     {
6552       Image
6553         *previous_image;
6554
6555       ssize_t
6556         bytes;
6557
6558       bytes=(ssize_t) ((*image)->columns*(*image)->rows*sizeof(PixelInfo));
6559       if (undo_image != (Image *) NULL)
6560         {
6561           /*
6562             Ensure the undo cache has enough memory available.
6563           */
6564           previous_image=undo_image;
6565           while (previous_image != (Image *) NULL)
6566           {
6567             bytes+=previous_image->list->columns*previous_image->list->rows*
6568               sizeof(PixelInfo);
6569             if (bytes <= (ssize_t) (resource_info->undo_cache << 20))
6570               {
6571                 previous_image=GetPreviousImageInList(previous_image);
6572                 continue;
6573               }
6574             bytes-=previous_image->list->columns*previous_image->list->rows*
6575               sizeof(PixelInfo);
6576             if (previous_image == undo_image)
6577               undo_image=NewImageList();
6578             else
6579               previous_image->next->previous=NewImageList();
6580             break;
6581           }
6582           while (previous_image != (Image *) NULL)
6583           {
6584             /*
6585               Delete any excess memory from undo cache.
6586             */
6587             cache_image=previous_image;
6588             previous_image=GetPreviousImageInList(previous_image);
6589             cache_image->list=DestroyImage(cache_image->list);
6590             cache_image=DestroyImage(cache_image);
6591           }
6592         }
6593       if (bytes > (ssize_t) (resource_info->undo_cache << 20))
6594         break;
6595       /*
6596         Save image before transformations are applied.
6597       */
6598       cache_image=AcquireImage((ImageInfo *) NULL,exception);
6599       if (cache_image == (Image *) NULL)
6600         break;
6601       XSetCursorState(display,windows,MagickTrue);
6602       XCheckRefreshWindows(display,windows);
6603       cache_image->list=CloneImage(*image,0,0,MagickTrue,exception);
6604       XSetCursorState(display,windows,MagickFalse);
6605       if (cache_image->list == (Image *) NULL)
6606         {
6607           cache_image=DestroyImage(cache_image);
6608           break;
6609         }
6610       cache_image->columns=(size_t) windows->image.ximage->width;
6611       cache_image->rows=(size_t) windows->image.ximage->height;
6612       cache_image->geometry=windows->image.crop_geometry;
6613       if (windows->image.crop_geometry != (char *) NULL)
6614         {
6615           cache_image->geometry=AcquireString((char *) NULL);
6616           (void) CopyMagickString(cache_image->geometry,
6617             windows->image.crop_geometry,MagickPathExtent);
6618         }
6619       if (undo_image == (Image *) NULL)
6620         {
6621           undo_image=cache_image;
6622           break;
6623         }
6624       undo_image->next=cache_image;
6625       undo_image->next->previous=undo_image;
6626       undo_image=undo_image->next;
6627       break;
6628     }
6629     default:
6630       break;
6631   }
6632   if (command == RedoCommand)
6633     {
6634       /*
6635         Redo the last image transformation.
6636       */
6637       if (redo_image == (Image *) NULL)
6638         {
6639           (void) XBell(display,0);
6640           return;
6641         }
6642       windows->image.window_changes.width=(int) redo_image->columns;
6643       windows->image.window_changes.height=(int) redo_image->rows;
6644       if (windows->image.crop_geometry != (char *) NULL)
6645         windows->image.crop_geometry=(char *)
6646           RelinquishMagickMemory(windows->image.crop_geometry);
6647       windows->image.crop_geometry=redo_image->geometry;
6648       *image=DestroyImage(*image);
6649       *image=redo_image;
6650       redo_image=NewImageList();
6651       if (windows->image.orphan != MagickFalse )
6652         return;
6653       XConfigureImageColormap(display,resource_info,windows,*image,exception);
6654       (void) XConfigureImage(display,resource_info,windows,*image,exception);
6655       return;
6656     }
6657   if (command != InfoCommand)
6658     return;
6659   /*
6660     Display image info.
6661   */
6662   XSetCursorState(display,windows,MagickTrue);
6663   XCheckRefreshWindows(display,windows);
6664   XDisplayImageInfo(display,resource_info,windows,undo_image,*image,exception);
6665   XSetCursorState(display,windows,MagickFalse);
6666 }
6667 \f
6668 /*
6669 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6670 %                                                                             %
6671 %                                                                             %
6672 %                                                                             %
6673 +   X I m a g e W i n d o w C o m m a n d                                     %
6674 %                                                                             %
6675 %                                                                             %
6676 %                                                                             %
6677 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6678 %
6679 %  XImageWindowCommand() makes a transform to the image or Image window as
6680 %  specified by a user menu button or keyboard command.
6681 %
6682 %  The format of the XImageWindowCommand method is:
6683 %
6684 %      CommandType XImageWindowCommand(Display *display,
6685 %        XResourceInfo *resource_info,XWindows *windows,
6686 %        const MagickStatusType state,KeySym key_symbol,Image **image,
6687 %        ExceptionInfo *exception)
6688 %
6689 %  A description of each parameter follows:
6690 %
6691 %    o nexus:  Method XImageWindowCommand returns an image when the
6692 %      user chooses 'Open Image' from the command menu.  Otherwise a null
6693 %      image is returned.
6694 %
6695 %    o display: Specifies a connection to an X server; returned from
6696 %      XOpenDisplay.
6697 %
6698 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6699 %
6700 %    o windows: Specifies a pointer to a XWindows structure.
6701 %
6702 %    o state: key mask.
6703 %
6704 %    o key_symbol: Specifies a command to perform.
6705 %
6706 %    o image: the image;  XImageWIndowCommand may transform the image and
6707 %      return a new image pointer.
6708 %
6709 %    o exception: return any errors or warnings in this structure.
6710 %
6711 */
6712 static CommandType XImageWindowCommand(Display *display,
6713   XResourceInfo *resource_info,XWindows *windows,const MagickStatusType state,
6714   KeySym key_symbol,Image **image,ExceptionInfo *exception)
6715 {
6716   static char
6717     delta[MagickPathExtent] = "";
6718
6719   static const char
6720     Digits[] = "01234567890";
6721
6722   static KeySym
6723     last_symbol = XK_0;
6724
6725   if ((key_symbol >= XK_0) && (key_symbol <= XK_9))
6726     {
6727       if (((last_symbol < XK_0) || (last_symbol > XK_9)))
6728         {
6729           *delta='\0';
6730           resource_info->quantum=1;
6731         }
6732       last_symbol=key_symbol;
6733       delta[strlen(delta)+1]='\0';
6734       delta[strlen(delta)]=Digits[key_symbol-XK_0];
6735       resource_info->quantum=StringToLong(delta);
6736       return(NullCommand);
6737     }
6738   last_symbol=key_symbol;
6739   if (resource_info->immutable)
6740     {
6741       /*
6742         Virtual image window has a restricted command set.
6743       */
6744       switch (key_symbol)
6745       {
6746         case XK_question:
6747           return(InfoCommand);
6748         case XK_p:
6749         case XK_Print:
6750           return(PrintCommand);
6751         case XK_space:
6752           return(NextCommand);
6753         case XK_q:
6754         case XK_Escape:
6755           return(QuitCommand);
6756         default:
6757           break;
6758       }
6759       return(NullCommand);
6760     }
6761   switch ((int) key_symbol)
6762   {
6763     case XK_o:
6764     {
6765       if ((state & ControlMask) == 0)
6766         break;
6767       return(OpenCommand);
6768     }
6769     case XK_space:
6770       return(NextCommand);
6771     case XK_BackSpace:
6772       return(FormerCommand);
6773     case XK_s:
6774     {
6775       if ((state & Mod1Mask) != 0)
6776         return(SwirlCommand);
6777       if ((state & ControlMask) == 0)
6778         return(ShearCommand);
6779       return(SaveCommand);
6780     }
6781     case XK_p:
6782     case XK_Print:
6783     {
6784       if ((state & Mod1Mask) != 0)
6785         return(OilPaintCommand);
6786       if ((state & Mod4Mask) != 0)
6787         return(ColorCommand);
6788       if ((state & ControlMask) == 0)
6789         return(NullCommand);
6790       return(PrintCommand);
6791     }
6792     case XK_d:
6793     {
6794       if ((state & Mod4Mask) != 0)
6795         return(DrawCommand);
6796       if ((state & ControlMask) == 0)
6797         return(NullCommand);
6798       return(DeleteCommand);
6799     }
6800     case XK_Select:
6801     {
6802       if ((state & ControlMask) == 0)
6803         return(NullCommand);
6804       return(SelectCommand);
6805     }
6806     case XK_n:
6807     {
6808       if ((state & ControlMask) == 0)
6809         return(NullCommand);
6810       return(NewCommand);
6811     }
6812     case XK_q:
6813     case XK_Escape:
6814       return(QuitCommand);
6815     case XK_z:
6816     case XK_Undo:
6817     {
6818       if ((state & ControlMask) == 0)
6819         return(NullCommand);
6820       return(UndoCommand);
6821     }
6822     case XK_r:
6823     case XK_Redo:
6824     {
6825       if ((state & ControlMask) == 0)
6826         return(RollCommand);
6827       return(RedoCommand);
6828     }
6829     case XK_x:
6830     {
6831       if ((state & ControlMask) == 0)
6832         return(NullCommand);
6833       return(CutCommand);
6834     }
6835     case XK_c:
6836     {
6837       if ((state & Mod1Mask) != 0)
6838         return(CharcoalDrawCommand);
6839       if ((state & ControlMask) == 0)
6840         return(CropCommand);
6841       return(CopyCommand);
6842     }
6843     case XK_v:
6844     case XK_Insert:
6845     {
6846       if ((state & Mod4Mask) != 0)
6847         return(CompositeCommand);
6848       if ((state & ControlMask) == 0)
6849         return(FlipCommand);
6850       return(PasteCommand);
6851     }
6852     case XK_less:
6853       return(HalfSizeCommand);
6854     case XK_minus:
6855       return(OriginalSizeCommand);
6856     case XK_greater:
6857       return(DoubleSizeCommand);
6858     case XK_percent:
6859       return(ResizeCommand);
6860     case XK_at:
6861       return(RefreshCommand);
6862     case XK_bracketleft:
6863       return(ChopCommand);
6864     case XK_h:
6865       return(FlopCommand);
6866     case XK_slash:
6867       return(RotateRightCommand);
6868     case XK_backslash:
6869       return(RotateLeftCommand);
6870     case XK_asterisk:
6871       return(RotateCommand);
6872     case XK_t:
6873       return(TrimCommand);
6874     case XK_H:
6875       return(HueCommand);
6876     case XK_S:
6877       return(SaturationCommand);
6878     case XK_L:
6879       return(BrightnessCommand);
6880     case XK_G:
6881       return(GammaCommand);
6882     case XK_C:
6883       return(SpiffCommand);
6884     case XK_Z:
6885       return(DullCommand);
6886     case XK_N:
6887       return(NormalizeCommand);
6888     case XK_equal:
6889       return(EqualizeCommand);
6890     case XK_asciitilde:
6891       return(NegateCommand);
6892     case XK_period:
6893       return(GrayscaleCommand);
6894     case XK_numbersign:
6895       return(QuantizeCommand);
6896     case XK_F2:
6897       return(DespeckleCommand);
6898     case XK_F3:
6899       return(EmbossCommand);
6900     case XK_F4:
6901       return(ReduceNoiseCommand);
6902     case XK_F5:
6903       return(AddNoiseCommand);
6904     case XK_F6:
6905       return(SharpenCommand);
6906     case XK_F7:
6907       return(BlurCommand);
6908     case XK_F8:
6909       return(ThresholdCommand);
6910     case XK_F9:
6911       return(EdgeDetectCommand);
6912     case XK_F10:
6913       return(SpreadCommand);
6914     case XK_F11:
6915       return(ShadeCommand);
6916     case XK_F12:
6917       return(RaiseCommand);
6918     case XK_F13:
6919       return(SegmentCommand);
6920     case XK_i:
6921     {
6922       if ((state & Mod1Mask) == 0)
6923         return(NullCommand);
6924       return(ImplodeCommand);
6925     }
6926     case XK_w:
6927     {
6928       if ((state & Mod1Mask) == 0)
6929         return(NullCommand);
6930       return(WaveCommand);
6931     }
6932     case XK_m:
6933     {
6934       if ((state & Mod4Mask) == 0)
6935         return(NullCommand);
6936       return(MatteCommand);
6937     }
6938     case XK_b:
6939     {
6940       if ((state & Mod4Mask) == 0)
6941         return(NullCommand);
6942       return(AddBorderCommand);
6943     }
6944     case XK_f:
6945     {
6946       if ((state & Mod4Mask) == 0)
6947         return(NullCommand);
6948       return(AddFrameCommand);
6949     }
6950     case XK_exclam:
6951     {
6952       if ((state & Mod4Mask) == 0)
6953         return(NullCommand);
6954       return(CommentCommand);
6955     }
6956     case XK_a:
6957     {
6958       if ((state & Mod1Mask) != 0)
6959         return(ApplyCommand);
6960       if ((state & Mod4Mask) != 0)
6961         return(AnnotateCommand);
6962       if ((state & ControlMask) == 0)
6963         return(NullCommand);
6964       return(RegionofInterestCommand);
6965     }
6966     case XK_question:
6967       return(InfoCommand);
6968     case XK_plus:
6969       return(ZoomCommand);
6970     case XK_P:
6971     {
6972       if ((state & ShiftMask) == 0)
6973         return(NullCommand);
6974       return(ShowPreviewCommand);
6975     }
6976     case XK_Execute:
6977       return(LaunchCommand);
6978     case XK_F1:
6979       return(HelpCommand);
6980     case XK_Find:
6981       return(BrowseDocumentationCommand);
6982     case XK_Menu:
6983     {
6984       (void) XMapRaised(display,windows->command.id);
6985       return(NullCommand);
6986     }
6987     case XK_Next:
6988     case XK_Prior:
6989     case XK_Home:
6990     case XK_KP_Home:
6991     {
6992       XTranslateImage(display,windows,*image,key_symbol);
6993       return(NullCommand);
6994     }
6995     case XK_Up:
6996     case XK_KP_Up:
6997     case XK_Down:
6998     case XK_KP_Down:
6999     case XK_Left:
7000     case XK_KP_Left:
7001     case XK_Right:
7002     case XK_KP_Right:
7003     {
7004       if ((state & Mod1Mask) != 0)
7005         {
7006           RectangleInfo
7007             crop_info;
7008
7009           /*
7010             Trim one pixel from edge of image.
7011           */
7012           crop_info.x=0;
7013           crop_info.y=0;
7014           crop_info.width=(size_t) windows->image.ximage->width;
7015           crop_info.height=(size_t) windows->image.ximage->height;
7016           if ((key_symbol == XK_Up) || (key_symbol == XK_KP_Up))
7017             {
7018               if (resource_info->quantum >= (int) crop_info.height)
7019                 resource_info->quantum=(int) crop_info.height-1;
7020               crop_info.height-=resource_info->quantum;
7021             }
7022           if ((key_symbol == XK_Down) || (key_symbol == XK_KP_Down))
7023             {
7024               if (resource_info->quantum >= (int) (crop_info.height-crop_info.y))
7025                 resource_info->quantum=(int) (crop_info.height-crop_info.y-1);
7026               crop_info.y+=resource_info->quantum;
7027               crop_info.height-=resource_info->quantum;
7028             }
7029           if ((key_symbol == XK_Left) || (key_symbol == XK_KP_Left))
7030             {
7031               if (resource_info->quantum >= (int) crop_info.width)
7032                 resource_info->quantum=(int) crop_info.width-1;
7033               crop_info.width-=resource_info->quantum;
7034             }
7035           if ((key_symbol == XK_Right) || (key_symbol == XK_KP_Right))
7036             {
7037               if (resource_info->quantum >= (int) (crop_info.width-crop_info.x))
7038                 resource_info->quantum=(int) (crop_info.width-crop_info.x-1);
7039               crop_info.x+=resource_info->quantum;
7040               crop_info.width-=resource_info->quantum;
7041             }
7042           if ((int) (windows->image.x+windows->image.width) >
7043               (int) crop_info.width)
7044             windows->image.x=(int) (crop_info.width-windows->image.width);
7045           if ((int) (windows->image.y+windows->image.height) >
7046               (int) crop_info.height)
7047             windows->image.y=(int) (crop_info.height-windows->image.height);
7048           XSetCropGeometry(display,windows,&crop_info,*image);
7049           windows->image.window_changes.width=(int) crop_info.width;
7050           windows->image.window_changes.height=(int) crop_info.height;
7051           (void) XSetWindowBackgroundPixmap(display,windows->image.id,None);
7052           (void) XConfigureImage(display,resource_info,windows,*image,
7053             exception);
7054           return(NullCommand);
7055         }
7056       XTranslateImage(display,windows,*image,key_symbol);
7057       return(NullCommand);
7058     }
7059     default:
7060       return(NullCommand);
7061   }
7062   return(NullCommand);
7063 }
7064 \f
7065 /*
7066 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7067 %                                                                             %
7068 %                                                                             %
7069 %                                                                             %
7070 +   X M a g i c k C o m m a n d                                               %
7071 %                                                                             %
7072 %                                                                             %
7073 %                                                                             %
7074 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7075 %
7076 %  XMagickCommand() makes a transform to the image or Image window as
7077 %  specified by a user menu button or keyboard command.
7078 %
7079 %  The format of the XMagickCommand method is:
7080 %
7081 %      Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7082 %        XWindows *windows,const CommandType command,Image **image,
7083 %        ExceptionInfo *exception)
7084 %
7085 %  A description of each parameter follows:
7086 %
7087 %    o display: Specifies a connection to an X server; returned from
7088 %      XOpenDisplay.
7089 %
7090 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
7091 %
7092 %    o windows: Specifies a pointer to a XWindows structure.
7093 %
7094 %    o command: Specifies a command to perform.
7095 %
7096 %    o image: the image;  XMagickCommand may transform the image and return a
7097 %      new image pointer.
7098 %
7099 %    o exception: return any errors or warnings in this structure.
7100 %
7101 */
7102 static Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7103   XWindows *windows,const CommandType command,Image **image,
7104   ExceptionInfo *exception)
7105 {
7106   char
7107     filename[MagickPathExtent],
7108     geometry[MagickPathExtent],
7109     modulate_factors[MagickPathExtent];
7110
7111   GeometryInfo
7112     geometry_info;
7113
7114   Image
7115     *nexus;
7116
7117   ImageInfo
7118     *image_info;
7119
7120   int
7121     x,
7122     y;
7123
7124   MagickStatusType
7125     flags,
7126     status;
7127
7128   QuantizeInfo
7129     quantize_info;
7130
7131   RectangleInfo
7132     page_geometry;
7133
7134   register int
7135     i;
7136
7137   static char
7138     color[MagickPathExtent] = "gray";
7139
7140   unsigned int
7141     height,
7142     width;
7143
7144   /*
7145     Process user command.
7146   */
7147   XCheckRefreshWindows(display,windows);
7148   XImageCache(display,resource_info,windows,command,image,exception);
7149   nexus=NewImageList();
7150   windows->image.window_changes.width=windows->image.ximage->width;
7151   windows->image.window_changes.height=windows->image.ximage->height;
7152   image_info=CloneImageInfo(resource_info->image_info);
7153   SetGeometryInfo(&geometry_info);
7154   GetQuantizeInfo(&quantize_info);
7155   switch (command)
7156   {
7157     case OpenCommand:
7158     {
7159       /*
7160         Load image.
7161       */
7162       nexus=XOpenImage(display,resource_info,windows,MagickFalse);
7163       break;
7164     }
7165     case NextCommand:
7166     {
7167       /*
7168         Display next image.
7169       */
7170       for (i=0; i < resource_info->quantum; i++)
7171         XClientMessage(display,windows->image.id,windows->im_protocols,
7172           windows->im_next_image,CurrentTime);
7173       break;
7174     }
7175     case FormerCommand:
7176     {
7177       /*
7178         Display former image.
7179       */
7180       for (i=0; i < resource_info->quantum; i++)
7181         XClientMessage(display,windows->image.id,windows->im_protocols,
7182           windows->im_former_image,CurrentTime);
7183       break;
7184     }
7185     case SelectCommand:
7186     {
7187       int
7188         status;
7189
7190       /*
7191         Select image.
7192       */
7193       if (*resource_info->home_directory == '\0')
7194         (void) CopyMagickString(resource_info->home_directory,".",
7195           MagickPathExtent);
7196       status=chdir(resource_info->home_directory);
7197       if (status == -1)
7198         (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
7199           "UnableToOpenFile","%s",resource_info->home_directory);
7200       nexus=XOpenImage(display,resource_info,windows,MagickTrue);
7201       break;
7202     }
7203     case SaveCommand:
7204     {
7205       /*
7206         Save image.
7207       */
7208       status=XSaveImage(display,resource_info,windows,*image,exception);
7209       if (status == MagickFalse)
7210         {
7211           char
7212             message[MagickPathExtent];
7213
7214           (void) FormatLocaleString(message,MagickPathExtent,"%s:%s",
7215             exception->reason != (char *) NULL ? exception->reason : "",
7216             exception->description != (char *) NULL ? exception->description :
7217             "");
7218           XNoticeWidget(display,windows,"Unable to save file:",message);
7219           break;
7220         }
7221       break;
7222     }
7223     case PrintCommand:
7224     {
7225       /*
7226         Print image.
7227       */
7228       status=XPrintImage(display,resource_info,windows,*image,exception);
7229       if (status == MagickFalse)
7230         {
7231           char
7232             message[MagickPathExtent];
7233
7234           (void) FormatLocaleString(message,MagickPathExtent,"%s:%s",
7235             exception->reason != (char *) NULL ? exception->reason : "",
7236             exception->description != (char *) NULL ? exception->description :
7237             "");
7238           XNoticeWidget(display,windows,"Unable to print file:",message);
7239           break;
7240         }
7241       break;
7242     }
7243     case DeleteCommand:
7244     {
7245       static char
7246         filename[MagickPathExtent] = "\0";
7247
7248       /*
7249         Delete image file.
7250       */
7251       XFileBrowserWidget(display,windows,"Delete",filename);
7252       if (*filename == '\0')
7253         break;
7254       status=ShredFile(filename);
7255       if (status != MagickFalse )
7256         XNoticeWidget(display,windows,"Unable to delete image file:",filename);
7257       break;
7258     }
7259     case NewCommand:
7260     {
7261       int
7262         status;
7263
7264       static char
7265         color[MagickPathExtent] = "gray",
7266         geometry[MagickPathExtent] = "640x480";
7267
7268       static const char
7269         *format = "gradient";
7270
7271       /*
7272         Query user for canvas geometry.
7273       */
7274       status=XDialogWidget(display,windows,"New","Enter image geometry:",
7275         geometry);
7276       if (*geometry == '\0')
7277         break;
7278       if (status == 0)
7279         format="xc";
7280       XColorBrowserWidget(display,windows,"Select",color);
7281       if (*color == '\0')
7282         break;
7283       /*
7284         Create canvas.
7285       */
7286       (void) FormatLocaleString(image_info->filename,MagickPathExtent,
7287         "%s:%s",format,color);
7288       (void) CloneString(&image_info->size,geometry);
7289       nexus=ReadImage(image_info,exception);
7290       CatchException(exception);
7291       XClientMessage(display,windows->image.id,windows->im_protocols,
7292         windows->im_next_image,CurrentTime);
7293       break;
7294     }
7295     case VisualDirectoryCommand:
7296     {
7297       /*
7298         Visual Image directory.
7299       */
7300       nexus=XVisualDirectoryImage(display,resource_info,windows,exception);
7301       break;
7302     }
7303     case QuitCommand:
7304     {
7305       /*
7306         exit program.
7307       */
7308       if (resource_info->confirm_exit == MagickFalse)
7309         XClientMessage(display,windows->image.id,windows->im_protocols,
7310           windows->im_exit,CurrentTime);
7311       else
7312         {
7313           int
7314             status;
7315
7316           /*
7317             Confirm program exit.
7318           */
7319           status=XConfirmWidget(display,windows,"Do you really want to exit",
7320             resource_info->client_name);
7321           if (status > 0)
7322             XClientMessage(display,windows->image.id,windows->im_protocols,
7323               windows->im_exit,CurrentTime);
7324         }
7325       break;
7326     }
7327     case CutCommand:
7328     {
7329       /*
7330         Cut image.
7331       */
7332       (void) XCropImage(display,resource_info,windows,*image,CutMode,exception);
7333       break;
7334     }
7335     case CopyCommand:
7336     {
7337       /*
7338         Copy image.
7339       */
7340       (void) XCropImage(display,resource_info,windows,*image,CopyMode,
7341         exception);
7342       break;
7343     }
7344     case PasteCommand:
7345     {
7346       /*
7347         Paste image.
7348       */
7349       status=XPasteImage(display,resource_info,windows,*image,exception);
7350       if (status == MagickFalse)
7351         {
7352           XNoticeWidget(display,windows,"Unable to paste X image",
7353             (*image)->filename);
7354           break;
7355         }
7356       break;
7357     }
7358     case HalfSizeCommand:
7359     {
7360       /*
7361         Half image size.
7362       */
7363       windows->image.window_changes.width=windows->image.ximage->width/2;
7364       windows->image.window_changes.height=windows->image.ximage->height/2;
7365       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7366       break;
7367     }
7368     case OriginalSizeCommand:
7369     {
7370       /*
7371         Original image size.
7372       */
7373       windows->image.window_changes.width=(int) (*image)->columns;
7374       windows->image.window_changes.height=(int) (*image)->rows;
7375       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7376       break;
7377     }
7378     case DoubleSizeCommand:
7379     {
7380       /*
7381         Double the image size.
7382       */
7383       windows->image.window_changes.width=windows->image.ximage->width << 1;
7384       windows->image.window_changes.height=windows->image.ximage->height << 1;
7385       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7386       break;
7387     }
7388     case ResizeCommand:
7389     {
7390       int
7391         status;
7392
7393       size_t
7394         height,
7395         width;
7396
7397       ssize_t
7398         x,
7399         y;
7400
7401       /*
7402         Resize image.
7403       */
7404       width=(size_t) windows->image.ximage->width;
7405       height=(size_t) windows->image.ximage->height;
7406       x=0;
7407       y=0;
7408       (void) FormatLocaleString(geometry,MagickPathExtent,"%.20gx%.20g+0+0",
7409         (double) width,(double) height);
7410       status=XDialogWidget(display,windows,"Resize",
7411         "Enter resize geometry (e.g. 640x480, 200%):",geometry);
7412       if (*geometry == '\0')
7413         break;
7414       if (status == 0)
7415         (void) ConcatenateMagickString(geometry,"!",MagickPathExtent);
7416       (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
7417       windows->image.window_changes.width=(int) width;
7418       windows->image.window_changes.height=(int) height;
7419       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7420       break;
7421     }
7422     case ApplyCommand:
7423     {
7424       char
7425         image_geometry[MagickPathExtent];
7426
7427       if ((windows->image.crop_geometry == (char *) NULL) &&
7428           ((int) (*image)->columns == windows->image.ximage->width) &&
7429           ((int) (*image)->rows == windows->image.ximage->height))
7430         break;
7431       /*
7432         Apply size transforms to image.
7433       */
7434       XSetCursorState(display,windows,MagickTrue);
7435       XCheckRefreshWindows(display,windows);
7436       /*
7437         Crop and/or scale displayed image.
7438       */
7439       (void) FormatLocaleString(image_geometry,MagickPathExtent,"%dx%d!",
7440         windows->image.ximage->width,windows->image.ximage->height);
7441       (void) TransformImage(image,windows->image.crop_geometry,image_geometry,
7442         exception);
7443       if (windows->image.crop_geometry != (char *) NULL)
7444         windows->image.crop_geometry=(char *) RelinquishMagickMemory(
7445           windows->image.crop_geometry);
7446       windows->image.x=0;
7447       windows->image.y=0;
7448       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7449       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7450       break;
7451     }
7452     case RefreshCommand:
7453     {
7454       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7455       break;
7456     }
7457     case RestoreCommand:
7458     {
7459       /*
7460         Restore Image window to its original size.
7461       */
7462       if ((windows->image.width == (unsigned int) (*image)->columns) &&
7463           (windows->image.height == (unsigned int) (*image)->rows) &&
7464           (windows->image.crop_geometry == (char *) NULL))
7465         {
7466           (void) XBell(display,0);
7467           break;
7468         }
7469       windows->image.window_changes.width=(int) (*image)->columns;
7470       windows->image.window_changes.height=(int) (*image)->rows;
7471       if (windows->image.crop_geometry != (char *) NULL)
7472         {
7473           windows->image.crop_geometry=(char *)
7474             RelinquishMagickMemory(windows->image.crop_geometry);
7475           windows->image.crop_geometry=(char *) NULL;
7476           windows->image.x=0;
7477           windows->image.y=0;
7478         }
7479       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7480       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7481       break;
7482     }
7483     case CropCommand:
7484     {
7485       /*
7486         Crop image.
7487       */
7488       (void) XCropImage(display,resource_info,windows,*image,CropMode,
7489         exception);
7490       break;
7491     }
7492     case ChopCommand:
7493     {
7494       /*
7495         Chop image.
7496       */
7497       status=XChopImage(display,resource_info,windows,image,exception);
7498       if (status == MagickFalse)
7499         {
7500           XNoticeWidget(display,windows,"Unable to cut X image",
7501             (*image)->filename);
7502           break;
7503         }
7504       break;
7505     }
7506     case FlopCommand:
7507     {
7508       Image
7509         *flop_image;
7510
7511       /*
7512         Flop image scanlines.
7513       */
7514       XSetCursorState(display,windows,MagickTrue);
7515       XCheckRefreshWindows(display,windows);
7516       flop_image=FlopImage(*image,exception);
7517       if (flop_image != (Image *) NULL)
7518         {
7519           *image=DestroyImage(*image);
7520           *image=flop_image;
7521         }
7522       CatchException(exception);
7523       XSetCursorState(display,windows,MagickFalse);
7524       if (windows->image.crop_geometry != (char *) NULL)
7525         {
7526           /*
7527             Flop crop geometry.
7528           */
7529           width=(unsigned int) (*image)->columns;
7530           height=(unsigned int) (*image)->rows;
7531           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7532             &width,&height);
7533           (void) FormatLocaleString(windows->image.crop_geometry,
7534             MagickPathExtent,"%ux%u%+d%+d",width,height,(int) (*image)->columns-
7535             (int) width-x,y);
7536         }
7537       if (windows->image.orphan != MagickFalse )
7538         break;
7539       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7540       break;
7541     }
7542     case FlipCommand:
7543     {
7544       Image
7545         *flip_image;
7546
7547       /*
7548         Flip image scanlines.
7549       */
7550       XSetCursorState(display,windows,MagickTrue);
7551       XCheckRefreshWindows(display,windows);
7552       flip_image=FlipImage(*image,exception);
7553       if (flip_image != (Image *) NULL)
7554         {
7555           *image=DestroyImage(*image);
7556           *image=flip_image;
7557         }
7558       CatchException(exception);
7559       XSetCursorState(display,windows,MagickFalse);
7560       if (windows->image.crop_geometry != (char *) NULL)
7561         {
7562           /*
7563             Flip crop geometry.
7564           */
7565           width=(unsigned int) (*image)->columns;
7566           height=(unsigned int) (*image)->rows;
7567           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7568             &width,&height);
7569           (void) FormatLocaleString(windows->image.crop_geometry,
7570             MagickPathExtent,"%ux%u%+d%+d",width,height,x,(int) (*image)->rows-
7571             (int) height-y);
7572         }
7573       if (windows->image.orphan != MagickFalse )
7574         break;
7575       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7576       break;
7577     }
7578     case RotateRightCommand:
7579     {
7580       /*
7581         Rotate image 90 degrees clockwise.
7582       */
7583       status=XRotateImage(display,resource_info,windows,90.0,image,exception);
7584       if (status == MagickFalse)
7585         {
7586           XNoticeWidget(display,windows,"Unable to rotate X image",
7587             (*image)->filename);
7588           break;
7589         }
7590       break;
7591     }
7592     case RotateLeftCommand:
7593     {
7594       /*
7595         Rotate image 90 degrees counter-clockwise.
7596       */
7597       status=XRotateImage(display,resource_info,windows,-90.0,image,exception);
7598       if (status == MagickFalse)
7599         {
7600           XNoticeWidget(display,windows,"Unable to rotate X image",
7601             (*image)->filename);
7602           break;
7603         }
7604       break;
7605     }
7606     case RotateCommand:
7607     {
7608       /*
7609         Rotate image.
7610       */
7611       status=XRotateImage(display,resource_info,windows,0.0,image,exception);
7612       if (status == MagickFalse)
7613         {
7614           XNoticeWidget(display,windows,"Unable to rotate X image",
7615             (*image)->filename);
7616           break;
7617         }
7618       break;
7619     }
7620     case ShearCommand:
7621     {
7622       Image
7623         *shear_image;
7624
7625       static char
7626         geometry[MagickPathExtent] = "45.0x45.0";
7627
7628       /*
7629         Query user for shear color and geometry.
7630       */
7631       XColorBrowserWidget(display,windows,"Select",color);
7632       if (*color == '\0')
7633         break;
7634       (void) XDialogWidget(display,windows,"Shear","Enter shear geometry:",
7635         geometry);
7636       if (*geometry == '\0')
7637         break;
7638       /*
7639         Shear image.
7640       */
7641       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
7642         exception);
7643       XSetCursorState(display,windows,MagickTrue);
7644       XCheckRefreshWindows(display,windows);
7645       (void) QueryColorCompliance(color,AllCompliance,
7646         &(*image)->background_color,exception);
7647       flags=ParseGeometry(geometry,&geometry_info);
7648       if ((flags & SigmaValue) == 0)
7649         geometry_info.sigma=geometry_info.rho;
7650       shear_image=ShearImage(*image,geometry_info.rho,geometry_info.sigma,
7651         exception);
7652       if (shear_image != (Image *) NULL)
7653         {
7654           *image=DestroyImage(*image);
7655           *image=shear_image;
7656         }
7657       CatchException(exception);
7658       XSetCursorState(display,windows,MagickFalse);
7659       if (windows->image.orphan != MagickFalse )
7660         break;
7661       windows->image.window_changes.width=(int) (*image)->columns;
7662       windows->image.window_changes.height=(int) (*image)->rows;
7663       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7664       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7665       break;
7666     }
7667     case RollCommand:
7668     {
7669       Image
7670         *roll_image;
7671
7672       static char
7673         geometry[MagickPathExtent] = "+2+2";
7674
7675       /*
7676         Query user for the roll geometry.
7677       */
7678       (void) XDialogWidget(display,windows,"Roll","Enter roll geometry:",
7679         geometry);
7680       if (*geometry == '\0')
7681         break;
7682       /*
7683         Roll image.
7684       */
7685       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
7686         exception);
7687       XSetCursorState(display,windows,MagickTrue);
7688       XCheckRefreshWindows(display,windows);
7689       (void) ParsePageGeometry(*image,geometry,&page_geometry,
7690         exception);
7691       roll_image=RollImage(*image,page_geometry.x,page_geometry.y,
7692         exception);
7693       if (roll_image != (Image *) NULL)
7694         {
7695           *image=DestroyImage(*image);
7696           *image=roll_image;
7697         }
7698       CatchException(exception);
7699       XSetCursorState(display,windows,MagickFalse);
7700       if (windows->image.orphan != MagickFalse )
7701         break;
7702       windows->image.window_changes.width=(int) (*image)->columns;
7703       windows->image.window_changes.height=(int) (*image)->rows;
7704       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7705       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7706       break;
7707     }
7708     case TrimCommand:
7709     {
7710       static char
7711         fuzz[MagickPathExtent];
7712
7713       /*
7714         Query user for the fuzz factor.
7715       */
7716       (void) FormatLocaleString(fuzz,MagickPathExtent,"%g%%",100.0*
7717         (*image)->fuzz/(QuantumRange+1.0));
7718       (void) XDialogWidget(display,windows,"Trim","Enter fuzz factor:",fuzz);
7719       if (*fuzz == '\0')
7720         break;
7721       (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+1.0);
7722       /*
7723         Trim image.
7724       */
7725       status=XTrimImage(display,resource_info,windows,*image,exception);
7726       if (status == MagickFalse)
7727         {
7728           XNoticeWidget(display,windows,"Unable to trim X image",
7729             (*image)->filename);
7730           break;
7731         }
7732       break;
7733     }
7734     case HueCommand:
7735     {
7736       static char
7737         hue_percent[MagickPathExtent] = "110";
7738
7739       /*
7740         Query user for percent hue change.
7741       */
7742       (void) XDialogWidget(display,windows,"Apply",
7743         "Enter percent change in image hue (0-200):",hue_percent);
7744       if (*hue_percent == '\0')
7745         break;
7746       /*
7747         Vary the image hue.
7748       */
7749       XSetCursorState(display,windows,MagickTrue);
7750       XCheckRefreshWindows(display,windows);
7751       (void) CopyMagickString(modulate_factors,"100.0/100.0/",MagickPathExtent);
7752       (void) ConcatenateMagickString(modulate_factors,hue_percent,
7753         MagickPathExtent);
7754       (void) ModulateImage(*image,modulate_factors,exception);
7755       XSetCursorState(display,windows,MagickFalse);
7756       if (windows->image.orphan != MagickFalse )
7757         break;
7758       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7759       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7760       break;
7761     }
7762     case SaturationCommand:
7763     {
7764       static char
7765         saturation_percent[MagickPathExtent] = "110";
7766
7767       /*
7768         Query user for percent saturation change.
7769       */
7770       (void) XDialogWidget(display,windows,"Apply",
7771         "Enter percent change in color saturation (0-200):",saturation_percent);
7772       if (*saturation_percent == '\0')
7773         break;
7774       /*
7775         Vary color saturation.
7776       */
7777       XSetCursorState(display,windows,MagickTrue);
7778       XCheckRefreshWindows(display,windows);
7779       (void) CopyMagickString(modulate_factors,"100.0/",MagickPathExtent);
7780       (void) ConcatenateMagickString(modulate_factors,saturation_percent,
7781         MagickPathExtent);
7782       (void) ModulateImage(*image,modulate_factors,exception);
7783       XSetCursorState(display,windows,MagickFalse);
7784       if (windows->image.orphan != MagickFalse )
7785         break;
7786       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7787       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7788       break;
7789     }
7790     case BrightnessCommand:
7791     {
7792       static char
7793         brightness_percent[MagickPathExtent] = "110";
7794
7795       /*
7796         Query user for percent brightness change.
7797       */
7798       (void) XDialogWidget(display,windows,"Apply",
7799         "Enter percent change in color brightness (0-200):",brightness_percent);
7800       if (*brightness_percent == '\0')
7801         break;
7802       /*
7803         Vary the color brightness.
7804       */
7805       XSetCursorState(display,windows,MagickTrue);
7806       XCheckRefreshWindows(display,windows);
7807       (void) CopyMagickString(modulate_factors,brightness_percent,
7808         MagickPathExtent);
7809       (void) ModulateImage(*image,modulate_factors,exception);
7810       XSetCursorState(display,windows,MagickFalse);
7811       if (windows->image.orphan != MagickFalse )
7812         break;
7813       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7814       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7815       break;
7816     }
7817     case GammaCommand:
7818     {
7819       static char
7820         factor[MagickPathExtent] = "1.6";
7821
7822       /*
7823         Query user for gamma value.
7824       */
7825       (void) XDialogWidget(display,windows,"Gamma",
7826         "Enter gamma value (e.g. 1.2):",factor);
7827       if (*factor == '\0')
7828         break;
7829       /*
7830         Gamma correct image.
7831       */
7832       XSetCursorState(display,windows,MagickTrue);
7833       XCheckRefreshWindows(display,windows);
7834       (void) GammaImage(*image,strtod(factor,(char **) NULL),exception);
7835       XSetCursorState(display,windows,MagickFalse);
7836       if (windows->image.orphan != MagickFalse )
7837         break;
7838       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7839       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7840       break;
7841     }
7842     case SpiffCommand:
7843     {
7844       /*
7845         Sharpen the image contrast.
7846       */
7847       XSetCursorState(display,windows,MagickTrue);
7848       XCheckRefreshWindows(display,windows);
7849       (void) ContrastImage(*image,MagickTrue,exception);
7850       XSetCursorState(display,windows,MagickFalse);
7851       if (windows->image.orphan != MagickFalse )
7852         break;
7853       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7854       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7855       break;
7856     }
7857     case DullCommand:
7858     {
7859       /*
7860         Dull the image contrast.
7861       */
7862       XSetCursorState(display,windows,MagickTrue);
7863       XCheckRefreshWindows(display,windows);
7864       (void) ContrastImage(*image,MagickFalse,exception);
7865       XSetCursorState(display,windows,MagickFalse);
7866       if (windows->image.orphan != MagickFalse )
7867         break;
7868       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7869       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7870       break;
7871     }
7872     case ContrastStretchCommand:
7873     {
7874       double
7875         black_point,
7876         white_point;
7877
7878       static char
7879         levels[MagickPathExtent] = "1%";
7880
7881       /*
7882         Query user for gamma value.
7883       */
7884       (void) XDialogWidget(display,windows,"Contrast Stretch",
7885         "Enter black and white points:",levels);
7886       if (*levels == '\0')
7887         break;
7888       /*
7889         Contrast stretch image.
7890       */
7891       XSetCursorState(display,windows,MagickTrue);
7892       XCheckRefreshWindows(display,windows);
7893       flags=ParseGeometry(levels,&geometry_info);
7894       black_point=geometry_info.rho;
7895       white_point=(flags & SigmaValue) != 0 ? geometry_info.sigma : black_point;
7896       if ((flags & PercentValue) != 0)
7897         {
7898           black_point*=(double) (*image)->columns*(*image)->rows/100.0;
7899           white_point*=(double) (*image)->columns*(*image)->rows/100.0;
7900         }
7901       white_point=(double) (*image)->columns*(*image)->rows-white_point;
7902       (void) ContrastStretchImage(*image,black_point,white_point,
7903         exception);
7904       XSetCursorState(display,windows,MagickFalse);
7905       if (windows->image.orphan != MagickFalse )
7906         break;
7907       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7908       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7909       break;
7910     }
7911     case SigmoidalContrastCommand:
7912     {
7913       GeometryInfo
7914         geometry_info;
7915
7916       MagickStatusType
7917         flags;
7918
7919       static char
7920         levels[MagickPathExtent] = "3x50%";
7921
7922       /*
7923         Query user for gamma value.
7924       */
7925       (void) XDialogWidget(display,windows,"Sigmoidal Contrast",
7926         "Enter contrast and midpoint:",levels);
7927       if (*levels == '\0')
7928         break;
7929       /*
7930         Contrast stretch image.
7931       */
7932       XSetCursorState(display,windows,MagickTrue);
7933       XCheckRefreshWindows(display,windows);
7934       flags=ParseGeometry(levels,&geometry_info);
7935       if ((flags & SigmaValue) == 0)
7936         geometry_info.sigma=1.0*QuantumRange/2.0;
7937       if ((flags & PercentValue) != 0)
7938         geometry_info.sigma=1.0*QuantumRange*geometry_info.sigma/100.0;
7939       (void) SigmoidalContrastImage(*image,MagickTrue,geometry_info.rho,
7940         geometry_info.sigma,exception);
7941       XSetCursorState(display,windows,MagickFalse);
7942       if (windows->image.orphan != MagickFalse )
7943         break;
7944       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7945       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7946       break;
7947     }
7948     case NormalizeCommand:
7949     {
7950       /*
7951         Perform histogram normalization on the image.
7952       */
7953       XSetCursorState(display,windows,MagickTrue);
7954       XCheckRefreshWindows(display,windows);
7955       (void) NormalizeImage(*image,exception);
7956       XSetCursorState(display,windows,MagickFalse);
7957       if (windows->image.orphan != MagickFalse )
7958         break;
7959       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7960       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7961       break;
7962     }
7963     case EqualizeCommand:
7964     {
7965       /*
7966         Perform histogram equalization on the image.
7967       */
7968       XSetCursorState(display,windows,MagickTrue);
7969       XCheckRefreshWindows(display,windows);
7970       (void) EqualizeImage(*image,exception);
7971       XSetCursorState(display,windows,MagickFalse);
7972       if (windows->image.orphan != MagickFalse )
7973         break;
7974       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7975       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7976       break;
7977     }
7978     case NegateCommand:
7979     {
7980       /*
7981         Negate colors in image.
7982       */
7983       XSetCursorState(display,windows,MagickTrue);
7984       XCheckRefreshWindows(display,windows);
7985       (void) NegateImage(*image,MagickFalse,exception);
7986       XSetCursorState(display,windows,MagickFalse);
7987       if (windows->image.orphan != MagickFalse )
7988         break;
7989       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7990       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7991       break;
7992     }
7993     case GrayscaleCommand:
7994     {
7995       /*
7996         Convert image to grayscale.
7997       */
7998       XSetCursorState(display,windows,MagickTrue);
7999       XCheckRefreshWindows(display,windows);
8000       (void) SetImageType(*image,(*image)->alpha_trait == UndefinedPixelTrait ?
8001         GrayscaleType : GrayscaleAlphaType,exception);
8002       XSetCursorState(display,windows,MagickFalse);
8003       if (windows->image.orphan != MagickFalse )
8004         break;
8005       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8006       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8007       break;
8008     }
8009     case MapCommand:
8010     {
8011       Image
8012         *affinity_image;
8013
8014       static char
8015         filename[MagickPathExtent] = "\0";
8016
8017       /*
8018         Request image file name from user.
8019       */
8020       XFileBrowserWidget(display,windows,"Map",filename);
8021       if (*filename == '\0')
8022         break;
8023       /*
8024         Map image.
8025       */
8026       XSetCursorState(display,windows,MagickTrue);
8027       XCheckRefreshWindows(display,windows);
8028       (void) CopyMagickString(image_info->filename,filename,MagickPathExtent);
8029       affinity_image=ReadImage(image_info,exception);
8030       if (affinity_image != (Image *) NULL)
8031         {
8032           (void) RemapImage(&quantize_info,*image,affinity_image,exception);
8033           affinity_image=DestroyImage(affinity_image);
8034         }
8035       CatchException(exception);
8036       XSetCursorState(display,windows,MagickFalse);
8037       if (windows->image.orphan != MagickFalse )
8038         break;
8039       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8040       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8041       break;
8042     }
8043     case QuantizeCommand:
8044     {
8045       int
8046         status;
8047
8048       static char
8049         colors[MagickPathExtent] = "256";
8050
8051       /*
8052         Query user for maximum number of colors.
8053       */
8054       status=XDialogWidget(display,windows,"Quantize",
8055         "Maximum number of colors:",colors);
8056       if (*colors == '\0')
8057         break;
8058       /*
8059         Color reduce the image.
8060       */
8061       XSetCursorState(display,windows,MagickTrue);
8062       XCheckRefreshWindows(display,windows);
8063       quantize_info.number_colors=StringToUnsignedLong(colors);
8064       quantize_info.dither_method=status != 0 ? RiemersmaDitherMethod :
8065         NoDitherMethod;
8066       (void) QuantizeImage(&quantize_info,*image,exception);
8067       XSetCursorState(display,windows,MagickFalse);
8068       if (windows->image.orphan != MagickFalse )
8069         break;
8070       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8071       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8072       break;
8073     }
8074     case DespeckleCommand:
8075     {
8076       Image
8077         *despeckle_image;
8078
8079       /*
8080         Despeckle image.
8081       */
8082       XSetCursorState(display,windows,MagickTrue);
8083       XCheckRefreshWindows(display,windows);
8084       despeckle_image=DespeckleImage(*image,exception);
8085       if (despeckle_image != (Image *) NULL)
8086         {
8087           *image=DestroyImage(*image);
8088           *image=despeckle_image;
8089         }
8090       CatchException(exception);
8091       XSetCursorState(display,windows,MagickFalse);
8092       if (windows->image.orphan != MagickFalse )
8093         break;
8094       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8095       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8096       break;
8097     }
8098     case EmbossCommand:
8099     {
8100       Image
8101         *emboss_image;
8102
8103       static char
8104         radius[MagickPathExtent] = "0.0x1.0";
8105
8106       /*
8107         Query user for emboss radius.
8108       */
8109       (void) XDialogWidget(display,windows,"Emboss",
8110         "Enter the emboss radius and standard deviation:",radius);
8111       if (*radius == '\0')
8112         break;
8113       /*
8114         Reduce noise in the image.
8115       */
8116       XSetCursorState(display,windows,MagickTrue);
8117       XCheckRefreshWindows(display,windows);
8118       flags=ParseGeometry(radius,&geometry_info);
8119       if ((flags & SigmaValue) == 0)
8120         geometry_info.sigma=1.0;
8121       emboss_image=EmbossImage(*image,geometry_info.rho,geometry_info.sigma,
8122         exception);
8123       if (emboss_image != (Image *) NULL)
8124         {
8125           *image=DestroyImage(*image);
8126           *image=emboss_image;
8127         }
8128       CatchException(exception);
8129       XSetCursorState(display,windows,MagickFalse);
8130       if (windows->image.orphan != MagickFalse )
8131         break;
8132       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8133       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8134       break;
8135     }
8136     case ReduceNoiseCommand:
8137     {
8138       Image
8139         *noise_image;
8140
8141       static char
8142         radius[MagickPathExtent] = "0";
8143
8144       /*
8145         Query user for noise radius.
8146       */
8147       (void) XDialogWidget(display,windows,"Reduce Noise",
8148         "Enter the noise radius:",radius);
8149       if (*radius == '\0')
8150         break;
8151       /*
8152         Reduce noise in the image.
8153       */
8154       XSetCursorState(display,windows,MagickTrue);
8155       XCheckRefreshWindows(display,windows);
8156       flags=ParseGeometry(radius,&geometry_info);
8157       noise_image=StatisticImage(*image,NonpeakStatistic,(size_t)
8158         geometry_info.rho,(size_t) geometry_info.rho,exception);
8159       if (noise_image != (Image *) NULL)
8160         {
8161           *image=DestroyImage(*image);
8162           *image=noise_image;
8163         }
8164       CatchException(exception);
8165       XSetCursorState(display,windows,MagickFalse);
8166       if (windows->image.orphan != MagickFalse )
8167         break;
8168       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8169       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8170       break;
8171     }
8172     case AddNoiseCommand:
8173     {
8174       char
8175         **noises;
8176
8177       Image
8178         *noise_image;
8179
8180       static char
8181         noise_type[MagickPathExtent] = "Gaussian";
8182
8183       /*
8184         Add noise to the image.
8185       */
8186       noises=GetCommandOptions(MagickNoiseOptions);
8187       if (noises == (char **) NULL)
8188         break;
8189       XListBrowserWidget(display,windows,&windows->widget,
8190         (const char **) noises,"Add Noise",
8191         "Select a type of noise to add to your image:",noise_type);
8192       noises=DestroyStringList(noises);
8193       if (*noise_type == '\0')
8194         break;
8195       XSetCursorState(display,windows,MagickTrue);
8196       XCheckRefreshWindows(display,windows);
8197       noise_image=AddNoiseImage(*image,(NoiseType) ParseCommandOption(
8198         MagickNoiseOptions,MagickFalse,noise_type),1.0,exception);
8199       if (noise_image != (Image *) NULL)
8200         {
8201           *image=DestroyImage(*image);
8202           *image=noise_image;
8203         }
8204       CatchException(exception);
8205       XSetCursorState(display,windows,MagickFalse);
8206       if (windows->image.orphan != MagickFalse )
8207         break;
8208       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8209       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8210       break;
8211     }
8212     case SharpenCommand:
8213     {
8214       Image
8215         *sharp_image;
8216
8217       static char
8218         radius[MagickPathExtent] = "0.0x1.0";
8219
8220       /*
8221         Query user for sharpen radius.
8222       */
8223       (void) XDialogWidget(display,windows,"Sharpen",
8224         "Enter the sharpen radius and standard deviation:",radius);
8225       if (*radius == '\0')
8226         break;
8227       /*
8228         Sharpen image scanlines.
8229       */
8230       XSetCursorState(display,windows,MagickTrue);
8231       XCheckRefreshWindows(display,windows);
8232       flags=ParseGeometry(radius,&geometry_info);
8233       sharp_image=SharpenImage(*image,geometry_info.rho,geometry_info.sigma,
8234         exception);
8235       if (sharp_image != (Image *) NULL)
8236         {
8237           *image=DestroyImage(*image);
8238           *image=sharp_image;
8239         }
8240       CatchException(exception);
8241       XSetCursorState(display,windows,MagickFalse);
8242       if (windows->image.orphan != MagickFalse )
8243         break;
8244       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8245       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8246       break;
8247     }
8248     case BlurCommand:
8249     {
8250       Image
8251         *blur_image;
8252
8253       static char
8254         radius[MagickPathExtent] = "0.0x1.0";
8255
8256       /*
8257         Query user for blur radius.
8258       */
8259       (void) XDialogWidget(display,windows,"Blur",
8260         "Enter the blur radius and standard deviation:",radius);
8261       if (*radius == '\0')
8262         break;
8263       /*
8264         Blur an image.
8265       */
8266       XSetCursorState(display,windows,MagickTrue);
8267       XCheckRefreshWindows(display,windows);
8268       flags=ParseGeometry(radius,&geometry_info);
8269       blur_image=BlurImage(*image,geometry_info.rho,geometry_info.sigma,
8270         exception);
8271       if (blur_image != (Image *) NULL)
8272         {
8273           *image=DestroyImage(*image);
8274           *image=blur_image;
8275         }
8276       CatchException(exception);
8277       XSetCursorState(display,windows,MagickFalse);
8278       if (windows->image.orphan != MagickFalse )
8279         break;
8280       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8281       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8282       break;
8283     }
8284     case ThresholdCommand:
8285     {
8286       double
8287         threshold;
8288
8289       static char
8290         factor[MagickPathExtent] = "128";
8291
8292       /*
8293         Query user for threshold value.
8294       */
8295       (void) XDialogWidget(display,windows,"Threshold",
8296         "Enter threshold value:",factor);
8297       if (*factor == '\0')
8298         break;
8299       /*
8300         Gamma correct image.
8301       */
8302       XSetCursorState(display,windows,MagickTrue);
8303       XCheckRefreshWindows(display,windows);
8304       threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8305       (void) BilevelImage(*image,threshold,exception);
8306       XSetCursorState(display,windows,MagickFalse);
8307       if (windows->image.orphan != MagickFalse )
8308         break;
8309       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8310       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8311       break;
8312     }
8313     case EdgeDetectCommand:
8314     {
8315       Image
8316         *edge_image;
8317
8318       static char
8319         radius[MagickPathExtent] = "0";
8320
8321       /*
8322         Query user for edge factor.
8323       */
8324       (void) XDialogWidget(display,windows,"Detect Edges",
8325         "Enter the edge detect radius:",radius);
8326       if (*radius == '\0')
8327         break;
8328       /*
8329         Detect edge in image.
8330       */
8331       XSetCursorState(display,windows,MagickTrue);
8332       XCheckRefreshWindows(display,windows);
8333       flags=ParseGeometry(radius,&geometry_info);
8334       edge_image=EdgeImage(*image,geometry_info.rho,exception);
8335       if (edge_image != (Image *) NULL)
8336         {
8337           *image=DestroyImage(*image);
8338           *image=edge_image;
8339         }
8340       CatchException(exception);
8341       XSetCursorState(display,windows,MagickFalse);
8342       if (windows->image.orphan != MagickFalse )
8343         break;
8344       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8345       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8346       break;
8347     }
8348     case SpreadCommand:
8349     {
8350       Image
8351         *spread_image;
8352
8353       static char
8354         amount[MagickPathExtent] = "2";
8355
8356       /*
8357         Query user for spread amount.
8358       */
8359       (void) XDialogWidget(display,windows,"Spread",
8360         "Enter the displacement amount:",amount);
8361       if (*amount == '\0')
8362         break;
8363       /*
8364         Displace image pixels by a random amount.
8365       */
8366       XSetCursorState(display,windows,MagickTrue);
8367       XCheckRefreshWindows(display,windows);
8368       flags=ParseGeometry(amount,&geometry_info);
8369       spread_image=EdgeImage(*image,geometry_info.rho,exception);
8370       if (spread_image != (Image *) NULL)
8371         {
8372           *image=DestroyImage(*image);
8373           *image=spread_image;
8374         }
8375       CatchException(exception);
8376       XSetCursorState(display,windows,MagickFalse);
8377       if (windows->image.orphan != MagickFalse )
8378         break;
8379       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8380       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8381       break;
8382     }
8383     case ShadeCommand:
8384     {
8385       Image
8386         *shade_image;
8387
8388       int
8389         status;
8390
8391       static char
8392         geometry[MagickPathExtent] = "30x30";
8393
8394       /*
8395         Query user for the shade geometry.
8396       */
8397       status=XDialogWidget(display,windows,"Shade",
8398         "Enter the azimuth and elevation of the light source:",geometry);
8399       if (*geometry == '\0')
8400         break;
8401       /*
8402         Shade image pixels.
8403       */
8404       XSetCursorState(display,windows,MagickTrue);
8405       XCheckRefreshWindows(display,windows);
8406       flags=ParseGeometry(geometry,&geometry_info);
8407       if ((flags & SigmaValue) == 0)
8408         geometry_info.sigma=1.0;
8409       shade_image=ShadeImage(*image,status != 0 ? MagickTrue : MagickFalse,
8410         geometry_info.rho,geometry_info.sigma,exception);
8411       if (shade_image != (Image *) NULL)
8412         {
8413           *image=DestroyImage(*image);
8414           *image=shade_image;
8415         }
8416       CatchException(exception);
8417       XSetCursorState(display,windows,MagickFalse);
8418       if (windows->image.orphan != MagickFalse )
8419         break;
8420       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8421       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8422       break;
8423     }
8424     case RaiseCommand:
8425     {
8426       static char
8427         bevel_width[MagickPathExtent] = "10";
8428
8429       /*
8430         Query user for bevel width.
8431       */
8432       (void) XDialogWidget(display,windows,"Raise","Bevel width:",bevel_width);
8433       if (*bevel_width == '\0')
8434         break;
8435       /*
8436         Raise an image.
8437       */
8438       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8439         exception);
8440       XSetCursorState(display,windows,MagickTrue);
8441       XCheckRefreshWindows(display,windows);
8442       (void) ParsePageGeometry(*image,bevel_width,&page_geometry,
8443         exception);
8444       (void) RaiseImage(*image,&page_geometry,MagickTrue,exception);
8445       XSetCursorState(display,windows,MagickFalse);
8446       if (windows->image.orphan != MagickFalse )
8447         break;
8448       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8449       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8450       break;
8451     }
8452     case SegmentCommand:
8453     {
8454       static char
8455         threshold[MagickPathExtent] = "1.0x1.5";
8456
8457       /*
8458         Query user for smoothing threshold.
8459       */
8460       (void) XDialogWidget(display,windows,"Segment","Smooth threshold:",
8461         threshold);
8462       if (*threshold == '\0')
8463         break;
8464       /*
8465         Segment an image.
8466       */
8467       XSetCursorState(display,windows,MagickTrue);
8468       XCheckRefreshWindows(display,windows);
8469       flags=ParseGeometry(threshold,&geometry_info);
8470       if ((flags & SigmaValue) == 0)
8471         geometry_info.sigma=1.0;
8472       (void) SegmentImage(*image,sRGBColorspace,MagickFalse,geometry_info.rho,
8473         geometry_info.sigma,exception);
8474       XSetCursorState(display,windows,MagickFalse);
8475       if (windows->image.orphan != MagickFalse )
8476         break;
8477       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8478       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8479       break;
8480     }
8481     case SepiaToneCommand:
8482     {
8483       double
8484         threshold;
8485
8486       Image
8487         *sepia_image;
8488
8489       static char
8490         factor[MagickPathExtent] = "80%";
8491
8492       /*
8493         Query user for sepia-tone factor.
8494       */
8495       (void) XDialogWidget(display,windows,"Sepia Tone",
8496         "Enter the sepia tone factor (0 - 99.9%):",factor);
8497       if (*factor == '\0')
8498         break;
8499       /*
8500         Sepia tone image pixels.
8501       */
8502       XSetCursorState(display,windows,MagickTrue);
8503       XCheckRefreshWindows(display,windows);
8504       threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8505       sepia_image=SepiaToneImage(*image,threshold,exception);
8506       if (sepia_image != (Image *) NULL)
8507         {
8508           *image=DestroyImage(*image);
8509           *image=sepia_image;
8510         }
8511       CatchException(exception);
8512       XSetCursorState(display,windows,MagickFalse);
8513       if (windows->image.orphan != MagickFalse )
8514         break;
8515       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8516       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8517       break;
8518     }
8519     case SolarizeCommand:
8520     {
8521       double
8522         threshold;
8523
8524       static char
8525         factor[MagickPathExtent] = "60%";
8526
8527       /*
8528         Query user for solarize factor.
8529       */
8530       (void) XDialogWidget(display,windows,"Solarize",
8531         "Enter the solarize factor (0 - 99.9%):",factor);
8532       if (*factor == '\0')
8533         break;
8534       /*
8535         Solarize image pixels.
8536       */
8537       XSetCursorState(display,windows,MagickTrue);
8538       XCheckRefreshWindows(display,windows);
8539       threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8540       (void) SolarizeImage(*image,threshold,exception);
8541       XSetCursorState(display,windows,MagickFalse);
8542       if (windows->image.orphan != MagickFalse )
8543         break;
8544       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8545       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8546       break;
8547     }
8548     case SwirlCommand:
8549     {
8550       Image
8551         *swirl_image;
8552
8553       static char
8554         degrees[MagickPathExtent] = "60";
8555
8556       /*
8557         Query user for swirl angle.
8558       */
8559       (void) XDialogWidget(display,windows,"Swirl","Enter the swirl angle:",
8560         degrees);
8561       if (*degrees == '\0')
8562         break;
8563       /*
8564         Swirl image pixels about the center.
8565       */
8566       XSetCursorState(display,windows,MagickTrue);
8567       XCheckRefreshWindows(display,windows);
8568       flags=ParseGeometry(degrees,&geometry_info);
8569       swirl_image=SwirlImage(*image,geometry_info.rho,(*image)->interpolate,
8570         exception);
8571       if (swirl_image != (Image *) NULL)
8572         {
8573           *image=DestroyImage(*image);
8574           *image=swirl_image;
8575         }
8576       CatchException(exception);
8577       XSetCursorState(display,windows,MagickFalse);
8578       if (windows->image.orphan != MagickFalse )
8579         break;
8580       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8581       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8582       break;
8583     }
8584     case ImplodeCommand:
8585     {
8586       Image
8587         *implode_image;
8588
8589       static char
8590         factor[MagickPathExtent] = "0.3";
8591
8592       /*
8593         Query user for implode factor.
8594       */
8595       (void) XDialogWidget(display,windows,"Implode",
8596         "Enter the implosion/explosion factor (-1.0 - 1.0):",factor);
8597       if (*factor == '\0')
8598         break;
8599       /*
8600         Implode image pixels about the center.
8601       */
8602       XSetCursorState(display,windows,MagickTrue);
8603       XCheckRefreshWindows(display,windows);
8604       flags=ParseGeometry(factor,&geometry_info);
8605       implode_image=ImplodeImage(*image,geometry_info.rho,(*image)->interpolate,
8606         exception);
8607       if (implode_image != (Image *) NULL)
8608         {
8609           *image=DestroyImage(*image);
8610           *image=implode_image;
8611         }
8612       CatchException(exception);
8613       XSetCursorState(display,windows,MagickFalse);
8614       if (windows->image.orphan != MagickFalse )
8615         break;
8616       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8617       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8618       break;
8619     }
8620     case VignetteCommand:
8621     {
8622       Image
8623         *vignette_image;
8624
8625       static char
8626         geometry[MagickPathExtent] = "0x20";
8627
8628       /*
8629         Query user for the vignette geometry.
8630       */
8631       (void) XDialogWidget(display,windows,"Vignette",
8632         "Enter the radius, sigma, and x and y offsets:",geometry);
8633       if (*geometry == '\0')
8634         break;
8635       /*
8636         Soften the edges of the image in vignette style
8637       */
8638       XSetCursorState(display,windows,MagickTrue);
8639       XCheckRefreshWindows(display,windows);
8640       flags=ParseGeometry(geometry,&geometry_info);
8641       if ((flags & SigmaValue) == 0)
8642         geometry_info.sigma=1.0;
8643       if ((flags & XiValue) == 0)
8644         geometry_info.xi=0.1*(*image)->columns;
8645       if ((flags & PsiValue) == 0)
8646         geometry_info.psi=0.1*(*image)->rows;
8647       vignette_image=VignetteImage(*image,geometry_info.rho,0.0,(ssize_t)
8648         ceil(geometry_info.xi-0.5),(ssize_t) ceil(geometry_info.psi-0.5),
8649         exception);
8650       if (vignette_image != (Image *) NULL)
8651         {
8652           *image=DestroyImage(*image);
8653           *image=vignette_image;
8654         }
8655       CatchException(exception);
8656       XSetCursorState(display,windows,MagickFalse);
8657       if (windows->image.orphan != MagickFalse )
8658         break;
8659       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8660       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8661       break;
8662     }
8663     case WaveCommand:
8664     {
8665       Image
8666         *wave_image;
8667
8668       static char
8669         geometry[MagickPathExtent] = "25x150";
8670
8671       /*
8672         Query user for the wave geometry.
8673       */
8674       (void) XDialogWidget(display,windows,"Wave",
8675         "Enter the amplitude and length of the wave:",geometry);
8676       if (*geometry == '\0')
8677         break;
8678       /*
8679         Alter an image along a sine wave.
8680       */
8681       XSetCursorState(display,windows,MagickTrue);
8682       XCheckRefreshWindows(display,windows);
8683       flags=ParseGeometry(geometry,&geometry_info);
8684       if ((flags & SigmaValue) == 0)
8685         geometry_info.sigma=1.0;
8686       wave_image=WaveImage(*image,geometry_info.rho,geometry_info.sigma,
8687         (*image)->interpolate,exception);
8688       if (wave_image != (Image *) NULL)
8689         {
8690           *image=DestroyImage(*image);
8691           *image=wave_image;
8692         }
8693       CatchException(exception);
8694       XSetCursorState(display,windows,MagickFalse);
8695       if (windows->image.orphan != MagickFalse )
8696         break;
8697       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8698       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8699       break;
8700     }
8701     case OilPaintCommand:
8702     {
8703       Image
8704         *paint_image;
8705
8706       static char
8707         radius[MagickPathExtent] = "0";
8708
8709       /*
8710         Query user for circular neighborhood radius.
8711       */
8712       (void) XDialogWidget(display,windows,"Oil Paint",
8713         "Enter the mask radius:",radius);
8714       if (*radius == '\0')
8715         break;
8716       /*
8717         OilPaint image scanlines.
8718       */
8719       XSetCursorState(display,windows,MagickTrue);
8720       XCheckRefreshWindows(display,windows);
8721       flags=ParseGeometry(radius,&geometry_info);
8722       paint_image=OilPaintImage(*image,geometry_info.rho,geometry_info.sigma,
8723         exception);
8724       if (paint_image != (Image *) NULL)
8725         {
8726           *image=DestroyImage(*image);
8727           *image=paint_image;
8728         }
8729       CatchException(exception);
8730       XSetCursorState(display,windows,MagickFalse);
8731       if (windows->image.orphan != MagickFalse )
8732         break;
8733       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8734       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8735       break;
8736     }
8737     case CharcoalDrawCommand:
8738     {
8739       Image
8740         *charcoal_image;
8741
8742       static char
8743         radius[MagickPathExtent] = "0x1";
8744
8745       /*
8746         Query user for charcoal radius.
8747       */
8748       (void) XDialogWidget(display,windows,"Charcoal Draw",
8749         "Enter the charcoal radius and sigma:",radius);
8750       if (*radius == '\0')
8751         break;
8752       /*
8753         Charcoal the image.
8754       */
8755       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8756         exception);
8757       XSetCursorState(display,windows,MagickTrue);
8758       XCheckRefreshWindows(display,windows);
8759       flags=ParseGeometry(radius,&geometry_info);
8760       if ((flags & SigmaValue) == 0)
8761         geometry_info.sigma=geometry_info.rho;
8762       charcoal_image=CharcoalImage(*image,geometry_info.rho,geometry_info.sigma,
8763         exception);
8764       if (charcoal_image != (Image *) NULL)
8765         {
8766           *image=DestroyImage(*image);
8767           *image=charcoal_image;
8768         }
8769       CatchException(exception);
8770       XSetCursorState(display,windows,MagickFalse);
8771       if (windows->image.orphan != MagickFalse )
8772         break;
8773       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8774       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8775       break;
8776     }
8777     case AnnotateCommand:
8778     {
8779       /*
8780         Annotate the image with text.
8781       */
8782       status=XAnnotateEditImage(display,resource_info,windows,*image,exception);
8783       if (status == MagickFalse)
8784         {
8785           XNoticeWidget(display,windows,"Unable to annotate X image",
8786             (*image)->filename);
8787           break;
8788         }
8789       break;
8790     }
8791     case DrawCommand:
8792     {
8793       /*
8794         Draw image.
8795       */
8796       status=XDrawEditImage(display,resource_info,windows,image,exception);
8797       if (status == MagickFalse)
8798         {
8799           XNoticeWidget(display,windows,"Unable to draw on the X image",
8800             (*image)->filename);
8801           break;
8802         }
8803       break;
8804     }
8805     case ColorCommand:
8806     {
8807       /*
8808         Color edit.
8809       */
8810       status=XColorEditImage(display,resource_info,windows,image,exception);
8811       if (status == MagickFalse)
8812         {
8813           XNoticeWidget(display,windows,"Unable to pixel edit X image",
8814             (*image)->filename);
8815           break;
8816         }
8817       break;
8818     }
8819     case MatteCommand:
8820     {
8821       /*
8822         Matte edit.
8823       */
8824       status=XMatteEditImage(display,resource_info,windows,image,exception);
8825       if (status == MagickFalse)
8826         {
8827           XNoticeWidget(display,windows,"Unable to matte edit X image",
8828             (*image)->filename);
8829           break;
8830         }
8831       break;
8832     }
8833     case CompositeCommand:
8834     {
8835       /*
8836         Composite image.
8837       */
8838       status=XCompositeImage(display,resource_info,windows,*image,
8839         exception);
8840       if (status == MagickFalse)
8841         {
8842           XNoticeWidget(display,windows,"Unable to composite X image",
8843             (*image)->filename);
8844           break;
8845         }
8846       break;
8847     }
8848     case AddBorderCommand:
8849     {
8850       Image
8851         *border_image;
8852
8853       static char
8854         geometry[MagickPathExtent] = "6x6";
8855
8856       /*
8857         Query user for border color and geometry.
8858       */
8859       XColorBrowserWidget(display,windows,"Select",color);
8860       if (*color == '\0')
8861         break;
8862       (void) XDialogWidget(display,windows,"Add Border",
8863         "Enter border geometry:",geometry);
8864       if (*geometry == '\0')
8865         break;
8866       /*
8867         Add a border to the image.
8868       */
8869       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8870         exception);
8871       XSetCursorState(display,windows,MagickTrue);
8872       XCheckRefreshWindows(display,windows);
8873       (void) QueryColorCompliance(color,AllCompliance,&(*image)->border_color,
8874         exception);
8875       (void) ParsePageGeometry(*image,geometry,&page_geometry,
8876         exception);
8877       border_image=BorderImage(*image,&page_geometry,(*image)->compose,
8878         exception);
8879       if (border_image != (Image *) NULL)
8880         {
8881           *image=DestroyImage(*image);
8882           *image=border_image;
8883         }
8884       CatchException(exception);
8885       XSetCursorState(display,windows,MagickFalse);
8886       if (windows->image.orphan != MagickFalse )
8887         break;
8888       windows->image.window_changes.width=(int) (*image)->columns;
8889       windows->image.window_changes.height=(int) (*image)->rows;
8890       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8891       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8892       break;
8893     }
8894     case AddFrameCommand:
8895     {
8896       FrameInfo
8897         frame_info;
8898
8899       Image
8900         *frame_image;
8901
8902       static char
8903         geometry[MagickPathExtent] = "6x6";
8904
8905       /*
8906         Query user for frame color and geometry.
8907       */
8908       XColorBrowserWidget(display,windows,"Select",color);
8909       if (*color == '\0')
8910         break;
8911       (void) XDialogWidget(display,windows,"Add Frame","Enter frame geometry:",
8912         geometry);
8913       if (*geometry == '\0')
8914         break;
8915       /*
8916         Surround image with an ornamental border.
8917       */
8918       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8919         exception);
8920       XSetCursorState(display,windows,MagickTrue);
8921       XCheckRefreshWindows(display,windows);
8922       (void) QueryColorCompliance(color,AllCompliance,&(*image)->matte_color,
8923         exception);
8924       (void) ParsePageGeometry(*image,geometry,&page_geometry,
8925         exception);
8926       frame_info.width=page_geometry.width;
8927       frame_info.height=page_geometry.height;
8928       frame_info.outer_bevel=page_geometry.x;
8929       frame_info.inner_bevel=page_geometry.y;
8930       frame_info.x=(ssize_t) frame_info.width;
8931       frame_info.y=(ssize_t) frame_info.height;
8932       frame_info.width=(*image)->columns+2*frame_info.width;
8933       frame_info.height=(*image)->rows+2*frame_info.height;
8934       frame_image=FrameImage(*image,&frame_info,(*image)->compose,exception);
8935       if (frame_image != (Image *) NULL)
8936         {
8937           *image=DestroyImage(*image);
8938           *image=frame_image;
8939         }
8940       CatchException(exception);
8941       XSetCursorState(display,windows,MagickFalse);
8942       if (windows->image.orphan != MagickFalse )
8943         break;
8944       windows->image.window_changes.width=(int) (*image)->columns;
8945       windows->image.window_changes.height=(int) (*image)->rows;
8946       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8947       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8948       break;
8949     }
8950     case CommentCommand:
8951     {
8952       const char
8953         *value;
8954
8955       FILE
8956         *file;
8957
8958       int
8959         unique_file;
8960
8961       /*
8962         Edit image comment.
8963       */
8964       unique_file=AcquireUniqueFileResource(image_info->filename);
8965       if (unique_file == -1)
8966         XNoticeWidget(display,windows,"Unable to edit image comment",
8967           image_info->filename);
8968       value=GetImageProperty(*image,"comment",exception);
8969       if (value == (char *) NULL)
8970         unique_file=close(unique_file)-1;
8971       else
8972         {
8973           register const char
8974             *p;
8975
8976           file=fdopen(unique_file,"w");
8977           if (file == (FILE *) NULL)
8978             {
8979               XNoticeWidget(display,windows,"Unable to edit image comment",
8980                 image_info->filename);
8981               break;
8982             }
8983           for (p=value; *p != '\0'; p++)
8984             (void) fputc((int) *p,file);
8985           (void) fputc('\n',file);
8986           (void) fclose(file);
8987         }
8988       XSetCursorState(display,windows,MagickTrue);
8989       XCheckRefreshWindows(display,windows);
8990       status=InvokeDelegate(image_info,*image,"edit",(char *) NULL,
8991         exception);
8992       if (status == MagickFalse)
8993         XNoticeWidget(display,windows,"Unable to edit image comment",
8994           (char *) NULL);
8995       else
8996         {
8997           char
8998             *comment;
8999
9000           comment=FileToString(image_info->filename,~0UL,exception);
9001           if (comment != (char *) NULL)
9002             {
9003               (void) SetImageProperty(*image,"comment",comment,exception);
9004               (*image)->taint=MagickTrue;
9005             }
9006         }
9007       (void) RelinquishUniqueFileResource(image_info->filename);
9008       XSetCursorState(display,windows,MagickFalse);
9009       break;
9010     }
9011     case LaunchCommand:
9012     {
9013       /*
9014         Launch program.
9015       */
9016       XSetCursorState(display,windows,MagickTrue);
9017       XCheckRefreshWindows(display,windows);
9018       (void) AcquireUniqueFilename(filename);
9019       (void) FormatLocaleString((*image)->filename,MagickPathExtent,"launch:%s",
9020         filename);
9021       status=WriteImage(image_info,*image,exception);
9022       if (status == MagickFalse)
9023         XNoticeWidget(display,windows,"Unable to launch image editor",
9024           (char *) NULL);
9025       else
9026         {
9027           nexus=ReadImage(resource_info->image_info,exception);
9028           CatchException(exception);
9029           XClientMessage(display,windows->image.id,windows->im_protocols,
9030             windows->im_next_image,CurrentTime);
9031         }
9032       (void) RelinquishUniqueFileResource(filename);
9033       XSetCursorState(display,windows,MagickFalse);
9034       break;
9035     }
9036     case RegionofInterestCommand:
9037     {
9038       /*
9039         Apply an image processing technique to a region of interest.
9040       */
9041       (void) XROIImage(display,resource_info,windows,image,exception);
9042       break;
9043     }
9044     case InfoCommand:
9045       break;
9046     case ZoomCommand:
9047     {
9048       /*
9049         Zoom image.
9050       */
9051       if (windows->magnify.mapped != MagickFalse )
9052         (void) XRaiseWindow(display,windows->magnify.id);
9053       else
9054         {
9055           /*
9056             Make magnify image.
9057           */
9058           XSetCursorState(display,windows,MagickTrue);
9059           (void) XMapRaised(display,windows->magnify.id);
9060           XSetCursorState(display,windows,MagickFalse);
9061         }
9062       break;
9063     }
9064     case ShowPreviewCommand:
9065     {
9066       char
9067         **previews,
9068         value[MagickPathExtent];
9069
9070       Image
9071         *preview_image;
9072
9073       PreviewType
9074         preview;
9075
9076       static char
9077         preview_type[MagickPathExtent] = "Gamma";
9078
9079       /*
9080         Select preview type from menu.
9081       */
9082       previews=GetCommandOptions(MagickPreviewOptions);
9083       if (previews == (char **) NULL)
9084         break;
9085       XListBrowserWidget(display,windows,&windows->widget,
9086         (const char **) previews,"Preview",
9087         "Select an enhancement, effect, or F/X:",preview_type);
9088       previews=DestroyStringList(previews);
9089       if (*preview_type == '\0')
9090         break;
9091       /*
9092         Show image preview.
9093       */
9094       XSetCursorState(display,windows,MagickTrue);
9095       XCheckRefreshWindows(display,windows);
9096       preview=(PreviewType) ParseCommandOption(MagickPreviewOptions,
9097         MagickFalse,preview_type);
9098       (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
9099         windows->image.id);
9100       (void) SetImageProperty(*image,"group",value,exception);
9101       (void) DeleteImageProperty(*image,"label");
9102       (void) SetImageProperty(*image,"label","Preview",exception);
9103       preview_image=PreviewImage(*image,preview,exception);
9104       if (preview_image == (Image *) NULL)
9105         break;
9106       (void) AcquireUniqueFilename(filename);
9107       (void) FormatLocaleString(preview_image->filename,MagickPathExtent,
9108         "show:%s",filename);
9109       status=WriteImage(image_info,preview_image,exception);
9110       (void) RelinquishUniqueFileResource(filename);
9111       preview_image=DestroyImage(preview_image);
9112       if (status == MagickFalse)
9113         XNoticeWidget(display,windows,"Unable to show image preview",
9114           (*image)->filename);
9115       XDelay(display,1500);
9116       XSetCursorState(display,windows,MagickFalse);
9117       break;
9118     }
9119     case ShowHistogramCommand:
9120     {
9121       char
9122         value[MagickPathExtent];
9123
9124       Image
9125         *histogram_image;
9126
9127       /*
9128         Show image histogram.
9129       */
9130       XSetCursorState(display,windows,MagickTrue);
9131       XCheckRefreshWindows(display,windows);
9132       (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
9133         windows->image.id);
9134       (void) SetImageProperty(*image,"group",value,exception);
9135       (void) DeleteImageProperty(*image,"label");
9136       (void) SetImageProperty(*image,"label","Histogram",exception);
9137       (void) AcquireUniqueFilename(filename);
9138       (void) FormatLocaleString((*image)->filename,MagickPathExtent,
9139         "histogram:%s",filename);
9140       status=WriteImage(image_info,*image,exception);
9141       (void) CopyMagickString(image_info->filename,filename,MagickPathExtent);
9142       histogram_image=ReadImage(image_info,exception);
9143       (void) RelinquishUniqueFileResource(filename);
9144       if (histogram_image == (Image *) NULL)
9145         break;
9146       (void) FormatLocaleString(histogram_image->filename,MagickPathExtent,
9147         "show:%s",filename);
9148       status=WriteImage(image_info,histogram_image,exception);
9149       histogram_image=DestroyImage(histogram_image);
9150       if (status == MagickFalse)
9151         XNoticeWidget(display,windows,"Unable to show histogram",
9152           (*image)->filename);
9153       XDelay(display,1500);
9154       XSetCursorState(display,windows,MagickFalse);
9155       break;
9156     }
9157     case ShowMatteCommand:
9158     {
9159       char
9160         value[MagickPathExtent];
9161
9162       Image
9163         *matte_image;
9164
9165       if ((*image)->alpha_trait == UndefinedPixelTrait)
9166         {
9167           XNoticeWidget(display,windows,
9168             "Image does not have any matte information",(*image)->filename);
9169           break;
9170         }
9171       /*
9172         Show image matte.
9173       */
9174       XSetCursorState(display,windows,MagickTrue);
9175       XCheckRefreshWindows(display,windows);
9176       (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
9177         windows->image.id);
9178       (void) SetImageProperty(*image,"group",value,exception);
9179       (void) DeleteImageProperty(*image,"label");
9180       (void) SetImageProperty(*image,"label","Matte",exception);
9181       (void) AcquireUniqueFilename(filename);
9182       (void) FormatLocaleString((*image)->filename,MagickPathExtent,"matte:%s",
9183         filename);
9184       status=WriteImage(image_info,*image,exception);
9185       (void) CopyMagickString(image_info->filename,filename,MagickPathExtent);
9186       matte_image=ReadImage(image_info,exception);
9187       (void) RelinquishUniqueFileResource(filename);
9188       if (matte_image == (Image *) NULL)
9189         break;
9190       (void) FormatLocaleString(matte_image->filename,MagickPathExtent,"show:%s",
9191         filename);
9192       status=WriteImage(image_info,matte_image,exception);
9193       matte_image=DestroyImage(matte_image);
9194       if (status == MagickFalse)
9195         XNoticeWidget(display,windows,"Unable to show matte",
9196           (*image)->filename);
9197       XDelay(display,1500);
9198       XSetCursorState(display,windows,MagickFalse);
9199       break;
9200     }
9201     case BackgroundCommand:
9202     {
9203       /*
9204         Background image.
9205       */
9206       status=XBackgroundImage(display,resource_info,windows,image,exception);
9207       if (status == MagickFalse)
9208         break;
9209       nexus=CloneImage(*image,0,0,MagickTrue,exception);
9210       if (nexus != (Image *) NULL)
9211         XClientMessage(display,windows->image.id,windows->im_protocols,
9212           windows->im_next_image,CurrentTime);
9213       break;
9214     }
9215     case SlideShowCommand:
9216     {
9217       static char
9218         delay[MagickPathExtent] = "5";
9219
9220       /*
9221         Display next image after pausing.
9222       */
9223       (void) XDialogWidget(display,windows,"Slide Show",
9224         "Pause how many 1/100ths of a second between images:",delay);
9225       if (*delay == '\0')
9226         break;
9227       resource_info->delay=StringToUnsignedLong(delay);
9228       XClientMessage(display,windows->image.id,windows->im_protocols,
9229         windows->im_next_image,CurrentTime);
9230       break;
9231     }
9232     case PreferencesCommand:
9233     {
9234       /*
9235         Set user preferences.
9236       */
9237       status=XPreferencesWidget(display,resource_info,windows);
9238       if (status == MagickFalse)
9239         break;
9240       nexus=CloneImage(*image,0,0,MagickTrue,exception);
9241       if (nexus != (Image *) NULL)
9242         XClientMessage(display,windows->image.id,windows->im_protocols,
9243           windows->im_next_image,CurrentTime);
9244       break;
9245     }
9246     case HelpCommand:
9247     {
9248       /*
9249         User requested help.
9250       */
9251       XTextViewWidget(display,resource_info,windows,MagickFalse,
9252         "Help Viewer - Display",DisplayHelp);
9253       break;
9254     }
9255     case BrowseDocumentationCommand:
9256     {
9257       Atom
9258         mozilla_atom;
9259
9260       Window
9261         mozilla_window,
9262         root_window;
9263
9264       /*
9265         Browse the ImageMagick documentation.
9266       */
9267       root_window=XRootWindow(display,XDefaultScreen(display));
9268       mozilla_atom=XInternAtom(display,"_MOZILLA_VERSION",MagickFalse);
9269       mozilla_window=XWindowByProperty(display,root_window,mozilla_atom);
9270       if (mozilla_window != (Window) NULL)
9271         {
9272           char
9273             command[MagickPathExtent],
9274             *url;
9275
9276           /*
9277             Display documentation using Netscape remote control.
9278           */
9279           url=GetMagickHomeURL();
9280           (void) FormatLocaleString(command,MagickPathExtent,
9281             "openurl(%s,new-tab)",url);
9282           url=DestroyString(url);
9283           mozilla_atom=XInternAtom(display,"_MOZILLA_COMMAND",MagickFalse);
9284           (void) XChangeProperty(display,mozilla_window,mozilla_atom,XA_STRING,
9285             8,PropModeReplace,(unsigned char *) command,(int) strlen(command));
9286           XSetCursorState(display,windows,MagickFalse);
9287           break;
9288         }
9289       XSetCursorState(display,windows,MagickTrue);
9290       XCheckRefreshWindows(display,windows);
9291       status=InvokeDelegate(image_info,*image,"browse",(char *) NULL,
9292         exception);
9293       if (status == MagickFalse)
9294         XNoticeWidget(display,windows,"Unable to browse documentation",
9295           (char *) NULL);
9296       XDelay(display,1500);
9297       XSetCursorState(display,windows,MagickFalse);
9298       break;
9299     }
9300     case VersionCommand:
9301     {
9302       XNoticeWidget(display,windows,GetMagickVersion((size_t *) NULL),
9303         GetMagickCopyright());
9304       break;
9305     }
9306     case SaveToUndoBufferCommand:
9307       break;
9308     default:
9309     {
9310       (void) XBell(display,0);
9311       break;
9312     }
9313   }
9314   image_info=DestroyImageInfo(image_info);
9315   return(nexus);
9316 }
9317 \f
9318 /*
9319 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9320 %                                                                             %
9321 %                                                                             %
9322 %                                                                             %
9323 +   X M a g n i f y I m a g e                                                 %
9324 %                                                                             %
9325 %                                                                             %
9326 %                                                                             %
9327 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9328 %
9329 %  XMagnifyImage() magnifies portions of the image as indicated by the pointer.
9330 %  The magnified portion is displayed in a separate window.
9331 %
9332 %  The format of the XMagnifyImage method is:
9333 %
9334 %      void XMagnifyImage(Display *display,XWindows *windows,XEvent *event,
9335 %        ExceptionInfo *exception)
9336 %
9337 %  A description of each parameter follows:
9338 %
9339 %    o display: Specifies a connection to an X server;  returned from
9340 %      XOpenDisplay.
9341 %
9342 %    o windows: Specifies a pointer to a XWindows structure.
9343 %
9344 %    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
9345 %      the entire image is refreshed.
9346 %
9347 %    o exception: return any errors or warnings in this structure.
9348 %
9349 */
9350 static void XMagnifyImage(Display *display,XWindows *windows,XEvent *event,
9351   ExceptionInfo *exception)
9352 {
9353   char
9354     text[MagickPathExtent];
9355
9356   register int
9357     x,
9358     y;
9359
9360   size_t
9361     state;
9362
9363   /*
9364     Update magnified image until the mouse button is released.
9365   */
9366   (void) XCheckDefineCursor(display,windows->image.id,windows->magnify.cursor);
9367   state=DefaultState;
9368   x=event->xbutton.x;
9369   y=event->xbutton.y;
9370   windows->magnify.x=(int) windows->image.x+x;
9371   windows->magnify.y=(int) windows->image.y+y;
9372   do
9373   {
9374     /*
9375       Map and unmap Info widget as text cursor crosses its boundaries.
9376     */
9377     if (windows->info.mapped != MagickFalse )
9378       {
9379         if ((x < (int) (windows->info.x+windows->info.width)) &&
9380             (y < (int) (windows->info.y+windows->info.height)))
9381           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
9382       }
9383     else
9384       if ((x > (int) (windows->info.x+windows->info.width)) ||
9385           (y > (int) (windows->info.y+windows->info.height)))
9386         (void) XMapWindow(display,windows->info.id);
9387     if (windows->info.mapped != MagickFalse )
9388       {
9389         /*
9390           Display pointer position.
9391         */
9392         (void) FormatLocaleString(text,MagickPathExtent," %+d%+d ",
9393           windows->magnify.x,windows->magnify.y);
9394         XInfoWidget(display,windows,text);
9395       }
9396     /*
9397       Wait for next event.
9398     */
9399     XScreenEvent(display,windows,event,exception);
9400     switch (event->type)
9401     {
9402       case ButtonPress:
9403         break;
9404       case ButtonRelease:
9405       {
9406         /*
9407           User has finished magnifying image.
9408         */
9409         x=event->xbutton.x;
9410         y=event->xbutton.y;
9411         state|=ExitState;
9412         break;
9413       }
9414       case Expose:
9415         break;
9416       case MotionNotify:
9417       {
9418         x=event->xmotion.x;
9419         y=event->xmotion.y;
9420         break;
9421       }
9422       default:
9423         break;
9424     }
9425     /*
9426       Check boundary conditions.
9427     */
9428     if (x < 0)
9429       x=0;
9430     else
9431       if (x >= (int) windows->image.width)
9432         x=(int) windows->image.width-1;
9433     if (y < 0)
9434       y=0;
9435     else
9436      if (y >= (int) windows->image.height)
9437        y=(int) windows->image.height-1;
9438   } while ((state & ExitState) == 0);
9439   /*
9440     Display magnified image.
9441   */
9442   XSetCursorState(display,windows,MagickFalse);
9443 }
9444 \f
9445 /*
9446 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9447 %                                                                             %
9448 %                                                                             %
9449 %                                                                             %
9450 +   X M a g n i f y W i n d o w C o m m a n d                                 %
9451 %                                                                             %
9452 %                                                                             %
9453 %                                                                             %
9454 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9455 %
9456 %  XMagnifyWindowCommand() moves the image within an Magnify window by one
9457 %  pixel as specified by the key symbol.
9458 %
9459 %  The format of the XMagnifyWindowCommand method is:
9460 %
9461 %      void XMagnifyWindowCommand(Display *display,XWindows *windows,
9462 %        const MagickStatusType state,const KeySym key_symbol,
9463 %        ExceptionInfo *exception)
9464 %
9465 %  A description of each parameter follows:
9466 %
9467 %    o display: Specifies a connection to an X server; returned from
9468 %      XOpenDisplay.
9469 %
9470 %    o windows: Specifies a pointer to a XWindows structure.
9471 %
9472 %    o state: key mask.
9473 %
9474 %    o key_symbol: Specifies a KeySym which indicates which side of the image
9475 %      to trim.
9476 %
9477 %    o exception: return any errors or warnings in this structure.
9478 %
9479 */
9480 static void XMagnifyWindowCommand(Display *display,XWindows *windows,
9481   const MagickStatusType state,const KeySym key_symbol,ExceptionInfo *exception)
9482 {
9483   unsigned int
9484     quantum;
9485
9486   /*
9487     User specified a magnify factor or position.
9488   */
9489   quantum=1;
9490   if ((state & Mod1Mask) != 0)
9491     quantum=10;
9492   switch ((int) key_symbol)
9493   {
9494     case QuitCommand:
9495     {
9496       (void) XWithdrawWindow(display,windows->magnify.id,
9497         windows->magnify.screen);
9498       break;
9499     }
9500     case XK_Home:
9501     case XK_KP_Home:
9502     {
9503       windows->magnify.x=(int) windows->image.width/2;
9504       windows->magnify.y=(int) windows->image.height/2;
9505       break;
9506     }
9507     case XK_Left:
9508     case XK_KP_Left:
9509     {
9510       if (windows->magnify.x > 0)
9511         windows->magnify.x-=quantum;
9512       break;
9513     }
9514     case XK_Up:
9515     case XK_KP_Up:
9516     {
9517       if (windows->magnify.y > 0)
9518         windows->magnify.y-=quantum;
9519       break;
9520     }
9521     case XK_Right:
9522     case XK_KP_Right:
9523     {
9524       if (windows->magnify.x < (int) (windows->image.ximage->width-1))
9525         windows->magnify.x+=quantum;
9526       break;
9527     }
9528     case XK_Down:
9529     case XK_KP_Down:
9530     {
9531       if (windows->magnify.y < (int) (windows->image.ximage->height-1))
9532         windows->magnify.y+=quantum;
9533       break;
9534     }
9535     case XK_0:
9536     case XK_1:
9537     case XK_2:
9538     case XK_3:
9539     case XK_4:
9540     case XK_5:
9541     case XK_6:
9542     case XK_7:
9543     case XK_8:
9544     case XK_9:
9545     {
9546       windows->magnify.data=(key_symbol-XK_0);
9547       break;
9548     }
9549     case XK_KP_0:
9550     case XK_KP_1:
9551     case XK_KP_2:
9552     case XK_KP_3:
9553     case XK_KP_4:
9554     case XK_KP_5:
9555     case XK_KP_6:
9556     case XK_KP_7:
9557     case XK_KP_8:
9558     case XK_KP_9:
9559     {
9560       windows->magnify.data=(key_symbol-XK_KP_0);
9561       break;
9562     }
9563     default:
9564       break;
9565   }
9566   XMakeMagnifyImage(display,windows,exception);
9567 }
9568 \f
9569 /*
9570 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9571 %                                                                             %
9572 %                                                                             %
9573 %                                                                             %
9574 +   X M a k e P a n I m a g e                                                 %
9575 %                                                                             %
9576 %                                                                             %
9577 %                                                                             %
9578 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9579 %
9580 %  XMakePanImage() creates a thumbnail of the image and displays it in the Pan
9581 %  icon window.
9582 %
9583 %  The format of the XMakePanImage method is:
9584 %
9585 %        void XMakePanImage(Display *display,XResourceInfo *resource_info,
9586 %          XWindows *windows,Image *image,ExceptionInfo *exception)
9587 %
9588 %  A description of each parameter follows:
9589 %
9590 %    o display: Specifies a connection to an X server;  returned from
9591 %      XOpenDisplay.
9592 %
9593 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9594 %
9595 %    o windows: Specifies a pointer to a XWindows structure.
9596 %
9597 %    o image: the image.
9598 %
9599 %    o exception: return any errors or warnings in this structure.
9600 %
9601 */
9602 static void XMakePanImage(Display *display,XResourceInfo *resource_info,
9603   XWindows *windows,Image *image,ExceptionInfo *exception)
9604 {
9605   MagickStatusType
9606     status;
9607
9608   /*
9609     Create and display image for panning icon.
9610   */
9611   XSetCursorState(display,windows,MagickTrue);
9612   XCheckRefreshWindows(display,windows);
9613   windows->pan.x=(int) windows->image.x;
9614   windows->pan.y=(int) windows->image.y;
9615   status=XMakeImage(display,resource_info,&windows->pan,image,
9616     windows->pan.width,windows->pan.height,exception);
9617   if (status == MagickFalse)
9618     ThrowXWindowException(ResourceLimitError,
9619      "MemoryAllocationFailed",image->filename);
9620   (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
9621     windows->pan.pixmap);
9622   (void) XClearWindow(display,windows->pan.id);
9623   XDrawPanRectangle(display,windows);
9624   XSetCursorState(display,windows,MagickFalse);
9625 }
9626 \f
9627 /*
9628 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9629 %                                                                             %
9630 %                                                                             %
9631 %                                                                             %
9632 +   X M a t t a E d i t I m a g e                                             %
9633 %                                                                             %
9634 %                                                                             %
9635 %                                                                             %
9636 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9637 %
9638 %  XMatteEditImage() allows the user to interactively change the Matte channel
9639 %  of an image.  If the image is PseudoClass it is promoted to DirectClass
9640 %  before the matte information is stored.
9641 %
9642 %  The format of the XMatteEditImage method is:
9643 %
9644 %      MagickBooleanType XMatteEditImage(Display *display,
9645 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
9646 %        ExceptionInfo *exception)
9647 %
9648 %  A description of each parameter follows:
9649 %
9650 %    o display: Specifies a connection to an X server;  returned from
9651 %      XOpenDisplay.
9652 %
9653 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9654 %
9655 %    o windows: Specifies a pointer to a XWindows structure.
9656 %
9657 %    o image: the image; returned from ReadImage.
9658 %
9659 %    o exception: return any errors or warnings in this structure.
9660 %
9661 */
9662 static MagickBooleanType XMatteEditImage(Display *display,
9663   XResourceInfo *resource_info,XWindows *windows,Image **image,
9664   ExceptionInfo *exception)
9665 {
9666   static char
9667     matte[MagickPathExtent] = "0";
9668
9669   static const char
9670     *MatteEditMenu[] =
9671     {
9672       "Method",
9673       "Border Color",
9674       "Fuzz",
9675       "Matte Value",
9676       "Undo",
9677       "Help",
9678       "Dismiss",
9679       (char *) NULL
9680     };
9681
9682   static const ModeType
9683     MatteEditCommands[] =
9684     {
9685       MatteEditMethod,
9686       MatteEditBorderCommand,
9687       MatteEditFuzzCommand,
9688       MatteEditValueCommand,
9689       MatteEditUndoCommand,
9690       MatteEditHelpCommand,
9691       MatteEditDismissCommand
9692     };
9693
9694   static PaintMethod
9695     method = PointMethod;
9696
9697   static XColor
9698     border_color = { 0, 0, 0, 0, 0, 0 };
9699
9700   char
9701     command[MagickPathExtent],
9702     text[MagickPathExtent];
9703
9704   Cursor
9705     cursor;
9706
9707   int
9708     entry,
9709     id,
9710     x,
9711     x_offset,
9712     y,
9713     y_offset;
9714
9715   register int
9716     i;
9717
9718   register Quantum
9719     *q;
9720
9721   unsigned int
9722     height,
9723     width;
9724
9725   size_t
9726     state;
9727
9728   XEvent
9729     event;
9730
9731   /*
9732     Map Command widget.
9733   */
9734   (void) CloneString(&windows->command.name,"Matte Edit");
9735   windows->command.data=4;
9736   (void) XCommandWidget(display,windows,MatteEditMenu,(XEvent *) NULL);
9737   (void) XMapRaised(display,windows->command.id);
9738   XClientMessage(display,windows->image.id,windows->im_protocols,
9739     windows->im_update_widget,CurrentTime);
9740   /*
9741     Make cursor.
9742   */
9743   cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
9744     resource_info->background_color,resource_info->foreground_color);
9745   (void) XCheckDefineCursor(display,windows->image.id,cursor);
9746   /*
9747     Track pointer until button 1 is pressed.
9748   */
9749   XQueryPosition(display,windows->image.id,&x,&y);
9750   (void) XSelectInput(display,windows->image.id,
9751     windows->image.attributes.event_mask | PointerMotionMask);
9752   state=DefaultState;
9753   do
9754   {
9755     if (windows->info.mapped != MagickFalse )
9756       {
9757         /*
9758           Display pointer position.
9759         */
9760         (void) FormatLocaleString(text,MagickPathExtent," %+d%+d ",
9761           x+windows->image.x,y+windows->image.y);
9762         XInfoWidget(display,windows,text);
9763       }
9764     /*
9765       Wait for next event.
9766     */
9767     XScreenEvent(display,windows,&event,exception);
9768     if (event.xany.window == windows->command.id)
9769       {
9770         /*
9771           Select a command from the Command widget.
9772         */
9773         id=XCommandWidget(display,windows,MatteEditMenu,&event);
9774         if (id < 0)
9775           {
9776             (void) XCheckDefineCursor(display,windows->image.id,cursor);
9777             continue;
9778           }
9779         switch (MatteEditCommands[id])
9780         {
9781           case MatteEditMethod:
9782           {
9783             char
9784               **methods;
9785
9786             /*
9787               Select a method from the pop-up menu.
9788             */
9789             methods=GetCommandOptions(MagickMethodOptions);
9790             if (methods == (char **) NULL)
9791               break;
9792             entry=XMenuWidget(display,windows,MatteEditMenu[id],
9793               (const char **) methods,command);
9794             if (entry >= 0)
9795               method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
9796                 MagickFalse,methods[entry]);
9797             methods=DestroyStringList(methods);
9798             break;
9799           }
9800           case MatteEditBorderCommand:
9801           {
9802             const char
9803               *ColorMenu[MaxNumberPens];
9804
9805             int
9806               pen_number;
9807
9808             /*
9809               Initialize menu selections.
9810             */
9811             for (i=0; i < (int) (MaxNumberPens-2); i++)
9812               ColorMenu[i]=resource_info->pen_colors[i];
9813             ColorMenu[MaxNumberPens-2]="Browser...";
9814             ColorMenu[MaxNumberPens-1]=(const char *) NULL;
9815             /*
9816               Select a pen color from the pop-up menu.
9817             */
9818             pen_number=XMenuWidget(display,windows,MatteEditMenu[id],
9819               (const char **) ColorMenu,command);
9820             if (pen_number < 0)
9821               break;
9822             if (pen_number == (MaxNumberPens-2))
9823               {
9824                 static char
9825                   color_name[MagickPathExtent] = "gray";
9826
9827                 /*
9828                   Select a pen color from a dialog.
9829                 */
9830                 resource_info->pen_colors[pen_number]=color_name;
9831                 XColorBrowserWidget(display,windows,"Select",color_name);
9832                 if (*color_name == '\0')
9833                   break;
9834               }
9835             /*
9836               Set border color.
9837             */
9838             (void) XParseColor(display,windows->map_info->colormap,
9839               resource_info->pen_colors[pen_number],&border_color);
9840             break;
9841           }
9842           case MatteEditFuzzCommand:
9843           {
9844             static char
9845               fuzz[MagickPathExtent];
9846
9847             static const char
9848               *FuzzMenu[] =
9849               {
9850                 "0%",
9851                 "2%",
9852                 "5%",
9853                 "10%",
9854                 "15%",
9855                 "Dialog...",
9856                 (char *) NULL,
9857               };
9858
9859             /*
9860               Select a command from the pop-up menu.
9861             */
9862             entry=XMenuWidget(display,windows,MatteEditMenu[id],FuzzMenu,
9863               command);
9864             if (entry < 0)
9865               break;
9866             if (entry != 5)
9867               {
9868                 (*image)->fuzz=StringToDoubleInterval(FuzzMenu[entry],(double)
9869                   QuantumRange+1.0);
9870                 break;
9871               }
9872             (void) CopyMagickString(fuzz,"20%",MagickPathExtent);
9873             (void) XDialogWidget(display,windows,"Ok",
9874               "Enter fuzz factor (0.0 - 99.9%):",fuzz);
9875             if (*fuzz == '\0')
9876               break;
9877             (void) ConcatenateMagickString(fuzz,"%",MagickPathExtent);
9878             (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+
9879               1.0);
9880             break;
9881           }
9882           case MatteEditValueCommand:
9883           {
9884             static char
9885               message[MagickPathExtent];
9886
9887             static const char
9888               *MatteMenu[] =
9889               {
9890                 "Opaque",
9891                 "Transparent",
9892                 "Dialog...",
9893                 (char *) NULL,
9894               };
9895
9896             /*
9897               Select a command from the pop-up menu.
9898             */
9899             entry=XMenuWidget(display,windows,MatteEditMenu[id],MatteMenu,
9900               command);
9901             if (entry < 0)
9902               break;
9903             if (entry != 2)
9904               {
9905                 (void) FormatLocaleString(matte,MagickPathExtent,QuantumFormat,
9906                   OpaqueAlpha);
9907                 if (LocaleCompare(MatteMenu[entry],"Transparent") == 0)
9908                   (void) FormatLocaleString(matte,MagickPathExtent,
9909                     QuantumFormat,(Quantum) TransparentAlpha);
9910                 break;
9911               }
9912             (void) FormatLocaleString(message,MagickPathExtent,
9913               "Enter matte value (0 - " QuantumFormat "):",(Quantum)
9914               QuantumRange);
9915             (void) XDialogWidget(display,windows,"Matte",message,matte);
9916             if (*matte == '\0')
9917               break;
9918             break;
9919           }
9920           case MatteEditUndoCommand:
9921           {
9922             (void) XMagickCommand(display,resource_info,windows,UndoCommand,
9923               image,exception);
9924             break;
9925           }
9926           case MatteEditHelpCommand:
9927           {
9928             XTextViewWidget(display,resource_info,windows,MagickFalse,
9929               "Help Viewer - Matte Edit",ImageMatteEditHelp);
9930             break;
9931           }
9932           case MatteEditDismissCommand:
9933           {
9934             /*
9935               Prematurely exit.
9936             */
9937             state|=EscapeState;
9938             state|=ExitState;
9939             break;
9940           }
9941           default:
9942             break;
9943         }
9944         (void) XCheckDefineCursor(display,windows->image.id,cursor);
9945         continue;
9946       }
9947     switch (event.type)
9948     {
9949       case ButtonPress:
9950       {
9951         if (event.xbutton.button != Button1)
9952           break;
9953         if ((event.xbutton.window != windows->image.id) &&
9954             (event.xbutton.window != windows->magnify.id))
9955           break;
9956         /*
9957           Update matte data.
9958         */
9959         x=event.xbutton.x;
9960         y=event.xbutton.y;
9961         (void) XMagickCommand(display,resource_info,windows,
9962           SaveToUndoBufferCommand,image,exception);
9963         state|=UpdateConfigurationState;
9964         break;
9965       }
9966       case ButtonRelease:
9967       {
9968         if (event.xbutton.button != Button1)
9969           break;
9970         if ((event.xbutton.window != windows->image.id) &&
9971             (event.xbutton.window != windows->magnify.id))
9972           break;
9973         /*
9974           Update colormap information.
9975         */
9976         x=event.xbutton.x;
9977         y=event.xbutton.y;
9978         XConfigureImageColormap(display,resource_info,windows,*image,exception);
9979         (void) XConfigureImage(display,resource_info,windows,*image,exception);
9980         XInfoWidget(display,windows,text);
9981         (void) XCheckDefineCursor(display,windows->image.id,cursor);
9982         state&=(~UpdateConfigurationState);
9983         break;
9984       }
9985       case Expose:
9986         break;
9987       case KeyPress:
9988       {
9989         char
9990           command[MagickPathExtent];
9991
9992         KeySym
9993           key_symbol;
9994
9995         if (event.xkey.window == windows->magnify.id)
9996           {
9997             Window
9998               window;
9999
10000             window=windows->magnify.id;
10001             while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
10002           }
10003         if (event.xkey.window != windows->image.id)
10004           break;
10005         /*
10006           Respond to a user key press.
10007         */
10008         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
10009           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
10010         switch ((int) key_symbol)
10011         {
10012           case XK_Escape:
10013           case XK_F20:
10014           {
10015             /*
10016               Prematurely exit.
10017             */
10018             state|=ExitState;
10019             break;
10020           }
10021           case XK_F1:
10022           case XK_Help:
10023           {
10024             XTextViewWidget(display,resource_info,windows,MagickFalse,
10025               "Help Viewer - Matte Edit",ImageMatteEditHelp);
10026             break;
10027           }
10028           default:
10029           {
10030             (void) XBell(display,0);
10031             break;
10032           }
10033         }
10034         break;
10035       }
10036       case MotionNotify:
10037       {
10038         /*
10039           Map and unmap Info widget as cursor crosses its boundaries.
10040         */
10041         x=event.xmotion.x;
10042         y=event.xmotion.y;
10043         if (windows->info.mapped != MagickFalse )
10044           {
10045             if ((x < (int) (windows->info.x+windows->info.width)) &&
10046                 (y < (int) (windows->info.y+windows->info.height)))
10047               (void) XWithdrawWindow(display,windows->info.id,
10048                 windows->info.screen);
10049           }
10050         else
10051           if ((x > (int) (windows->info.x+windows->info.width)) ||
10052               (y > (int) (windows->info.y+windows->info.height)))
10053             (void) XMapWindow(display,windows->info.id);
10054         break;
10055       }
10056       default:
10057         break;
10058     }
10059     if (event.xany.window == windows->magnify.id)
10060       {
10061         x=windows->magnify.x-windows->image.x;
10062         y=windows->magnify.y-windows->image.y;
10063       }
10064     x_offset=x;
10065     y_offset=y;
10066     if ((state & UpdateConfigurationState) != 0)
10067       {
10068         CacheView
10069           *image_view;
10070
10071         int
10072           x,
10073           y;
10074
10075         /*
10076           Matte edit is relative to image configuration.
10077         */
10078         (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
10079           MagickTrue);
10080         XPutPixel(windows->image.ximage,x_offset,y_offset,
10081           windows->pixel_info->background_color.pixel);
10082         width=(unsigned int) (*image)->columns;
10083         height=(unsigned int) (*image)->rows;
10084         x=0;
10085         y=0;
10086         if (windows->image.crop_geometry != (char *) NULL)
10087           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,
10088             &height);
10089         x_offset=(int) (width*(windows->image.x+x_offset)/
10090           windows->image.ximage->width+x);
10091         y_offset=(int) (height*(windows->image.y+y_offset)/
10092           windows->image.ximage->height+y);
10093         if ((x_offset < 0) || (y_offset < 0))
10094           continue;
10095         if ((x_offset >= (int) (*image)->columns) ||
10096             (y_offset >= (int) (*image)->rows))
10097           continue;
10098         if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
10099           return(MagickFalse);
10100         if ((*image)->alpha_trait == UndefinedPixelTrait)
10101           (void) SetImageAlphaChannel(*image,OpaqueAlphaChannel,exception);
10102         image_view=AcquireAuthenticCacheView(*image,exception);
10103         switch (method)
10104         {
10105           case PointMethod:
10106           default:
10107           {
10108             /*
10109               Update matte information using point algorithm.
10110             */
10111             q=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,
10112               (ssize_t) y_offset,1,1,exception);
10113             if (q == (Quantum *) NULL)
10114               break;
10115             SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10116             (void) SyncCacheViewAuthenticPixels(image_view,exception);
10117             break;
10118           }
10119           case ReplaceMethod:
10120           {
10121             PixelInfo
10122               pixel,
10123               target;
10124
10125             /*
10126               Update matte information using replace algorithm.
10127             */
10128             (void) GetOneCacheViewVirtualPixelInfo(image_view,(ssize_t)
10129               x_offset,(ssize_t) y_offset,&target,exception);
10130             for (y=0; y < (int) (*image)->rows; y++)
10131             {
10132               q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10133                 (*image)->columns,1,exception);
10134               if (q == (Quantum *) NULL)
10135                 break;
10136               for (x=0; x < (int) (*image)->columns; x++)
10137               {
10138                 GetPixelInfoPixel(*image,q,&pixel);
10139                 if (IsFuzzyEquivalencePixelInfo(&pixel,&target))
10140                   SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10141                 q+=GetPixelChannels(*image);
10142               }
10143               if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
10144                 break;
10145             }
10146             break;
10147           }
10148           case FloodfillMethod:
10149           case FillToBorderMethod:
10150           {
10151             ChannelType
10152               channel_mask;
10153
10154             DrawInfo
10155               *draw_info;
10156
10157             PixelInfo
10158               target;
10159
10160             /*
10161               Update matte information using floodfill algorithm.
10162             */
10163             (void) GetOneVirtualPixelInfo(*image,
10164               GetPixelCacheVirtualMethod(*image),(ssize_t) x_offset,(ssize_t)
10165               y_offset,&target,exception);
10166             if (method == FillToBorderMethod)
10167               {
10168                 target.red=(double) ScaleShortToQuantum(
10169                   border_color.red);
10170                 target.green=(double) ScaleShortToQuantum(
10171                   border_color.green);
10172                 target.blue=(double) ScaleShortToQuantum(
10173                   border_color.blue);
10174               }
10175             draw_info=CloneDrawInfo(resource_info->image_info,
10176               (DrawInfo *) NULL);
10177             draw_info->fill.alpha=(double) ClampToQuantum(
10178               StringToDouble(matte,(char **) NULL));
10179             channel_mask=SetImageChannelMask(*image,AlphaChannel);
10180             (void) FloodfillPaintImage(*image,draw_info,&target,(ssize_t)
10181               x_offset,(ssize_t) y_offset,
10182               method != FloodfillMethod ? MagickTrue : MagickFalse,exception);
10183             (void) SetPixelChannelMask(*image,channel_mask);
10184             draw_info=DestroyDrawInfo(draw_info);
10185             break;
10186           }
10187           case ResetMethod:
10188           {
10189             /*
10190               Update matte information using reset algorithm.
10191             */
10192             if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
10193               return(MagickFalse);
10194             for (y=0; y < (int) (*image)->rows; y++)
10195             {
10196               q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10197                 (*image)->columns,1,exception);
10198               if (q == (Quantum *) NULL)
10199                 break;
10200               for (x=0; x < (int) (*image)->columns; x++)
10201               {
10202                 SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10203                 q+=GetPixelChannels(*image);
10204               }
10205               if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
10206                 break;
10207             }
10208             if (StringToLong(matte) == (long) OpaqueAlpha)
10209               (*image)->alpha_trait=UndefinedPixelTrait;
10210             break;
10211           }
10212         }
10213         image_view=DestroyCacheView(image_view);
10214         state&=(~UpdateConfigurationState);
10215       }
10216   } while ((state & ExitState) == 0);
10217   (void) XSelectInput(display,windows->image.id,
10218     windows->image.attributes.event_mask);
10219   XSetCursorState(display,windows,MagickFalse);
10220   (void) XFreeCursor(display,cursor);
10221   return(MagickTrue);
10222 }
10223 \f
10224 /*
10225 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10226 %                                                                             %
10227 %                                                                             %
10228 %                                                                             %
10229 +   X O p e n I m a g e                                                       %
10230 %                                                                             %
10231 %                                                                             %
10232 %                                                                             %
10233 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10234 %
10235 %  XOpenImage() loads an image from a file.
10236 %
10237 %  The format of the XOpenImage method is:
10238 %
10239 %     Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10240 %       XWindows *windows,const unsigned int command)
10241 %
10242 %  A description of each parameter follows:
10243 %
10244 %    o display: Specifies a connection to an X server; returned from
10245 %      XOpenDisplay.
10246 %
10247 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10248 %
10249 %    o windows: Specifies a pointer to a XWindows structure.
10250 %
10251 %    o command: A value other than zero indicates that the file is selected
10252 %      from the command line argument list.
10253 %
10254 */
10255 static Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10256   XWindows *windows,const MagickBooleanType command)
10257 {
10258   const MagickInfo
10259     *magick_info;
10260
10261   ExceptionInfo
10262     *exception;
10263
10264   Image
10265     *nexus;
10266
10267   ImageInfo
10268     *image_info;
10269
10270   static char
10271     filename[MagickPathExtent] = "\0";
10272
10273   /*
10274     Request file name from user.
10275   */
10276   if (command == MagickFalse)
10277     XFileBrowserWidget(display,windows,"Open",filename);
10278   else
10279     {
10280       char
10281         **filelist,
10282         **files;
10283
10284       int
10285         count,
10286         status;
10287
10288       register int
10289         i,
10290         j;
10291
10292       /*
10293         Select next image from the command line.
10294       */
10295       status=XGetCommand(display,windows->image.id,&files,&count);
10296       if (status == 0)
10297         {
10298           ThrowXWindowException(XServerError,"UnableToGetProperty","...");
10299           return((Image *) NULL);
10300         }
10301       filelist=(char **) AcquireQuantumMemory((size_t) count,sizeof(*filelist));
10302       if (filelist == (char **) NULL)
10303         {
10304           ThrowXWindowException(ResourceLimitError,
10305             "MemoryAllocationFailed","...");
10306           (void) XFreeStringList(files);
10307           return((Image *) NULL);
10308         }
10309       j=0;
10310       for (i=1; i < count; i++)
10311         if (*files[i] != '-')
10312           filelist[j++]=files[i];
10313       filelist[j]=(char *) NULL;
10314       XListBrowserWidget(display,windows,&windows->widget,
10315         (const char **) filelist,"Load","Select Image to Load:",filename);
10316       filelist=(char **) RelinquishMagickMemory(filelist);
10317       (void) XFreeStringList(files);
10318     }
10319   if (*filename == '\0')
10320     return((Image *) NULL);
10321   image_info=CloneImageInfo(resource_info->image_info);
10322   (void) SetImageInfoProgressMonitor(image_info,(MagickProgressMonitor) NULL,
10323     (void *) NULL);
10324   (void) CopyMagickString(image_info->filename,filename,MagickPathExtent);
10325   exception=AcquireExceptionInfo();
10326   (void) SetImageInfo(image_info,0,exception);
10327   if (LocaleCompare(image_info->magick,"X") == 0)
10328     {
10329       char
10330         seconds[MagickPathExtent];
10331
10332       /*
10333         User may want to delay the X server screen grab.
10334       */
10335       (void) CopyMagickString(seconds,"0",MagickPathExtent);
10336       (void) XDialogWidget(display,windows,"Grab","Enter any delay in seconds:",
10337         seconds);
10338       if (*seconds == '\0')
10339         return((Image *) NULL);
10340       XDelay(display,(size_t) (1000*StringToLong(seconds)));
10341     }
10342   magick_info=GetMagickInfo(image_info->magick,exception);
10343   if ((magick_info != (const MagickInfo *) NULL) &&
10344       GetMagickRawSupport(magick_info) == MagickTrue)
10345     {
10346       char
10347         geometry[MagickPathExtent];
10348
10349       /*
10350         Request image size from the user.
10351       */
10352       (void) CopyMagickString(geometry,"512x512",MagickPathExtent);
10353       if (image_info->size != (char *) NULL)
10354         (void) CopyMagickString(geometry,image_info->size,MagickPathExtent);
10355       (void) XDialogWidget(display,windows,"Load","Enter the image geometry:",
10356         geometry);
10357       (void) CloneString(&image_info->size,geometry);
10358     }
10359   /*
10360     Load the image.
10361   */
10362   XSetCursorState(display,windows,MagickTrue);
10363   XCheckRefreshWindows(display,windows);
10364   (void) CopyMagickString(image_info->filename,filename,MagickPathExtent);
10365   nexus=ReadImage(image_info,exception);
10366   CatchException(exception);
10367   XSetCursorState(display,windows,MagickFalse);
10368   if (nexus != (Image *) NULL)
10369     XClientMessage(display,windows->image.id,windows->im_protocols,
10370       windows->im_next_image,CurrentTime);
10371   else
10372     {
10373       char
10374         *text,
10375         **textlist;
10376
10377       /*
10378         Unknown image format.
10379       */
10380       text=FileToString(filename,~0UL,exception);
10381       if (text == (char *) NULL)
10382         return((Image *) NULL);
10383       textlist=StringToList(text);
10384       if (textlist != (char **) NULL)
10385         {
10386           char
10387             title[MagickPathExtent];
10388
10389           register int
10390             i;
10391
10392           (void) FormatLocaleString(title,MagickPathExtent,
10393             "Unknown format: %s",filename);
10394           XTextViewWidget(display,resource_info,windows,MagickTrue,title,
10395             (const char **) textlist);
10396           for (i=0; textlist[i] != (char *) NULL; i++)
10397             textlist[i]=DestroyString(textlist[i]);
10398           textlist=(char **) RelinquishMagickMemory(textlist);
10399         }
10400       text=DestroyString(text);
10401     }
10402   exception=DestroyExceptionInfo(exception);
10403   image_info=DestroyImageInfo(image_info);
10404   return(nexus);
10405 }
10406 \f
10407 /*
10408 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10409 %                                                                             %
10410 %                                                                             %
10411 %                                                                             %
10412 +   X P a n I m a g e                                                         %
10413 %                                                                             %
10414 %                                                                             %
10415 %                                                                             %
10416 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10417 %
10418 %  XPanImage() pans the image until the mouse button is released.
10419 %
10420 %  The format of the XPanImage method is:
10421 %
10422 %      void XPanImage(Display *display,XWindows *windows,XEvent *event,
10423 %        ExceptionInfo *exception)
10424 %
10425 %  A description of each parameter follows:
10426 %
10427 %    o display: Specifies a connection to an X server;  returned from
10428 %      XOpenDisplay.
10429 %
10430 %    o windows: Specifies a pointer to a XWindows structure.
10431 %
10432 %    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
10433 %      the entire image is refreshed.
10434 %
10435 %    o exception: return any errors or warnings in this structure.
10436 %
10437 */
10438 static void XPanImage(Display *display,XWindows *windows,XEvent *event,
10439   ExceptionInfo *exception)
10440 {
10441   char
10442     text[MagickPathExtent];
10443
10444   Cursor
10445     cursor;
10446
10447   double
10448     x_factor,
10449     y_factor;
10450
10451   RectangleInfo
10452     pan_info;
10453
10454   size_t
10455     state;
10456
10457   /*
10458     Define cursor.
10459   */
10460   if ((windows->image.ximage->width > (int) windows->image.width) &&
10461       (windows->image.ximage->height > (int) windows->image.height))
10462     cursor=XCreateFontCursor(display,XC_fleur);
10463   else
10464     if (windows->image.ximage->width > (int) windows->image.width)
10465       cursor=XCreateFontCursor(display,XC_sb_h_double_arrow);
10466     else
10467       if (windows->image.ximage->height > (int) windows->image.height)
10468         cursor=XCreateFontCursor(display,XC_sb_v_double_arrow);
10469       else
10470         cursor=XCreateFontCursor(display,XC_arrow);
10471   (void) XCheckDefineCursor(display,windows->pan.id,cursor);
10472   /*
10473     Pan image as pointer moves until the mouse button is released.
10474   */
10475   x_factor=(double) windows->image.ximage->width/windows->pan.width;
10476   y_factor=(double) windows->image.ximage->height/windows->pan.height;
10477   pan_info.width=windows->pan.width*windows->image.width/
10478     windows->image.ximage->width;
10479   pan_info.height=windows->pan.height*windows->image.height/
10480     windows->image.ximage->height;
10481   pan_info.x=0;
10482   pan_info.y=0;
10483   state=UpdateConfigurationState;
10484   do
10485   {
10486     switch (event->type)
10487     {
10488       case ButtonPress:
10489       {
10490         /*
10491           User choose an initial pan location.
10492         */
10493         pan_info.x=(ssize_t) event->xbutton.x;
10494         pan_info.y=(ssize_t) event->xbutton.y;
10495         state|=UpdateConfigurationState;
10496         break;
10497       }
10498       case ButtonRelease:
10499       {
10500         /*
10501           User has finished panning the image.
10502         */
10503         pan_info.x=(ssize_t) event->xbutton.x;
10504         pan_info.y=(ssize_t) event->xbutton.y;
10505         state|=UpdateConfigurationState | ExitState;
10506         break;
10507       }
10508       case MotionNotify:
10509       {
10510         pan_info.x=(ssize_t) event->xmotion.x;
10511         pan_info.y=(ssize_t) event->xmotion.y;
10512         state|=UpdateConfigurationState;
10513       }
10514       default:
10515         break;
10516     }
10517     if ((state & UpdateConfigurationState) != 0)
10518       {
10519         /*
10520           Check boundary conditions.
10521         */
10522         if (pan_info.x < (ssize_t) (pan_info.width/2))
10523           pan_info.x=0;
10524         else
10525           pan_info.x=(ssize_t) (x_factor*(pan_info.x-(pan_info.width/2)));
10526         if (pan_info.x < 0)
10527           pan_info.x=0;
10528         else
10529           if ((int) (pan_info.x+windows->image.width) >
10530               windows->image.ximage->width)
10531             pan_info.x=(ssize_t)
10532               (windows->image.ximage->width-windows->image.width);
10533         if (pan_info.y < (ssize_t) (pan_info.height/2))
10534           pan_info.y=0;
10535         else
10536           pan_info.y=(ssize_t) (y_factor*(pan_info.y-(pan_info.height/2)));
10537         if (pan_info.y < 0)
10538           pan_info.y=0;
10539         else
10540           if ((int) (pan_info.y+windows->image.height) >
10541               windows->image.ximage->height)
10542             pan_info.y=(ssize_t)
10543               (windows->image.ximage->height-windows->image.height);
10544         if ((windows->image.x != (int) pan_info.x) ||
10545             (windows->image.y != (int) pan_info.y))
10546           {
10547             /*
10548               Display image pan offset.
10549             */
10550             windows->image.x=(int) pan_info.x;
10551             windows->image.y=(int) pan_info.y;
10552             (void) FormatLocaleString(text,MagickPathExtent," %ux%u%+d%+d ",
10553               windows->image.width,windows->image.height,windows->image.x,
10554               windows->image.y);
10555             XInfoWidget(display,windows,text);
10556             /*
10557               Refresh Image window.
10558             */
10559             XDrawPanRectangle(display,windows);
10560             XRefreshWindow(display,&windows->image,(XEvent *) NULL);
10561           }
10562         state&=(~UpdateConfigurationState);
10563       }
10564     /*
10565       Wait for next event.
10566     */
10567     if ((state & ExitState) == 0)
10568       XScreenEvent(display,windows,event,exception);
10569   } while ((state & ExitState) == 0);
10570   /*
10571     Restore cursor.
10572   */
10573   (void) XCheckDefineCursor(display,windows->pan.id,windows->pan.cursor);
10574   (void) XFreeCursor(display,cursor);
10575   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
10576 }
10577 \f
10578 /*
10579 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10580 %                                                                             %
10581 %                                                                             %
10582 %                                                                             %
10583 +   X P a s t e I m a g e                                                     %
10584 %                                                                             %
10585 %                                                                             %
10586 %                                                                             %
10587 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10588 %
10589 %  XPasteImage() pastes an image previously saved with XCropImage in the X
10590 %  window image at a location the user chooses with the pointer.
10591 %
10592 %  The format of the XPasteImage method is:
10593 %
10594 %      MagickBooleanType XPasteImage(Display *display,
10595 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
10596 %        ExceptionInfo *exception)
10597 %
10598 %  A description of each parameter follows:
10599 %
10600 %    o display: Specifies a connection to an X server;  returned from
10601 %      XOpenDisplay.
10602 %
10603 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10604 %
10605 %    o windows: Specifies a pointer to a XWindows structure.
10606 %
10607 %    o image: the image; returned from ReadImage.
10608 %
10609 %    o exception: return any errors or warnings in this structure.
10610 %
10611 */
10612 static MagickBooleanType XPasteImage(Display *display,
10613   XResourceInfo *resource_info,XWindows *windows,Image *image,
10614   ExceptionInfo *exception)
10615 {
10616   static const char
10617     *PasteMenu[] =
10618     {
10619       "Operator",
10620       "Help",
10621       "Dismiss",
10622       (char *) NULL
10623     };
10624
10625   static const ModeType
10626     PasteCommands[] =
10627     {
10628       PasteOperatorsCommand,
10629       PasteHelpCommand,
10630       PasteDismissCommand
10631     };
10632
10633   static CompositeOperator
10634     compose = CopyCompositeOp;
10635
10636   char
10637     text[MagickPathExtent];
10638
10639   Cursor
10640     cursor;
10641
10642   Image
10643     *paste_image;
10644
10645   int
10646     entry,
10647     id,
10648     x,
10649     y;
10650
10651   double
10652     scale_factor;
10653
10654   RectangleInfo
10655     highlight_info,
10656     paste_info;
10657
10658   unsigned int
10659     height,
10660     width;
10661
10662   size_t
10663     state;
10664
10665   XEvent
10666     event;
10667
10668   /*
10669     Copy image.
10670   */
10671   if (resource_info->copy_image == (Image *) NULL)
10672     return(MagickFalse);
10673   paste_image=CloneImage(resource_info->copy_image,0,0,MagickTrue,exception);
10674   if (paste_image == (Image *) NULL)
10675     return(MagickFalse);
10676   /*
10677     Map Command widget.
10678   */
10679   (void) CloneString(&windows->command.name,"Paste");
10680   windows->command.data=1;
10681   (void) XCommandWidget(display,windows,PasteMenu,(XEvent *) NULL);
10682   (void) XMapRaised(display,windows->command.id);
10683   XClientMessage(display,windows->image.id,windows->im_protocols,
10684     windows->im_update_widget,CurrentTime);
10685   /*
10686     Track pointer until button 1 is pressed.
10687   */
10688   XSetCursorState(display,windows,MagickFalse);
10689   XQueryPosition(display,windows->image.id,&x,&y);
10690   (void) XSelectInput(display,windows->image.id,
10691     windows->image.attributes.event_mask | PointerMotionMask);
10692   paste_info.x=(ssize_t) windows->image.x+x;
10693   paste_info.y=(ssize_t) windows->image.y+y;
10694   paste_info.width=0;
10695   paste_info.height=0;
10696   cursor=XCreateFontCursor(display,XC_ul_angle);
10697   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
10698   state=DefaultState;
10699   do
10700   {
10701     if (windows->info.mapped != MagickFalse )
10702       {
10703         /*
10704           Display pointer position.
10705         */
10706         (void) FormatLocaleString(text,MagickPathExtent," %+ld%+ld ",
10707           (long) paste_info.x,(long) paste_info.y);
10708         XInfoWidget(display,windows,text);
10709       }
10710     highlight_info=paste_info;
10711     highlight_info.x=paste_info.x-windows->image.x;
10712     highlight_info.y=paste_info.y-windows->image.y;
10713     XHighlightRectangle(display,windows->image.id,
10714       windows->image.highlight_context,&highlight_info);
10715     /*
10716       Wait for next event.
10717     */
10718     XScreenEvent(display,windows,&event,exception);
10719     XHighlightRectangle(display,windows->image.id,
10720       windows->image.highlight_context,&highlight_info);
10721     if (event.xany.window == windows->command.id)
10722       {
10723         /*
10724           Select a command from the Command widget.
10725         */
10726         id=XCommandWidget(display,windows,PasteMenu,&event);
10727         if (id < 0)
10728           continue;
10729         switch (PasteCommands[id])
10730         {
10731           case PasteOperatorsCommand:
10732           {
10733             char
10734               command[MagickPathExtent],
10735               **operators;
10736
10737             /*
10738               Select a command from the pop-up menu.
10739             */
10740             operators=GetCommandOptions(MagickComposeOptions);
10741             if (operators == (char **) NULL)
10742               break;
10743             entry=XMenuWidget(display,windows,PasteMenu[id],
10744               (const char **) operators,command);
10745             if (entry >= 0)
10746               compose=(CompositeOperator) ParseCommandOption(
10747                 MagickComposeOptions,MagickFalse,operators[entry]);
10748             operators=DestroyStringList(operators);
10749             break;
10750           }
10751           case PasteHelpCommand:
10752           {
10753             XTextViewWidget(display,resource_info,windows,MagickFalse,
10754               "Help Viewer - Image Composite",ImagePasteHelp);
10755             break;
10756           }
10757           case PasteDismissCommand:
10758           {
10759             /*
10760               Prematurely exit.
10761             */
10762             state|=EscapeState;
10763             state|=ExitState;
10764             break;
10765           }
10766           default:
10767             break;
10768         }
10769         continue;
10770       }
10771     switch (event.type)
10772     {
10773       case ButtonPress:
10774       {
10775         if (image->debug != MagickFalse )
10776           (void) LogMagickEvent(X11Event,GetMagickModule(),
10777             "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
10778             event.xbutton.button,event.xbutton.x,event.xbutton.y);
10779         if (event.xbutton.button != Button1)
10780           break;
10781         if (event.xbutton.window != windows->image.id)
10782           break;
10783         /*
10784           Paste rectangle is relative to image configuration.
10785         */
10786         width=(unsigned int) image->columns;
10787         height=(unsigned int) image->rows;
10788         x=0;
10789         y=0;
10790         if (windows->image.crop_geometry != (char *) NULL)
10791           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
10792             &width,&height);
10793         scale_factor=(double) windows->image.ximage->width/width;
10794         paste_info.width=(unsigned int) (scale_factor*paste_image->columns+0.5);
10795         scale_factor=(double) windows->image.ximage->height/height;
10796         paste_info.height=(unsigned int) (scale_factor*paste_image->rows+0.5);
10797         (void) XCheckDefineCursor(display,windows->image.id,cursor);
10798         paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10799         paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10800         break;
10801       }
10802       case ButtonRelease:
10803       {
10804         if (image->debug != MagickFalse )
10805           (void) LogMagickEvent(X11Event,GetMagickModule(),
10806             "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
10807             event.xbutton.button,event.xbutton.x,event.xbutton.y);
10808         if (event.xbutton.button != Button1)
10809           break;
10810         if (event.xbutton.window != windows->image.id)
10811           break;
10812         if ((paste_info.width != 0) && (paste_info.height != 0))
10813           {
10814             /*
10815               User has selected the location of the paste image.
10816             */
10817             paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10818             paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10819             state|=ExitState;
10820           }
10821         break;
10822       }
10823       case Expose:
10824         break;
10825       case KeyPress:
10826       {
10827         char
10828           command[MagickPathExtent];
10829
10830         KeySym
10831           key_symbol;
10832
10833         int
10834           length;
10835
10836         if (event.xkey.window != windows->image.id)
10837           break;
10838         /*
10839           Respond to a user key press.
10840         */
10841         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
10842           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
10843         *(command+length)='\0';
10844         if (image->debug != MagickFalse )
10845           (void) LogMagickEvent(X11Event,GetMagickModule(),
10846             "Key press: 0x%lx (%s)",(long) key_symbol,command);
10847         switch ((int) key_symbol)
10848         {
10849           case XK_Escape:
10850           case XK_F20:
10851           {
10852             /*
10853               Prematurely exit.
10854             */
10855             paste_image=DestroyImage(paste_image);
10856             state|=EscapeState;
10857             state|=ExitState;
10858             break;
10859           }
10860           case XK_F1:
10861           case XK_Help:
10862           {
10863             (void) XSetFunction(display,windows->image.highlight_context,
10864               GXcopy);
10865             XTextViewWidget(display,resource_info,windows,MagickFalse,
10866               "Help Viewer - Image Composite",ImagePasteHelp);
10867             (void) XSetFunction(display,windows->image.highlight_context,
10868               GXinvert);
10869             break;
10870           }
10871           default:
10872           {
10873             (void) XBell(display,0);
10874             break;
10875           }
10876         }
10877         break;
10878       }
10879       case MotionNotify:
10880       {
10881         /*
10882           Map and unmap Info widget as text cursor crosses its boundaries.
10883         */
10884         x=event.xmotion.x;
10885         y=event.xmotion.y;
10886         if (windows->info.mapped != MagickFalse )
10887           {
10888             if ((x < (int) (windows->info.x+windows->info.width)) &&
10889                 (y < (int) (windows->info.y+windows->info.height)))
10890               (void) XWithdrawWindow(display,windows->info.id,
10891                 windows->info.screen);
10892           }
10893         else
10894           if ((x > (int) (windows->info.x+windows->info.width)) ||
10895               (y > (int) (windows->info.y+windows->info.height)))
10896             (void) XMapWindow(display,windows->info.id);
10897         paste_info.x=(ssize_t) windows->image.x+x;
10898         paste_info.y=(ssize_t) windows->image.y+y;
10899         break;
10900       }
10901       default:
10902       {
10903         if (image->debug != MagickFalse )
10904           (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
10905             event.type);
10906         break;
10907       }
10908     }
10909   } while ((state & ExitState) == 0);
10910   (void) XSelectInput(display,windows->image.id,
10911     windows->image.attributes.event_mask);
10912   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
10913   XSetCursorState(display,windows,MagickFalse);
10914   (void) XFreeCursor(display,cursor);
10915   if ((state & EscapeState) != 0)
10916     return(MagickTrue);
10917   /*
10918     Image pasting is relative to image configuration.
10919   */
10920   XSetCursorState(display,windows,MagickTrue);
10921   XCheckRefreshWindows(display,windows);
10922   width=(unsigned int) image->columns;
10923   height=(unsigned int) image->rows;
10924   x=0;
10925   y=0;
10926   if (windows->image.crop_geometry != (char *) NULL)
10927     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
10928   scale_factor=(double) width/windows->image.ximage->width;
10929   paste_info.x+=x;
10930   paste_info.x=(ssize_t) (scale_factor*paste_info.x+0.5);
10931   paste_info.width=(unsigned int) (scale_factor*paste_info.width+0.5);
10932   scale_factor=(double) height/windows->image.ximage->height;
10933   paste_info.y+=y;
10934   paste_info.y=(ssize_t) (scale_factor*paste_info.y*scale_factor+0.5);
10935   paste_info.height=(unsigned int) (scale_factor*paste_info.height+0.5);
10936   /*
10937     Paste image with X Image window.
10938   */
10939   (void) CompositeImage(image,paste_image,compose,MagickTrue,paste_info.x,
10940     paste_info.y,exception);
10941   paste_image=DestroyImage(paste_image);
10942   XSetCursorState(display,windows,MagickFalse);
10943   /*
10944     Update image colormap.
10945   */
10946   XConfigureImageColormap(display,resource_info,windows,image,exception);
10947   (void) XConfigureImage(display,resource_info,windows,image,exception);
10948   return(MagickTrue);
10949 }
10950 \f
10951 /*
10952 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10953 %                                                                             %
10954 %                                                                             %
10955 %                                                                             %
10956 +   X P r i n t I m a g e                                                     %
10957 %                                                                             %
10958 %                                                                             %
10959 %                                                                             %
10960 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10961 %
10962 %  XPrintImage() prints an image to a Postscript printer.
10963 %
10964 %  The format of the XPrintImage method is:
10965 %
10966 %      MagickBooleanType XPrintImage(Display *display,
10967 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
10968 %        ExceptionInfo *exception)
10969 %
10970 %  A description of each parameter follows:
10971 %
10972 %    o display: Specifies a connection to an X server; returned from
10973 %      XOpenDisplay.
10974 %
10975 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10976 %
10977 %    o windows: Specifies a pointer to a XWindows structure.
10978 %
10979 %    o image: the image.
10980 %
10981 %    o exception: return any errors or warnings in this structure.
10982 %
10983 */
10984 static MagickBooleanType XPrintImage(Display *display,
10985   XResourceInfo *resource_info,XWindows *windows,Image *image,
10986   ExceptionInfo *exception)
10987 {
10988   char
10989     filename[MagickPathExtent],
10990     geometry[MagickPathExtent];
10991
10992   Image
10993     *print_image;
10994
10995   ImageInfo
10996     *image_info;
10997
10998   MagickStatusType
10999     status;
11000
11001   /*
11002     Request Postscript page geometry from user.
11003   */
11004   image_info=CloneImageInfo(resource_info->image_info);
11005   (void) FormatLocaleString(geometry,MagickPathExtent,"Letter");
11006   if (image_info->page != (char *) NULL)
11007     (void) CopyMagickString(geometry,image_info->page,MagickPathExtent);
11008   XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
11009     "Select Postscript Page Geometry:",geometry);
11010   if (*geometry == '\0')
11011     return(MagickTrue);
11012   image_info->page=GetPageGeometry(geometry);
11013   /*
11014     Apply image transforms.
11015   */
11016   XSetCursorState(display,windows,MagickTrue);
11017   XCheckRefreshWindows(display,windows);
11018   print_image=CloneImage(image,0,0,MagickTrue,exception);
11019   if (print_image == (Image *) NULL)
11020     return(MagickFalse);
11021   (void) FormatLocaleString(geometry,MagickPathExtent,"%dx%d!",
11022     windows->image.ximage->width,windows->image.ximage->height);
11023   (void) TransformImage(&print_image,windows->image.crop_geometry,geometry,
11024     exception);
11025   /*
11026     Print image.
11027   */
11028   (void) AcquireUniqueFilename(filename);
11029   (void) FormatLocaleString(print_image->filename,MagickPathExtent,"print:%s",
11030     filename);
11031   status=WriteImage(image_info,print_image,exception);
11032   (void) RelinquishUniqueFileResource(filename);
11033   print_image=DestroyImage(print_image);
11034   image_info=DestroyImageInfo(image_info);
11035   XSetCursorState(display,windows,MagickFalse);
11036   return(status != 0 ? MagickTrue : MagickFalse);
11037 }
11038 \f
11039 /*
11040 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11041 %                                                                             %
11042 %                                                                             %
11043 %                                                                             %
11044 +   X R O I I m a g e                                                         %
11045 %                                                                             %
11046 %                                                                             %
11047 %                                                                             %
11048 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11049 %
11050 %  XROIImage() applies an image processing technique to a region of interest.
11051 %
11052 %  The format of the XROIImage method is:
11053 %
11054 %      MagickBooleanType XROIImage(Display *display,
11055 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
11056 %        ExceptionInfo *exception)
11057 %
11058 %  A description of each parameter follows:
11059 %
11060 %    o display: Specifies a connection to an X server; returned from
11061 %      XOpenDisplay.
11062 %
11063 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
11064 %
11065 %    o windows: Specifies a pointer to a XWindows structure.
11066 %
11067 %    o image: the image; returned from ReadImage.
11068 %
11069 %    o exception: return any errors or warnings in this structure.
11070 %
11071 */
11072 static MagickBooleanType XROIImage(Display *display,
11073   XResourceInfo *resource_info,XWindows *windows,Image **image,
11074   ExceptionInfo *exception)
11075 {
11076 #define ApplyMenus  7
11077
11078   static const char
11079     *ROIMenu[] =
11080     {
11081       "Help",
11082       "Dismiss",
11083       (char *) NULL
11084     },
11085     *ApplyMenu[] =
11086     {
11087       "File",
11088       "Edit",
11089       "Transform",
11090       "Enhance",
11091       "Effects",
11092       "F/X",
11093       "Miscellany",
11094       "Help",
11095       "Dismiss",
11096       (char *) NULL
11097     },
11098     *FileMenu[] =
11099     {
11100       "Save...",
11101       "Print...",
11102       (char *) NULL
11103     },
11104     *EditMenu[] =
11105     {
11106       "Undo",
11107       "Redo",
11108       (char *) NULL
11109     },
11110     *TransformMenu[] =
11111     {
11112       "Flop",
11113       "Flip",
11114       "Rotate Right",
11115       "Rotate Left",
11116       (char *) NULL
11117     },
11118     *EnhanceMenu[] =
11119     {
11120       "Hue...",
11121       "Saturation...",
11122       "Brightness...",
11123       "Gamma...",
11124       "Spiff",
11125       "Dull",
11126       "Contrast Stretch...",
11127       "Sigmoidal Contrast...",
11128       "Normalize",
11129       "Equalize",
11130       "Negate",
11131       "Grayscale",
11132       "Map...",
11133       "Quantize...",
11134       (char *) NULL
11135     },
11136     *EffectsMenu[] =
11137     {
11138       "Despeckle",
11139       "Emboss",
11140       "Reduce Noise",
11141       "Add Noise",
11142       "Sharpen...",
11143       "Blur...",
11144       "Threshold...",
11145       "Edge Detect...",
11146       "Spread...",
11147       "Shade...",
11148       "Raise...",
11149       "Segment...",
11150       (char *) NULL
11151     },
11152     *FXMenu[] =
11153     {
11154       "Solarize...",
11155       "Sepia Tone...",
11156       "Swirl...",
11157       "Implode...",
11158       "Vignette...",
11159       "Wave...",
11160       "Oil Paint...",
11161       "Charcoal Draw...",
11162       (char *) NULL
11163     },
11164     *MiscellanyMenu[] =
11165     {
11166       "Image Info",
11167       "Zoom Image",
11168       "Show Preview...",
11169       "Show Histogram",
11170       "Show Matte",
11171       (char *) NULL
11172     };
11173
11174   static const char
11175     **Menus[ApplyMenus] =
11176     {
11177       FileMenu,
11178       EditMenu,
11179       TransformMenu,
11180       EnhanceMenu,
11181       EffectsMenu,
11182       FXMenu,
11183       MiscellanyMenu
11184     };
11185
11186   static const CommandType
11187     ApplyCommands[] =
11188     {
11189       NullCommand,
11190       NullCommand,
11191       NullCommand,
11192       NullCommand,
11193       NullCommand,
11194       NullCommand,
11195       NullCommand,
11196       HelpCommand,
11197       QuitCommand
11198     },
11199     FileCommands[] =
11200     {
11201       SaveCommand,
11202       PrintCommand
11203     },
11204     EditCommands[] =
11205     {
11206       UndoCommand,
11207       RedoCommand
11208     },
11209     TransformCommands[] =
11210     {
11211       FlopCommand,
11212       FlipCommand,
11213       RotateRightCommand,
11214       RotateLeftCommand
11215     },
11216     EnhanceCommands[] =
11217     {
11218       HueCommand,
11219       SaturationCommand,
11220       BrightnessCommand,
11221       GammaCommand,
11222       SpiffCommand,
11223       DullCommand,
11224       ContrastStretchCommand,
11225       SigmoidalContrastCommand,
11226       NormalizeCommand,
11227       EqualizeCommand,
11228       NegateCommand,
11229       GrayscaleCommand,
11230       MapCommand,
11231       QuantizeCommand
11232     },
11233     EffectsCommands[] =
11234     {
11235       DespeckleCommand,
11236       EmbossCommand,
11237       ReduceNoiseCommand,
11238       AddNoiseCommand,
11239       SharpenCommand,
11240       BlurCommand,
11241       EdgeDetectCommand,
11242       SpreadCommand,
11243       ShadeCommand,
11244       RaiseCommand,
11245       SegmentCommand
11246     },
11247     FXCommands[] =
11248     {
11249       SolarizeCommand,
11250       SepiaToneCommand,
11251       SwirlCommand,
11252       ImplodeCommand,
11253       VignetteCommand,
11254       WaveCommand,
11255       OilPaintCommand,
11256       CharcoalDrawCommand
11257     },
11258     MiscellanyCommands[] =
11259     {
11260       InfoCommand,
11261       ZoomCommand,
11262       ShowPreviewCommand,
11263       ShowHistogramCommand,
11264       ShowMatteCommand
11265     },
11266     ROICommands[] =
11267     {
11268       ROIHelpCommand,
11269       ROIDismissCommand
11270     };
11271
11272   static const CommandType
11273     *Commands[ApplyMenus] =
11274     {
11275       FileCommands,
11276       EditCommands,
11277       TransformCommands,
11278       EnhanceCommands,
11279       EffectsCommands,
11280       FXCommands,
11281       MiscellanyCommands
11282     };
11283
11284   char
11285     command[MagickPathExtent],
11286     text[MagickPathExtent];
11287
11288   CommandType
11289     command_type;
11290
11291   Cursor
11292     cursor;
11293
11294   Image
11295     *roi_image;
11296
11297   int
11298     entry,
11299     id,
11300     x,
11301     y;
11302
11303   double
11304     scale_factor;
11305
11306   MagickProgressMonitor
11307     progress_monitor;
11308
11309   RectangleInfo
11310     crop_info,
11311     highlight_info,
11312     roi_info;
11313
11314   unsigned int
11315     height,
11316     width;
11317
11318   size_t
11319     state;
11320
11321   XEvent
11322     event;
11323
11324   /*
11325     Map Command widget.
11326   */
11327   (void) CloneString(&windows->command.name,"ROI");
11328   windows->command.data=0;
11329   (void) XCommandWidget(display,windows,ROIMenu,(XEvent *) NULL);
11330   (void) XMapRaised(display,windows->command.id);
11331   XClientMessage(display,windows->image.id,windows->im_protocols,
11332     windows->im_update_widget,CurrentTime);
11333   /*
11334     Track pointer until button 1 is pressed.
11335   */
11336   XQueryPosition(display,windows->image.id,&x,&y);
11337   (void) XSelectInput(display,windows->image.id,
11338     windows->image.attributes.event_mask | PointerMotionMask);
11339   roi_info.x=(ssize_t) windows->image.x+x;
11340   roi_info.y=(ssize_t) windows->image.y+y;
11341   roi_info.width=0;
11342   roi_info.height=0;
11343   cursor=XCreateFontCursor(display,XC_fleur);
11344   state=DefaultState;
11345   do
11346   {
11347     if (windows->info.mapped != MagickFalse )
11348       {
11349         /*
11350           Display pointer position.
11351         */
11352         (void) FormatLocaleString(text,MagickPathExtent," %+ld%+ld ",
11353           (long) roi_info.x,(long) roi_info.y);
11354         XInfoWidget(display,windows,text);
11355       }
11356     /*
11357       Wait for next event.
11358     */
11359     XScreenEvent(display,windows,&event,exception);
11360     if (event.xany.window == windows->command.id)
11361       {
11362         /*
11363           Select a command from the Command widget.
11364         */
11365         id=XCommandWidget(display,windows,ROIMenu,&event);
11366         if (id < 0)
11367           continue;
11368         switch (ROICommands[id])
11369         {
11370           case ROIHelpCommand:
11371           {
11372             XTextViewWidget(display,resource_info,windows,MagickFalse,
11373               "Help Viewer - Region of Interest",ImageROIHelp);
11374             break;
11375           }
11376           case ROIDismissCommand:
11377           {
11378             /*
11379               Prematurely exit.
11380             */
11381             state|=EscapeState;
11382             state|=ExitState;
11383             break;
11384           }
11385           default:
11386             break;
11387         }
11388         continue;
11389       }
11390     switch (event.type)
11391     {
11392       case ButtonPress:
11393       {
11394         if (event.xbutton.button != Button1)
11395           break;
11396         if (event.xbutton.window != windows->image.id)
11397           break;
11398         /*
11399           Note first corner of region of interest rectangle-- exit loop.
11400         */
11401         (void) XCheckDefineCursor(display,windows->image.id,cursor);
11402         roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11403         roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11404         state|=ExitState;
11405         break;
11406       }
11407       case ButtonRelease:
11408         break;
11409       case Expose:
11410         break;
11411       case KeyPress:
11412       {
11413         KeySym
11414           key_symbol;
11415
11416         if (event.xkey.window != windows->image.id)
11417           break;
11418         /*
11419           Respond to a user key press.
11420         */
11421         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11422           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11423         switch ((int) key_symbol)
11424         {
11425           case XK_Escape:
11426           case XK_F20:
11427           {
11428             /*
11429               Prematurely exit.
11430             */
11431             state|=EscapeState;
11432             state|=ExitState;
11433             break;
11434           }
11435           case XK_F1:
11436           case XK_Help:
11437           {
11438             XTextViewWidget(display,resource_info,windows,MagickFalse,
11439               "Help Viewer - Region of Interest",ImageROIHelp);
11440             break;
11441           }
11442           default:
11443           {
11444             (void) XBell(display,0);
11445             break;
11446           }
11447         }
11448         break;
11449       }
11450       case MotionNotify:
11451       {
11452         /*
11453           Map and unmap Info widget as text cursor crosses its boundaries.
11454         */
11455         x=event.xmotion.x;
11456         y=event.xmotion.y;
11457         if (windows->info.mapped != MagickFalse )
11458           {
11459             if ((x < (int) (windows->info.x+windows->info.width)) &&
11460                 (y < (int) (windows->info.y+windows->info.height)))
11461               (void) XWithdrawWindow(display,windows->info.id,
11462                 windows->info.screen);
11463           }
11464         else
11465           if ((x > (int) (windows->info.x+windows->info.width)) ||
11466               (y > (int) (windows->info.y+windows->info.height)))
11467             (void) XMapWindow(display,windows->info.id);
11468         roi_info.x=(ssize_t) windows->image.x+x;
11469         roi_info.y=(ssize_t) windows->image.y+y;
11470         break;
11471       }
11472       default:
11473         break;
11474     }
11475   } while ((state & ExitState) == 0);
11476   (void) XSelectInput(display,windows->image.id,
11477     windows->image.attributes.event_mask);
11478   if ((state & EscapeState) != 0)
11479     {
11480       /*
11481         User want to exit without region of interest.
11482       */
11483       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11484       (void) XFreeCursor(display,cursor);
11485       return(MagickTrue);
11486     }
11487   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
11488   do
11489   {
11490     /*
11491       Size rectangle as pointer moves until the mouse button is released.
11492     */
11493     x=(int) roi_info.x;
11494     y=(int) roi_info.y;
11495     roi_info.width=0;
11496     roi_info.height=0;
11497     state=DefaultState;
11498     do
11499     {
11500       highlight_info=roi_info;
11501       highlight_info.x=roi_info.x-windows->image.x;
11502       highlight_info.y=roi_info.y-windows->image.y;
11503       if ((highlight_info.width > 3) && (highlight_info.height > 3))
11504         {
11505           /*
11506             Display info and draw region of interest rectangle.
11507           */
11508           if (windows->info.mapped == MagickFalse)
11509             (void) XMapWindow(display,windows->info.id);
11510           (void) FormatLocaleString(text,MagickPathExtent,
11511             " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11512             roi_info.height,(double) roi_info.x,(double) roi_info.y);
11513           XInfoWidget(display,windows,text);
11514           XHighlightRectangle(display,windows->image.id,
11515             windows->image.highlight_context,&highlight_info);
11516         }
11517       else
11518         if (windows->info.mapped != MagickFalse )
11519           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11520       /*
11521         Wait for next event.
11522       */
11523       XScreenEvent(display,windows,&event,exception);
11524       if ((highlight_info.width > 3) && (highlight_info.height > 3))
11525         XHighlightRectangle(display,windows->image.id,
11526           windows->image.highlight_context,&highlight_info);
11527       switch (event.type)
11528       {
11529         case ButtonPress:
11530         {
11531           roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11532           roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11533           break;
11534         }
11535         case ButtonRelease:
11536         {
11537           /*
11538             User has committed to region of interest rectangle.
11539           */
11540           roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11541           roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11542           XSetCursorState(display,windows,MagickFalse);
11543           state|=ExitState;
11544           if (LocaleCompare(windows->command.name,"Apply") == 0)
11545             break;
11546           (void) CloneString(&windows->command.name,"Apply");
11547           windows->command.data=ApplyMenus;
11548           (void) XCommandWidget(display,windows,ApplyMenu,(XEvent *) NULL);
11549           break;
11550         }
11551         case Expose:
11552           break;
11553         case MotionNotify:
11554         {
11555           roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11556           roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11557         }
11558         default:
11559           break;
11560       }
11561       if ((((int) roi_info.x != x) && ((int) roi_info.y != y)) ||
11562           ((state & ExitState) != 0))
11563         {
11564           /*
11565             Check boundary conditions.
11566           */
11567           if (roi_info.x < 0)
11568             roi_info.x=0;
11569           else
11570             if (roi_info.x > (ssize_t) windows->image.ximage->width)
11571               roi_info.x=(ssize_t) windows->image.ximage->width;
11572           if ((int) roi_info.x < x)
11573             roi_info.width=(unsigned int) (x-roi_info.x);
11574           else
11575             {
11576               roi_info.width=(unsigned int) (roi_info.x-x);
11577               roi_info.x=(ssize_t) x;
11578             }
11579           if (roi_info.y < 0)
11580             roi_info.y=0;
11581           else
11582             if (roi_info.y > (ssize_t) windows->image.ximage->height)
11583               roi_info.y=(ssize_t) windows->image.ximage->height;
11584           if ((int) roi_info.y < y)
11585             roi_info.height=(unsigned int) (y-roi_info.y);
11586           else
11587             {
11588               roi_info.height=(unsigned int) (roi_info.y-y);
11589               roi_info.y=(ssize_t) y;
11590             }
11591         }
11592     } while ((state & ExitState) == 0);
11593     /*
11594       Wait for user to grab a corner of the rectangle or press return.
11595     */
11596     state=DefaultState;
11597     command_type=NullCommand;
11598     crop_info.x=0;
11599     crop_info.y=0;
11600     (void) XMapWindow(display,windows->info.id);
11601     do
11602     {
11603       if (windows->info.mapped != MagickFalse )
11604         {
11605           /*
11606             Display pointer position.
11607           */
11608           (void) FormatLocaleString(text,MagickPathExtent,
11609             " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11610             roi_info.height,(double) roi_info.x,(double) roi_info.y);
11611           XInfoWidget(display,windows,text);
11612         }
11613       highlight_info=roi_info;
11614       highlight_info.x=roi_info.x-windows->image.x;
11615       highlight_info.y=roi_info.y-windows->image.y;
11616       if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
11617         {
11618           state|=EscapeState;
11619           state|=ExitState;
11620           break;
11621         }
11622       if ((state & UpdateRegionState) != 0)
11623         {
11624           (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11625           switch (command_type)
11626           {
11627             case UndoCommand:
11628             case RedoCommand:
11629             {
11630               (void) XMagickCommand(display,resource_info,windows,command_type,
11631                 image,exception);
11632               break;
11633             }
11634             default:
11635             {
11636               /*
11637                 Region of interest is relative to image configuration.
11638               */
11639               progress_monitor=SetImageProgressMonitor(*image,
11640                 (MagickProgressMonitor) NULL,(*image)->client_data);
11641               crop_info=roi_info;
11642               width=(unsigned int) (*image)->columns;
11643               height=(unsigned int) (*image)->rows;
11644               x=0;
11645               y=0;
11646               if (windows->image.crop_geometry != (char *) NULL)
11647                 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
11648                   &width,&height);
11649               scale_factor=(double) width/windows->image.ximage->width;
11650               crop_info.x+=x;
11651               crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
11652               crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
11653               scale_factor=(double)
11654                 height/windows->image.ximage->height;
11655               crop_info.y+=y;
11656               crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
11657               crop_info.height=(unsigned int)
11658                 (scale_factor*crop_info.height+0.5);
11659               roi_image=CropImage(*image,&crop_info,exception);
11660               (void) SetImageProgressMonitor(*image,progress_monitor,
11661                 (*image)->client_data);
11662               if (roi_image == (Image *) NULL)
11663                 continue;
11664               /*
11665                 Apply image processing technique to the region of interest.
11666               */
11667               windows->image.orphan=MagickTrue;
11668               (void) XMagickCommand(display,resource_info,windows,command_type,
11669                 &roi_image,exception);
11670               progress_monitor=SetImageProgressMonitor(*image,
11671                 (MagickProgressMonitor) NULL,(*image)->client_data);
11672               (void) XMagickCommand(display,resource_info,windows,
11673                 SaveToUndoBufferCommand,image,exception);
11674               windows->image.orphan=MagickFalse;
11675               (void) CompositeImage(*image,roi_image,CopyCompositeOp,
11676                 MagickTrue,crop_info.x,crop_info.y,exception);
11677               roi_image=DestroyImage(roi_image);
11678               (void) SetImageProgressMonitor(*image,progress_monitor,
11679                 (*image)->client_data);
11680               break;
11681             }
11682           }
11683           if (command_type != InfoCommand)
11684             {
11685               XConfigureImageColormap(display,resource_info,windows,*image,
11686                 exception);
11687               (void) XConfigureImage(display,resource_info,windows,*image,
11688                 exception);
11689             }
11690           XCheckRefreshWindows(display,windows);
11691           XInfoWidget(display,windows,text);
11692           (void) XSetFunction(display,windows->image.highlight_context,
11693             GXinvert);
11694           state&=(~UpdateRegionState);
11695         }
11696       XHighlightRectangle(display,windows->image.id,
11697         windows->image.highlight_context,&highlight_info);
11698       XScreenEvent(display,windows,&event,exception);
11699       if (event.xany.window == windows->command.id)
11700         {
11701           /*
11702             Select a command from the Command widget.
11703           */
11704           (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11705           command_type=NullCommand;
11706           id=XCommandWidget(display,windows,ApplyMenu,&event);
11707           if (id >= 0)
11708             {
11709               (void) CopyMagickString(command,ApplyMenu[id],MagickPathExtent);
11710               command_type=ApplyCommands[id];
11711               if (id < ApplyMenus)
11712                 {
11713                   /*
11714                     Select a command from a pop-up menu.
11715                   */
11716                   entry=XMenuWidget(display,windows,ApplyMenu[id],
11717                     (const char **) Menus[id],command);
11718                   if (entry >= 0)
11719                     {
11720                       (void) CopyMagickString(command,Menus[id][entry],
11721                         MagickPathExtent);
11722                       command_type=Commands[id][entry];
11723                     }
11724                 }
11725             }
11726           (void) XSetFunction(display,windows->image.highlight_context,
11727             GXinvert);
11728           XHighlightRectangle(display,windows->image.id,
11729             windows->image.highlight_context,&highlight_info);
11730           if (command_type == HelpCommand)
11731             {
11732               (void) XSetFunction(display,windows->image.highlight_context,
11733                 GXcopy);
11734               XTextViewWidget(display,resource_info,windows,MagickFalse,
11735                 "Help Viewer - Region of Interest",ImageROIHelp);
11736               (void) XSetFunction(display,windows->image.highlight_context,
11737                 GXinvert);
11738               continue;
11739             }
11740           if (command_type == QuitCommand)
11741             {
11742               /*
11743                 exit.
11744               */
11745               state|=EscapeState;
11746               state|=ExitState;
11747               continue;
11748             }
11749           if (command_type != NullCommand)
11750             state|=UpdateRegionState;
11751           continue;
11752         }
11753       XHighlightRectangle(display,windows->image.id,
11754         windows->image.highlight_context,&highlight_info);
11755       switch (event.type)
11756       {
11757         case ButtonPress:
11758         {
11759           x=windows->image.x;
11760           y=windows->image.y;
11761           if (event.xbutton.button != Button1)
11762             break;
11763           if (event.xbutton.window != windows->image.id)
11764             break;
11765           x=windows->image.x+event.xbutton.x;
11766           y=windows->image.y+event.xbutton.y;
11767           if ((x < (int) (roi_info.x+RoiDelta)) &&
11768               (x > (int) (roi_info.x-RoiDelta)) &&
11769               (y < (int) (roi_info.y+RoiDelta)) &&
11770               (y > (int) (roi_info.y-RoiDelta)))
11771             {
11772               roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11773               roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11774               state|=UpdateConfigurationState;
11775               break;
11776             }
11777           if ((x < (int) (roi_info.x+RoiDelta)) &&
11778               (x > (int) (roi_info.x-RoiDelta)) &&
11779               (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11780               (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11781             {
11782               roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11783               state|=UpdateConfigurationState;
11784               break;
11785             }
11786           if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11787               (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11788               (y < (int) (roi_info.y+RoiDelta)) &&
11789               (y > (int) (roi_info.y-RoiDelta)))
11790             {
11791               roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11792               state|=UpdateConfigurationState;
11793               break;
11794             }
11795           if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11796               (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11797               (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11798               (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11799             {
11800               state|=UpdateConfigurationState;
11801               break;
11802             }
11803         }
11804         case ButtonRelease:
11805         {
11806           if (event.xbutton.window == windows->pan.id)
11807             if ((highlight_info.x != crop_info.x-windows->image.x) ||
11808                 (highlight_info.y != crop_info.y-windows->image.y))
11809               XHighlightRectangle(display,windows->image.id,
11810                 windows->image.highlight_context,&highlight_info);
11811           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11812             event.xbutton.time);
11813           break;
11814         }
11815         case Expose:
11816         {
11817           if (event.xexpose.window == windows->image.id)
11818             if (event.xexpose.count == 0)
11819               {
11820                 event.xexpose.x=(int) highlight_info.x;
11821                 event.xexpose.y=(int) highlight_info.y;
11822                 event.xexpose.width=(int) highlight_info.width;
11823                 event.xexpose.height=(int) highlight_info.height;
11824                 XRefreshWindow(display,&windows->image,&event);
11825               }
11826           if (event.xexpose.window == windows->info.id)
11827             if (event.xexpose.count == 0)
11828               XInfoWidget(display,windows,text);
11829           break;
11830         }
11831         case KeyPress:
11832         {
11833           KeySym
11834             key_symbol;
11835
11836           if (event.xkey.window != windows->image.id)
11837             break;
11838           /*
11839             Respond to a user key press.
11840           */
11841           (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11842             sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11843           switch ((int) key_symbol)
11844           {
11845             case XK_Shift_L:
11846             case XK_Shift_R:
11847               break;
11848             case XK_Escape:
11849             case XK_F20:
11850               state|=EscapeState;
11851             case XK_Return:
11852             {
11853               state|=ExitState;
11854               break;
11855             }
11856             case XK_Home:
11857             case XK_KP_Home:
11858             {
11859               roi_info.x=(ssize_t) (windows->image.width/2L-roi_info.width/2L);
11860               roi_info.y=(ssize_t) (windows->image.height/2L-
11861                 roi_info.height/2L);
11862               break;
11863             }
11864             case XK_Left:
11865             case XK_KP_Left:
11866             {
11867               roi_info.x--;
11868               break;
11869             }
11870             case XK_Up:
11871             case XK_KP_Up:
11872             case XK_Next:
11873             {
11874               roi_info.y--;
11875               break;
11876             }
11877             case XK_Right:
11878             case XK_KP_Right:
11879             {
11880               roi_info.x++;
11881               break;
11882             }
11883             case XK_Prior:
11884             case XK_Down:
11885             case XK_KP_Down:
11886             {
11887               roi_info.y++;
11888               break;
11889             }
11890             case XK_F1:
11891             case XK_Help:
11892             {
11893               (void) XSetFunction(display,windows->image.highlight_context,
11894                 GXcopy);
11895               XTextViewWidget(display,resource_info,windows,MagickFalse,
11896                 "Help Viewer - Region of Interest",ImageROIHelp);
11897               (void) XSetFunction(display,windows->image.highlight_context,
11898                 GXinvert);
11899               break;
11900             }
11901             default:
11902             {
11903               command_type=XImageWindowCommand(display,resource_info,windows,
11904                 event.xkey.state,key_symbol,image,exception);
11905               if (command_type != NullCommand)
11906                 state|=UpdateRegionState;
11907               break;
11908             }
11909           }
11910           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11911             event.xkey.time);
11912           break;
11913         }
11914         case KeyRelease:
11915           break;
11916         case MotionNotify:
11917         {
11918           if (event.xbutton.window != windows->image.id)
11919             break;
11920           /*
11921             Map and unmap Info widget as text cursor crosses its boundaries.
11922           */
11923           x=event.xmotion.x;
11924           y=event.xmotion.y;
11925           if (windows->info.mapped != MagickFalse )
11926             {
11927               if ((x < (int) (windows->info.x+windows->info.width)) &&
11928                   (y < (int) (windows->info.y+windows->info.height)))
11929                 (void) XWithdrawWindow(display,windows->info.id,
11930                   windows->info.screen);
11931             }
11932           else
11933             if ((x > (int) (windows->info.x+windows->info.width)) ||
11934                 (y > (int) (windows->info.y+windows->info.height)))
11935               (void) XMapWindow(display,windows->info.id);
11936           roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11937           roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11938           break;
11939         }
11940         case SelectionRequest:
11941         {
11942           XSelectionEvent
11943             notify;
11944
11945           XSelectionRequestEvent
11946             *request;
11947
11948           /*
11949             Set primary selection.
11950           */
11951           (void) FormatLocaleString(text,MagickPathExtent,
11952             "%.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11953             roi_info.height,(double) roi_info.x,(double) roi_info.y);
11954           request=(&(event.xselectionrequest));
11955           (void) XChangeProperty(request->display,request->requestor,
11956             request->property,request->target,8,PropModeReplace,
11957             (unsigned char *) text,(int) strlen(text));
11958           notify.type=SelectionNotify;
11959           notify.display=request->display;
11960           notify.requestor=request->requestor;
11961           notify.selection=request->selection;
11962           notify.target=request->target;
11963           notify.time=request->time;
11964           if (request->property == None)
11965             notify.property=request->target;
11966           else
11967             notify.property=request->property;
11968           (void) XSendEvent(request->display,request->requestor,False,0,
11969             (XEvent *) &notify);
11970         }
11971         default:
11972           break;
11973       }
11974       if ((state & UpdateConfigurationState) != 0)
11975         {
11976           (void) XPutBackEvent(display,&event);
11977           (void) XCheckDefineCursor(display,windows->image.id,cursor);
11978           break;
11979         }
11980     } while ((state & ExitState) == 0);
11981   } while ((state & ExitState) == 0);
11982   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11983   XSetCursorState(display,windows,MagickFalse);
11984   if ((state & EscapeState) != 0)
11985     return(MagickTrue);
11986   return(MagickTrue);
11987 }
11988 \f
11989 /*
11990 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11991 %                                                                             %
11992 %                                                                             %
11993 %                                                                             %
11994 +   X R o t a t e I m a g e                                                   %
11995 %                                                                             %
11996 %                                                                             %
11997 %                                                                             %
11998 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11999 %
12000 %  XRotateImage() rotates the X image.  If the degrees parameter if zero, the
12001 %  rotation angle is computed from the slope of a line drawn by the user.
12002 %
12003 %  The format of the XRotateImage method is:
12004 %
12005 %      MagickBooleanType XRotateImage(Display *display,
12006 %        XResourceInfo *resource_info,XWindows *windows,double degrees,
12007 %        Image **image,ExceptionInfo *exception)
12008 %
12009 %  A description of each parameter follows:
12010 %
12011 %    o display: Specifies a connection to an X server; returned from
12012 %      XOpenDisplay.
12013 %
12014 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12015 %
12016 %    o windows: Specifies a pointer to a XWindows structure.
12017 %
12018 %    o degrees: Specifies the number of degrees to rotate the image.
12019 %
12020 %    o image: the image.
12021 %
12022 %    o exception: return any errors or warnings in this structure.
12023 %
12024 */
12025 static MagickBooleanType XRotateImage(Display *display,
12026   XResourceInfo *resource_info,XWindows *windows,double degrees,Image **image,
12027   ExceptionInfo *exception)
12028 {
12029   static const char
12030     *RotateMenu[] =
12031     {
12032       "Pixel Color",
12033       "Direction",
12034       "Help",
12035       "Dismiss",
12036       (char *) NULL
12037     };
12038
12039   static ModeType
12040     direction = HorizontalRotateCommand;
12041
12042   static const ModeType
12043     DirectionCommands[] =
12044     {
12045       HorizontalRotateCommand,
12046       VerticalRotateCommand
12047     },
12048     RotateCommands[] =
12049     {
12050       RotateColorCommand,
12051       RotateDirectionCommand,
12052       RotateHelpCommand,
12053       RotateDismissCommand
12054     };
12055
12056   static unsigned int
12057     pen_id = 0;
12058
12059   char
12060     command[MagickPathExtent],
12061     text[MagickPathExtent];
12062
12063   Image
12064     *rotate_image;
12065
12066   int
12067     id,
12068     x,
12069     y;
12070
12071   double
12072     normalized_degrees;
12073
12074   register int
12075     i;
12076
12077   unsigned int
12078     height,
12079     rotations,
12080     width;
12081
12082   if (degrees == 0.0)
12083     {
12084       unsigned int
12085         distance;
12086
12087       size_t
12088         state;
12089
12090       XEvent
12091         event;
12092
12093       XSegment
12094         rotate_info;
12095
12096       /*
12097         Map Command widget.
12098       */
12099       (void) CloneString(&windows->command.name,"Rotate");
12100       windows->command.data=2;
12101       (void) XCommandWidget(display,windows,RotateMenu,(XEvent *) NULL);
12102       (void) XMapRaised(display,windows->command.id);
12103       XClientMessage(display,windows->image.id,windows->im_protocols,
12104         windows->im_update_widget,CurrentTime);
12105       /*
12106         Wait for first button press.
12107       */
12108       (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12109       XQueryPosition(display,windows->image.id,&x,&y);
12110       rotate_info.x1=x;
12111       rotate_info.y1=y;
12112       rotate_info.x2=x;
12113       rotate_info.y2=y;
12114       state=DefaultState;
12115       do
12116       {
12117         XHighlightLine(display,windows->image.id,
12118           windows->image.highlight_context,&rotate_info);
12119         /*
12120           Wait for next event.
12121         */
12122         XScreenEvent(display,windows,&event,exception);
12123         XHighlightLine(display,windows->image.id,
12124           windows->image.highlight_context,&rotate_info);
12125         if (event.xany.window == windows->command.id)
12126           {
12127             /*
12128               Select a command from the Command widget.
12129             */
12130             id=XCommandWidget(display,windows,RotateMenu,&event);
12131             if (id < 0)
12132               continue;
12133             (void) XSetFunction(display,windows->image.highlight_context,
12134               GXcopy);
12135             switch (RotateCommands[id])
12136             {
12137               case RotateColorCommand:
12138               {
12139                 const char
12140                   *ColorMenu[MaxNumberPens];
12141
12142                 int
12143                   pen_number;
12144
12145                 XColor
12146                   color;
12147
12148                 /*
12149                   Initialize menu selections.
12150                 */
12151                 for (i=0; i < (int) (MaxNumberPens-2); i++)
12152                   ColorMenu[i]=resource_info->pen_colors[i];
12153                 ColorMenu[MaxNumberPens-2]="Browser...";
12154                 ColorMenu[MaxNumberPens-1]=(const char *) NULL;
12155                 /*
12156                   Select a pen color from the pop-up menu.
12157                 */
12158                 pen_number=XMenuWidget(display,windows,RotateMenu[id],
12159                   (const char **) ColorMenu,command);
12160                 if (pen_number < 0)
12161                   break;
12162                 if (pen_number == (MaxNumberPens-2))
12163                   {
12164                     static char
12165                       color_name[MagickPathExtent] = "gray";
12166
12167                     /*
12168                       Select a pen color from a dialog.
12169                     */
12170                     resource_info->pen_colors[pen_number]=color_name;
12171                     XColorBrowserWidget(display,windows,"Select",color_name);
12172                     if (*color_name == '\0')
12173                       break;
12174                   }
12175                 /*
12176                   Set pen color.
12177                 */
12178                 (void) XParseColor(display,windows->map_info->colormap,
12179                   resource_info->pen_colors[pen_number],&color);
12180                 XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
12181                   (unsigned int) MaxColors,&color);
12182                 windows->pixel_info->pen_colors[pen_number]=color;
12183                 pen_id=(unsigned int) pen_number;
12184                 break;
12185               }
12186               case RotateDirectionCommand:
12187               {
12188                 static const char
12189                   *Directions[] =
12190                   {
12191                     "horizontal",
12192                     "vertical",
12193                     (char *) NULL,
12194                   };
12195
12196                 /*
12197                   Select a command from the pop-up menu.
12198                 */
12199                 id=XMenuWidget(display,windows,RotateMenu[id],
12200                   Directions,command);
12201                 if (id >= 0)
12202                   direction=DirectionCommands[id];
12203                 break;
12204               }
12205               case RotateHelpCommand:
12206               {
12207                 XTextViewWidget(display,resource_info,windows,MagickFalse,
12208                   "Help Viewer - Image Rotation",ImageRotateHelp);
12209                 break;
12210               }
12211               case RotateDismissCommand:
12212               {
12213                 /*
12214                   Prematurely exit.
12215                 */
12216                 state|=EscapeState;
12217                 state|=ExitState;
12218                 break;
12219               }
12220               default:
12221                 break;
12222             }
12223             (void) XSetFunction(display,windows->image.highlight_context,
12224               GXinvert);
12225             continue;
12226           }
12227         switch (event.type)
12228         {
12229           case ButtonPress:
12230           {
12231             if (event.xbutton.button != Button1)
12232               break;
12233             if (event.xbutton.window != windows->image.id)
12234               break;
12235             /*
12236               exit loop.
12237             */
12238             (void) XSetFunction(display,windows->image.highlight_context,
12239               GXcopy);
12240             rotate_info.x1=event.xbutton.x;
12241             rotate_info.y1=event.xbutton.y;
12242             state|=ExitState;
12243             break;
12244           }
12245           case ButtonRelease:
12246             break;
12247           case Expose:
12248             break;
12249           case KeyPress:
12250           {
12251             char
12252               command[MagickPathExtent];
12253
12254             KeySym
12255               key_symbol;
12256
12257             if (event.xkey.window != windows->image.id)
12258               break;
12259             /*
12260               Respond to a user key press.
12261             */
12262             (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
12263               sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12264             switch ((int) key_symbol)
12265             {
12266               case XK_Escape:
12267               case XK_F20:
12268               {
12269                 /*
12270                   Prematurely exit.
12271                 */
12272                 state|=EscapeState;
12273                 state|=ExitState;
12274                 break;
12275               }
12276               case XK_F1:
12277               case XK_Help:
12278               {
12279                 (void) XSetFunction(display,windows->image.highlight_context,
12280                   GXcopy);
12281                 XTextViewWidget(display,resource_info,windows,MagickFalse,
12282                   "Help Viewer - Image Rotation",ImageRotateHelp);
12283                 (void) XSetFunction(display,windows->image.highlight_context,
12284                   GXinvert);
12285                 break;
12286               }
12287               default:
12288               {
12289                 (void) XBell(display,0);
12290                 break;
12291               }
12292             }
12293             break;
12294           }
12295           case MotionNotify:
12296           {
12297             rotate_info.x1=event.xmotion.x;
12298             rotate_info.y1=event.xmotion.y;
12299           }
12300         }
12301         rotate_info.x2=rotate_info.x1;
12302         rotate_info.y2=rotate_info.y1;
12303         if (direction == HorizontalRotateCommand)
12304           rotate_info.x2+=32;
12305         else
12306           rotate_info.y2-=32;
12307       } while ((state & ExitState) == 0);
12308       (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12309       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12310       if ((state & EscapeState) != 0)
12311         return(MagickTrue);
12312       /*
12313         Draw line as pointer moves until the mouse button is released.
12314       */
12315       distance=0;
12316       (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12317       state=DefaultState;
12318       do
12319       {
12320         if (distance > 9)
12321           {
12322             /*
12323               Display info and draw rotation line.
12324             */
12325             if (windows->info.mapped == MagickFalse)
12326               (void) XMapWindow(display,windows->info.id);
12327             (void) FormatLocaleString(text,MagickPathExtent," %g",
12328               direction == VerticalRotateCommand ? degrees-90.0 : degrees);
12329             XInfoWidget(display,windows,text);
12330             XHighlightLine(display,windows->image.id,
12331               windows->image.highlight_context,&rotate_info);
12332           }
12333         else
12334           if (windows->info.mapped != MagickFalse )
12335             (void) XWithdrawWindow(display,windows->info.id,
12336               windows->info.screen);
12337         /*
12338           Wait for next event.
12339         */
12340         XScreenEvent(display,windows,&event,exception);
12341         if (distance > 9)
12342           XHighlightLine(display,windows->image.id,
12343             windows->image.highlight_context,&rotate_info);
12344         switch (event.type)
12345         {
12346           case ButtonPress:
12347             break;
12348           case ButtonRelease:
12349           {
12350             /*
12351               User has committed to rotation line.
12352             */
12353             rotate_info.x2=event.xbutton.x;
12354             rotate_info.y2=event.xbutton.y;
12355             state|=ExitState;
12356             break;
12357           }
12358           case Expose:
12359             break;
12360           case MotionNotify:
12361           {
12362             rotate_info.x2=event.xmotion.x;
12363             rotate_info.y2=event.xmotion.y;
12364           }
12365           default:
12366             break;
12367         }
12368         /*
12369           Check boundary conditions.
12370         */
12371         if (rotate_info.x2 < 0)
12372           rotate_info.x2=0;
12373         else
12374           if (rotate_info.x2 > (int) windows->image.width)
12375             rotate_info.x2=(short) windows->image.width;
12376         if (rotate_info.y2 < 0)
12377           rotate_info.y2=0;
12378         else
12379           if (rotate_info.y2 > (int) windows->image.height)
12380             rotate_info.y2=(short) windows->image.height;
12381         /*
12382           Compute rotation angle from the slope of the line.
12383         */
12384         degrees=0.0;
12385         distance=(unsigned int)
12386           ((rotate_info.x2-rotate_info.x1+1)*(rotate_info.x2-rotate_info.x1+1))+
12387           ((rotate_info.y2-rotate_info.y1+1)*(rotate_info.y2-rotate_info.y1+1));
12388         if (distance > 9)
12389           degrees=RadiansToDegrees(-atan2((double) (rotate_info.y2-
12390             rotate_info.y1),(double) (rotate_info.x2-rotate_info.x1)));
12391       } while ((state & ExitState) == 0);
12392       (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12393       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12394       if (distance <= 9)
12395         return(MagickTrue);
12396     }
12397   if (direction == VerticalRotateCommand)
12398     degrees-=90.0;
12399   if (degrees == 0.0)
12400     return(MagickTrue);
12401   /*
12402     Rotate image.
12403   */
12404   normalized_degrees=degrees;
12405   while (normalized_degrees < -45.0)
12406     normalized_degrees+=360.0;
12407   for (rotations=0; normalized_degrees > 45.0; rotations++)
12408     normalized_degrees-=90.0;
12409   if (normalized_degrees != 0.0)
12410     (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
12411       exception);
12412   XSetCursorState(display,windows,MagickTrue);
12413   XCheckRefreshWindows(display,windows);
12414   (*image)->background_color.red=(double) ScaleShortToQuantum(
12415     windows->pixel_info->pen_colors[pen_id].red);
12416   (*image)->background_color.green=(double) ScaleShortToQuantum(
12417     windows->pixel_info->pen_colors[pen_id].green);
12418   (*image)->background_color.blue=(double) ScaleShortToQuantum(
12419     windows->pixel_info->pen_colors[pen_id].blue);
12420   rotate_image=RotateImage(*image,degrees,exception);
12421   XSetCursorState(display,windows,MagickFalse);
12422   if (rotate_image == (Image *) NULL)
12423     return(MagickFalse);
12424   *image=DestroyImage(*image);
12425   *image=rotate_image;
12426   if (windows->image.crop_geometry != (char *) NULL)
12427     {
12428       /*
12429         Rotate crop geometry.
12430       */
12431       width=(unsigned int) (*image)->columns;
12432       height=(unsigned int) (*image)->rows;
12433       (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12434       switch (rotations % 4)
12435       {
12436         default:
12437         case 0:
12438           break;
12439         case 1:
12440         {
12441           /*
12442             Rotate 90 degrees.
12443           */
12444           (void) FormatLocaleString(windows->image.crop_geometry,
12445             MagickPathExtent,"%ux%u%+d%+d",height,width,(int) (*image)->columns-
12446             (int) height-y,x);
12447           break;
12448         }
12449         case 2:
12450         {
12451           /*
12452             Rotate 180 degrees.
12453           */
12454           (void) FormatLocaleString(windows->image.crop_geometry,
12455             MagickPathExtent,"%ux%u%+d%+d",width,height,(int) width-x,(int)
12456             height-y);
12457           break;
12458         }
12459         case 3:
12460         {
12461           /*
12462             Rotate 270 degrees.
12463           */
12464           (void) FormatLocaleString(windows->image.crop_geometry,
12465             MagickPathExtent,"%ux%u%+d%+d",height,width,y,(int) (*image)->rows-
12466             (int) width-x);
12467           break;
12468         }
12469       }
12470     }
12471   if (windows->image.orphan != MagickFalse )
12472     return(MagickTrue);
12473   if (normalized_degrees != 0.0)
12474     {
12475       /*
12476         Update image colormap.
12477       */
12478       windows->image.window_changes.width=(int) (*image)->columns;
12479       windows->image.window_changes.height=(int) (*image)->rows;
12480       if (windows->image.crop_geometry != (char *) NULL)
12481         {
12482           /*
12483             Obtain dimensions of image from crop geometry.
12484           */
12485           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
12486             &width,&height);
12487           windows->image.window_changes.width=(int) width;
12488           windows->image.window_changes.height=(int) height;
12489         }
12490       XConfigureImageColormap(display,resource_info,windows,*image,exception);
12491     }
12492   else
12493     if (((rotations % 4) == 1) || ((rotations % 4) == 3))
12494       {
12495         windows->image.window_changes.width=windows->image.ximage->height;
12496         windows->image.window_changes.height=windows->image.ximage->width;
12497       }
12498   /*
12499     Update image configuration.
12500   */
12501   (void) XConfigureImage(display,resource_info,windows,*image,exception);
12502   return(MagickTrue);
12503 }
12504 \f
12505 /*
12506 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12507 %                                                                             %
12508 %                                                                             %
12509 %                                                                             %
12510 +   X S a v e I m a g e                                                       %
12511 %                                                                             %
12512 %                                                                             %
12513 %                                                                             %
12514 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12515 %
12516 %  XSaveImage() saves an image to a file.
12517 %
12518 %  The format of the XSaveImage method is:
12519 %
12520 %      MagickBooleanType XSaveImage(Display *display,
12521 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
12522 %        ExceptionInfo *exception)
12523 %
12524 %  A description of each parameter follows:
12525 %
12526 %    o display: Specifies a connection to an X server; returned from
12527 %      XOpenDisplay.
12528 %
12529 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12530 %
12531 %    o windows: Specifies a pointer to a XWindows structure.
12532 %
12533 %    o image: the image.
12534 %
12535 %    o exception: return any errors or warnings in this structure.
12536 %
12537 */
12538 static MagickBooleanType XSaveImage(Display *display,
12539   XResourceInfo *resource_info,XWindows *windows,Image *image,
12540   ExceptionInfo *exception)
12541 {
12542   char
12543     filename[MagickPathExtent],
12544     geometry[MagickPathExtent];
12545
12546   Image
12547     *save_image;
12548
12549   ImageInfo
12550     *image_info;
12551
12552   MagickStatusType
12553     status;
12554
12555   /*
12556     Request file name from user.
12557   */
12558   if (resource_info->write_filename != (char *) NULL)
12559     (void) CopyMagickString(filename,resource_info->write_filename,
12560       MagickPathExtent);
12561   else
12562     {
12563       char
12564         path[MagickPathExtent];
12565
12566       int
12567         status;
12568
12569       GetPathComponent(image->filename,HeadPath,path);
12570       GetPathComponent(image->filename,TailPath,filename);
12571       if (*path != '\0')
12572         {
12573           status=chdir(path);
12574           if (status == -1)
12575             (void) ThrowMagickException(exception,GetMagickModule(),
12576               FileOpenError,"UnableToOpenFile","%s",path);
12577         }
12578     }
12579   XFileBrowserWidget(display,windows,"Save",filename);
12580   if (*filename == '\0')
12581     return(MagickTrue);
12582   if (IsPathAccessible(filename) != MagickFalse )
12583     {
12584       int
12585         status;
12586
12587       /*
12588         File exists-- seek user's permission before overwriting.
12589       */
12590       status=XConfirmWidget(display,windows,"Overwrite",filename);
12591       if (status <= 0)
12592         return(MagickTrue);
12593     }
12594   image_info=CloneImageInfo(resource_info->image_info);
12595   (void) CopyMagickString(image_info->filename,filename,MagickPathExtent);
12596   (void) SetImageInfo(image_info,1,exception);
12597   if ((LocaleCompare(image_info->magick,"JPEG") == 0) ||
12598       (LocaleCompare(image_info->magick,"JPG") == 0))
12599     {
12600       char
12601         quality[MagickPathExtent];
12602
12603       int
12604         status;
12605
12606       /*
12607         Request JPEG quality from user.
12608       */
12609       (void) FormatLocaleString(quality,MagickPathExtent,"%.20g",(double)
12610         image->quality);
12611       status=XDialogWidget(display,windows,"Save","Enter JPEG quality:",
12612         quality);
12613       if (*quality == '\0')
12614         return(MagickTrue);
12615       image->quality=StringToUnsignedLong(quality);
12616       image_info->interlace=status != 0 ? NoInterlace : PlaneInterlace;
12617     }
12618   if ((LocaleCompare(image_info->magick,"EPS") == 0) ||
12619       (LocaleCompare(image_info->magick,"PDF") == 0) ||
12620       (LocaleCompare(image_info->magick,"PS") == 0) ||
12621       (LocaleCompare(image_info->magick,"PS2") == 0))
12622     {
12623       char
12624         geometry[MagickPathExtent];
12625
12626       /*
12627         Request page geometry from user.
12628       */
12629       (void) CopyMagickString(geometry,PSPageGeometry,MagickPathExtent);
12630       if (LocaleCompare(image_info->magick,"PDF") == 0)
12631         (void) CopyMagickString(geometry,PSPageGeometry,MagickPathExtent);
12632       if (image_info->page != (char *) NULL)
12633         (void) CopyMagickString(geometry,image_info->page,MagickPathExtent);
12634       XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
12635         "Select page geometry:",geometry);
12636       if (*geometry != '\0')
12637         image_info->page=GetPageGeometry(geometry);
12638     }
12639   /*
12640     Apply image transforms.
12641   */
12642   XSetCursorState(display,windows,MagickTrue);
12643   XCheckRefreshWindows(display,windows);
12644   save_image=CloneImage(image,0,0,MagickTrue,exception);
12645   if (save_image == (Image *) NULL)
12646     return(MagickFalse);
12647   (void) FormatLocaleString(geometry,MagickPathExtent,"%dx%d!",
12648     windows->image.ximage->width,windows->image.ximage->height);
12649   (void) TransformImage(&save_image,windows->image.crop_geometry,geometry,
12650     exception);
12651   /*
12652     Write image.
12653   */
12654   (void) CopyMagickString(save_image->filename,filename,MagickPathExtent);
12655   status=WriteImage(image_info,save_image,exception);
12656   if (status != MagickFalse )
12657     image->taint=MagickFalse;
12658   save_image=DestroyImage(save_image);
12659   image_info=DestroyImageInfo(image_info);
12660   XSetCursorState(display,windows,MagickFalse);
12661   return(status != 0 ? MagickTrue : MagickFalse);
12662 }
12663 \f
12664 /*
12665 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12666 %                                                                             %
12667 %                                                                             %
12668 %                                                                             %
12669 +   X S c r e e n E v e n t                                                   %
12670 %                                                                             %
12671 %                                                                             %
12672 %                                                                             %
12673 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12674 %
12675 %  XScreenEvent() handles global events associated with the Pan and Magnify
12676 %  windows.
12677 %
12678 %  The format of the XScreenEvent function is:
12679 %
12680 %      void XScreenEvent(Display *display,XWindows *windows,XEvent *event,
12681 %        ExceptionInfo *exception)
12682 %
12683 %  A description of each parameter follows:
12684 %
12685 %    o display: Specifies a pointer to the Display structure;  returned from
12686 %      XOpenDisplay.
12687 %
12688 %    o windows: Specifies a pointer to a XWindows structure.
12689 %
12690 %    o event: Specifies a pointer to a X11 XEvent structure.
12691 %
12692 %    o exception: return any errors or warnings in this structure.
12693 %
12694 */
12695
12696 #if defined(__cplusplus) || defined(c_plusplus)
12697 extern "C" {
12698 #endif
12699
12700 static int XPredicate(Display *magick_unused(display),XEvent *event,char *data)
12701 {
12702   register XWindows
12703     *windows;
12704
12705   windows=(XWindows *) data;
12706   if ((event->type == ClientMessage) &&
12707       (event->xclient.window == windows->image.id))
12708     return(MagickFalse);
12709   return(MagickTrue);
12710 }
12711
12712 #if defined(__cplusplus) || defined(c_plusplus)
12713 }
12714 #endif
12715
12716 static void XScreenEvent(Display *display,XWindows *windows,XEvent *event,
12717   ExceptionInfo *exception)
12718 {
12719   register int
12720     x,
12721     y;
12722
12723   (void) XIfEvent(display,event,XPredicate,(char *) windows);
12724   if (event->xany.window == windows->command.id)
12725     return;
12726   switch (event->type)
12727   {
12728     case ButtonPress:
12729     case ButtonRelease:
12730     {
12731       if ((event->xbutton.button == Button3) &&
12732           (event->xbutton.state & Mod1Mask))
12733         {
12734           /*
12735             Convert Alt-Button3 to Button2.
12736           */
12737           event->xbutton.button=Button2;
12738           event->xbutton.state&=(~Mod1Mask);
12739         }
12740       if (event->xbutton.window == windows->backdrop.id)
12741         {
12742           (void) XSetInputFocus(display,event->xbutton.window,RevertToParent,
12743             event->xbutton.time);
12744           break;
12745         }
12746       if (event->xbutton.window == windows->pan.id)
12747         {
12748           XPanImage(display,windows,event,exception);
12749           break;
12750         }
12751       if (event->xbutton.window == windows->image.id)
12752         if (event->xbutton.button == Button2)
12753           {
12754             /*
12755               Update magnified image.
12756             */
12757             x=event->xbutton.x;
12758             y=event->xbutton.y;
12759             if (x < 0)
12760               x=0;
12761             else
12762               if (x >= (int) windows->image.width)
12763                 x=(int) (windows->image.width-1);
12764             windows->magnify.x=(int) windows->image.x+x;
12765             if (y < 0)
12766               y=0;
12767             else
12768              if (y >= (int) windows->image.height)
12769                y=(int) (windows->image.height-1);
12770             windows->magnify.y=windows->image.y+y;
12771             if (windows->magnify.mapped == MagickFalse)
12772               (void) XMapRaised(display,windows->magnify.id);
12773             XMakeMagnifyImage(display,windows,exception);
12774             if (event->type == ButtonRelease)
12775               (void) XWithdrawWindow(display,windows->info.id,
12776                 windows->info.screen);
12777             break;
12778           }
12779       break;
12780     }
12781     case ClientMessage:
12782     {
12783       /*
12784         If client window delete message, exit.
12785       */
12786       if (event->xclient.message_type != windows->wm_protocols)
12787         break;
12788       if (*event->xclient.data.l != (long) windows->wm_delete_window)
12789         break;
12790       if (event->xclient.window == windows->magnify.id)
12791         {
12792           (void) XWithdrawWindow(display,windows->magnify.id,
12793             windows->magnify.screen);
12794           break;
12795         }
12796       break;
12797     }
12798     case ConfigureNotify:
12799     {
12800       if (event->xconfigure.window == windows->magnify.id)
12801         {
12802           unsigned int
12803             magnify;
12804
12805           /*
12806             Magnify window has a new configuration.
12807           */
12808           windows->magnify.width=(unsigned int) event->xconfigure.width;
12809           windows->magnify.height=(unsigned int) event->xconfigure.height;
12810           if (windows->magnify.mapped == MagickFalse)
12811             break;
12812           magnify=1;
12813           while ((int) magnify <= event->xconfigure.width)
12814             magnify<<=1;
12815           while ((int) magnify <= event->xconfigure.height)
12816             magnify<<=1;
12817           magnify>>=1;
12818           if (((int) magnify != event->xconfigure.width) ||
12819               ((int) magnify != event->xconfigure.height))
12820             {
12821               XWindowChanges
12822                 window_changes;
12823
12824               window_changes.width=(int) magnify;
12825               window_changes.height=(int) magnify;
12826               (void) XReconfigureWMWindow(display,windows->magnify.id,
12827                 windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
12828                 &window_changes);
12829               break;
12830             }
12831           XMakeMagnifyImage(display,windows,exception);
12832           break;
12833         }
12834       break;
12835     }
12836     case Expose:
12837     {
12838       if (event->xexpose.window == windows->image.id)
12839         {
12840           XRefreshWindow(display,&windows->image,event);
12841           break;
12842         }
12843       if (event->xexpose.window == windows->pan.id)
12844         if (event->xexpose.count == 0)
12845           {
12846             XDrawPanRectangle(display,windows);
12847             break;
12848           }
12849       if (event->xexpose.window == windows->magnify.id)
12850         if (event->xexpose.count == 0)
12851           {
12852             XMakeMagnifyImage(display,windows,exception);
12853             break;
12854           }
12855       break;
12856     }
12857     case KeyPress:
12858     {
12859       char
12860         command[MagickPathExtent];
12861
12862       KeySym
12863         key_symbol;
12864
12865       if (event->xkey.window != windows->magnify.id)
12866         break;
12867       /*
12868         Respond to a user key press.
12869       */
12870       (void) XLookupString((XKeyEvent *) &event->xkey,command,(int)
12871         sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12872       XMagnifyWindowCommand(display,windows,event->xkey.state,key_symbol,
12873         exception);
12874       break;
12875     }
12876     case MapNotify:
12877     {
12878       if (event->xmap.window == windows->magnify.id)
12879         {
12880           windows->magnify.mapped=MagickTrue;
12881           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12882           break;
12883         }
12884       if (event->xmap.window == windows->info.id)
12885         {
12886           windows->info.mapped=MagickTrue;
12887           break;
12888         }
12889       break;
12890     }
12891     case MotionNotify:
12892     {
12893       while (XCheckMaskEvent(display,ButtonMotionMask,event)) ;
12894       if (event->xmotion.window == windows->image.id)
12895         if (windows->magnify.mapped != MagickFalse )
12896           {
12897             /*
12898               Update magnified image.
12899             */
12900             x=event->xmotion.x;
12901             y=event->xmotion.y;
12902             if (x < 0)
12903               x=0;
12904             else
12905               if (x >= (int) windows->image.width)
12906                 x=(int) (windows->image.width-1);
12907             windows->magnify.x=(int) windows->image.x+x;
12908             if (y < 0)
12909               y=0;
12910             else
12911              if (y >= (int) windows->image.height)
12912                y=(int) (windows->image.height-1);
12913             windows->magnify.y=windows->image.y+y;
12914             XMakeMagnifyImage(display,windows,exception);
12915           }
12916       break;
12917     }
12918     case UnmapNotify:
12919     {
12920       if (event->xunmap.window == windows->magnify.id)
12921         {
12922           windows->magnify.mapped=MagickFalse;
12923           break;
12924         }
12925       if (event->xunmap.window == windows->info.id)
12926         {
12927           windows->info.mapped=MagickFalse;
12928           break;
12929         }
12930       break;
12931     }
12932     default:
12933       break;
12934   }
12935 }
12936 \f
12937 /*
12938 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12939 %                                                                             %
12940 %                                                                             %
12941 %                                                                             %
12942 +   X S e t C r o p G e o m e t r y                                           %
12943 %                                                                             %
12944 %                                                                             %
12945 %                                                                             %
12946 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12947 %
12948 %  XSetCropGeometry() accepts a cropping geometry relative to the Image window
12949 %  and translates it to a cropping geometry relative to the image.
12950 %
12951 %  The format of the XSetCropGeometry method is:
12952 %
12953 %      void XSetCropGeometry(Display *display,XWindows *windows,
12954 %        RectangleInfo *crop_info,Image *image)
12955 %
12956 %  A description of each parameter follows:
12957 %
12958 %    o display: Specifies a connection to an X server; returned from
12959 %      XOpenDisplay.
12960 %
12961 %    o windows: Specifies a pointer to a XWindows structure.
12962 %
12963 %    o crop_info:  A pointer to a RectangleInfo that defines a region of the
12964 %      Image window to crop.
12965 %
12966 %    o image: the image.
12967 %
12968 */
12969 static void XSetCropGeometry(Display *display,XWindows *windows,
12970   RectangleInfo *crop_info,Image *image)
12971 {
12972   char
12973     text[MagickPathExtent];
12974
12975   int
12976     x,
12977     y;
12978
12979   double
12980     scale_factor;
12981
12982   unsigned int
12983     height,
12984     width;
12985
12986   if (windows->info.mapped != MagickFalse )
12987     {
12988       /*
12989         Display info on cropping rectangle.
12990       */
12991       (void) FormatLocaleString(text,MagickPathExtent," %.20gx%.20g%+.20g%+.20g",
12992         (double) crop_info->width,(double) crop_info->height,(double)
12993         crop_info->x,(double) crop_info->y);
12994       XInfoWidget(display,windows,text);
12995     }
12996   /*
12997     Cropping geometry is relative to any previous crop geometry.
12998   */
12999   x=0;
13000   y=0;
13001   width=(unsigned int) image->columns;
13002   height=(unsigned int) image->rows;
13003   if (windows->image.crop_geometry != (char *) NULL)
13004     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
13005   else
13006     windows->image.crop_geometry=AcquireString((char *) NULL);
13007   /*
13008     Define the crop geometry string from the cropping rectangle.
13009   */
13010   scale_factor=(double) width/windows->image.ximage->width;
13011   if (crop_info->x > 0)
13012     x+=(int) (scale_factor*crop_info->x+0.5);
13013   width=(unsigned int) (scale_factor*crop_info->width+0.5);
13014   if (width == 0)
13015     width=1;
13016   scale_factor=(double) height/windows->image.ximage->height;
13017   if (crop_info->y > 0)
13018     y+=(int) (scale_factor*crop_info->y+0.5);
13019   height=(unsigned int) (scale_factor*crop_info->height+0.5);
13020   if (height == 0)
13021     height=1;
13022   (void) FormatLocaleString(windows->image.crop_geometry,MagickPathExtent,
13023     "%ux%u%+d%+d",width,height,x,y);
13024 }
13025 \f
13026 /*
13027 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13028 %                                                                             %
13029 %                                                                             %
13030 %                                                                             %
13031 +   X T i l e I m a g e                                                       %
13032 %                                                                             %
13033 %                                                                             %
13034 %                                                                             %
13035 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13036 %
13037 %  XTileImage() loads or deletes a selected tile from a visual image directory.
13038 %  The load or delete command is chosen from a menu.
13039 %
13040 %  The format of the XTileImage method is:
13041 %
13042 %      Image *XTileImage(Display *display,XResourceInfo *resource_info,
13043 %        XWindows *windows,Image *image,XEvent *event,ExceptionInfo *exception)
13044 %
13045 %  A description of each parameter follows:
13046 %
13047 %    o tile_image:  XTileImage reads or deletes the tile image
13048 %      and returns it.  A null image is returned if an error occurs.
13049 %
13050 %    o display: Specifies a connection to an X server;  returned from
13051 %      XOpenDisplay.
13052 %
13053 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13054 %
13055 %    o windows: Specifies a pointer to a XWindows structure.
13056 %
13057 %    o image: the image; returned from ReadImage.
13058 %
13059 %    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
13060 %      the entire image is refreshed.
13061 %
13062 %    o exception: return any errors or warnings in this structure.
13063 %
13064 */
13065 static Image *XTileImage(Display *display,XResourceInfo *resource_info,
13066   XWindows *windows,Image *image,XEvent *event,ExceptionInfo *exception)
13067 {
13068   static const char
13069     *VerbMenu[] =
13070     {
13071       "Load",
13072       "Next",
13073       "Former",
13074       "Delete",
13075       "Update",
13076       (char *) NULL,
13077     };
13078
13079   static const ModeType
13080     TileCommands[] =
13081     {
13082       TileLoadCommand,
13083       TileNextCommand,
13084       TileFormerCommand,
13085       TileDeleteCommand,
13086       TileUpdateCommand
13087     };
13088
13089   char
13090     command[MagickPathExtent],
13091     filename[MagickPathExtent];
13092
13093   Image
13094     *tile_image;
13095
13096   int
13097     id,
13098     status,
13099     tile,
13100     x,
13101     y;
13102
13103   double
13104     scale_factor;
13105
13106   register char
13107     *p,
13108     *q;
13109
13110   register int
13111     i;
13112
13113   unsigned int
13114     height,
13115     width;
13116
13117   /*
13118     Tile image is relative to montage image configuration.
13119   */
13120   x=0;
13121   y=0;
13122   width=(unsigned int) image->columns;
13123   height=(unsigned int) image->rows;
13124   if (windows->image.crop_geometry != (char *) NULL)
13125     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
13126   scale_factor=(double) width/windows->image.ximage->width;
13127   event->xbutton.x+=windows->image.x;
13128   event->xbutton.x=(int) (scale_factor*event->xbutton.x+x+0.5);
13129   scale_factor=(double) height/windows->image.ximage->height;
13130   event->xbutton.y+=windows->image.y;
13131   event->xbutton.y=(int) (scale_factor*event->xbutton.y+y+0.5);
13132   /*
13133     Determine size and location of each tile in the visual image directory.
13134   */
13135   width=(unsigned int) image->columns;
13136   height=(unsigned int) image->rows;
13137   x=0;
13138   y=0;
13139   (void) XParseGeometry(image->montage,&x,&y,&width,&height);
13140   tile=((event->xbutton.y-y)/height)*(((int) image->columns-x)/width)+
13141     (event->xbutton.x-x)/width;
13142   if (tile < 0)
13143     {
13144       /*
13145         Button press is outside any tile.
13146       */
13147       (void) XBell(display,0);
13148       return((Image *) NULL);
13149     }
13150   /*
13151     Determine file name from the tile directory.
13152   */
13153   p=image->directory;
13154   for (i=tile; (i != 0) && (*p != '\0'); )
13155   {
13156     if (*p == '\xff')
13157       i--;
13158     p++;
13159   }
13160   if (*p == '\0')
13161     {
13162       /*
13163         Button press is outside any tile.
13164       */
13165       (void) XBell(display,0);
13166       return((Image *) NULL);
13167     }
13168   /*
13169     Select a command from the pop-up menu.
13170   */
13171   id=XMenuWidget(display,windows,"Tile Verb",VerbMenu,command);
13172   if (id < 0)
13173     return((Image *) NULL);
13174   q=p;
13175   while ((*q != '\n') && (*q != '\0'))
13176     q++;
13177   (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13178   /*
13179     Perform command for the selected tile.
13180   */
13181   XSetCursorState(display,windows,MagickTrue);
13182   XCheckRefreshWindows(display,windows);
13183   tile_image=NewImageList();
13184   switch (TileCommands[id])
13185   {
13186     case TileLoadCommand:
13187     {
13188       /*
13189         Load tile image.
13190       */
13191       XCheckRefreshWindows(display,windows);
13192       (void) CopyMagickString(resource_info->image_info->magick,"MIFF",
13193         MagickPathExtent);
13194       (void) CopyMagickString(resource_info->image_info->filename,filename,
13195         MagickPathExtent);
13196       tile_image=ReadImage(resource_info->image_info,exception);
13197       CatchException(exception);
13198       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13199       break;
13200     }
13201     case TileNextCommand:
13202     {
13203       /*
13204         Display next image.
13205       */
13206       XClientMessage(display,windows->image.id,windows->im_protocols,
13207         windows->im_next_image,CurrentTime);
13208       break;
13209     }
13210     case TileFormerCommand:
13211     {
13212       /*
13213         Display former image.
13214       */
13215       XClientMessage(display,windows->image.id,windows->im_protocols,
13216         windows->im_former_image,CurrentTime);
13217       break;
13218     }
13219     case TileDeleteCommand:
13220     {
13221       /*
13222         Delete tile image.
13223       */
13224       if (IsPathAccessible(filename) == MagickFalse)
13225         {
13226           XNoticeWidget(display,windows,"Image file does not exist:",filename);
13227           break;
13228         }
13229       status=XConfirmWidget(display,windows,"Really delete tile",filename);
13230       if (status <= 0)
13231         break;
13232       status=ShredFile(filename);
13233       if (status != MagickFalse )
13234         {
13235           XNoticeWidget(display,windows,"Unable to delete image file:",
13236             filename);
13237           break;
13238         }
13239     }
13240     case TileUpdateCommand:
13241     {
13242       int
13243         x_offset,
13244         y_offset;
13245
13246       PixelInfo
13247         pixel;
13248
13249       register int
13250         j;
13251
13252       register Quantum
13253         *s;
13254
13255       /*
13256         Ensure all the images exist.
13257       */
13258       tile=0;
13259       GetPixelInfo(image,&pixel);
13260       for (p=image->directory; *p != '\0'; p++)
13261       {
13262         CacheView
13263           *image_view;
13264
13265         q=p;
13266         while ((*q != '\xff') && (*q != '\0'))
13267           q++;
13268         (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13269         p=q;
13270         if (IsPathAccessible(filename) != MagickFalse )
13271           {
13272             tile++;
13273             continue;
13274           }
13275         /*
13276           Overwrite tile with background color.
13277         */
13278         x_offset=(int) (width*(tile % (((int) image->columns-x)/width))+x);
13279         y_offset=(int) (height*(tile/(((int) image->columns-x)/width))+y);
13280         image_view=AcquireAuthenticCacheView(image,exception);
13281         (void) GetOneCacheViewVirtualPixelInfo(image_view,0,0,&pixel,exception);
13282         for (i=0; i < (int) height; i++)
13283         {
13284           s=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,(ssize_t)
13285             y_offset+i,width,1,exception);
13286           if (s == (Quantum *) NULL)
13287             break;
13288           for (j=0; j < (int) width; j++)
13289           {
13290             SetPixelViaPixelInfo(image,&pixel,s);
13291             s+=GetPixelChannels(image);
13292           }
13293           if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
13294             break;
13295         }
13296         image_view=DestroyCacheView(image_view);
13297         tile++;
13298       }
13299       windows->image.window_changes.width=(int) image->columns;
13300       windows->image.window_changes.height=(int) image->rows;
13301       XConfigureImageColormap(display,resource_info,windows,image,exception);
13302       (void) XConfigureImage(display,resource_info,windows,image,exception);
13303       break;
13304     }
13305     default:
13306       break;
13307   }
13308   XSetCursorState(display,windows,MagickFalse);
13309   return(tile_image);
13310 }
13311 \f
13312 /*
13313 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13314 %                                                                             %
13315 %                                                                             %
13316 %                                                                             %
13317 +   X T r a n s l a t e I m a g e                                             %
13318 %                                                                             %
13319 %                                                                             %
13320 %                                                                             %
13321 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13322 %
13323 %  XTranslateImage() translates the image within an Image window by one pixel
13324 %  as specified by the key symbol.  If the image has a montage string the
13325 %  translation is respect to the width and height contained within the string.
13326 %
13327 %  The format of the XTranslateImage method is:
13328 %
13329 %      void XTranslateImage(Display *display,XWindows *windows,
13330 %        Image *image,const KeySym key_symbol)
13331 %
13332 %  A description of each parameter follows:
13333 %
13334 %    o display: Specifies a connection to an X server; returned from
13335 %      XOpenDisplay.
13336 %
13337 %    o windows: Specifies a pointer to a XWindows structure.
13338 %
13339 %    o image: the image.
13340 %
13341 %    o key_symbol: Specifies a KeySym which indicates which side of the image
13342 %      to trim.
13343 %
13344 */
13345 static void XTranslateImage(Display *display,XWindows *windows,
13346   Image *image,const KeySym key_symbol)
13347 {
13348   char
13349     text[MagickPathExtent];
13350
13351   int
13352     x,
13353     y;
13354
13355   unsigned int
13356     x_offset,
13357     y_offset;
13358
13359   /*
13360     User specified a pan position offset.
13361   */
13362   x_offset=windows->image.width;
13363   y_offset=windows->image.height;
13364   if (image->montage != (char *) NULL)
13365     (void) XParseGeometry(image->montage,&x,&y,&x_offset,&y_offset);
13366   switch ((int) key_symbol)
13367   {
13368     case XK_Home:
13369     case XK_KP_Home:
13370     {
13371       windows->image.x=(int) windows->image.width/2;
13372       windows->image.y=(int) windows->image.height/2;
13373       break;
13374     }
13375     case XK_Left:
13376     case XK_KP_Left:
13377     {
13378       windows->image.x-=x_offset;
13379       break;
13380     }
13381     case XK_Next:
13382     case XK_Up:
13383     case XK_KP_Up:
13384     {
13385       windows->image.y-=y_offset;
13386       break;
13387     }
13388     case XK_Right:
13389     case XK_KP_Right:
13390     {
13391       windows->image.x+=x_offset;
13392       break;
13393     }
13394     case XK_Prior:
13395     case XK_Down:
13396     case XK_KP_Down:
13397     {
13398       windows->image.y+=y_offset;
13399       break;
13400     }
13401     default:
13402       return;
13403   }
13404   /*
13405     Check boundary conditions.
13406   */
13407   if (windows->image.x < 0)
13408     windows->image.x=0;
13409   else
13410     if ((int) (windows->image.x+windows->image.width) >
13411         windows->image.ximage->width)
13412       windows->image.x=(int) windows->image.ximage->width-windows->image.width;
13413   if (windows->image.y < 0)
13414     windows->image.y=0;
13415   else
13416     if ((int) (windows->image.y+windows->image.height) >
13417         windows->image.ximage->height)
13418       windows->image.y=(int) windows->image.ximage->height-windows->image.height;
13419   /*
13420     Refresh Image window.
13421   */
13422   (void) FormatLocaleString(text,MagickPathExtent," %ux%u%+d%+d ",
13423     windows->image.width,windows->image.height,windows->image.x,
13424     windows->image.y);
13425   XInfoWidget(display,windows,text);
13426   XCheckRefreshWindows(display,windows);
13427   XDrawPanRectangle(display,windows);
13428   XRefreshWindow(display,&windows->image,(XEvent *) NULL);
13429   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13430 }
13431 \f
13432 /*
13433 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13434 %                                                                             %
13435 %                                                                             %
13436 %                                                                             %
13437 +   X T r i m I m a g e                                                       %
13438 %                                                                             %
13439 %                                                                             %
13440 %                                                                             %
13441 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13442 %
13443 %  XTrimImage() trims the edges from the Image window.
13444 %
13445 %  The format of the XTrimImage method is:
13446 %
13447 %      MagickBooleanType XTrimImage(Display *display,
13448 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
13449 %        ExceptionInfo *exception)
13450 %
13451 %  A description of each parameter follows:
13452 %
13453 %    o display: Specifies a connection to an X server; returned from
13454 %      XOpenDisplay.
13455 %
13456 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13457 %
13458 %    o windows: Specifies a pointer to a XWindows structure.
13459 %
13460 %    o image: the image.
13461 %
13462 %    o exception: return any errors or warnings in this structure.
13463 %
13464 */
13465 static MagickBooleanType XTrimImage(Display *display,
13466   XResourceInfo *resource_info,XWindows *windows,Image *image,
13467   ExceptionInfo *exception)
13468 {
13469   RectangleInfo
13470     trim_info;
13471
13472   register int
13473     x,
13474     y;
13475
13476   size_t
13477     background,
13478     pixel;
13479
13480   /*
13481     Trim edges from image.
13482   */
13483   XSetCursorState(display,windows,MagickTrue);
13484   XCheckRefreshWindows(display,windows);
13485   /*
13486     Crop the left edge.
13487   */
13488   background=XGetPixel(windows->image.ximage,0,0);
13489   trim_info.width=(size_t) windows->image.ximage->width;
13490   for (x=0; x < windows->image.ximage->width; x++)
13491   {
13492     for (y=0; y < windows->image.ximage->height; y++)
13493     {
13494       pixel=XGetPixel(windows->image.ximage,x,y);
13495       if (pixel != background)
13496         break;
13497     }
13498     if (y < windows->image.ximage->height)
13499       break;
13500   }
13501   trim_info.x=(ssize_t) x;
13502   if (trim_info.x == (ssize_t) windows->image.ximage->width)
13503     {
13504       XSetCursorState(display,windows,MagickFalse);
13505       return(MagickFalse);
13506     }
13507   /*
13508     Crop the right edge.
13509   */
13510   background=XGetPixel(windows->image.ximage,windows->image.ximage->width-1,0);
13511   for (x=windows->image.ximage->width-1; x != 0; x--)
13512   {
13513     for (y=0; y < windows->image.ximage->height; y++)
13514     {
13515       pixel=XGetPixel(windows->image.ximage,x,y);
13516       if (pixel != background)
13517         break;
13518     }
13519     if (y < windows->image.ximage->height)
13520       break;
13521   }
13522   trim_info.width=(size_t) (x-trim_info.x+1);
13523   /*
13524     Crop the top edge.
13525   */
13526   background=XGetPixel(windows->image.ximage,0,0);
13527   trim_info.height=(size_t) windows->image.ximage->height;
13528   for (y=0; y < windows->image.ximage->height; y++)
13529   {
13530     for (x=0; x < windows->image.ximage->width; x++)
13531     {
13532       pixel=XGetPixel(windows->image.ximage,x,y);
13533       if (pixel != background)
13534         break;
13535     }
13536     if (x < windows->image.ximage->width)
13537       break;
13538   }
13539   trim_info.y=(ssize_t) y;
13540   /*
13541     Crop the bottom edge.
13542   */
13543   background=XGetPixel(windows->image.ximage,0,windows->image.ximage->height-1);
13544   for (y=windows->image.ximage->height-1; y != 0; y--)
13545   {
13546     for (x=0; x < windows->image.ximage->width; x++)
13547     {
13548       pixel=XGetPixel(windows->image.ximage,x,y);
13549       if (pixel != background)
13550         break;
13551     }
13552     if (x < windows->image.ximage->width)
13553       break;
13554   }
13555   trim_info.height=(size_t) y-trim_info.y+1;
13556   if (((unsigned int) trim_info.width != windows->image.width) ||
13557       ((unsigned int) trim_info.height != windows->image.height))
13558     {
13559       /*
13560         Reconfigure Image window as defined by the trimming rectangle.
13561       */
13562       XSetCropGeometry(display,windows,&trim_info,image);
13563       windows->image.window_changes.width=(int) trim_info.width;
13564       windows->image.window_changes.height=(int) trim_info.height;
13565       (void) XConfigureImage(display,resource_info,windows,image,exception);
13566     }
13567   XSetCursorState(display,windows,MagickFalse);
13568   return(MagickTrue);
13569 }
13570 \f
13571 /*
13572 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13573 %                                                                             %
13574 %                                                                             %
13575 %                                                                             %
13576 +   X V i s u a l D i r e c t o r y I m a g e                                 %
13577 %                                                                             %
13578 %                                                                             %
13579 %                                                                             %
13580 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13581 %
13582 %  XVisualDirectoryImage() creates a Visual Image Directory.
13583 %
13584 %  The format of the XVisualDirectoryImage method is:
13585 %
13586 %      Image *XVisualDirectoryImage(Display *display,
13587 %        XResourceInfo *resource_info,XWindows *windows,
13588 %        ExceptionInfo *exception)
13589 %
13590 %  A description of each parameter follows:
13591 %
13592 %    o display: Specifies a connection to an X server; returned from
13593 %      XOpenDisplay.
13594 %
13595 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13596 %
13597 %    o windows: Specifies a pointer to a XWindows structure.
13598 %
13599 %    o exception: return any errors or warnings in this structure.
13600 %
13601 */
13602 static Image *XVisualDirectoryImage(Display *display,
13603   XResourceInfo *resource_info,XWindows *windows,ExceptionInfo *exception)
13604 {
13605 #define TileImageTag  "Scale/Image"
13606 #define XClientName  "montage"
13607
13608   char
13609     **filelist;
13610
13611   Image
13612     *images,
13613     *montage_image,
13614     *next_image,
13615     *thumbnail_image;
13616
13617   ImageInfo
13618     *read_info;
13619
13620   int
13621     number_files;
13622
13623   MagickBooleanType
13624     backdrop;
13625
13626   MagickStatusType
13627     status;
13628
13629   MontageInfo
13630     *montage_info;
13631
13632   RectangleInfo
13633     geometry;
13634
13635   register int
13636     i;
13637
13638   static char
13639     filename[MagickPathExtent] = "\0",
13640     filenames[MagickPathExtent] = "*";
13641
13642   XResourceInfo
13643     background_resources;
13644
13645   /*
13646     Request file name from user.
13647   */
13648   XFileBrowserWidget(display,windows,"Directory",filenames);
13649   if (*filenames == '\0')
13650     return((Image *) NULL);
13651   /*
13652     Expand the filenames.
13653   */
13654   filelist=(char **) AcquireMagickMemory(sizeof(*filelist));
13655   if (filelist == (char **) NULL)
13656     {
13657       ThrowXWindowException(ResourceLimitError,"MemoryAllocationFailed",
13658         filenames);
13659       return((Image *) NULL);
13660     }
13661   number_files=1;
13662   filelist[0]=filenames;
13663   status=ExpandFilenames(&number_files,&filelist);
13664   if ((status == MagickFalse) || (number_files == 0))
13665     {
13666       if (number_files == 0)
13667         ThrowXWindowException(ImageError,"NoImagesWereFound",filenames)
13668       else
13669         ThrowXWindowException(ResourceLimitError,"MemoryAllocationFailed",
13670           filenames);
13671       return((Image *) NULL);
13672     }
13673   /*
13674     Set image background resources.
13675   */
13676   background_resources=(*resource_info);
13677   background_resources.window_id=AcquireString("");
13678   (void) FormatLocaleString(background_resources.window_id,MagickPathExtent,
13679     "0x%lx",windows->image.id);
13680   background_resources.backdrop=MagickTrue;
13681   /*
13682     Read each image and convert them to a tile.
13683   */
13684   backdrop=((windows->visual_info->klass == TrueColor) ||
13685     (windows->visual_info->klass == DirectColor)) ? MagickTrue : MagickFalse;
13686   read_info=CloneImageInfo(resource_info->image_info);
13687   (void) SetImageOption(read_info,"jpeg:size","120x120");
13688   (void) CloneString(&read_info->size,DefaultTileGeometry);
13689   (void) SetImageInfoProgressMonitor(read_info,(MagickProgressMonitor) NULL,
13690     (void *) NULL);
13691   images=NewImageList();
13692   XSetCursorState(display,windows,MagickTrue);
13693   XCheckRefreshWindows(display,windows);
13694   for (i=0; i < (int) number_files; i++)
13695   {
13696     (void) CopyMagickString(read_info->filename,filelist[i],MagickPathExtent);
13697     filelist[i]=DestroyString(filelist[i]);
13698     *read_info->magick='\0';
13699     next_image=ReadImage(read_info,exception);
13700     CatchException(exception);
13701     if (next_image != (Image *) NULL)
13702       {
13703         (void) DeleteImageProperty(next_image,"label");
13704         (void) SetImageProperty(next_image,"label",InterpretImageProperties(
13705           read_info,next_image,DefaultTileLabel,exception),exception);
13706         (void) ParseRegionGeometry(next_image,read_info->size,&geometry,
13707           exception);
13708         thumbnail_image=ThumbnailImage(next_image,geometry.width,
13709           geometry.height,exception);
13710         if (thumbnail_image != (Image *) NULL)
13711           {
13712             next_image=DestroyImage(next_image);
13713             next_image=thumbnail_image;
13714           }
13715         if (backdrop)
13716           {
13717             (void) XDisplayBackgroundImage(display,&background_resources,
13718               next_image,exception);
13719             XSetCursorState(display,windows,MagickTrue);
13720           }
13721         AppendImageToList(&images,next_image);
13722         if (images->progress_monitor != (MagickProgressMonitor) NULL)
13723           {
13724             MagickBooleanType
13725               proceed;
13726
13727             proceed=SetImageProgress(images,LoadImageTag,(MagickOffsetType) i,
13728               (MagickSizeType) number_files);
13729             if (proceed == MagickFalse)
13730               break;
13731           }
13732       }
13733   }
13734   filelist=(char **) RelinquishMagickMemory(filelist);
13735   if (images == (Image *) NULL)
13736     {
13737       read_info=DestroyImageInfo(read_info);
13738       XSetCursorState(display,windows,MagickFalse);
13739       ThrowXWindowException(ImageError,"NoImagesWereLoaded",filenames);
13740       return((Image *) NULL);
13741     }
13742   /*
13743     Create the Visual Image Directory.
13744   */
13745   montage_info=CloneMontageInfo(read_info,(MontageInfo *) NULL);
13746   montage_info->pointsize=10;
13747   if (resource_info->font != (char *) NULL)
13748     (void) CloneString(&montage_info->font,resource_info->font);
13749   (void) CopyMagickString(montage_info->filename,filename,MagickPathExtent);
13750   montage_image=MontageImageList(read_info,montage_info,GetFirstImageInList(
13751     images),exception);
13752   images=DestroyImageList(images);
13753   montage_info=DestroyMontageInfo(montage_info);
13754   read_info=DestroyImageInfo(read_info);
13755   XSetCursorState(display,windows,MagickFalse);
13756   if (montage_image == (Image *) NULL)
13757     return(montage_image);
13758   XClientMessage(display,windows->image.id,windows->im_protocols,
13759     windows->im_next_image,CurrentTime);
13760   return(montage_image);
13761 }
13762 \f
13763 /*
13764 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13765 %                                                                             %
13766 %                                                                             %
13767 %                                                                             %
13768 %   X D i s p l a y B a c k g r o u n d I m a g e                             %
13769 %                                                                             %
13770 %                                                                             %
13771 %                                                                             %
13772 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13773 %
13774 %  XDisplayBackgroundImage() displays an image in the background of a window.
13775 %
13776 %  The format of the XDisplayBackgroundImage method is:
13777 %
13778 %      MagickBooleanType XDisplayBackgroundImage(Display *display,
13779 %        XResourceInfo *resource_info,Image *image,ExceptionInfo *exception)
13780 %
13781 %  A description of each parameter follows:
13782 %
13783 %    o display: Specifies a connection to an X server;  returned from
13784 %      XOpenDisplay.
13785 %
13786 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13787 %
13788 %    o image: the image.
13789 %
13790 %    o exception: return any errors or warnings in this structure.
13791 %
13792 */
13793 MagickExport MagickBooleanType XDisplayBackgroundImage(Display *display,
13794   XResourceInfo *resource_info,Image *image,ExceptionInfo *exception)
13795 {
13796   char
13797     geometry[MagickPathExtent],
13798     visual_type[MagickPathExtent];
13799
13800   int
13801     height,
13802     status,
13803     width;
13804
13805   RectangleInfo
13806     geometry_info;
13807
13808   static XPixelInfo
13809     pixel;
13810
13811   static XStandardColormap
13812     *map_info;
13813
13814   static XVisualInfo
13815     *visual_info = (XVisualInfo *) NULL;
13816
13817   static XWindowInfo
13818     window_info;
13819
13820   size_t
13821     delay;
13822
13823   Window
13824     root_window;
13825
13826   XGCValues
13827     context_values;
13828
13829   XResourceInfo
13830     resources;
13831
13832   XWindowAttributes
13833     window_attributes;
13834
13835   /*
13836     Determine target window.
13837   */
13838   assert(image != (Image *) NULL);
13839   assert(image->signature == MagickCoreSignature);
13840   if (image->debug != MagickFalse )
13841     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
13842   resources=(*resource_info);
13843   window_info.id=(Window) NULL;
13844   root_window=XRootWindow(display,XDefaultScreen(display));
13845   if (LocaleCompare(resources.window_id,"root") == 0)
13846     window_info.id=root_window;
13847   else
13848     {
13849       if (isdigit((int) ((unsigned char) *resources.window_id)) != 0)
13850         window_info.id=XWindowByID(display,root_window,
13851           (Window) strtol((char *) resources.window_id,(char **) NULL,0));
13852       if (window_info.id == (Window) NULL)
13853         window_info.id=XWindowByName(display,root_window,resources.window_id);
13854     }
13855   if (window_info.id == (Window) NULL)
13856     {
13857       ThrowXWindowException(XServerError,"NoWindowWithSpecifiedIDExists",
13858         resources.window_id);
13859       return(MagickFalse);
13860     }
13861   /*
13862     Determine window visual id.
13863   */
13864   window_attributes.width=XDisplayWidth(display,XDefaultScreen(display));
13865   window_attributes.height=XDisplayHeight(display,XDefaultScreen(display));
13866   (void) CopyMagickString(visual_type,"default",MagickPathExtent);
13867   status=XGetWindowAttributes(display,window_info.id,&window_attributes);
13868   if (status != 0)
13869     (void) FormatLocaleString(visual_type,MagickPathExtent,"0x%lx",
13870       XVisualIDFromVisual(window_attributes.visual));
13871   if (visual_info == (XVisualInfo *) NULL)
13872     {
13873       /*
13874         Allocate standard colormap.
13875       */
13876       map_info=XAllocStandardColormap();
13877       if (map_info == (XStandardColormap *) NULL)
13878         ThrowXWindowFatalException(XServerFatalError,"MemoryAllocationFailed",
13879           image->filename);
13880       map_info->colormap=(Colormap) NULL;
13881       pixel.pixels=(unsigned long *) NULL;
13882       /*
13883         Initialize visual info.
13884       */
13885       resources.map_type=(char *) NULL;
13886       resources.visual_type=visual_type;
13887       visual_info=XBestVisualInfo(display,map_info,&resources);
13888       if (visual_info == (XVisualInfo *) NULL)
13889         ThrowXWindowFatalException(XServerFatalError,"UnableToGetVisual",
13890           resources.visual_type);
13891       /*
13892         Initialize window info.
13893       */
13894       window_info.ximage=(XImage *) NULL;
13895       window_info.matte_image=(XImage *) NULL;
13896       window_info.pixmap=(Pixmap) NULL;
13897       window_info.matte_pixmap=(Pixmap) NULL;
13898     }
13899   /*
13900     Free previous root colors.
13901   */
13902   if (window_info.id == root_window)
13903     (void) XDestroyWindowColors(display,root_window);
13904   /*
13905     Initialize Standard Colormap.
13906   */
13907   resources.colormap=SharedColormap;
13908   XMakeStandardColormap(display,visual_info,&resources,image,map_info,&pixel,
13909     exception);
13910   /*
13911     Graphic context superclass.
13912   */
13913   context_values.background=pixel.background_color.pixel;
13914   context_values.foreground=pixel.foreground_color.pixel;
13915   pixel.annotate_context=XCreateGC(display,window_info.id,
13916     (size_t) (GCBackground | GCForeground),&context_values);
13917   if (pixel.annotate_context == (GC) NULL)
13918     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
13919       image->filename);
13920   /*
13921     Initialize Image window attributes.
13922   */
13923   window_info.name=AcquireString("\0");
13924   window_info.icon_name=AcquireString("\0");
13925   XGetWindowInfo(display,visual_info,map_info,&pixel,(XFontStruct *) NULL,
13926     &resources,&window_info);
13927   /*
13928     Create the X image.
13929   */
13930   window_info.width=(unsigned int) image->columns;
13931   window_info.height=(unsigned int) image->rows;
13932   if ((image->columns != window_info.width) ||
13933       (image->rows != window_info.height))
13934     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13935       image->filename);
13936   (void) FormatLocaleString(geometry,MagickPathExtent,"%ux%u+0+0>",
13937     window_attributes.width,window_attributes.height);
13938   geometry_info.width=window_info.width;
13939   geometry_info.height=window_info.height;
13940   geometry_info.x=(ssize_t) window_info.x;
13941   geometry_info.y=(ssize_t) window_info.y;
13942   (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
13943     &geometry_info.width,&geometry_info.height);
13944   window_info.width=(unsigned int) geometry_info.width;
13945   window_info.height=(unsigned int) geometry_info.height;
13946   window_info.x=(int) geometry_info.x;
13947   window_info.y=(int) geometry_info.y;
13948   status=XMakeImage(display,&resources,&window_info,image,window_info.width,
13949     window_info.height,exception);
13950   if (status == MagickFalse)
13951     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13952       image->filename);
13953   window_info.x=0;
13954   window_info.y=0;
13955   if (image->debug != MagickFalse )
13956     {
13957       (void) LogMagickEvent(X11Event,GetMagickModule(),
13958         "Image: %s[%.20g] %.20gx%.20g ",image->filename,(double) image->scene,
13959         (double) image->columns,(double) image->rows);
13960       if (image->colors != 0)
13961         (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
13962           image->colors);
13963       (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",image->magick);
13964     }
13965   /*
13966     Adjust image dimensions as specified by backdrop or geometry options.
13967   */
13968   width=(int) window_info.width;
13969   height=(int) window_info.height;
13970   if (resources.backdrop != MagickFalse )
13971     {
13972       /*
13973         Center image on window.
13974       */
13975       window_info.x=(window_attributes.width/2)-
13976         (window_info.ximage->width/2);
13977       window_info.y=(window_attributes.height/2)-
13978         (window_info.ximage->height/2);
13979       width=window_attributes.width;
13980       height=window_attributes.height;
13981     }
13982   if ((resources.image_geometry != (char *) NULL) &&
13983       (*resources.image_geometry != '\0'))
13984     {
13985       char
13986         default_geometry[MagickPathExtent];
13987
13988       int
13989         flags,
13990         gravity;
13991
13992       XSizeHints
13993         *size_hints;
13994
13995       /*
13996         User specified geometry.
13997       */
13998       size_hints=XAllocSizeHints();
13999       if (size_hints == (XSizeHints *) NULL)
14000         ThrowXWindowFatalException(ResourceLimitFatalError,
14001           "MemoryAllocationFailed",image->filename);
14002       size_hints->flags=0L;
14003       (void) FormatLocaleString(default_geometry,MagickPathExtent,"%dx%d",
14004         width,height);
14005       flags=XWMGeometry(display,visual_info->screen,resources.image_geometry,
14006         default_geometry,window_info.border_width,size_hints,&window_info.x,
14007         &window_info.y,&width,&height,&gravity);
14008       if (flags & (XValue | YValue))
14009         {
14010           width=window_attributes.width;
14011           height=window_attributes.height;
14012         }
14013       (void) XFree((void *) size_hints);
14014     }
14015   /*
14016     Create the X pixmap.
14017   */
14018   window_info.pixmap=XCreatePixmap(display,window_info.id,(unsigned int) width,
14019     (unsigned int) height,window_info.depth);
14020   if (window_info.pixmap == (Pixmap) NULL)
14021     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXPixmap",
14022       image->filename);
14023   /*
14024     Display pixmap on the window.
14025   */
14026   if (((unsigned int) width > window_info.width) ||
14027       ((unsigned int) height > window_info.height))
14028     (void) XFillRectangle(display,window_info.pixmap,
14029       window_info.annotate_context,0,0,(unsigned int) width,
14030       (unsigned int) height);
14031   (void) XPutImage(display,window_info.pixmap,window_info.annotate_context,
14032     window_info.ximage,0,0,window_info.x,window_info.y,(unsigned int)
14033     window_info.width,(unsigned int) window_info.height);
14034   (void) XSetWindowBackgroundPixmap(display,window_info.id,window_info.pixmap);
14035   (void) XClearWindow(display,window_info.id);
14036   delay=1000*image->delay/MagickMax(image->ticks_per_second,1L);
14037   XDelay(display,delay == 0UL ? 10UL : delay);
14038   (void) XSync(display,MagickFalse);
14039   return(window_info.id == root_window ? MagickTrue : MagickFalse);
14040 }
14041 \f
14042 /*
14043 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
14044 %                                                                             %
14045 %                                                                             %
14046 %                                                                             %
14047 +   X D i s p l a y I m a g e                                                 %
14048 %                                                                             %
14049 %                                                                             %
14050 %                                                                             %
14051 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
14052 %
14053 %  XDisplayImage() displays an image via X11.  A new image is created and
14054 %  returned if the user interactively transforms the displayed image.
14055 %
14056 %  The format of the XDisplayImage method is:
14057 %
14058 %      Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
14059 %        char **argv,int argc,Image **image,size_t *state,
14060 %        ExceptionInfo *exception)
14061 %
14062 %  A description of each parameter follows:
14063 %
14064 %    o nexus:  Method XDisplayImage returns an image when the
14065 %      user chooses 'Open Image' from the command menu or picks a tile
14066 %      from the image directory.  Otherwise a null image is returned.
14067 %
14068 %    o display: Specifies a connection to an X server;  returned from
14069 %      XOpenDisplay.
14070 %
14071 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
14072 %
14073 %    o argv: Specifies the application's argument list.
14074 %
14075 %    o argc: Specifies the number of arguments.
14076 %
14077 %    o image: Specifies an address to an address of an Image structure;
14078 %
14079 %    o exception: return any errors or warnings in this structure.
14080 %
14081 */
14082 MagickExport Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
14083   char **argv,int argc,Image **image,size_t *state,ExceptionInfo *exception)
14084 {
14085 #define MagnifySize  256  /* must be a power of 2 */
14086 #define MagickMenus  10
14087 #define MagickTitle  "Commands"
14088
14089   static const char
14090     *CommandMenu[] =
14091     {
14092       "File",
14093       "Edit",
14094       "View",
14095       "Transform",
14096       "Enhance",
14097       "Effects",
14098       "F/X",
14099       "Image Edit",
14100       "Miscellany",
14101       "Help",
14102       (char *) NULL
14103     },
14104     *FileMenu[] =
14105     {
14106       "Open...",
14107       "Next",
14108       "Former",
14109       "Select...",
14110       "Save...",
14111       "Print...",
14112       "Delete...",
14113       "New...",
14114       "Visual Directory...",
14115       "Quit",
14116       (char *) NULL
14117     },
14118     *EditMenu[] =
14119     {
14120       "Undo",
14121       "Redo",
14122       "Cut",
14123       "Copy",
14124       "Paste",
14125       (char *) NULL
14126     },
14127     *ViewMenu[] =
14128     {
14129       "Half Size",
14130       "Original Size",
14131       "Double Size",
14132       "Resize...",
14133       "Apply",
14134       "Refresh",
14135       "Restore",
14136       (char *) NULL
14137     },
14138     *TransformMenu[] =
14139     {
14140       "Crop",
14141       "Chop",
14142       "Flop",
14143       "Flip",
14144       "Rotate Right",
14145       "Rotate Left",
14146       "Rotate...",
14147       "Shear...",
14148       "Roll...",
14149       "Trim Edges",
14150       (char *) NULL
14151     },
14152     *EnhanceMenu[] =
14153     {
14154       "Hue...",
14155       "Saturation...",
14156       "Brightness...",
14157       "Gamma...",
14158       "Spiff",
14159       "Dull",
14160       "Contrast Stretch...",
14161       "Sigmoidal Contrast...",
14162       "Normalize",
14163       "Equalize",
14164       "Negate",
14165       "Grayscale",
14166       "Map...",
14167       "Quantize...",
14168       (char *) NULL
14169     },
14170     *EffectsMenu[] =
14171     {
14172       "Despeckle",
14173       "Emboss",
14174       "Reduce Noise",
14175       "Add Noise...",
14176       "Sharpen...",
14177       "Blur...",
14178       "Threshold...",
14179       "Edge Detect...",
14180       "Spread...",
14181       "Shade...",
14182       "Raise...",
14183       "Segment...",
14184       (char *) NULL
14185     },
14186     *FXMenu[] =
14187     {
14188       "Solarize...",
14189       "Sepia Tone...",
14190       "Swirl...",
14191       "Implode...",
14192       "Vignette...",
14193       "Wave...",
14194       "Oil Paint...",
14195       "Charcoal Draw...",
14196       (char *) NULL
14197     },
14198     *ImageEditMenu[] =
14199     {
14200       "Annotate...",
14201       "Draw...",
14202       "Color...",
14203       "Matte...",
14204       "Composite...",
14205       "Add Border...",
14206       "Add Frame...",
14207       "Comment...",
14208       "Launch...",
14209       "Region of Interest...",
14210       (char *) NULL
14211     },
14212     *MiscellanyMenu[] =
14213     {
14214       "Image Info",
14215       "Zoom Image",
14216       "Show Preview...",
14217       "Show Histogram",
14218       "Show Matte",
14219       "Background...",
14220       "Slide Show...",
14221       "Preferences...",
14222       (char *) NULL
14223     },
14224     *HelpMenu[] =
14225     {
14226       "Overview",
14227       "Browse Documentation",
14228       "About Display",
14229       (char *) NULL
14230     },
14231     *ShortCutsMenu[] =
14232     {
14233       "Next",
14234       "Former",
14235       "Open...",
14236       "Save...",
14237       "Print...",
14238       "Undo",
14239       "Restore",
14240       "Image Info",
14241       "Quit",
14242       (char *) NULL
14243     },
14244     *VirtualMenu[] =
14245     {
14246       "Image Info",
14247       "Print",
14248       "Next",
14249       "Quit",
14250       (char *) NULL
14251     };
14252
14253   static const char
14254     **Menus[MagickMenus] =
14255     {
14256       FileMenu,
14257       EditMenu,
14258       ViewMenu,
14259       TransformMenu,
14260       EnhanceMenu,
14261       EffectsMenu,
14262       FXMenu,
14263       ImageEditMenu,
14264       MiscellanyMenu,
14265       HelpMenu
14266     };
14267
14268   static CommandType
14269     CommandMenus[] =
14270     {
14271       NullCommand,
14272       NullCommand,
14273       NullCommand,
14274       NullCommand,
14275       NullCommand,
14276       NullCommand,
14277       NullCommand,
14278       NullCommand,
14279       NullCommand,
14280       NullCommand,
14281     },
14282     FileCommands[] =
14283     {
14284       OpenCommand,
14285       NextCommand,
14286       FormerCommand,
14287       SelectCommand,
14288       SaveCommand,
14289       PrintCommand,
14290       DeleteCommand,
14291       NewCommand,
14292       VisualDirectoryCommand,
14293       QuitCommand
14294     },
14295     EditCommands[] =
14296     {
14297       UndoCommand,
14298       RedoCommand,
14299       CutCommand,
14300       CopyCommand,
14301       PasteCommand
14302     },
14303     ViewCommands[] =
14304     {
14305       HalfSizeCommand,
14306       OriginalSizeCommand,
14307       DoubleSizeCommand,
14308       ResizeCommand,
14309       ApplyCommand,
14310       RefreshCommand,
14311       RestoreCommand
14312     },
14313     TransformCommands[] =
14314     {
14315       CropCommand,
14316       ChopCommand,
14317       FlopCommand,
14318       FlipCommand,
14319       RotateRightCommand,
14320       RotateLeftCommand,
14321       RotateCommand,
14322       ShearCommand,
14323       RollCommand,
14324       TrimCommand
14325     },
14326     EnhanceCommands[] =
14327     {
14328       HueCommand,
14329       SaturationCommand,
14330       BrightnessCommand,
14331       GammaCommand,
14332       SpiffCommand,
14333       DullCommand,
14334       ContrastStretchCommand,
14335       SigmoidalContrastCommand,
14336       NormalizeCommand,
14337       EqualizeCommand,
14338       NegateCommand,
14339       GrayscaleCommand,
14340       MapCommand,
14341       QuantizeCommand
14342     },
14343     EffectsCommands[] =
14344     {
14345       DespeckleCommand,
14346       EmbossCommand,
14347       ReduceNoiseCommand,
14348       AddNoiseCommand,
14349       SharpenCommand,
14350       BlurCommand,
14351       ThresholdCommand,
14352       EdgeDetectCommand,
14353       SpreadCommand,
14354       ShadeCommand,
14355       RaiseCommand,
14356       SegmentCommand
14357     },
14358     FXCommands[] =
14359     {
14360       SolarizeCommand,
14361       SepiaToneCommand,
14362       SwirlCommand,
14363       ImplodeCommand,
14364       VignetteCommand,
14365       WaveCommand,
14366       OilPaintCommand,
14367       CharcoalDrawCommand
14368     },
14369     ImageEditCommands[] =
14370     {
14371       AnnotateCommand,
14372       DrawCommand,
14373       ColorCommand,
14374       MatteCommand,
14375       CompositeCommand,
14376       AddBorderCommand,
14377       AddFrameCommand,
14378       CommentCommand,
14379       LaunchCommand,
14380       RegionofInterestCommand
14381     },
14382     MiscellanyCommands[] =
14383     {
14384       InfoCommand,
14385       ZoomCommand,
14386       ShowPreviewCommand,
14387       ShowHistogramCommand,
14388       ShowMatteCommand,
14389       BackgroundCommand,
14390       SlideShowCommand,
14391       PreferencesCommand
14392     },
14393     HelpCommands[] =
14394     {
14395       HelpCommand,
14396       BrowseDocumentationCommand,
14397       VersionCommand
14398     },
14399     ShortCutsCommands[] =
14400     {
14401       NextCommand,
14402       FormerCommand,
14403       OpenCommand,
14404       SaveCommand,
14405       PrintCommand,
14406       UndoCommand,
14407       RestoreCommand,
14408       InfoCommand,
14409       QuitCommand
14410     },
14411     VirtualCommands[] =
14412     {
14413       InfoCommand,
14414       PrintCommand,
14415       NextCommand,
14416       QuitCommand
14417     };
14418
14419   static CommandType
14420     *Commands[MagickMenus] =
14421     {
14422       FileCommands,
14423       EditCommands,
14424       ViewCommands,
14425       TransformCommands,
14426       EnhanceCommands,
14427       EffectsCommands,
14428       FXCommands,
14429       ImageEditCommands,
14430       MiscellanyCommands,
14431       HelpCommands
14432     };
14433
14434   char
14435     command[MagickPathExtent],
14436     *directory,
14437     geometry[MagickPathExtent],
14438     resource_name[MagickPathExtent];
14439
14440   CommandType
14441     command_type;
14442
14443   Image
14444     *display_image,
14445     *nexus;
14446
14447   int
14448     entry,
14449     id;
14450
14451   KeySym
14452     key_symbol;
14453
14454   MagickStatusType
14455     context_mask,
14456     status;
14457
14458   RectangleInfo
14459     geometry_info;
14460
14461   register int
14462     i;
14463
14464   static char
14465     working_directory[MagickPathExtent];
14466
14467   static XPoint
14468     vid_info;
14469
14470   static XWindowInfo
14471     *magick_windows[MaxXWindows];
14472
14473   static unsigned int
14474     number_windows;
14475
14476   struct stat
14477     attributes;
14478
14479   time_t
14480     timer,
14481     timestamp,
14482     update_time;
14483
14484   unsigned int
14485     height,
14486     width;
14487
14488   size_t
14489     delay;
14490
14491   WarningHandler
14492     warning_handler;
14493
14494   Window
14495     root_window;
14496
14497   XClassHint
14498     *class_hints;
14499
14500   XEvent
14501     event;
14502
14503   XFontStruct
14504     *font_info;
14505
14506   XGCValues
14507     context_values;
14508
14509   XPixelInfo
14510     *icon_pixel,
14511     *pixel;
14512
14513   XResourceInfo
14514     *icon_resources;
14515
14516   XStandardColormap
14517     *icon_map,
14518     *map_info;
14519
14520   XVisualInfo
14521     *icon_visual,
14522     *visual_info;
14523
14524   XWindowChanges
14525     window_changes;
14526
14527   XWindows
14528     *windows;
14529
14530   XWMHints
14531     *manager_hints;
14532
14533   assert(image != (Image **) NULL);
14534   assert((*image)->signature == MagickCoreSignature);
14535   if ((*image)->debug != MagickFalse )
14536     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*image)->filename);
14537   display_image=(*image);
14538   warning_handler=(WarningHandler) NULL;
14539   windows=XSetWindows((XWindows *) ~0);
14540   if (windows != (XWindows *) NULL)
14541     {
14542       int
14543         status;
14544
14545       if (*working_directory == '\0')
14546         (void) CopyMagickString(working_directory,".",MagickPathExtent);
14547       status=chdir(working_directory);
14548       if (status == -1)
14549         (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
14550           "UnableToOpenFile","%s",working_directory);
14551       warning_handler=resource_info->display_warnings ?
14552         SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
14553       warning_handler=resource_info->display_warnings ?
14554         SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
14555     }
14556   else
14557     {
14558       /*
14559         Allocate windows structure.
14560       */
14561       resource_info->colors=display_image->colors;
14562       windows=XSetWindows(XInitializeWindows(display,resource_info));
14563       if (windows == (XWindows *) NULL)
14564         ThrowXWindowFatalException(XServerFatalError,"UnableToCreateWindow",
14565           (*image)->filename);
14566       /*
14567         Initialize window id's.
14568       */
14569       number_windows=0;
14570       magick_windows[number_windows++]=(&windows->icon);
14571       magick_windows[number_windows++]=(&windows->backdrop);
14572       magick_windows[number_windows++]=(&windows->image);
14573       magick_windows[number_windows++]=(&windows->info);
14574       magick_windows[number_windows++]=(&windows->command);
14575       magick_windows[number_windows++]=(&windows->widget);
14576       magick_windows[number_windows++]=(&windows->popup);
14577       magick_windows[number_windows++]=(&windows->magnify);
14578       magick_windows[number_windows++]=(&windows->pan);
14579       for (i=0; i < (int) number_windows; i++)
14580         magick_windows[i]->id=(Window) NULL;
14581       vid_info.x=0;
14582       vid_info.y=0;
14583     }
14584   /*
14585     Initialize font info.
14586   */
14587   if (windows->font_info != (XFontStruct *) NULL)
14588     (void) XFreeFont(display,windows->font_info);
14589   windows->font_info=XBestFont(display,resource_info,MagickFalse);
14590   if (windows->font_info == (XFontStruct *) NULL)
14591     ThrowXWindowFatalException(XServerFatalError,"UnableToLoadFont",
14592       resource_info->font);
14593   /*
14594     Initialize Standard Colormap.
14595   */
14596   map_info=windows->map_info;
14597   icon_map=windows->icon_map;
14598   visual_info=windows->visual_info;
14599   icon_visual=windows->icon_visual;
14600   pixel=windows->pixel_info;
14601   icon_pixel=windows->icon_pixel;
14602   font_info=windows->font_info;
14603   icon_resources=windows->icon_resources;
14604   class_hints=windows->class_hints;
14605   manager_hints=windows->manager_hints;
14606   root_window=XRootWindow(display,visual_info->screen);
14607   nexus=NewImageList();
14608   if (display_image->debug != MagickFalse )
14609     {
14610       (void) LogMagickEvent(X11Event,GetMagickModule(),
14611         "Image: %s[%.20g] %.20gx%.20g ",display_image->filename,
14612         (double) display_image->scene,(double) display_image->columns,
14613         (double) display_image->rows);
14614       if (display_image->colors != 0)
14615         (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
14616           display_image->colors);
14617       (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",
14618         display_image->magick);
14619     }
14620   XMakeStandardColormap(display,visual_info,resource_info,display_image,
14621     map_info,pixel,exception);
14622   display_image->taint=MagickFalse;
14623   /*
14624     Initialize graphic context.
14625   */
14626   windows->context.id=(Window) NULL;
14627   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14628     resource_info,&windows->context);
14629   (void) CloneString(&class_hints->res_name,resource_info->client_name);
14630   (void) CloneString(&class_hints->res_class,resource_info->client_name);
14631   class_hints->res_class[0]=(char) toupper((int) class_hints->res_class[0]);
14632   manager_hints->flags=InputHint | StateHint;
14633   manager_hints->input=MagickFalse;
14634   manager_hints->initial_state=WithdrawnState;
14635   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14636     &windows->context);
14637   if (display_image->debug != MagickFalse )
14638     (void) LogMagickEvent(X11Event,GetMagickModule(),
14639       "Window id: 0x%lx (context)",windows->context.id);
14640   context_values.background=pixel->background_color.pixel;
14641   context_values.font=font_info->fid;
14642   context_values.foreground=pixel->foreground_color.pixel;
14643   context_values.graphics_exposures=MagickFalse;
14644   context_mask=(MagickStatusType)
14645     (GCBackground | GCFont | GCForeground | GCGraphicsExposures);
14646   if (pixel->annotate_context != (GC) NULL)
14647     (void) XFreeGC(display,pixel->annotate_context);
14648   pixel->annotate_context=XCreateGC(display,windows->context.id,
14649     context_mask,&context_values);
14650   if (pixel->annotate_context == (GC) NULL)
14651     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14652       display_image->filename);
14653   context_values.background=pixel->depth_color.pixel;
14654   if (pixel->widget_context != (GC) NULL)
14655     (void) XFreeGC(display,pixel->widget_context);
14656   pixel->widget_context=XCreateGC(display,windows->context.id,context_mask,
14657     &context_values);
14658   if (pixel->widget_context == (GC) NULL)
14659     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14660       display_image->filename);
14661   context_values.background=pixel->foreground_color.pixel;
14662   context_values.foreground=pixel->background_color.pixel;
14663   context_values.plane_mask=context_values.background ^
14664     context_values.foreground;
14665   if (pixel->highlight_context != (GC) NULL)
14666     (void) XFreeGC(display,pixel->highlight_context);
14667   pixel->highlight_context=XCreateGC(display,windows->context.id,
14668     (size_t) (context_mask | GCPlaneMask),&context_values);
14669   if (pixel->highlight_context == (GC) NULL)
14670     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14671       display_image->filename);
14672   (void) XDestroyWindow(display,windows->context.id);
14673   /*
14674     Initialize icon window.
14675   */
14676   XGetWindowInfo(display,icon_visual,icon_map,icon_pixel,(XFontStruct *) NULL,
14677     icon_resources,&windows->icon);
14678   windows->icon.geometry=resource_info->icon_geometry;
14679   XBestIconSize(display,&windows->icon,display_image);
14680   windows->icon.attributes.colormap=XDefaultColormap(display,
14681     icon_visual->screen);
14682   windows->icon.attributes.event_mask=ExposureMask | StructureNotifyMask;
14683   manager_hints->flags=InputHint | StateHint;
14684   manager_hints->input=MagickFalse;
14685   manager_hints->initial_state=IconicState;
14686   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14687     &windows->icon);
14688   if (display_image->debug != MagickFalse )
14689     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (icon)",
14690       windows->icon.id);
14691   /*
14692     Initialize graphic context for icon window.
14693   */
14694   if (icon_pixel->annotate_context != (GC) NULL)
14695     (void) XFreeGC(display,icon_pixel->annotate_context);
14696   context_values.background=icon_pixel->background_color.pixel;
14697   context_values.foreground=icon_pixel->foreground_color.pixel;
14698   icon_pixel->annotate_context=XCreateGC(display,windows->icon.id,
14699     (size_t) (GCBackground | GCForeground),&context_values);
14700   if (icon_pixel->annotate_context == (GC) NULL)
14701     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14702       display_image->filename);
14703   windows->icon.annotate_context=icon_pixel->annotate_context;
14704   /*
14705     Initialize Image window.
14706   */
14707   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14708     &windows->image);
14709   windows->image.shape=MagickTrue;  /* non-rectangular shape hint */
14710   if (resource_info->use_shared_memory == MagickFalse)
14711     windows->image.shared_memory=MagickFalse;
14712   if ((resource_info->title != (char *) NULL) && !(*state & MontageImageState))
14713     {
14714       char
14715         *title;
14716
14717       title=InterpretImageProperties(resource_info->image_info,display_image,
14718         resource_info->title,exception);
14719       (void) CopyMagickString(windows->image.name,title,MagickPathExtent);
14720       (void) CopyMagickString(windows->image.icon_name,title,MagickPathExtent);
14721       title=DestroyString(title);
14722     }
14723   else
14724     {
14725       char
14726         filename[MagickPathExtent];
14727
14728       /*
14729         Window name is the base of the filename.
14730       */
14731       GetPathComponent(display_image->magick_filename,TailPath,filename);
14732       if (display_image->scene == 0)
14733         (void) FormatLocaleString(windows->image.name,MagickPathExtent,
14734           "%s: %s",MagickPackageName,filename);
14735       else
14736         (void) FormatLocaleString(windows->image.name,MagickPathExtent,
14737           "%s: %s[scene: %.20g frames: %.20g]",MagickPackageName,filename,
14738           (double) display_image->scene,(double) GetImageListLength(
14739           display_image));
14740       (void) CopyMagickString(windows->image.icon_name,filename,
14741         MagickPathExtent);
14742     }
14743   if (resource_info->immutable)
14744     windows->image.immutable=MagickTrue;
14745   windows->image.use_pixmap=resource_info->use_pixmap;
14746   windows->image.geometry=resource_info->image_geometry;
14747   (void) FormatLocaleString(geometry,MagickPathExtent,"%ux%u+0+0>!",
14748     XDisplayWidth(display,visual_info->screen),
14749     XDisplayHeight(display,visual_info->screen));
14750   geometry_info.width=display_image->columns;
14751   geometry_info.height=display_image->rows;
14752   geometry_info.x=0;
14753   geometry_info.y=0;
14754   (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
14755     &geometry_info.width,&geometry_info.height);
14756   windows->image.width=(unsigned int) geometry_info.width;
14757   windows->image.height=(unsigned int) geometry_info.height;
14758   windows->image.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14759     ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14760     KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14761     PropertyChangeMask | StructureNotifyMask | SubstructureNotifyMask;
14762   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14763     resource_info,&windows->backdrop);
14764   if ((resource_info->backdrop) || (windows->backdrop.id != (Window) NULL))
14765     {
14766       /*
14767         Initialize backdrop window.
14768       */
14769       windows->backdrop.x=0;
14770       windows->backdrop.y=0;
14771       (void) CloneString(&windows->backdrop.name,"Backdrop");
14772       windows->backdrop.flags=(size_t) (USSize | USPosition);
14773       windows->backdrop.width=(unsigned int)
14774         XDisplayWidth(display,visual_info->screen);
14775       windows->backdrop.height=(unsigned int)
14776         XDisplayHeight(display,visual_info->screen);
14777       windows->backdrop.border_width=0;
14778       windows->backdrop.immutable=MagickTrue;
14779       windows->backdrop.attributes.do_not_propagate_mask=ButtonPressMask |
14780         ButtonReleaseMask;
14781       windows->backdrop.attributes.event_mask=ButtonPressMask | KeyPressMask |
14782         StructureNotifyMask;
14783       manager_hints->flags=IconWindowHint | InputHint | StateHint;
14784       manager_hints->icon_window=windows->icon.id;
14785       manager_hints->input=MagickTrue;
14786       manager_hints->initial_state=resource_info->iconic ? IconicState :
14787         NormalState;
14788       XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14789         &windows->backdrop);
14790       if (display_image->debug != MagickFalse )
14791         (void) LogMagickEvent(X11Event,GetMagickModule(),
14792           "Window id: 0x%lx (backdrop)",windows->backdrop.id);
14793       (void) XMapWindow(display,windows->backdrop.id);
14794       (void) XClearWindow(display,windows->backdrop.id);
14795       if (windows->image.id != (Window) NULL)
14796         {
14797           (void) XDestroyWindow(display,windows->image.id);
14798           windows->image.id=(Window) NULL;
14799         }
14800       /*
14801         Position image in the center the backdrop.
14802       */
14803       windows->image.flags|=USPosition;
14804       windows->image.x=(XDisplayWidth(display,visual_info->screen)/2)-
14805         (windows->image.width/2);
14806       windows->image.y=(XDisplayHeight(display,visual_info->screen)/2)-
14807         (windows->image.height/2);
14808     }
14809   manager_hints->flags=IconWindowHint | InputHint | StateHint;
14810   manager_hints->icon_window=windows->icon.id;
14811   manager_hints->input=MagickTrue;
14812   manager_hints->initial_state=resource_info->iconic ? IconicState :
14813     NormalState;
14814   if (windows->group_leader.id != (Window) NULL)
14815     {
14816       /*
14817         Follow the leader.
14818       */
14819       manager_hints->flags|=WindowGroupHint;
14820       manager_hints->window_group=windows->group_leader.id;
14821       (void) XSelectInput(display,windows->group_leader.id,StructureNotifyMask);
14822       if (display_image->debug != MagickFalse )
14823         (void) LogMagickEvent(X11Event,GetMagickModule(),
14824           "Window id: 0x%lx (group leader)",windows->group_leader.id);
14825     }
14826   XMakeWindow(display,
14827     (Window) (resource_info->backdrop ? windows->backdrop.id : root_window),
14828     argv,argc,class_hints,manager_hints,&windows->image);
14829   (void) XChangeProperty(display,windows->image.id,windows->im_protocols,
14830     XA_STRING,8,PropModeReplace,(unsigned char *) NULL,0);
14831   if (windows->group_leader.id != (Window) NULL)
14832     (void) XSetTransientForHint(display,windows->image.id,
14833       windows->group_leader.id);
14834   if (display_image->debug != MagickFalse )
14835     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (image)",
14836       windows->image.id);
14837   /*
14838     Initialize Info widget.
14839   */
14840   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14841     &windows->info);
14842   (void) CloneString(&windows->info.name,"Info");
14843   (void) CloneString(&windows->info.icon_name,"Info");
14844   windows->info.border_width=1;
14845   windows->info.x=2;
14846   windows->info.y=2;
14847   windows->info.flags|=PPosition;
14848   windows->info.attributes.win_gravity=UnmapGravity;
14849   windows->info.attributes.event_mask=ButtonPressMask | ExposureMask |
14850     StructureNotifyMask;
14851   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14852   manager_hints->input=MagickFalse;
14853   manager_hints->initial_state=NormalState;
14854   manager_hints->window_group=windows->image.id;
14855   XMakeWindow(display,windows->image.id,argv,argc,class_hints,manager_hints,
14856     &windows->info);
14857   windows->info.highlight_stipple=XCreateBitmapFromData(display,
14858     windows->info.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14859   windows->info.shadow_stipple=XCreateBitmapFromData(display,
14860     windows->info.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14861   (void) XSetTransientForHint(display,windows->info.id,windows->image.id);
14862   if (windows->image.mapped != MagickFalse )
14863     (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14864   if (display_image->debug != MagickFalse )
14865     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (info)",
14866       windows->info.id);
14867   /*
14868     Initialize Command widget.
14869   */
14870   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14871     resource_info,&windows->command);
14872   windows->command.data=MagickMenus;
14873   (void) XCommandWidget(display,windows,CommandMenu,(XEvent *) NULL);
14874   (void) FormatLocaleString(resource_name,MagickPathExtent,"%s.command",
14875     resource_info->client_name);
14876   windows->command.geometry=XGetResourceClass(resource_info->resource_database,
14877     resource_name,"geometry",(char *) NULL);
14878   (void) CloneString(&windows->command.name,MagickTitle);
14879   windows->command.border_width=0;
14880   windows->command.flags|=PPosition;
14881   windows->command.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14882     ButtonReleaseMask | EnterWindowMask | ExposureMask | LeaveWindowMask |
14883     OwnerGrabButtonMask | StructureNotifyMask;
14884   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14885   manager_hints->input=MagickTrue;
14886   manager_hints->initial_state=NormalState;
14887   manager_hints->window_group=windows->image.id;
14888   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14889     &windows->command);
14890   windows->command.highlight_stipple=XCreateBitmapFromData(display,
14891     windows->command.id,(char *) HighlightBitmap,HighlightWidth,
14892     HighlightHeight);
14893   windows->command.shadow_stipple=XCreateBitmapFromData(display,
14894     windows->command.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14895   (void) XSetTransientForHint(display,windows->command.id,windows->image.id);
14896   if (windows->command.mapped != MagickFalse )
14897     (void) XMapRaised(display,windows->command.id);
14898   if (display_image->debug != MagickFalse )
14899     (void) LogMagickEvent(X11Event,GetMagickModule(),
14900       "Window id: 0x%lx (command)",windows->command.id);
14901   /*
14902     Initialize Widget window.
14903   */
14904   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14905     resource_info,&windows->widget);
14906   (void) FormatLocaleString(resource_name,MagickPathExtent,"%s.widget",
14907     resource_info->client_name);
14908   windows->widget.geometry=XGetResourceClass(resource_info->resource_database,
14909     resource_name,"geometry",(char *) NULL);
14910   windows->widget.border_width=0;
14911   windows->widget.flags|=PPosition;
14912   windows->widget.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14913     ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14914     KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14915     StructureNotifyMask;
14916   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14917   manager_hints->input=MagickTrue;
14918   manager_hints->initial_state=NormalState;
14919   manager_hints->window_group=windows->image.id;
14920   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14921     &windows->widget);
14922   windows->widget.highlight_stipple=XCreateBitmapFromData(display,
14923     windows->widget.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14924   windows->widget.shadow_stipple=XCreateBitmapFromData(display,
14925     windows->widget.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14926   (void) XSetTransientForHint(display,windows->widget.id,windows->image.id);
14927   if (display_image->debug != MagickFalse )
14928     (void) LogMagickEvent(X11Event,GetMagickModule(),
14929       "Window id: 0x%lx (widget)",windows->widget.id);
14930   /*
14931     Initialize popup window.
14932   */
14933   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14934     resource_info,&windows->popup);
14935   windows->popup.border_width=0;
14936   windows->popup.flags|=PPosition;
14937   windows->popup.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14938     ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14939     KeyReleaseMask | LeaveWindowMask | StructureNotifyMask;
14940   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14941   manager_hints->input=MagickTrue;
14942   manager_hints->initial_state=NormalState;
14943   manager_hints->window_group=windows->image.id;
14944   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14945     &windows->popup);
14946   windows->popup.highlight_stipple=XCreateBitmapFromData(display,
14947     windows->popup.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14948   windows->popup.shadow_stipple=XCreateBitmapFromData(display,
14949     windows->popup.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14950   (void) XSetTransientForHint(display,windows->popup.id,windows->image.id);
14951   if (display_image->debug != MagickFalse )
14952     (void) LogMagickEvent(X11Event,GetMagickModule(),
14953       "Window id: 0x%lx (pop up)",windows->popup.id);
14954   /*
14955     Initialize Magnify window and cursor.
14956   */
14957   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14958     resource_info,&windows->magnify);
14959   if (resource_info->use_shared_memory == MagickFalse)
14960     windows->magnify.shared_memory=MagickFalse;
14961   (void) FormatLocaleString(resource_name,MagickPathExtent,"%s.magnify",
14962     resource_info->client_name);
14963   windows->magnify.geometry=XGetResourceClass(resource_info->resource_database,
14964     resource_name,"geometry",(char *) NULL);
14965   (void) FormatLocaleString(windows->magnify.name,MagickPathExtent,"Magnify %uX",
14966     resource_info->magnify);
14967   if (windows->magnify.cursor != (Cursor) NULL)
14968     (void) XFreeCursor(display,windows->magnify.cursor);
14969   windows->magnify.cursor=XMakeCursor(display,windows->image.id,
14970     map_info->colormap,resource_info->background_color,
14971     resource_info->foreground_color);
14972   if (windows->magnify.cursor == (Cursor) NULL)
14973     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateCursor",
14974       display_image->filename);
14975   windows->magnify.width=MagnifySize;
14976   windows->magnify.height=MagnifySize;
14977   windows->magnify.flags|=PPosition;
14978   windows->magnify.min_width=MagnifySize;
14979   windows->magnify.min_height=MagnifySize;
14980   windows->magnify.width_inc=MagnifySize;
14981   windows->magnify.height_inc=MagnifySize;
14982   windows->magnify.data=resource_info->magnify;
14983   windows->magnify.attributes.cursor=windows->magnify.cursor;
14984   windows->magnify.attributes.event_mask=ButtonPressMask | ButtonReleaseMask |
14985     ExposureMask | KeyPressMask | KeyReleaseMask | OwnerGrabButtonMask |
14986     StructureNotifyMask;
14987   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14988   manager_hints->input=MagickTrue;
14989   manager_hints->initial_state=NormalState;
14990   manager_hints->window_group=windows->image.id;
14991   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14992     &windows->magnify);
14993   if (display_image->debug != MagickFalse )
14994     (void) LogMagickEvent(X11Event,GetMagickModule(),
14995       "Window id: 0x%lx (magnify)",windows->magnify.id);
14996   (void) XSetTransientForHint(display,windows->magnify.id,windows->image.id);
14997   /*
14998     Initialize panning window.
14999   */
15000   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
15001     resource_info,&windows->pan);
15002   (void) CloneString(&windows->pan.name,"Pan Icon");
15003   windows->pan.width=windows->icon.width;
15004   windows->pan.height=windows->icon.height;
15005   (void) FormatLocaleString(resource_name,MagickPathExtent,"%s.pan",
15006     resource_info->client_name);
15007   windows->pan.geometry=XGetResourceClass(resource_info->resource_database,
15008     resource_name,"geometry",(char *) NULL);
15009   (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
15010     &windows->pan.width,&windows->pan.height);
15011   windows->pan.flags|=PPosition;
15012   windows->pan.immutable=MagickTrue;
15013   windows->pan.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
15014     ButtonReleaseMask | ExposureMask | KeyPressMask | KeyReleaseMask |
15015     StructureNotifyMask;
15016   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
15017   manager_hints->input=MagickFalse;
15018   manager_hints->initial_state=NormalState;
15019   manager_hints->window_group=windows->image.id;
15020   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
15021     &windows->pan);
15022   if (display_image->debug != MagickFalse )
15023     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (pan)",
15024       windows->pan.id);
15025   (void) XSetTransientForHint(display,windows->pan.id,windows->image.id);
15026   if (windows->info.mapped != MagickFalse )
15027     (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15028   if ((windows->image.mapped == MagickFalse) ||
15029       (windows->backdrop.id != (Window) NULL))
15030     (void) XMapWindow(display,windows->image.id);
15031   /*
15032     Set our progress monitor and warning handlers.
15033   */
15034   if (warning_handler == (WarningHandler) NULL)
15035     {
15036       warning_handler=resource_info->display_warnings ?
15037         SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
15038       warning_handler=resource_info->display_warnings ?
15039         SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
15040     }
15041   /*
15042     Initialize Image and Magnify X images.
15043   */
15044   windows->image.x=0;
15045   windows->image.y=0;
15046   windows->magnify.shape=MagickFalse;
15047   width=(unsigned int) display_image->columns;
15048   height=(unsigned int) display_image->rows;
15049   if ((display_image->columns != width) || (display_image->rows != height))
15050     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15051       display_image->filename);
15052   status=XMakeImage(display,resource_info,&windows->image,display_image,
15053     width,height,exception);
15054   if (status == MagickFalse)
15055     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15056       display_image->filename);
15057   status=XMakeImage(display,resource_info,&windows->magnify,(Image *) NULL,
15058     windows->magnify.width,windows->magnify.height,exception);
15059   if (status == MagickFalse)
15060     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15061       display_image->filename);
15062   if (windows->magnify.mapped != MagickFalse )
15063     (void) XMapRaised(display,windows->magnify.id);
15064   if (windows->pan.mapped != MagickFalse )
15065     (void) XMapRaised(display,windows->pan.id);
15066   windows->image.window_changes.width=(int) display_image->columns;
15067   windows->image.window_changes.height=(int) display_image->rows;
15068   (void) XConfigureImage(display,resource_info,windows,display_image,exception);
15069   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15070   (void) XSync(display,MagickFalse);
15071   /*
15072     Respond to events.
15073   */
15074   delay=display_image->delay/MagickMax(display_image->ticks_per_second,1L);
15075   timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15076   update_time=0;
15077   if (resource_info->update != MagickFalse )
15078     {
15079       MagickBooleanType
15080         status;
15081
15082       /*
15083         Determine when file data was last modified.
15084       */
15085       status=GetPathAttributes(display_image->filename,&attributes);
15086       if (status != MagickFalse )
15087         update_time=attributes.st_mtime;
15088     }
15089   *state&=(~FormerImageState);
15090   *state&=(~MontageImageState);
15091   *state&=(~NextImageState);
15092   do
15093   {
15094     /*
15095       Handle a window event.
15096     */
15097     if (windows->image.mapped != MagickFalse )
15098       if ((display_image->delay != 0) || (resource_info->update != 0))
15099         {
15100           if (timer < time((time_t *) NULL))
15101             {
15102               if (resource_info->update == MagickFalse)
15103                 *state|=NextImageState | ExitState;
15104               else
15105                 {
15106                   MagickBooleanType
15107                     status;
15108
15109                   /*
15110                     Determine if image file was modified.
15111                   */
15112                   status=GetPathAttributes(display_image->filename,&attributes);
15113                   if (status != MagickFalse )
15114                     if (update_time != attributes.st_mtime)
15115                       {
15116                         /*
15117                           Redisplay image.
15118                         */
15119                         (void) FormatLocaleString(
15120                           resource_info->image_info->filename,MagickPathExtent,
15121                           "%s:%s",display_image->magick,
15122                           display_image->filename);
15123                         nexus=ReadImage(resource_info->image_info,exception);
15124                         if (nexus != (Image *) NULL)
15125                           *state|=NextImageState | ExitState;
15126                       }
15127                   delay=display_image->delay/MagickMax(
15128                     display_image->ticks_per_second,1L);
15129                   timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15130                 }
15131             }
15132           if (XEventsQueued(display,QueuedAfterFlush) == 0)
15133             {
15134               /*
15135                 Do not block if delay > 0.
15136               */
15137               XDelay(display,SuspendTime << 2);
15138               continue;
15139             }
15140         }
15141     timestamp=time((time_t *) NULL);
15142     (void) XNextEvent(display,&event);
15143     if ((windows->image.stasis == MagickFalse) ||
15144         (windows->magnify.stasis == MagickFalse))
15145       {
15146         if ((time((time_t *) NULL)-timestamp) > 0)
15147           {
15148             windows->image.stasis=MagickTrue;
15149             windows->magnify.stasis=MagickTrue;
15150           }
15151       }
15152     if (event.xany.window == windows->command.id)
15153       {
15154         /*
15155           Select a command from the Command widget.
15156         */
15157         id=XCommandWidget(display,windows,CommandMenu,&event);
15158         if (id < 0)
15159           continue;
15160         (void) CopyMagickString(command,CommandMenu[id],MagickPathExtent);
15161         command_type=CommandMenus[id];
15162         if (id < MagickMenus)
15163           {
15164             /*
15165               Select a command from a pop-up menu.
15166             */
15167             entry=XMenuWidget(display,windows,CommandMenu[id],Menus[id],
15168               command);
15169             if (entry < 0)
15170               continue;
15171             (void) CopyMagickString(command,Menus[id][entry],MagickPathExtent);
15172             command_type=Commands[id][entry];
15173           }
15174         if (command_type != NullCommand)
15175           nexus=XMagickCommand(display,resource_info,windows,command_type,
15176             &display_image,exception);
15177         continue;
15178       }
15179     switch (event.type)
15180     {
15181       case ButtonPress:
15182       {
15183         if (display_image->debug != MagickFalse )
15184           (void) LogMagickEvent(X11Event,GetMagickModule(),
15185             "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
15186             event.xbutton.button,event.xbutton.x,event.xbutton.y);
15187         if ((event.xbutton.button == Button3) &&
15188             (event.xbutton.state & Mod1Mask))
15189           {
15190             /*
15191               Convert Alt-Button3 to Button2.
15192             */
15193             event.xbutton.button=Button2;
15194             event.xbutton.state&=(~Mod1Mask);
15195           }
15196         if (event.xbutton.window == windows->backdrop.id)
15197           {
15198             (void) XSetInputFocus(display,event.xbutton.window,RevertToParent,
15199               event.xbutton.time);
15200             break;
15201           }
15202         if (event.xbutton.window == windows->image.id)
15203           {
15204             switch (event.xbutton.button)
15205             {
15206               case Button1:
15207               {
15208                 if (resource_info->immutable)
15209                   {
15210                     /*
15211                       Select a command from the Virtual menu.
15212                     */
15213                     entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15214                       command);
15215                     if (entry >= 0)
15216                       nexus=XMagickCommand(display,resource_info,windows,
15217                         VirtualCommands[entry],&display_image,exception);
15218                     break;
15219                   }
15220                 /*
15221                   Map/unmap Command widget.
15222                 */
15223                 if (windows->command.mapped != MagickFalse )
15224                   (void) XWithdrawWindow(display,windows->command.id,
15225                     windows->command.screen);
15226                 else
15227                   {
15228                     (void) XCommandWidget(display,windows,CommandMenu,
15229                       (XEvent *) NULL);
15230                     (void) XMapRaised(display,windows->command.id);
15231                   }
15232                 break;
15233               }
15234               case Button2:
15235               {
15236                 /*
15237                   User pressed the image magnify button.
15238                 */
15239                 (void) XMagickCommand(display,resource_info,windows,ZoomCommand,
15240                   &display_image,exception);
15241                 XMagnifyImage(display,windows,&event,exception);
15242                 break;
15243               }
15244               case Button3:
15245               {
15246                 if (resource_info->immutable)
15247                   {
15248                     /*
15249                       Select a command from the Virtual menu.
15250                     */
15251                     entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15252                       command);
15253                     if (entry >= 0)
15254                       nexus=XMagickCommand(display,resource_info,windows,
15255                         VirtualCommands[entry],&display_image,exception);
15256                     break;
15257                   }
15258                 if (display_image->montage != (char *) NULL)
15259                   {
15260                     /*
15261                       Open or delete a tile from a visual image directory.
15262                     */
15263                     nexus=XTileImage(display,resource_info,windows,
15264                       display_image,&event,exception);
15265                     if (nexus != (Image *) NULL)
15266                       *state|=MontageImageState | NextImageState | ExitState;
15267                     vid_info.x=(short int) windows->image.x;
15268                     vid_info.y=(short int) windows->image.y;
15269                     break;
15270                   }
15271                 /*
15272                   Select a command from the Short Cuts menu.
15273                 */
15274                 entry=XMenuWidget(display,windows,"Short Cuts",ShortCutsMenu,
15275                   command);
15276                 if (entry >= 0)
15277                   nexus=XMagickCommand(display,resource_info,windows,
15278                     ShortCutsCommands[entry],&display_image,exception);
15279                 break;
15280               }
15281               case Button4:
15282               {
15283                 /*
15284                   Wheel up.
15285                 */
15286                 XTranslateImage(display,windows,*image,XK_Up);
15287                 break;
15288               }
15289               case Button5:
15290               {
15291                 /*
15292                   Wheel down.
15293                 */
15294                 XTranslateImage(display,windows,*image,XK_Down);
15295                 break;
15296               }
15297               default:
15298                 break;
15299             }
15300             break;
15301           }
15302         if (event.xbutton.window == windows->magnify.id)
15303           {
15304             int
15305               factor;
15306
15307             static const char
15308               *MagnifyMenu[] =
15309               {
15310                 "2",
15311                 "4",
15312                 "5",
15313                 "6",
15314                 "7",
15315                 "8",
15316                 "9",
15317                 "3",
15318                 (char *) NULL,
15319               };
15320
15321             static KeySym
15322               MagnifyCommands[] =
15323               {
15324                 XK_2,
15325                 XK_4,
15326                 XK_5,
15327                 XK_6,
15328                 XK_7,
15329                 XK_8,
15330                 XK_9,
15331                 XK_3
15332               };
15333
15334             /*
15335               Select a magnify factor from the pop-up menu.
15336             */
15337             factor=XMenuWidget(display,windows,"Magnify",MagnifyMenu,command);
15338             if (factor >= 0)
15339               XMagnifyWindowCommand(display,windows,0,MagnifyCommands[factor],
15340                 exception);
15341             break;
15342           }
15343         if (event.xbutton.window == windows->pan.id)
15344           {
15345             switch (event.xbutton.button)
15346             {
15347               case Button4:
15348               {
15349                 /*
15350                   Wheel up.
15351                 */
15352                 XTranslateImage(display,windows,*image,XK_Up);
15353                 break;
15354               }
15355               case Button5:
15356               {
15357                 /*
15358                   Wheel down.
15359                 */
15360                 XTranslateImage(display,windows,*image,XK_Down);
15361                 break;
15362               }
15363               default:
15364               {
15365                 XPanImage(display,windows,&event,exception);
15366                 break;
15367               }
15368             }
15369             break;
15370           }
15371         delay=display_image->delay/MagickMax(display_image->ticks_per_second,
15372           1L);
15373         timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15374         break;
15375       }
15376       case ButtonRelease:
15377       {
15378         if (display_image->debug != MagickFalse )
15379           (void) LogMagickEvent(X11Event,GetMagickModule(),
15380             "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
15381             event.xbutton.button,event.xbutton.x,event.xbutton.y);
15382         break;
15383       }
15384       case ClientMessage:
15385       {
15386         if (display_image->debug != MagickFalse )
15387           (void) LogMagickEvent(X11Event,GetMagickModule(),
15388             "Client Message: 0x%lx 0x%lx %d 0x%lx",event.xclient.window,
15389             event.xclient.message_type,event.xclient.format,(unsigned long)
15390             event.xclient.data.l[0]);
15391         if (event.xclient.message_type == windows->im_protocols)
15392           {
15393             if (*event.xclient.data.l == (long) windows->im_update_widget)
15394               {
15395                 (void) CloneString(&windows->command.name,MagickTitle);
15396                 windows->command.data=MagickMenus;
15397                 (void) XCommandWidget(display,windows,CommandMenu,
15398                   (XEvent *) NULL);
15399                 break;
15400               }
15401             if (*event.xclient.data.l == (long) windows->im_update_colormap)
15402               {
15403                 /*
15404                   Update graphic context and window colormap.
15405                 */
15406                 for (i=0; i < (int) number_windows; i++)
15407                 {
15408                   if (magick_windows[i]->id == windows->icon.id)
15409                     continue;
15410                   context_values.background=pixel->background_color.pixel;
15411                   context_values.foreground=pixel->foreground_color.pixel;
15412                   (void) XChangeGC(display,magick_windows[i]->annotate_context,
15413                     context_mask,&context_values);
15414                   (void) XChangeGC(display,magick_windows[i]->widget_context,
15415                     context_mask,&context_values);
15416                   context_values.background=pixel->foreground_color.pixel;
15417                   context_values.foreground=pixel->background_color.pixel;
15418                   context_values.plane_mask=context_values.background ^
15419                     context_values.foreground;
15420                   (void) XChangeGC(display,magick_windows[i]->highlight_context,
15421                     (size_t) (context_mask | GCPlaneMask),
15422                     &context_values);
15423                   magick_windows[i]->attributes.background_pixel=
15424                     pixel->background_color.pixel;
15425                   magick_windows[i]->attributes.border_pixel=
15426                     pixel->border_color.pixel;
15427                   magick_windows[i]->attributes.colormap=map_info->colormap;
15428                   (void) XChangeWindowAttributes(display,magick_windows[i]->id,
15429                     (unsigned long) magick_windows[i]->mask,
15430                     &magick_windows[i]->attributes);
15431                 }
15432                 if (windows->pan.mapped != MagickFalse )
15433                   {
15434                     (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
15435                       windows->pan.pixmap);
15436                     (void) XClearWindow(display,windows->pan.id);
15437                     XDrawPanRectangle(display,windows);
15438                   }
15439                 if (windows->backdrop.id != (Window) NULL)
15440                   (void) XInstallColormap(display,map_info->colormap);
15441                 break;
15442               }
15443             if (*event.xclient.data.l == (long) windows->im_former_image)
15444               {
15445                 *state|=FormerImageState | ExitState;
15446                 break;
15447               }
15448             if (*event.xclient.data.l == (long) windows->im_next_image)
15449               {
15450                 *state|=NextImageState | ExitState;
15451                 break;
15452               }
15453             if (*event.xclient.data.l == (long) windows->im_retain_colors)
15454               {
15455                 *state|=RetainColorsState;
15456                 break;
15457               }
15458             if (*event.xclient.data.l == (long) windows->im_exit)
15459               {
15460                 *state|=ExitState;
15461                 break;
15462               }
15463             break;
15464           }
15465         if (event.xclient.message_type == windows->dnd_protocols)
15466           {
15467             Atom
15468               selection,
15469               type;
15470
15471             int
15472               format,
15473               status;
15474
15475             unsigned char
15476               *data;
15477
15478             unsigned long
15479               after,
15480               length;
15481
15482             /*
15483               Display image named by the Drag-and-Drop selection.
15484             */
15485             if ((*event.xclient.data.l != 2) && (*event.xclient.data.l != 128))
15486               break;
15487             selection=XInternAtom(display,"DndSelection",MagickFalse);
15488             status=XGetWindowProperty(display,root_window,selection,0L,(long)
15489               MagickPathExtent,MagickFalse,(Atom) AnyPropertyType,&type,&format,
15490               &length,&after,&data);
15491             if ((status != Success) || (length == 0))
15492               break;
15493             if (*event.xclient.data.l == 2)
15494               {
15495                 /*
15496                   Offix DND.
15497                 */
15498                 (void) CopyMagickString(resource_info->image_info->filename,
15499                   (char *) data,MagickPathExtent);
15500               }
15501             else
15502               {
15503                 /*
15504                   XDND.
15505                 */
15506                 if (strncmp((char *) data, "file:", 5) != 0)
15507                   {
15508                     (void) XFree((void *) data);
15509                     break;
15510                   }
15511                 (void) CopyMagickString(resource_info->image_info->filename,
15512                   ((char *) data)+5,MagickPathExtent);
15513               }
15514             nexus=ReadImage(resource_info->image_info,exception);
15515             CatchException(exception);
15516             if (nexus != (Image *) NULL)
15517               *state|=NextImageState | ExitState;
15518             (void) XFree((void *) data);
15519             break;
15520           }
15521         /*
15522           If client window delete message, exit.
15523         */
15524         if (event.xclient.message_type != windows->wm_protocols)
15525           break;
15526         if (*event.xclient.data.l != (long) windows->wm_delete_window)
15527           break;
15528         (void) XWithdrawWindow(display,event.xclient.window,
15529           visual_info->screen);
15530         if (event.xclient.window == windows->image.id)
15531           {
15532             *state|=ExitState;
15533             break;
15534           }
15535         if (event.xclient.window == windows->pan.id)
15536           {
15537             /*
15538               Restore original image size when pan window is deleted.
15539             */
15540             windows->image.window_changes.width=windows->image.ximage->width;
15541             windows->image.window_changes.height=windows->image.ximage->height;
15542             (void) XConfigureImage(display,resource_info,windows,
15543               display_image,exception);
15544           }
15545         break;
15546       }
15547       case ConfigureNotify:
15548       {
15549         if (display_image->debug != MagickFalse )
15550           (void) LogMagickEvent(X11Event,GetMagickModule(),
15551             "Configure Notify: 0x%lx %dx%d+%d+%d %d",event.xconfigure.window,
15552             event.xconfigure.width,event.xconfigure.height,event.xconfigure.x,
15553             event.xconfigure.y,event.xconfigure.send_event);
15554         if (event.xconfigure.window == windows->image.id)
15555           {
15556             /*
15557               Image window has a new configuration.
15558             */
15559             if (event.xconfigure.send_event != 0)
15560               {
15561                 XWindowChanges
15562                   window_changes;
15563
15564                 /*
15565                   Position the transient windows relative of the Image window.
15566                 */
15567                 if (windows->command.geometry == (char *) NULL)
15568                   if (windows->command.mapped == MagickFalse)
15569                     {
15570                       windows->command.x=event.xconfigure.x-
15571                         windows->command.width-25;
15572                       windows->command.y=event.xconfigure.y;
15573                       XConstrainWindowPosition(display,&windows->command);
15574                       window_changes.x=windows->command.x;
15575                       window_changes.y=windows->command.y;
15576                       (void) XReconfigureWMWindow(display,windows->command.id,
15577                         windows->command.screen,(unsigned int) (CWX | CWY),
15578                         &window_changes);
15579                     }
15580                 if (windows->widget.geometry == (char *) NULL)
15581                   if (windows->widget.mapped == MagickFalse)
15582                     {
15583                       windows->widget.x=event.xconfigure.x+
15584                         event.xconfigure.width/10;
15585                       windows->widget.y=event.xconfigure.y+
15586                         event.xconfigure.height/10;
15587                       XConstrainWindowPosition(display,&windows->widget);
15588                       window_changes.x=windows->widget.x;
15589                       window_changes.y=windows->widget.y;
15590                       (void) XReconfigureWMWindow(display,windows->widget.id,
15591                         windows->widget.screen,(unsigned int) (CWX | CWY),
15592                         &window_changes);
15593                     }
15594                 if (windows->magnify.geometry == (char *) NULL)
15595                   if (windows->magnify.mapped == MagickFalse)
15596                     {
15597                       windows->magnify.x=event.xconfigure.x+
15598                         event.xconfigure.width+25;
15599                       windows->magnify.y=event.xconfigure.y;
15600                       XConstrainWindowPosition(display,&windows->magnify);
15601                       window_changes.x=windows->magnify.x;
15602                       window_changes.y=windows->magnify.y;
15603                       (void) XReconfigureWMWindow(display,windows->magnify.id,
15604                         windows->magnify.screen,(unsigned int) (CWX | CWY),
15605                         &window_changes);
15606                     }
15607                 if (windows->pan.geometry == (char *) NULL)
15608                   if (windows->pan.mapped == MagickFalse)
15609                     {
15610                       windows->pan.x=event.xconfigure.x+
15611                         event.xconfigure.width+25;
15612                       windows->pan.y=event.xconfigure.y+
15613                         windows->magnify.height+50;
15614                       XConstrainWindowPosition(display,&windows->pan);
15615                       window_changes.x=windows->pan.x;
15616                       window_changes.y=windows->pan.y;
15617                       (void) XReconfigureWMWindow(display,windows->pan.id,
15618                         windows->pan.screen,(unsigned int) (CWX | CWY),
15619                         &window_changes);
15620                     }
15621               }
15622             if ((event.xconfigure.width == (int) windows->image.width) &&
15623                 (event.xconfigure.height == (int) windows->image.height))
15624               break;
15625             windows->image.width=(unsigned int) event.xconfigure.width;
15626             windows->image.height=(unsigned int) event.xconfigure.height;
15627             windows->image.x=0;
15628             windows->image.y=0;
15629             if (display_image->montage != (char *) NULL)
15630               {
15631                 windows->image.x=vid_info.x;
15632                 windows->image.y=vid_info.y;
15633               }
15634             if (windows->image.mapped != MagickFalse &&
15635                 windows->image.stasis != MagickFalse )
15636               {
15637                 /*
15638                   Update image window configuration.
15639                 */
15640                 windows->image.window_changes.width=event.xconfigure.width;
15641                 windows->image.window_changes.height=event.xconfigure.height;
15642                 (void) XConfigureImage(display,resource_info,windows,
15643                   display_image,exception);
15644               }
15645             /*
15646               Update pan window configuration.
15647             */
15648             if ((event.xconfigure.width < windows->image.ximage->width) ||
15649                 (event.xconfigure.height < windows->image.ximage->height))
15650               {
15651                 (void) XMapRaised(display,windows->pan.id);
15652                 XDrawPanRectangle(display,windows);
15653               }
15654             else
15655               if (windows->pan.mapped != MagickFalse )
15656                 (void) XWithdrawWindow(display,windows->pan.id,
15657                   windows->pan.screen);
15658             break;
15659           }
15660         if (event.xconfigure.window == windows->magnify.id)
15661           {
15662             unsigned int
15663               magnify;
15664
15665             /*
15666               Magnify window has a new configuration.
15667             */
15668             windows->magnify.width=(unsigned int) event.xconfigure.width;
15669             windows->magnify.height=(unsigned int) event.xconfigure.height;
15670             if (windows->magnify.mapped == MagickFalse)
15671               break;
15672             magnify=1;
15673             while ((int) magnify <= event.xconfigure.width)
15674               magnify<<=1;
15675             while ((int) magnify <= event.xconfigure.height)
15676               magnify<<=1;
15677             magnify>>=1;
15678             if (((int) magnify != event.xconfigure.width) ||
15679                 ((int) magnify != event.xconfigure.height))
15680               {
15681                 window_changes.width=(int) magnify;
15682                 window_changes.height=(int) magnify;
15683                 (void) XReconfigureWMWindow(display,windows->magnify.id,
15684                   windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
15685                   &window_changes);
15686                 break;
15687               }
15688             if (windows->magnify.mapped != MagickFalse &&
15689                 windows->magnify.stasis != MagickFalse )
15690               {
15691                 status=XMakeImage(display,resource_info,&windows->magnify,
15692                   display_image,windows->magnify.width,windows->magnify.height,
15693                   exception);
15694                 XMakeMagnifyImage(display,windows,exception);
15695               }
15696             break;
15697           }
15698         if (windows->magnify.mapped != MagickFalse &&
15699             (event.xconfigure.window == windows->pan.id))
15700           {
15701             /*
15702               Pan icon window has a new configuration.
15703             */
15704             if (event.xconfigure.send_event != 0)
15705               {
15706                 windows->pan.x=event.xconfigure.x;
15707                 windows->pan.y=event.xconfigure.y;
15708               }
15709             windows->pan.width=(unsigned int) event.xconfigure.width;
15710             windows->pan.height=(unsigned int) event.xconfigure.height;
15711             break;
15712           }
15713         if (event.xconfigure.window == windows->icon.id)
15714           {
15715             /*
15716               Icon window has a new configuration.
15717             */
15718             windows->icon.width=(unsigned int) event.xconfigure.width;
15719             windows->icon.height=(unsigned int) event.xconfigure.height;
15720             break;
15721           }
15722         break;
15723       }
15724       case DestroyNotify:
15725       {
15726         /*
15727           Group leader has exited.
15728         */
15729         if (display_image->debug != MagickFalse )
15730           (void) LogMagickEvent(X11Event,GetMagickModule(),
15731             "Destroy Notify: 0x%lx",event.xdestroywindow.window);
15732         if (event.xdestroywindow.window == windows->group_leader.id)
15733           {
15734             *state|=ExitState;
15735             break;
15736           }
15737         break;
15738       }
15739       case EnterNotify:
15740       {
15741         /*
15742           Selectively install colormap.
15743         */
15744         if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15745           if (event.xcrossing.mode != NotifyUngrab)
15746             XInstallColormap(display,map_info->colormap);
15747         break;
15748       }
15749       case Expose:
15750       {
15751         if (display_image->debug != MagickFalse )
15752           (void) LogMagickEvent(X11Event,GetMagickModule(),
15753             "Expose: 0x%lx %dx%d+%d+%d",event.xexpose.window,
15754             event.xexpose.width,event.xexpose.height,event.xexpose.x,
15755             event.xexpose.y);
15756         /*
15757           Refresh windows that are now exposed.
15758         */
15759         if ((event.xexpose.window == windows->image.id) &&
15760             windows->image.mapped != MagickFalse )
15761           {
15762             XRefreshWindow(display,&windows->image,&event);
15763             delay=display_image->delay/MagickMax(
15764               display_image->ticks_per_second,1L);
15765             timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15766             break;
15767           }
15768         if ((event.xexpose.window == windows->magnify.id) &&
15769             windows->magnify.mapped != MagickFalse)
15770           {
15771             XMakeMagnifyImage(display,windows,exception);
15772             break;
15773           }
15774         if (event.xexpose.window == windows->pan.id)
15775           {
15776             XDrawPanRectangle(display,windows);
15777             break;
15778           }
15779         if (event.xexpose.window == windows->icon.id)
15780           {
15781             XRefreshWindow(display,&windows->icon,&event);
15782             break;
15783           }
15784         break;
15785       }
15786       case KeyPress:
15787       {
15788         int
15789           length;
15790
15791         /*
15792           Respond to a user key press.
15793         */
15794         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
15795           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15796         *(command+length)='\0';
15797         if (display_image->debug != MagickFalse )
15798           (void) LogMagickEvent(X11Event,GetMagickModule(),
15799             "Key press: %d 0x%lx (%s)",event.xkey.state,(unsigned long)
15800             key_symbol,command);
15801         if (event.xkey.window == windows->image.id)
15802           {
15803             command_type=XImageWindowCommand(display,resource_info,windows,
15804               event.xkey.state,key_symbol,&display_image,exception);
15805             if (command_type != NullCommand)
15806               nexus=XMagickCommand(display,resource_info,windows,command_type,
15807                 &display_image,exception);
15808           }
15809         if (event.xkey.window == windows->magnify.id)
15810           XMagnifyWindowCommand(display,windows,event.xkey.state,key_symbol,
15811             exception);
15812         if (event.xkey.window == windows->pan.id)
15813           {
15814             if ((key_symbol == XK_q) || (key_symbol == XK_Escape))
15815               (void) XWithdrawWindow(display,windows->pan.id,
15816                 windows->pan.screen);
15817             else
15818               if ((key_symbol == XK_F1) || (key_symbol == XK_Help))
15819                 XTextViewWidget(display,resource_info,windows,MagickFalse,
15820                   "Help Viewer - Image Pan",ImagePanHelp);
15821               else
15822                 XTranslateImage(display,windows,*image,key_symbol);
15823           }
15824         delay=display_image->delay/MagickMax(
15825           display_image->ticks_per_second,1L);
15826         timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15827         break;
15828       }
15829       case KeyRelease:
15830       {
15831         /*
15832           Respond to a user key release.
15833         */
15834         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
15835           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15836         if (display_image->debug != MagickFalse )
15837           (void) LogMagickEvent(X11Event,GetMagickModule(),
15838             "Key release: 0x%lx (%c)",(unsigned long) key_symbol,*command);
15839         break;
15840       }
15841       case LeaveNotify:
15842       {
15843         /*
15844           Selectively uninstall colormap.
15845         */
15846         if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15847           if (event.xcrossing.mode != NotifyUngrab)
15848             XUninstallColormap(display,map_info->colormap);
15849         break;
15850       }
15851       case MapNotify:
15852       {
15853         if (display_image->debug != MagickFalse )
15854           (void) LogMagickEvent(X11Event,GetMagickModule(),"Map Notify: 0x%lx",
15855             event.xmap.window);
15856         if (event.xmap.window == windows->backdrop.id)
15857           {
15858             (void) XSetInputFocus(display,event.xmap.window,RevertToParent,
15859               CurrentTime);
15860             windows->backdrop.mapped=MagickTrue;
15861             break;
15862           }
15863         if (event.xmap.window == windows->image.id)
15864           {
15865             if (windows->backdrop.id != (Window) NULL)
15866               (void) XInstallColormap(display,map_info->colormap);
15867             if (LocaleCompare(display_image->magick,"LOGO") == 0)
15868               {
15869                 if (LocaleCompare(display_image->filename,"LOGO") == 0)
15870                   nexus=XOpenImage(display,resource_info,windows,MagickFalse);
15871               }
15872             if (((int) windows->image.width < windows->image.ximage->width) ||
15873                 ((int) windows->image.height < windows->image.ximage->height))
15874               (void) XMapRaised(display,windows->pan.id);
15875             windows->image.mapped=MagickTrue;
15876             break;
15877           }
15878         if (event.xmap.window == windows->magnify.id)
15879           {
15880             XMakeMagnifyImage(display,windows,exception);
15881             windows->magnify.mapped=MagickTrue;
15882             (void) XWithdrawWindow(display,windows->info.id,
15883               windows->info.screen);
15884             break;
15885           }
15886         if (event.xmap.window == windows->pan.id)
15887           {
15888             XMakePanImage(display,resource_info,windows,display_image,
15889               exception);
15890             windows->pan.mapped=MagickTrue;
15891             break;
15892           }
15893         if (event.xmap.window == windows->info.id)
15894           {
15895             windows->info.mapped=MagickTrue;
15896             break;
15897           }
15898         if (event.xmap.window == windows->icon.id)
15899           {
15900             MagickBooleanType
15901               taint;
15902
15903             /*
15904               Create an icon image.
15905             */
15906             taint=display_image->taint;
15907             XMakeStandardColormap(display,icon_visual,icon_resources,
15908               display_image,icon_map,icon_pixel,exception);
15909             (void) XMakeImage(display,icon_resources,&windows->icon,
15910               display_image,windows->icon.width,windows->icon.height,
15911               exception);
15912             display_image->taint=taint;
15913             (void) XSetWindowBackgroundPixmap(display,windows->icon.id,
15914               windows->icon.pixmap);
15915             (void) XClearWindow(display,windows->icon.id);
15916             (void) XWithdrawWindow(display,windows->info.id,
15917               windows->info.screen);
15918             windows->icon.mapped=MagickTrue;
15919             break;
15920           }
15921         if (event.xmap.window == windows->command.id)
15922           {
15923             windows->command.mapped=MagickTrue;
15924             break;
15925           }
15926         if (event.xmap.window == windows->popup.id)
15927           {
15928             windows->popup.mapped=MagickTrue;
15929             break;
15930           }
15931         if (event.xmap.window == windows->widget.id)
15932           {
15933             windows->widget.mapped=MagickTrue;
15934             break;
15935           }
15936         break;
15937       }
15938       case MappingNotify:
15939       {
15940         (void) XRefreshKeyboardMapping(&event.xmapping);
15941         break;
15942       }
15943       case NoExpose:
15944         break;
15945       case PropertyNotify:
15946       {
15947         Atom
15948           type;
15949
15950         int
15951           format,
15952           status;
15953
15954         unsigned char
15955           *data;
15956
15957         unsigned long
15958           after,
15959           length;
15960
15961         if (display_image->debug != MagickFalse )
15962           (void) LogMagickEvent(X11Event,GetMagickModule(),
15963             "Property Notify: 0x%lx 0x%lx %d",event.xproperty.window,
15964             event.xproperty.atom,event.xproperty.state);
15965         if (event.xproperty.atom != windows->im_remote_command)
15966           break;
15967         /*
15968           Display image named by the remote command protocol.
15969         */
15970         status=XGetWindowProperty(display,event.xproperty.window,
15971           event.xproperty.atom,0L,(long) MagickPathExtent,MagickFalse,(Atom)
15972           AnyPropertyType,&type,&format,&length,&after,&data);
15973         if ((status != Success) || (length == 0))
15974           break;
15975         if (LocaleCompare((char *) data,"-quit") == 0)
15976           {
15977             XClientMessage(display,windows->image.id,windows->im_protocols,
15978               windows->im_exit,CurrentTime);
15979             (void) XFree((void *) data);
15980             break;
15981           }
15982         (void) CopyMagickString(resource_info->image_info->filename,
15983           (char *) data,MagickPathExtent);
15984         (void) XFree((void *) data);
15985         nexus=ReadImage(resource_info->image_info,exception);
15986         CatchException(exception);
15987         if (nexus != (Image *) NULL)
15988           *state|=NextImageState | ExitState;
15989         break;
15990       }
15991       case ReparentNotify:
15992       {
15993         if (display_image->debug != MagickFalse )
15994           (void) LogMagickEvent(X11Event,GetMagickModule(),
15995             "Reparent Notify: 0x%lx=>0x%lx",event.xreparent.parent,
15996             event.xreparent.window);
15997         break;
15998       }
15999       case UnmapNotify:
16000       {
16001         if (display_image->debug != MagickFalse )
16002           (void) LogMagickEvent(X11Event,GetMagickModule(),
16003             "Unmap Notify: 0x%lx",event.xunmap.window);
16004         if (event.xunmap.window == windows->backdrop.id)
16005           {
16006             windows->backdrop.mapped=MagickFalse;
16007             break;
16008           }
16009         if (event.xunmap.window == windows->image.id)
16010           {
16011             windows->image.mapped=MagickFalse;
16012             break;
16013           }
16014         if (event.xunmap.window == windows->magnify.id)
16015           {
16016             windows->magnify.mapped=MagickFalse;
16017             break;
16018           }
16019         if (event.xunmap.window == windows->pan.id)
16020           {
16021             windows->pan.mapped=MagickFalse;
16022             break;
16023           }
16024         if (event.xunmap.window == windows->info.id)
16025           {
16026             windows->info.mapped=MagickFalse;
16027             break;
16028           }
16029         if (event.xunmap.window == windows->icon.id)
16030           {
16031             if (map_info->colormap == icon_map->colormap)
16032               XConfigureImageColormap(display,resource_info,windows,
16033                 display_image,exception);
16034             (void) XFreeStandardColormap(display,icon_visual,icon_map,
16035               icon_pixel);
16036             windows->icon.mapped=MagickFalse;
16037             break;
16038           }
16039         if (event.xunmap.window == windows->command.id)
16040           {
16041             windows->command.mapped=MagickFalse;
16042             break;
16043           }
16044         if (event.xunmap.window == windows->popup.id)
16045           {
16046             if (windows->backdrop.id != (Window) NULL)
16047               (void) XSetInputFocus(display,windows->image.id,RevertToParent,
16048                 CurrentTime);
16049             windows->popup.mapped=MagickFalse;
16050             break;
16051           }
16052         if (event.xunmap.window == windows->widget.id)
16053           {
16054             if (windows->backdrop.id != (Window) NULL)
16055               (void) XSetInputFocus(display,windows->image.id,RevertToParent,
16056                 CurrentTime);
16057             windows->widget.mapped=MagickFalse;
16058             break;
16059           }
16060         break;
16061       }
16062       default:
16063       {
16064         if (display_image->debug != MagickFalse )
16065           (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
16066             event.type);
16067         break;
16068       }
16069     }
16070   } while (!(*state & ExitState));
16071   if ((*state & ExitState) == 0)
16072     (void) XMagickCommand(display,resource_info,windows,FreeBuffersCommand,
16073       &display_image,exception);
16074   else
16075     if (resource_info->confirm_edit != MagickFalse )
16076       {
16077         /*
16078           Query user if image has changed.
16079         */
16080         if ((resource_info->immutable == MagickFalse) &&
16081             display_image->taint != MagickFalse)
16082           {
16083             int
16084               status;
16085
16086             status=XConfirmWidget(display,windows,"Your image changed.",
16087               "Do you want to save it");
16088             if (status == 0)
16089               *state&=(~ExitState);
16090             else
16091               if (status > 0)
16092                 (void) XMagickCommand(display,resource_info,windows,SaveCommand,
16093                   &display_image,exception);
16094           }
16095       }
16096   if ((windows->visual_info->klass == GrayScale) ||
16097       (windows->visual_info->klass == PseudoColor) ||
16098       (windows->visual_info->klass == DirectColor))
16099     {
16100       /*
16101         Withdraw pan and Magnify window.
16102       */
16103       if (windows->info.mapped != MagickFalse )
16104         (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
16105       if (windows->magnify.mapped != MagickFalse )
16106         (void) XWithdrawWindow(display,windows->magnify.id,
16107           windows->magnify.screen);
16108       if (windows->command.mapped != MagickFalse )
16109         (void) XWithdrawWindow(display,windows->command.id,
16110           windows->command.screen);
16111     }
16112   if (windows->pan.mapped != MagickFalse )
16113     (void) XWithdrawWindow(display,windows->pan.id,windows->pan.screen);
16114   if (resource_info->backdrop == MagickFalse)
16115     if (windows->backdrop.mapped)
16116       {
16117         (void) XWithdrawWindow(display,windows->backdrop.id,
16118           windows->backdrop.screen);
16119         (void) XDestroyWindow(display,windows->backdrop.id);
16120         windows->backdrop.id=(Window) NULL;
16121         (void) XWithdrawWindow(display,windows->image.id,
16122           windows->image.screen);
16123         (void) XDestroyWindow(display,windows->image.id);
16124         windows->image.id=(Window) NULL;
16125       }
16126   XSetCursorState(display,windows,MagickTrue);
16127   XCheckRefreshWindows(display,windows);
16128   if (((*state & FormerImageState) != 0) || ((*state & NextImageState) != 0))
16129     *state&=(~ExitState);
16130   if (*state & ExitState)
16131     {
16132       /*
16133         Free Standard Colormap.
16134       */
16135       (void) XFreeStandardColormap(display,icon_visual,icon_map,icon_pixel);
16136       if (resource_info->map_type == (char *) NULL)
16137         (void) XFreeStandardColormap(display,visual_info,map_info,pixel);
16138       /*
16139         Free X resources.
16140       */
16141       if (resource_info->copy_image != (Image *) NULL)
16142         {
16143           resource_info->copy_image=DestroyImage(resource_info->copy_image);
16144           resource_info->copy_image=NewImageList();
16145         }
16146       DestroyXResources();
16147     }
16148   (void) XSync(display,MagickFalse);
16149   /*
16150     Restore our progress monitor and warning handlers.
16151   */
16152   (void) SetErrorHandler(warning_handler);
16153   (void) SetWarningHandler(warning_handler);
16154   /*
16155     Change to home directory.
16156   */
16157   directory=getcwd(working_directory,MagickPathExtent);
16158   (void) directory;
16159   {
16160     int
16161       status;
16162
16163     if (*resource_info->home_directory == '\0')
16164       (void) CopyMagickString(resource_info->home_directory,".",MagickPathExtent);
16165     status=chdir(resource_info->home_directory);
16166     if (status == -1)
16167       (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
16168         "UnableToOpenFile","%s",resource_info->home_directory);
16169   }
16170   *image=display_image;
16171   return(nexus);
16172 }
16173 #else
16174 \f
16175 /*
16176 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16177 %                                                                             %
16178 %                                                                             %
16179 %                                                                             %
16180 +   D i s p l a y I m a g e s                                                 %
16181 %                                                                             %
16182 %                                                                             %
16183 %                                                                             %
16184 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16185 %
16186 %  DisplayImages() displays an image sequence to any X window screen.  It
16187 %  returns a value other than 0 if successful.  Check the exception member
16188 %  of image to determine the reason for any failure.
16189 %
16190 %  The format of the DisplayImages method is:
16191 %
16192 %      MagickBooleanType DisplayImages(const ImageInfo *image_info,
16193 %        Image *images,ExceptionInfo *exception)
16194 %
16195 %  A description of each parameter follows:
16196 %
16197 %    o image_info: the image info.
16198 %
16199 %    o image: the image.
16200 %
16201 %    o exception: return any errors or warnings in this structure.
16202 %
16203 */
16204 MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
16205   Image *image,ExceptionInfo *exception)
16206 {
16207   assert(image_info != (const ImageInfo *) NULL);
16208   assert(image_info->signature == MagickCoreSignature);
16209   assert(image != (Image *) NULL);
16210   assert(image->signature == MagickCoreSignature);
16211   (void) image_info;
16212   if (image->debug != MagickFalse )
16213     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
16214   (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16215     "DelegateLibrarySupportNotBuiltIn","'%s' (X11)",image->filename);
16216   return(MagickFalse);
16217 }
16218 \f
16219 /*
16220 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16221 %                                                                             %
16222 %                                                                             %
16223 %                                                                             %
16224 +   R e m o t e D i s p l a y C o m m a n d                                   %
16225 %                                                                             %
16226 %                                                                             %
16227 %                                                                             %
16228 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16229 %
16230 %  RemoteDisplayCommand() encourages a remote display program to display the
16231 %  specified image filename.
16232 %
16233 %  The format of the RemoteDisplayCommand method is:
16234 %
16235 %      MagickBooleanType RemoteDisplayCommand(const ImageInfo *image,
16236 %        const char *window,const char *filename,ExceptionInfo *exception)
16237 %
16238 %  A description of each parameter follows:
16239 %
16240 %    o image_info: the image info.
16241 %
16242 %    o window: Specifies the name or id of an X window.
16243 %
16244 %    o filename: the name of the image filename to display.
16245 %
16246 %    o exception: return any errors or warnings in this structure.
16247 %
16248 */
16249 MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
16250   const char *window,const char *filename,ExceptionInfo *exception)
16251 {
16252   assert(image_info != (const ImageInfo *) NULL);
16253   assert(image_info->signature == MagickCoreSignature);
16254   assert(filename != (char *) NULL);
16255   (void) window;
16256   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
16257   (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16258     "DelegateLibrarySupportNotBuiltIn","'%s' (X11)",image_info->filename);
16259   return(MagickFalse);
16260 }
16261 #endif