]> granicus.if.org Git - imagemagick/blob - MagickCore/display.c
(no commit message)
[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 %                               John Cristy                                   %
17 %                                July 1992                                    %
18 %                                                                             %
19 %                                                                             %
20 %  Copyright 1999-2011 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 %    http://www.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/blob.h"
45 #include "MagickCore/cache.h"
46 #include "MagickCore/cache-private.h"
47 #include "MagickCore/client.h"
48 #include "MagickCore/color.h"
49 #include "MagickCore/colorspace.h"
50 #include "MagickCore/composite.h"
51 #include "MagickCore/constitute.h"
52 #include "MagickCore/decorate.h"
53 #include "MagickCore/delegate.h"
54 #include "MagickCore/display.h"
55 #include "MagickCore/display-private.h"
56 #include "MagickCore/distort.h"
57 #include "MagickCore/draw.h"
58 #include "MagickCore/effect.h"
59 #include "MagickCore/enhance.h"
60 #include "MagickCore/exception.h"
61 #include "MagickCore/exception-private.h"
62 #include "MagickCore/fx.h"
63 #include "MagickCore/geometry.h"
64 #include "MagickCore/image.h"
65 #include "MagickCore/image-private.h"
66 #include "MagickCore/list.h"
67 #include "MagickCore/log.h"
68 #include "MagickCore/magick.h"
69 #include "MagickCore/memory_.h"
70 #include "MagickCore/monitor.h"
71 #include "MagickCore/monitor-private.h"
72 #include "MagickCore/montage.h"
73 #include "MagickCore/option.h"
74 #include "MagickCore/paint.h"
75 #include "MagickCore/pixel.h"
76 #include "MagickCore/pixel-accessor.h"
77 #include "MagickCore/PreRvIcccm.h"
78 #include "MagickCore/property.h"
79 #include "MagickCore/quantum.h"
80 #include "MagickCore/quantum-private.h"
81 #include "MagickCore/resize.h"
82 #include "MagickCore/resource_.h"
83 #include "MagickCore/shear.h"
84 #include "MagickCore/segment.h"
85 #include "MagickCore/statistic.h"
86 #include "MagickCore/string_.h"
87 #include "MagickCore/string-private.h"
88 #include "MagickCore/transform.h"
89 #include "MagickCore/threshold.h"
90 #include "MagickCore/utility.h"
91 #include "MagickCore/utility-private.h"
92 #include "MagickCore/version.h"
93 #include "MagickCore/widget.h"
94 #include "MagickCore/widget-private.h"
95 #include "MagickCore/xwindow.h"
96 #include "MagickCore/xwindow-private.h"
97 \f
98 #if defined(MAGICKCORE_X11_DELEGATE)
99 /*
100   Define declarations.
101 */
102 #define MaxColors  MagickMin((ssize_t) windows->visual_info->colormap_size,256L)
103 \f
104 /*
105   Constant declarations.
106 */
107 static const unsigned char
108   HighlightBitmap[8] =
109   {
110     0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55
111   },
112   OpaqueBitmap[8] =
113   {
114     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
115   },
116   ShadowBitmap[8] =
117   {
118     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
119   };
120
121 static const char
122   *PageSizes[] =
123   {
124     "Letter",
125     "Tabloid",
126     "Ledger",
127     "Legal",
128     "Statement",
129     "Executive",
130     "A3",
131     "A4",
132     "A5",
133     "B4",
134     "B5",
135     "Folio",
136     "Quarto",
137     "10x14",
138     (char *) NULL
139   };
140 \f
141 /*
142   Help widget declarations.
143 */
144 static const char
145   *ImageAnnotateHelp[] =
146   {
147     "In annotate mode, the Command widget has these options:",
148     "",
149     "    Font Name",
150     "      fixed",
151     "      variable",
152     "      5x8",
153     "      6x10",
154     "      7x13bold",
155     "      8x13bold",
156     "      9x15bold",
157     "      10x20",
158     "      12x24",
159     "      Browser...",
160     "    Font Color",
161     "      black",
162     "      blue",
163     "      cyan",
164     "      green",
165     "      gray",
166     "      red",
167     "      magenta",
168     "      yellow",
169     "      white",
170     "      transparent",
171     "      Browser...",
172     "    Font Color",
173     "      black",
174     "      blue",
175     "      cyan",
176     "      green",
177     "      gray",
178     "      red",
179     "      magenta",
180     "      yellow",
181     "      white",
182     "      transparent",
183     "      Browser...",
184     "    Rotate Text",
185     "      -90",
186     "      -45",
187     "      -30",
188     "      0",
189     "      30",
190     "      45",
191     "      90",
192     "      180",
193     "      Dialog...",
194     "    Help",
195     "    Dismiss",
196     "",
197     "Choose a font name from the Font Name sub-menu.  Additional",
198     "font names can be specified with the font browser.  You can",
199     "change the menu names by setting the X resources font1",
200     "through font9.",
201     "",
202     "Choose a font color from the Font Color sub-menu.",
203     "Additional font colors can be specified with the color",
204     "browser.  You can change the menu colors by setting the X",
205     "resources pen1 through pen9.",
206     "",
207     "If you select the color browser and press Grab, you can",
208     "choose the font color by moving the pointer to the desired",
209     "color on the screen and press any button.",
210     "",
211     "If you choose to rotate the text, choose Rotate Text from the",
212     "menu and select an angle.  Typically you will only want to",
213     "rotate one line of text at a time.  Depending on the angle you",
214     "choose, subsequent lines may end up overwriting each other.",
215     "",
216     "Choosing a font and its color is optional.  The default font",
217     "is fixed and the default color is black.  However, you must",
218     "choose a location to begin entering text and press button 1.",
219     "An underscore character will appear at the location of the",
220     "pointer.  The cursor changes to a pencil to indicate you are",
221     "in text mode.  To exit immediately, press Dismiss.",
222     "",
223     "In text mode, any key presses will display the character at",
224     "the location of the underscore and advance the underscore",
225     "cursor.  Enter your text and once completed press Apply to",
226     "finish your image annotation.  To correct errors press BACK",
227     "SPACE.  To delete an entire line of text, press DELETE.  Any",
228     "text that exceeds the boundaries of the image window is",
229     "automagically continued onto the next line.",
230     "",
231     "The actual color you request for the font is saved in the",
232     "image.  However, the color that appears in your image window",
233     "may be different.  For example, on a monochrome screen the",
234     "text will appear black or white even if you choose the color",
235     "red as the font color.  However, the image saved to a file",
236     "with -write is written with red lettering.  To assure the",
237     "correct color text in the final image, any PseudoClass image",
238     "is promoted to DirectClass (see miff(5)).  To force a",
239     "PseudoClass image to remain PseudoClass, use -colors.",
240     (char *) NULL,
241   },
242   *ImageChopHelp[] =
243   {
244     "In chop mode, the Command widget has these options:",
245     "",
246     "    Direction",
247     "      horizontal",
248     "      vertical",
249     "    Help",
250     "    Dismiss",
251     "",
252     "If the you choose the horizontal direction (this the",
253     "default), the area of the image between the two horizontal",
254     "endpoints of the chop line is removed.  Otherwise, the area",
255     "of the image between the two vertical endpoints of the chop",
256     "line is removed.",
257     "",
258     "Select a location within the image window to begin your chop,",
259     "press and hold any button.  Next, move the pointer to",
260     "another location in the image.  As you move a line will",
261     "connect the initial location and the pointer.  When you",
262     "release the button, the area within the image to chop is",
263     "determined by which direction you choose from the Command",
264     "widget.",
265     "",
266     "To cancel the image chopping, move the pointer back to the",
267     "starting point of the line and release the button.",
268     (char *) NULL,
269   },
270   *ImageColorEditHelp[] =
271   {
272     "In color edit mode, the Command widget has these options:",
273     "",
274     "    Method",
275     "      point",
276     "      replace",
277     "      floodfill",
278     "      filltoborder",
279     "      reset",
280     "    Pixel Color",
281     "      black",
282     "      blue",
283     "      cyan",
284     "      green",
285     "      gray",
286     "      red",
287     "      magenta",
288     "      yellow",
289     "      white",
290     "      Browser...",
291     "    Border Color",
292     "      black",
293     "      blue",
294     "      cyan",
295     "      green",
296     "      gray",
297     "      red",
298     "      magenta",
299     "      yellow",
300     "      white",
301     "      Browser...",
302     "    Fuzz",
303     "      0%",
304     "      2%",
305     "      5%",
306     "      10%",
307     "      15%",
308     "      Dialog...",
309     "    Undo",
310     "    Help",
311     "    Dismiss",
312     "",
313     "Choose a color editing method from the Method sub-menu",
314     "of the Command widget.  The point method recolors any pixel",
315     "selected with the pointer until the button is released.  The",
316     "replace method recolors any pixel that matches the color of",
317     "the pixel you select with a button press.  Floodfill recolors",
318     "any pixel that matches the color of the pixel you select with",
319     "a button press and is a neighbor.  Whereas filltoborder recolors",
320     "any neighbor pixel that is not the border color.  Finally reset",
321     "changes the entire image to the designated color.",
322     "",
323     "Next, choose a pixel color from the Pixel Color sub-menu.",
324     "Additional pixel colors can be specified with the color",
325     "browser.  You can change the menu colors by setting the X",
326     "resources pen1 through pen9.",
327     "",
328     "Now press button 1 to select a pixel within the image window",
329     "to change its color.  Additional pixels may be recolored as",
330     "prescribed by the method you choose.",
331     "",
332     "If the Magnify widget is mapped, it can be helpful in positioning",
333     "your pointer within the image (refer to button 2).",
334     "",
335     "The actual color you request for the pixels is saved in the",
336     "image.  However, the color that appears in your image window",
337     "may be different.  For example, on a monochrome screen the",
338     "pixel will appear black or white even if you choose the",
339     "color red as the pixel color.  However, the image saved to a",
340     "file with -write is written with red pixels.  To assure the",
341     "correct color text in the final image, any PseudoClass image",
342     "is promoted to DirectClass (see miff(5)).  To force a",
343     "PseudoClass image to remain PseudoClass, use -colors.",
344     (char *) NULL,
345   },
346   *ImageCompositeHelp[] =
347   {
348     "First a widget window is displayed requesting you to enter an",
349     "image name. Press Composite, Grab or type a file name.",
350     "Press Cancel if you choose not to create a composite image.",
351     "When you choose Grab, move the pointer to the desired window",
352     "and press any button.",
353     "",
354     "If the Composite image does not have any matte information,",
355     "you are informed and the file browser is displayed again.",
356     "Enter the name of a mask image.  The image is typically",
357     "grayscale and the same size as the composite image.  If the",
358     "image is not grayscale, it is converted to grayscale and the",
359     "resulting intensities are used as matte information.",
360     "",
361     "A small window appears showing the location of the cursor in",
362     "the image window. You are now in composite mode.  To exit",
363     "immediately, press Dismiss.  In composite mode, the Command",
364     "widget has these options:",
365     "",
366     "    Operators",
367     "      Over",
368     "      In",
369     "      Out",
370     "      Atop",
371     "      Xor",
372     "      Plus",
373     "      Minus",
374     "      Add",
375     "      Subtract",
376     "      Difference",
377     "      Multiply",
378     "      Bumpmap",
379     "      Copy",
380     "      CopyRed",
381     "      CopyGreen",
382     "      CopyBlue",
383     "      CopyOpacity",
384     "      Clear",
385     "    Dissolve",
386     "    Displace",
387     "    Help",
388     "    Dismiss",
389     "",
390     "Choose a composite operation from the Operators sub-menu of",
391     "the Command widget.  How each operator behaves is described",
392     "below.  Image window is the image currently displayed on",
393     "your X server and image is the image obtained with the File",
394     "Browser widget.",
395     "",
396     "Over     The result is the union of the two image shapes,",
397     "         with image obscuring image window in the region of",
398     "         overlap.",
399     "",
400     "In       The result is simply image cut by the shape of",
401     "         image window.  None of the image data of image",
402     "         window is in the result.",
403     "",
404     "Out      The resulting image is image with the shape of",
405     "         image window cut out.",
406     "",
407     "Atop     The result is the same shape as image image window,",
408     "         with image obscuring image window where the image",
409     "         shapes overlap.  Note this differs from over",
410     "         because the portion of image outside image window's",
411     "         shape does not appear in the result.",
412     "",
413     "Xor      The result is the image data from both image and",
414     "         image window that is outside the overlap region.",
415     "         The overlap region is blank.",
416     "",
417     "Plus     The result is just the sum of the image data.",
418     "         Output values are cropped to QuantumRange (no overflow).",
419     "",
420     "Minus    The result of image - image window, with underflow",
421     "         cropped to zero.",
422     "",
423     "Add      The result of image + image window, with overflow",
424     "         wrapping around (mod 256).",
425     "",
426     "Subtract The result of image - image window, with underflow",
427     "         wrapping around (mod 256).  The add and subtract",
428     "         operators can be used to perform reversible",
429     "         transformations.",
430     "",
431     "Difference",
432     "         The result of abs(image - image window).  This",
433     "         useful for comparing two very similar images.",
434     "",
435     "Multiply",
436     "         The result of image * image window.  This",
437     "         useful for the creation of drop-shadows.",
438     "",
439     "Bumpmap  The result of surface normals from image * image",
440     "         window.",
441     "",
442     "Copy     The resulting image is image window replaced with",
443     "         image.  Here the matte information is ignored.",
444     "",
445     "CopyRed  The red layer of the image window is replace with",
446     "         the red layer of the image.  The other layers are",
447     "         untouched.",
448     "",
449     "CopyGreen",
450     "         The green layer of the image window is replace with",
451     "         the green layer of the image.  The other layers are",
452     "         untouched.",
453     "",
454     "CopyBlue The blue layer of the image window is replace with",
455     "         the blue layer of the image.  The other layers are",
456     "         untouched.",
457     "",
458     "CopyOpacity",
459     "         The matte layer of the image window is replace with",
460     "         the matte layer of the image.  The other layers are",
461     "         untouched.",
462     "",
463     "The image compositor requires a matte, or alpha channel in",
464     "the image for some operations.  This extra channel usually",
465     "defines a mask which represents a sort of a cookie-cutter",
466     "for the image.  This the case when matte is opaque (full",
467     "coverage) for pixels inside the shape, zero outside, and",
468     "between 0 and QuantumRange on the boundary.  If image does not",
469     "have a matte channel, it is initialized with 0 for any pixel",
470     "matching in color to pixel location (0,0), otherwise QuantumRange.",
471     "",
472     "If you choose Dissolve, the composite operator becomes Over.  The",
473     "image matte channel percent transparency is initialized to factor.",
474     "The image window is initialized to (100-factor). Where factor is the",
475     "value you specify in the Dialog widget.",
476     "",
477     "Displace shifts the image pixels as defined by a displacement",
478     "map.  With this option, image is used as a displacement map.",
479     "Black, within the displacement map, is a maximum positive",
480     "displacement.  White is a maximum negative displacement and",
481     "middle gray is neutral.  The displacement is scaled to determine",
482     "the pixel shift.  By default, the displacement applies in both the",
483     "horizontal and vertical directions.  However, if you specify a mask,",
484     "image is the horizontal X displacement and mask the vertical Y",
485     "displacement.",
486     "",
487     "Note that matte information for image window is not retained",
488     "for colormapped X server visuals (e.g. StaticColor,",
489     "StaticColor, GrayScale, PseudoColor).  Correct compositing",
490     "behavior may require a TrueColor or DirectColor visual or a",
491     "Standard Colormap.",
492     "",
493     "Choosing a composite operator is optional.  The default",
494     "operator is replace.  However, you must choose a location to",
495     "composite your image and press button 1.  Press and hold the",
496     "button before releasing and an outline of the image will",
497     "appear to help you identify your location.",
498     "",
499     "The actual colors of the composite image is saved.  However,",
500     "the color that appears in image window may be different.",
501     "For example, on a monochrome screen image window will appear",
502     "black or white even though your composited image may have",
503     "many colors.  If the image is saved to a file it is written",
504     "with the correct colors.  To assure the correct colors are",
505     "saved in the final image, any PseudoClass image is promoted",
506     "to DirectClass (see miff(5)).  To force a PseudoClass image",
507     "to remain PseudoClass, use -colors.",
508     (char *) NULL,
509   },
510   *ImageCutHelp[] =
511   {
512     "In cut mode, the Command widget has these options:",
513     "",
514     "    Help",
515     "    Dismiss",
516     "",
517     "To define a cut region, press button 1 and drag.  The",
518     "cut region is defined by a highlighted rectangle that",
519     "expands or contracts as it follows the pointer.  Once you",
520     "are satisfied with the cut region, release the button.",
521     "You are now in rectify mode.  In rectify mode, the Command",
522     "widget has these options:",
523     "",
524     "    Cut",
525     "    Help",
526     "    Dismiss",
527     "",
528     "You can make adjustments by moving the pointer to one of the",
529     "cut rectangle corners, pressing a button, and dragging.",
530     "Finally, press Cut to commit your copy region.  To",
531     "exit without cutting the image, press Dismiss.",
532     (char *) NULL,
533   },
534   *ImageCopyHelp[] =
535   {
536     "In copy mode, the Command widget has these options:",
537     "",
538     "    Help",
539     "    Dismiss",
540     "",
541     "To define a copy region, press button 1 and drag.  The",
542     "copy region is defined by a highlighted rectangle that",
543     "expands or contracts as it follows the pointer.  Once you",
544     "are satisfied with the copy region, release the button.",
545     "You are now in rectify mode.  In rectify mode, the Command",
546     "widget has these options:",
547     "",
548     "    Copy",
549     "    Help",
550     "    Dismiss",
551     "",
552     "You can make adjustments by moving the pointer to one of the",
553     "copy rectangle corners, pressing a button, and dragging.",
554     "Finally, press Copy to commit your copy region.  To",
555     "exit without copying the image, press Dismiss.",
556     (char *) NULL,
557   },
558   *ImageCropHelp[] =
559   {
560     "In crop mode, the Command widget has these options:",
561     "",
562     "    Help",
563     "    Dismiss",
564     "",
565     "To define a cropping region, press button 1 and drag.  The",
566     "cropping region is defined by a highlighted rectangle that",
567     "expands or contracts as it follows the pointer.  Once you",
568     "are satisfied with the cropping region, release the button.",
569     "You are now in rectify mode.  In rectify mode, the Command",
570     "widget has these options:",
571     "",
572     "    Crop",
573     "    Help",
574     "    Dismiss",
575     "",
576     "You can make adjustments by moving the pointer to one of the",
577     "cropping rectangle corners, pressing a button, and dragging.",
578     "Finally, press Crop to commit your cropping region.  To",
579     "exit without cropping the image, press Dismiss.",
580     (char *) NULL,
581   },
582   *ImageDrawHelp[] =
583   {
584     "The cursor changes to a crosshair to indicate you are in",
585     "draw mode.  To exit immediately, press Dismiss.  In draw mode,",
586     "the Command widget has these options:",
587     "",
588     "    Element",
589     "      point",
590     "      line",
591     "      rectangle",
592     "      fill rectangle",
593     "      circle",
594     "      fill circle",
595     "      ellipse",
596     "      fill ellipse",
597     "      polygon",
598     "      fill polygon",
599     "    Color",
600     "      black",
601     "      blue",
602     "      cyan",
603     "      green",
604     "      gray",
605     "      red",
606     "      magenta",
607     "      yellow",
608     "      white",
609     "      transparent",
610     "      Browser...",
611     "    Stipple",
612     "      Brick",
613     "      Diagonal",
614     "      Scales",
615     "      Vertical",
616     "      Wavy",
617     "      Translucent",
618     "      Opaque",
619     "      Open...",
620     "    Width",
621     "      1",
622     "      2",
623     "      4",
624     "      8",
625     "      16",
626     "      Dialog...",
627     "    Undo",
628     "    Help",
629     "    Dismiss",
630     "",
631     "Choose a drawing primitive from the Element sub-menu.",
632     "",
633     "Choose a color from the Color sub-menu.  Additional",
634     "colors can be specified with the color browser.",
635     "",
636     "If you choose the color browser and press Grab, you can",
637     "select the color by moving the pointer to the desired",
638     "color on the screen and press any button.  The transparent",
639     "color updates the image matte channel and is useful for",
640     "image compositing.",
641     "",
642     "Choose a stipple, if appropriate, from the Stipple sub-menu.",
643     "Additional stipples can be specified with the file browser.",
644     "Stipples obtained from the file browser must be on disk in the",
645     "X11 bitmap format.",
646     "",
647     "Choose a width, if appropriate, from the Width sub-menu.  To",
648     "choose a specific width select the Dialog widget.",
649     "",
650     "Choose a point in the Image window and press button 1 and",
651     "hold.  Next, move the pointer to another location in the",
652     "image.  As you move, a line connects the initial location and",
653     "the pointer.  When you release the button, the image is",
654     "updated with the primitive you just drew.  For polygons, the",
655     "image is updated when you press and release the button without",
656     "moving the pointer.",
657     "",
658     "To cancel image drawing, move the pointer back to the",
659     "starting point of the line and release the button.",
660     (char *) NULL,
661   },
662   *DisplayHelp[] =
663   {
664     "BUTTONS",
665     "  The effects of each button press is described below.  Three",
666     "  buttons are required.  If you have a two button mouse,",
667     "  button 1 and 3 are returned.  Press ALT and button 3 to",
668     "  simulate button 2.",
669     "",
670     "  1    Press this button to map or unmap the Command widget.",
671     "",
672     "  2    Press and drag to define a region of the image to",
673     "       magnify.",
674     "",
675     "  3    Press and drag to choose from a select set of commands.",
676     "       This button behaves differently if the image being",
677     "       displayed is a visual image directory.  Here, choose a",
678     "       particular tile of the directory and press this button and",
679     "       drag to select a command from a pop-up menu.  Choose from",
680     "       these menu items:",
681     "",
682     "           Open",
683     "           Next",
684     "           Former",
685     "           Delete",
686     "           Update",
687     "",
688     "       If you choose Open, the image represented by the tile is",
689     "       displayed.  To return to the visual image directory, choose",
690     "       Next from the Command widget.  Next and Former moves to the",
691     "       next or former image respectively.  Choose Delete to delete",
692     "       a particular image tile.  Finally, choose Update to",
693     "       synchronize all the image tiles with their respective",
694     "       images.",
695     "",
696     "COMMAND WIDGET",
697     "  The Command widget lists a number of sub-menus and commands.",
698     "  They are",
699     "",
700     "      File",
701     "        Open...",
702     "        Next",
703     "        Former",
704     "        Select...",
705     "        Save...",
706     "        Print...",
707     "        Delete...",
708     "        New...",
709     "        Visual Directory...",
710     "        Quit",
711     "      Edit",
712     "        Undo",
713     "        Redo",
714     "        Cut",
715     "        Copy",
716     "        Paste",
717     "      View",
718     "        Half Size",
719     "        Original Size",
720     "        Double Size",
721     "        Resize...",
722     "        Apply",
723     "        Refresh",
724     "        Restore",
725     "      Transform",
726     "        Crop",
727     "        Chop",
728     "        Flop",
729     "        Flip",
730     "        Rotate Right",
731     "        Rotate Left",
732     "        Rotate...",
733     "        Shear...",
734     "        Roll...",
735     "        Trim Edges",
736     "      Enhance",
737     "        Brightness...",
738     "        Saturation...",
739     "        Hue...",
740     "        Gamma...",
741     "        Sharpen...",
742     "        Dull",
743     "        Contrast Stretch...",
744     "        Sigmoidal Contrast...",
745     "        Normalize",
746     "        Equalize",
747     "        Negate",
748     "        Grayscale",
749     "        Map...",
750     "        Quantize...",
751     "      Effects",
752     "        Despeckle",
753     "        Emboss",
754     "        Reduce Noise",
755     "        Add Noise",
756     "        Sharpen...",
757     "        Blur...",
758     "        Threshold...",
759     "        Edge Detect...",
760     "        Spread...",
761     "        Shade...",
762     "        Painting...",
763     "        Segment...",
764     "      F/X",
765     "        Solarize...",
766     "        Sepia Tone...",
767     "        Swirl...",
768     "        Implode...",
769     "        Vignette...",
770     "        Wave...",
771     "        Oil Painting...",
772     "        Charcoal Drawing...",
773     "      Image Edit",
774     "        Annotate...",
775     "        Draw...",
776     "        Color...",
777     "        Matte...",
778     "        Composite...",
779     "        Add Border...",
780     "        Add Frame...",
781     "        Comment...",
782     "        Launch...",
783     "        Region of Interest...",
784     "      Miscellany",
785     "        Image Info",
786     "        Zoom Image",
787     "        Show Preview...",
788     "        Show Histogram",
789     "        Show Matte",
790     "        Background...",
791     "        Slide Show",
792     "        Preferences...",
793     "      Help",
794     "        Overview",
795     "        Browse Documentation",
796     "        About Display",
797     "",
798     "  Menu items with a indented triangle have a sub-menu.  They",
799     "  are represented above as the indented items.  To access a",
800     "  sub-menu item, move the pointer to the appropriate menu and",
801     "  press a button and drag.  When you find the desired sub-menu",
802     "  item, release the button and the command is executed.  Move",
803     "  the pointer away from the sub-menu if you decide not to",
804     "  execute a particular command.",
805     "",
806     "KEYBOARD ACCELERATORS",
807     "  Accelerators are one or two key presses that effect a",
808     "  particular command.  The keyboard accelerators that",
809     "  display(1) understands is:",
810     "",
811     "  Ctl+O     Press to open an image from a file.",
812     "",
813     "  space     Press to display the next image.",
814     "",
815     "            If the image is a multi-paged document such as a Postscript",
816     "            document, you can skip ahead several pages by preceding",
817     "            this command with a number.  For example to display the",
818     "            third page beyond the current page, press 3<space>.",
819     "",
820     "  backspace Press to display the former image.",
821     "",
822     "            If the image is a multi-paged document such as a Postscript",
823     "            document, you can skip behind several pages by preceding",
824     "            this command with a number.  For example to display the",
825     "            third page preceding the current page, press 3<backspace>.",
826     "",
827     "  Ctl+S     Press to write the image to a file.",
828     "",
829     "  Ctl+P     Press to print the image to a Postscript printer.",
830     "",
831     "  Ctl+D     Press to delete an image file.",
832     "",
833     "  Ctl+N     Press to create a blank canvas.",
834     "",
835     "  Ctl+Q     Press to discard all images and exit program.",
836     "",
837     "  Ctl+Z     Press to undo last image transformation.",
838     "",
839     "  Ctl+R     Press to redo last image transformation.",
840     "",
841     "  Ctl+X     Press to cut a region of the image.",
842     "",
843     "  Ctl+C     Press to copy a region of the image.",
844     "",
845     "  Ctl+V     Press to paste a region to the image.",
846     "",
847     "  <         Press to half the image size.",
848     "",
849     "  -         Press to return to the original image size.",
850     "",
851     "  >         Press to double the image size.",
852     "",
853     "  %         Press to resize the image to a width and height you",
854     "            specify.",
855     "",
856     "Cmd-A       Press to make any image transformations permanent."
857     "",
858     "            By default, any image size transformations are applied",
859     "            to the original image to create the image displayed on",
860     "            the X server.  However, the transformations are not",
861     "            permanent (i.e. the original image does not change",
862     "            size only the X image does).  For example, if you",
863     "            press > the X image will appear to double in size,",
864     "            but the original image will in fact remain the same size.",
865     "            To force the original image to double in size, press >",
866     "            followed by Cmd-A.",
867     "",
868     "  @         Press to refresh the image window.",
869     "",
870     "  C         Press to cut out a rectangular region of the image.",
871     "",
872     "  [         Press to chop the image.",
873     "",
874     "  H         Press to flop image in the horizontal direction.",
875     "",
876     "  V         Press to flip image in the vertical direction.",
877     "",
878     "  /         Press to rotate the image 90 degrees clockwise.",
879     "",
880     " \\         Press to rotate the image 90 degrees counter-clockwise.",
881     "",
882     "  *         Press to rotate the image the number of degrees you",
883     "            specify.",
884     "",
885     "  S         Press to shear the image the number of degrees you",
886     "            specify.",
887     "",
888     "  R         Press to roll the image.",
889     "",
890     "  T         Press to trim the image edges.",
891     "",
892     "  Shft-H    Press to vary the image hue.",
893     "",
894     "  Shft-S    Press to vary the color saturation.",
895     "",
896     "  Shft-L    Press to vary the color brightness.",
897     "",
898     "  Shft-G    Press to gamma correct the image.",
899     "",
900     "  Shft-C    Press to sharpen the image contrast.",
901     "",
902     "  Shft-Z    Press to dull the image contrast.",
903     "",
904     "  =         Press to perform histogram equalization on the image.",
905     "",
906     "  Shft-N    Press to perform histogram normalization on the image.",
907     "",
908     "  Shft-~    Press to negate the colors of the image.",
909     "",
910     "  .         Press to convert the image colors to gray.",
911     "",
912     "  Shft-#    Press to set the maximum number of unique colors in the",
913     "            image.",
914     "",
915     "  F2        Press to reduce the speckles in an image.",
916     "",
917     "  F3        Press to eliminate peak noise from an image.",
918     "",
919     "  F4        Press to add noise to an image.",
920     "",
921     "  F5        Press to sharpen an image.",
922     "",
923     "  F6        Press to delete an image file.",
924     "",
925     "  F7        Press to threshold the image.",
926     "",
927     "  F8        Press to detect edges within an image.",
928     "",
929     "  F9        Press to emboss an image.",
930     "",
931     "  F10       Press to displace pixels by a random amount.",
932     "",
933     "  F11       Press to negate all pixels above the threshold level.",
934     "",
935     "  F12       Press to shade the image using a distant light source.",
936     "",
937     "  F13       Press to lighten or darken image edges to create a 3-D effect.",
938     "",
939     "  F14       Press to segment the image by color.",
940     "",
941     "  Meta-S    Press to swirl image pixels about the center.",
942     "",
943     "  Meta-I    Press to implode image pixels about the center.",
944     "",
945     "  Meta-W    Press to alter an image along a sine wave.",
946     "",
947     "  Meta-P    Press to simulate an oil painting.",
948     "",
949     "  Meta-C    Press to simulate a charcoal drawing.",
950     "",
951     "  Alt-A     Press to annotate the image with text.",
952     "",
953     "  Alt-D     Press to draw on an image.",
954     "",
955     "  Alt-P     Press to edit an image pixel color.",
956     "",
957     "  Alt-M     Press to edit the image matte information.",
958     "",
959     "  Alt-V     Press to composite the image with another.",
960     "",
961     "  Alt-B     Press to add a border to the image.",
962     "",
963     "  Alt-F     Press to add an ornamental border to the image.",
964     "",
965     "  Alt-Shft-!",
966     "            Press to add an image comment.",
967     "",
968     "  Ctl-A     Press to apply image processing techniques to a region",
969     "            of interest.",
970     "",
971     "  Shft-?    Press to display information about the image.",
972     "",
973     "  Shft-+    Press to map the zoom image window.",
974     "",
975     "  Shft-P    Press to preview an image enhancement, effect, or f/x.",
976     "",
977     "  F1        Press to display helpful information about display(1).",
978     "",
979     "  Find      Press to browse documentation about ImageMagick.",
980     "",
981     "  1-9       Press to change the level of magnification.",
982     "",
983     "  Use the arrow keys to move the image one pixel up, down,",
984     "  left, or right within the magnify window.  Be sure to first",
985     "  map the magnify window by pressing button 2.",
986     "",
987     "  Press ALT and one of the arrow keys to trim off one pixel",
988     "  from any side of the image.",
989     (char *) NULL,
990   },
991   *ImageMatteEditHelp[] =
992   {
993     "Matte information within an image is useful for some",
994     "operations such as image compositing (See IMAGE",
995     "COMPOSITING).  This extra channel usually defines a mask",
996     "which represents a sort of a cookie-cutter for the image.",
997     "This the case when matte is opaque (full coverage) for",
998     "pixels inside the shape, zero outside, and between 0 and",
999     "QuantumRange on the boundary.",
1000     "",
1001     "A small window appears showing the location of the cursor in",
1002     "the image window. You are now in matte edit mode.  To exit",
1003     "immediately, press Dismiss.  In matte edit mode, the Command",
1004     "widget has these options:",
1005     "",
1006     "    Method",
1007     "      point",
1008     "      replace",
1009     "      floodfill",
1010     "      filltoborder",
1011     "      reset",
1012     "    Border Color",
1013     "      black",
1014     "      blue",
1015     "      cyan",
1016     "      green",
1017     "      gray",
1018     "      red",
1019     "      magenta",
1020     "      yellow",
1021     "      white",
1022     "      Browser...",
1023     "    Fuzz",
1024     "      0%",
1025     "      2%",
1026     "      5%",
1027     "      10%",
1028     "      15%",
1029     "      Dialog...",
1030     "    Matte",
1031     "      Opaque",
1032     "      Transparent",
1033     "      Dialog...",
1034     "    Undo",
1035     "    Help",
1036     "    Dismiss",
1037     "",
1038     "Choose a matte editing method from the Method sub-menu of",
1039     "the Command widget.  The point method changes the matte value",
1040     "of any pixel selected with the pointer until the button is",
1041     "is released.  The replace method changes the matte value of",
1042     "any pixel that matches the color of the pixel you select with",
1043     "a button press.  Floodfill changes the matte value of any pixel",
1044     "that matches the color of the pixel you select with a button",
1045     "press and is a neighbor.  Whereas filltoborder changes the matte",
1046     "value any neighbor pixel that is not the border color.  Finally",
1047     "reset changes the entire image to the designated matte value.",
1048     "",
1049     "Choose Matte Value and pick Opaque or Transarent.  For other values",
1050     "select the Dialog entry.  Here a dialog appears requesting a matte",
1051     "value.  The value you select is assigned as the opacity value of the",
1052     "selected pixel or pixels.",
1053     "",
1054     "Now, press any button to select a pixel within the image",
1055     "window to change its matte value.",
1056     "",
1057     "If the Magnify widget is mapped, it can be helpful in positioning",
1058     "your pointer within the image (refer to button 2).",
1059     "",
1060     "Matte information is only valid in a DirectClass image.",
1061     "Therefore, any PseudoClass image is promoted to DirectClass",
1062     "(see miff(5)).  Note that matte information for PseudoClass",
1063     "is not retained for colormapped X server visuals (e.g.",
1064     "StaticColor, StaticColor, GrayScale, PseudoColor) unless you",
1065     "immediately save your image to a file (refer to Write).",
1066     "Correct matte editing behavior may require a TrueColor or",
1067     "DirectColor visual or a Standard Colormap.",
1068     (char *) NULL,
1069   },
1070   *ImagePanHelp[] =
1071   {
1072     "When an image exceeds the width or height of the X server",
1073     "screen, display maps a small panning icon.  The rectangle",
1074     "within the panning icon shows the area that is currently",
1075     "displayed in the image window.  To pan about the image,",
1076     "press any button and drag the pointer within the panning",
1077     "icon.  The pan rectangle moves with the pointer and the",
1078     "image window is updated to reflect the location of the",
1079     "rectangle within the panning icon.  When you have selected",
1080     "the area of the image you wish to view, release the button.",
1081     "",
1082     "Use the arrow keys to pan the image one pixel up, down,",
1083     "left, or right within the image window.",
1084     "",
1085     "The panning icon is withdrawn if the image becomes smaller",
1086     "than the dimensions of the X server screen.",
1087     (char *) NULL,
1088   },
1089   *ImagePasteHelp[] =
1090   {
1091     "A small window appears showing the location of the cursor in",
1092     "the image window. You are now in paste mode.  To exit",
1093     "immediately, press Dismiss.  In paste mode, the Command",
1094     "widget has these options:",
1095     "",
1096     "    Operators",
1097     "      over",
1098     "      in",
1099     "      out",
1100     "      atop",
1101     "      xor",
1102     "      plus",
1103     "      minus",
1104     "      add",
1105     "      subtract",
1106     "      difference",
1107     "      replace",
1108     "    Help",
1109     "    Dismiss",
1110     "",
1111     "Choose a composite operation from the Operators sub-menu of",
1112     "the Command widget.  How each operator behaves is described",
1113     "below.  Image window is the image currently displayed on",
1114     "your X server and image is the image obtained with the File",
1115     "Browser widget.",
1116     "",
1117     "Over     The result is the union of the two image shapes,",
1118     "         with image obscuring image window in the region of",
1119     "         overlap.",
1120     "",
1121     "In       The result is simply image cut by the shape of",
1122     "         image window.  None of the image data of image",
1123     "         window is in the result.",
1124     "",
1125     "Out      The resulting image is image with the shape of",
1126     "         image window cut out.",
1127     "",
1128     "Atop     The result is the same shape as image image window,",
1129     "         with image obscuring image window where the image",
1130     "         shapes overlap.  Note this differs from over",
1131     "         because the portion of image outside image window's",
1132     "         shape does not appear in the result.",
1133     "",
1134     "Xor      The result is the image data from both image and",
1135     "         image window that is outside the overlap region.",
1136     "         The overlap region is blank.",
1137     "",
1138     "Plus     The result is just the sum of the image data.",
1139     "         Output values are cropped to QuantumRange (no overflow).",
1140     "         This operation is independent of the matte",
1141     "         channels.",
1142     "",
1143     "Minus    The result of image - image window, with underflow",
1144     "         cropped to zero.",
1145     "",
1146     "Add      The result of image + image window, with overflow",
1147     "         wrapping around (mod 256).",
1148     "",
1149     "Subtract The result of image - image window, with underflow",
1150     "         wrapping around (mod 256).  The add and subtract",
1151     "         operators can be used to perform reversible",
1152     "         transformations.",
1153     "",
1154     "Difference",
1155     "         The result of abs(image - image window).  This",
1156     "         useful for comparing two very similar images.",
1157     "",
1158     "Copy     The resulting image is image window replaced with",
1159     "         image.  Here the matte information is ignored.",
1160     "",
1161     "CopyRed  The red layer of the image window is replace with",
1162     "         the red layer of the image.  The other layers are",
1163     "         untouched.",
1164     "",
1165     "CopyGreen",
1166     "         The green layer of the image window is replace with",
1167     "         the green layer of the image.  The other layers are",
1168     "         untouched.",
1169     "",
1170     "CopyBlue The blue layer of the image window is replace with",
1171     "         the blue layer of the image.  The other layers are",
1172     "         untouched.",
1173     "",
1174     "CopyOpacity",
1175     "         The matte layer of the image window is replace with",
1176     "         the matte layer of the image.  The other layers are",
1177     "         untouched.",
1178     "",
1179     "The image compositor requires a matte, or alpha channel in",
1180     "the image for some operations.  This extra channel usually",
1181     "defines a mask which represents a sort of a cookie-cutter",
1182     "for the image.  This the case when matte is opaque (full",
1183     "coverage) for pixels inside the shape, zero outside, and",
1184     "between 0 and QuantumRange on the boundary.  If image does not",
1185     "have a matte channel, it is initialized with 0 for any pixel",
1186     "matching in color to pixel location (0,0), otherwise QuantumRange.",
1187     "",
1188     "Note that matte information for image window is not retained",
1189     "for colormapped X server visuals (e.g. StaticColor,",
1190     "StaticColor, GrayScale, PseudoColor).  Correct compositing",
1191     "behavior may require a TrueColor or DirectColor visual or a",
1192     "Standard Colormap.",
1193     "",
1194     "Choosing a composite operator is optional.  The default",
1195     "operator is replace.  However, you must choose a location to",
1196     "paste your image and press button 1.  Press and hold the",
1197     "button before releasing and an outline of the image will",
1198     "appear to help you identify your location.",
1199     "",
1200     "The actual colors of the pasted image is saved.  However,",
1201     "the color that appears in image window may be different.",
1202     "For example, on a monochrome screen image window will appear",
1203     "black or white even though your pasted image may have",
1204     "many colors.  If the image is saved to a file it is written",
1205     "with the correct colors.  To assure the correct colors are",
1206     "saved in the final image, any PseudoClass image is promoted",
1207     "to DirectClass (see miff(5)).  To force a PseudoClass image",
1208     "to remain PseudoClass, use -colors.",
1209     (char *) NULL,
1210   },
1211   *ImageROIHelp[] =
1212   {
1213     "In region of interest mode, the Command widget has these",
1214     "options:",
1215     "",
1216     "    Help",
1217     "    Dismiss",
1218     "",
1219     "To define a region of interest, press button 1 and drag.",
1220     "The region of interest is defined by a highlighted rectangle",
1221     "that expands or contracts as it follows the pointer.  Once",
1222     "you are satisfied with the region of interest, release the",
1223     "button.  You are now in apply mode.  In apply mode the",
1224     "Command widget has these options:",
1225     "",
1226     "      File",
1227     "        Save...",
1228     "        Print...",
1229     "      Edit",
1230     "        Undo",
1231     "        Redo",
1232     "      Transform",
1233     "        Flop",
1234     "        Flip",
1235     "        Rotate Right",
1236     "        Rotate Left",
1237     "      Enhance",
1238     "        Hue...",
1239     "        Saturation...",
1240     "        Brightness...",
1241     "        Gamma...",
1242     "        Spiff",
1243     "        Dull",
1244     "        Contrast Stretch",
1245     "        Sigmoidal Contrast...",
1246     "        Normalize",
1247     "        Equalize",
1248     "        Negate",
1249     "        Grayscale",
1250     "        Map...",
1251     "        Quantize...",
1252     "      Effects",
1253     "        Despeckle",
1254     "        Emboss",
1255     "        Reduce Noise",
1256     "        Sharpen...",
1257     "        Blur...",
1258     "        Threshold...",
1259     "        Edge Detect...",
1260     "        Spread...",
1261     "        Shade...",
1262     "        Raise...",
1263     "        Segment...",
1264     "      F/X",
1265     "        Solarize...",
1266     "        Sepia Tone...",
1267     "        Swirl...",
1268     "        Implode...",
1269     "        Vignette...",
1270     "        Wave...",
1271     "        Oil Painting...",
1272     "        Charcoal Drawing...",
1273     "      Miscellany",
1274     "        Image Info",
1275     "        Zoom Image",
1276     "        Show Preview...",
1277     "        Show Histogram",
1278     "        Show Matte",
1279     "      Help",
1280     "      Dismiss",
1281     "",
1282     "You can make adjustments to the region of interest by moving",
1283     "the pointer to one of the rectangle corners, pressing a",
1284     "button, and dragging.  Finally, choose an image processing",
1285     "technique from the Command widget.  You can choose more than",
1286     "one image processing technique to apply to an area.",
1287     "Alternatively, you can move the region of interest before",
1288     "applying another image processing technique.  To exit, press",
1289     "Dismiss.",
1290     (char *) NULL,
1291   },
1292   *ImageRotateHelp[] =
1293   {
1294     "In rotate mode, the Command widget has these options:",
1295     "",
1296     "    Pixel Color",
1297     "      black",
1298     "      blue",
1299     "      cyan",
1300     "      green",
1301     "      gray",
1302     "      red",
1303     "      magenta",
1304     "      yellow",
1305     "      white",
1306     "      Browser...",
1307     "    Direction",
1308     "      horizontal",
1309     "      vertical",
1310     "    Help",
1311     "    Dismiss",
1312     "",
1313     "Choose a background color from the Pixel Color sub-menu.",
1314     "Additional background colors can be specified with the color",
1315     "browser.  You can change the menu colors by setting the X",
1316     "resources pen1 through pen9.",
1317     "",
1318     "If you choose the color browser and press Grab, you can",
1319     "select the background color by moving the pointer to the",
1320     "desired color on the screen and press any button.",
1321     "",
1322     "Choose a point in the image window and press this button and",
1323     "hold.  Next, move the pointer to another location in the",
1324     "image.  As you move a line connects the initial location and",
1325     "the pointer.  When you release the button, the degree of",
1326     "image rotation is determined by the slope of the line you",
1327     "just drew.  The slope is relative to the direction you",
1328     "choose from the Direction sub-menu of the Command widget.",
1329     "",
1330     "To cancel the image rotation, move the pointer back to the",
1331     "starting point of the line and release the button.",
1332     (char *) NULL,
1333   };
1334 \f
1335 /*
1336   Enumeration declarations.
1337 */
1338 typedef enum
1339 {
1340   CopyMode,
1341   CropMode,
1342   CutMode
1343 } ClipboardMode;
1344
1345 typedef enum
1346 {
1347   OpenCommand,
1348   NextCommand,
1349   FormerCommand,
1350   SelectCommand,
1351   SaveCommand,
1352   PrintCommand,
1353   DeleteCommand,
1354   NewCommand,
1355   VisualDirectoryCommand,
1356   QuitCommand,
1357   UndoCommand,
1358   RedoCommand,
1359   CutCommand,
1360   CopyCommand,
1361   PasteCommand,
1362   HalfSizeCommand,
1363   OriginalSizeCommand,
1364   DoubleSizeCommand,
1365   ResizeCommand,
1366   ApplyCommand,
1367   RefreshCommand,
1368   RestoreCommand,
1369   CropCommand,
1370   ChopCommand,
1371   FlopCommand,
1372   FlipCommand,
1373   RotateRightCommand,
1374   RotateLeftCommand,
1375   RotateCommand,
1376   ShearCommand,
1377   RollCommand,
1378   TrimCommand,
1379   HueCommand,
1380   SaturationCommand,
1381   BrightnessCommand,
1382   GammaCommand,
1383   SpiffCommand,
1384   DullCommand,
1385   ContrastStretchCommand,
1386   SigmoidalContrastCommand,
1387   NormalizeCommand,
1388   EqualizeCommand,
1389   NegateCommand,
1390   GrayscaleCommand,
1391   MapCommand,
1392   QuantizeCommand,
1393   DespeckleCommand,
1394   EmbossCommand,
1395   ReduceNoiseCommand,
1396   AddNoiseCommand,
1397   SharpenCommand,
1398   BlurCommand,
1399   ThresholdCommand,
1400   EdgeDetectCommand,
1401   SpreadCommand,
1402   ShadeCommand,
1403   RaiseCommand,
1404   SegmentCommand,
1405   SolarizeCommand,
1406   SepiaToneCommand,
1407   SwirlCommand,
1408   ImplodeCommand,
1409   VignetteCommand,
1410   WaveCommand,
1411   OilPaintCommand,
1412   CharcoalDrawCommand,
1413   AnnotateCommand,
1414   DrawCommand,
1415   ColorCommand,
1416   MatteCommand,
1417   CompositeCommand,
1418   AddBorderCommand,
1419   AddFrameCommand,
1420   CommentCommand,
1421   LaunchCommand,
1422   RegionofInterestCommand,
1423   ROIHelpCommand,
1424   ROIDismissCommand,
1425   InfoCommand,
1426   ZoomCommand,
1427   ShowPreviewCommand,
1428   ShowHistogramCommand,
1429   ShowMatteCommand,
1430   BackgroundCommand,
1431   SlideShowCommand,
1432   PreferencesCommand,
1433   HelpCommand,
1434   BrowseDocumentationCommand,
1435   VersionCommand,
1436   SaveToUndoBufferCommand,
1437   FreeBuffersCommand,
1438   NullCommand
1439 } CommandType;
1440
1441 typedef enum
1442 {
1443   AnnotateNameCommand,
1444   AnnotateFontColorCommand,
1445   AnnotateBackgroundColorCommand,
1446   AnnotateRotateCommand,
1447   AnnotateHelpCommand,
1448   AnnotateDismissCommand,
1449   TextHelpCommand,
1450   TextApplyCommand,
1451   ChopDirectionCommand,
1452   ChopHelpCommand,
1453   ChopDismissCommand,
1454   HorizontalChopCommand,
1455   VerticalChopCommand,
1456   ColorEditMethodCommand,
1457   ColorEditColorCommand,
1458   ColorEditBorderCommand,
1459   ColorEditFuzzCommand,
1460   ColorEditUndoCommand,
1461   ColorEditHelpCommand,
1462   ColorEditDismissCommand,
1463   CompositeOperatorsCommand,
1464   CompositeDissolveCommand,
1465   CompositeDisplaceCommand,
1466   CompositeHelpCommand,
1467   CompositeDismissCommand,
1468   CropHelpCommand,
1469   CropDismissCommand,
1470   RectifyCopyCommand,
1471   RectifyHelpCommand,
1472   RectifyDismissCommand,
1473   DrawElementCommand,
1474   DrawColorCommand,
1475   DrawStippleCommand,
1476   DrawWidthCommand,
1477   DrawUndoCommand,
1478   DrawHelpCommand,
1479   DrawDismissCommand,
1480   MatteEditMethod,
1481   MatteEditBorderCommand,
1482   MatteEditFuzzCommand,
1483   MatteEditValueCommand,
1484   MatteEditUndoCommand,
1485   MatteEditHelpCommand,
1486   MatteEditDismissCommand,
1487   PasteOperatorsCommand,
1488   PasteHelpCommand,
1489   PasteDismissCommand,
1490   RotateColorCommand,
1491   RotateDirectionCommand,
1492   RotateCropCommand,
1493   RotateSharpenCommand,
1494   RotateHelpCommand,
1495   RotateDismissCommand,
1496   HorizontalRotateCommand,
1497   VerticalRotateCommand,
1498   TileLoadCommand,
1499   TileNextCommand,
1500   TileFormerCommand,
1501   TileDeleteCommand,
1502   TileUpdateCommand
1503 } ModeType;
1504 \f
1505 /*
1506   Stipples.
1507 */
1508 #define BricksWidth  20
1509 #define BricksHeight  20
1510 #define DiagonalWidth  16
1511 #define DiagonalHeight  16
1512 #define HighlightWidth  8
1513 #define HighlightHeight  8
1514 #define OpaqueWidth  8
1515 #define OpaqueHeight  8
1516 #define ScalesWidth  16
1517 #define ScalesHeight  16
1518 #define ShadowWidth  8
1519 #define ShadowHeight  8
1520 #define VerticalWidth  16
1521 #define VerticalHeight  16
1522 #define WavyWidth  16
1523 #define WavyHeight  16
1524 \f
1525 /*
1526   Constant declaration.
1527 */
1528 static const int
1529   RoiDelta = 8;
1530
1531 static const unsigned char
1532   BricksBitmap[] =
1533   {
1534     0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00,
1535     0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01,
1536     0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00,
1537     0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f,
1538     0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01
1539   },
1540   DiagonalBitmap[] =
1541   {
1542     0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88,
1543     0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22,
1544     0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22
1545   },
1546   ScalesBitmap[] =
1547   {
1548     0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3, 0x80, 0x80, 0x80, 0x80,
1549     0x41, 0x41, 0x3e, 0x3e, 0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3,
1550     0x80, 0x80, 0x80, 0x80, 0x41, 0x41, 0x3e, 0x3e
1551   },
1552   VerticalBitmap[] =
1553   {
1554     0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1555     0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1556     0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11
1557   },
1558   WavyBitmap[] =
1559   {
1560     0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfb, 0xff,
1561     0xe7, 0xff, 0x1f, 0xff, 0xff, 0xf8, 0xff, 0xe7, 0xff, 0xdf, 0xff, 0xbf,
1562     0xff, 0xbf, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f
1563   };
1564 \f
1565 /*
1566   Function prototypes.
1567 */
1568 static CommandType
1569   XImageWindowCommand(Display *,XResourceInfo *,XWindows *,
1570     const MagickStatusType,KeySym,Image **,ExceptionInfo *);
1571
1572 static Image
1573   *XMagickCommand(Display *,XResourceInfo *,XWindows *,const CommandType,
1574     Image **,ExceptionInfo *),
1575   *XOpenImage(Display *,XResourceInfo *,XWindows *,const MagickBooleanType),
1576   *XTileImage(Display *,XResourceInfo *,XWindows *,Image *,XEvent *,
1577     ExceptionInfo *),
1578   *XVisualDirectoryImage(Display *,XResourceInfo *,XWindows *,
1579     ExceptionInfo *);
1580
1581 static MagickBooleanType
1582   XAnnotateEditImage(Display *,XResourceInfo *,XWindows *,Image *,
1583     ExceptionInfo *),
1584   XBackgroundImage(Display *,XResourceInfo *,XWindows *,Image **,
1585     ExceptionInfo *),
1586   XChopImage(Display *,XResourceInfo *,XWindows *,Image **,
1587     ExceptionInfo *),
1588   XCropImage(Display *,XResourceInfo *,XWindows *,Image *,const ClipboardMode,
1589     ExceptionInfo *),
1590   XColorEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1591     ExceptionInfo *),
1592   XCompositeImage(Display *,XResourceInfo *,XWindows *,Image *,
1593     ExceptionInfo *),
1594   XConfigureImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1595   XDrawEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1596     ExceptionInfo *),
1597   XMatteEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1598     ExceptionInfo *),
1599   XPasteImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1600   XPrintImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1601   XRotateImage(Display *,XResourceInfo *,XWindows *,double,Image **,
1602     ExceptionInfo *),
1603   XROIImage(Display *,XResourceInfo *,XWindows *,Image **,ExceptionInfo *),
1604   XSaveImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1605   XTrimImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *);
1606
1607 static void
1608   XDrawPanRectangle(Display *,XWindows *),
1609   XImageCache(Display *,XResourceInfo *,XWindows *,const CommandType,Image **,
1610     ExceptionInfo *),
1611   XMagnifyImage(Display *,XWindows *,XEvent *,ExceptionInfo *),
1612   XMakePanImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1613   XPanImage(Display *,XWindows *,XEvent *,ExceptionInfo *),
1614   XMagnifyWindowCommand(Display *,XWindows *,const MagickStatusType,
1615     const KeySym,ExceptionInfo *),
1616   XSetCropGeometry(Display *,XWindows *,RectangleInfo *,Image *),
1617   XScreenEvent(Display *,XWindows *,XEvent *,ExceptionInfo *),
1618   XTranslateImage(Display *,XWindows *,Image *,const KeySym);
1619 \f
1620 /*
1621 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1622 %                                                                             %
1623 %                                                                             %
1624 %                                                                             %
1625 %   D i s p l a y I m a g e s                                                 %
1626 %                                                                             %
1627 %                                                                             %
1628 %                                                                             %
1629 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1630 %
1631 %  DisplayImages() displays an image sequence to any X window screen.  It
1632 %  returns a value other than 0 if successful.  Check the exception member
1633 %  of image to determine the reason for any failure.
1634 %
1635 %  The format of the DisplayImages method is:
1636 %
1637 %      MagickBooleanType DisplayImages(const ImageInfo *image_info,
1638 %        Image *images,ExceptionInfo *exception)
1639 %
1640 %  A description of each parameter follows:
1641 %
1642 %    o image_info: the image info.
1643 %
1644 %    o image: the image.
1645 %
1646 %    o exception: return any errors or warnings in this structure.
1647 %
1648 */
1649 MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
1650   Image *images,ExceptionInfo *exception)
1651 {
1652   char
1653     *argv[1];
1654
1655   Display
1656     *display;
1657
1658   Image
1659     *image;
1660
1661   register ssize_t
1662     i;
1663
1664   size_t
1665     state;
1666
1667   XrmDatabase
1668     resource_database;
1669
1670   XResourceInfo
1671     resource_info;
1672
1673   assert(image_info != (const ImageInfo *) NULL);
1674   assert(image_info->signature == MagickSignature);
1675   assert(images != (Image *) NULL);
1676   assert(images->signature == MagickSignature);
1677   if (images->debug != MagickFalse)
1678     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
1679   display=XOpenDisplay(image_info->server_name);
1680   if (display == (Display *) NULL)
1681     {
1682       (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
1683         "UnableToOpenXServer","`%s'",XDisplayName(image_info->server_name));
1684       return(MagickFalse);
1685     }
1686   if (exception->severity != UndefinedException)
1687     CatchException(exception);
1688   (void) XSetErrorHandler(XError);
1689   resource_database=XGetResourceDatabase(display,GetClientName());
1690   (void) ResetMagickMemory(&resource_info,0,sizeof(resource_info));
1691   XGetResourceInfo(image_info,resource_database,GetClientName(),&resource_info);
1692   if (image_info->page != (char *) NULL)
1693     resource_info.image_geometry=AcquireString(image_info->page);
1694   resource_info.immutable=MagickTrue;
1695   argv[0]=AcquireString(GetClientName());
1696   state=DefaultState;
1697   for (i=0; (state & ExitState) == 0; i++)
1698   {
1699     if ((images->iterations != 0) && (i >= (ssize_t) images->iterations))
1700       break;
1701     image=GetImageFromList(images,i % GetImageListLength(images));
1702     (void) XDisplayImage(display,&resource_info,argv,1,&image,&state,exception);
1703   }
1704   SetErrorHandler((ErrorHandler) NULL);
1705   SetWarningHandler((WarningHandler) NULL);
1706   argv[0]=DestroyString(argv[0]);
1707   (void) XCloseDisplay(display);
1708   XDestroyResourceInfo(&resource_info);
1709   if (exception->severity != UndefinedException)
1710     return(MagickFalse);
1711   return(MagickTrue);
1712 }
1713 \f
1714 /*
1715 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1716 %                                                                             %
1717 %                                                                             %
1718 %                                                                             %
1719 %   R e m o t e D i s p l a y C o m m a n d                                   %
1720 %                                                                             %
1721 %                                                                             %
1722 %                                                                             %
1723 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1724 %
1725 %  RemoteDisplayCommand() encourages a remote display program to display the
1726 %  specified image filename.
1727 %
1728 %  The format of the RemoteDisplayCommand method is:
1729 %
1730 %      MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1731 %        const char *window,const char *filename,ExceptionInfo *exception)
1732 %
1733 %  A description of each parameter follows:
1734 %
1735 %    o image_info: the image info.
1736 %
1737 %    o window: Specifies the name or id of an X window.
1738 %
1739 %    o filename: the name of the image filename to display.
1740 %
1741 %    o exception: return any errors or warnings in this structure.
1742 %
1743 */
1744 MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1745   const char *window,const char *filename,ExceptionInfo *exception)
1746 {
1747   Display
1748     *display;
1749
1750   MagickStatusType
1751     status;
1752
1753   assert(image_info != (const ImageInfo *) NULL);
1754   assert(image_info->signature == MagickSignature);
1755   assert(filename != (char *) NULL);
1756   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
1757   display=XOpenDisplay(image_info->server_name);
1758   if (display == (Display *) NULL)
1759     {
1760       (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
1761         "UnableToOpenXServer","`%s'",XDisplayName(image_info->server_name));
1762       return(MagickFalse);
1763     }
1764   (void) XSetErrorHandler(XError);
1765   status=XRemoteCommand(display,window,filename);
1766   (void) XCloseDisplay(display);
1767   return(status != 0 ? MagickTrue : MagickFalse);
1768 }
1769 \f
1770 /*
1771 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1772 %                                                                             %
1773 %                                                                             %
1774 %                                                                             %
1775 +   X A n n o t a t e E d i t I m a g e                                       %
1776 %                                                                             %
1777 %                                                                             %
1778 %                                                                             %
1779 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1780 %
1781 %  XAnnotateEditImage() annotates the image with text.
1782 %
1783 %  The format of the XAnnotateEditImage method is:
1784 %
1785 %      MagickBooleanType XAnnotateEditImage(Display *display,
1786 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
1787 %        ExceptionInfo *exception)
1788 %
1789 %  A description of each parameter follows:
1790 %
1791 %    o display: Specifies a connection to an X server;  returned from
1792 %      XOpenDisplay.
1793 %
1794 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
1795 %
1796 %    o windows: Specifies a pointer to a XWindows structure.
1797 %
1798 %    o image: the image; returned from ReadImage.
1799 %
1800 */
1801
1802 static inline ssize_t MagickMax(const ssize_t x,const ssize_t y)
1803 {
1804   if (x > y)
1805     return(x);
1806   return(y);
1807 }
1808
1809 static inline ssize_t MagickMin(const ssize_t x,const ssize_t y)
1810 {
1811   if (x < y)
1812     return(x);
1813   return(y);
1814 }
1815
1816 static MagickBooleanType XAnnotateEditImage(Display *display,
1817   XResourceInfo *resource_info,XWindows *windows,Image *image,
1818   ExceptionInfo *exception)
1819 {
1820   static const char
1821     *AnnotateMenu[] =
1822     {
1823       "Font Name",
1824       "Font Color",
1825       "Box Color",
1826       "Rotate Text",
1827       "Help",
1828       "Dismiss",
1829       (char *) NULL
1830     },
1831     *TextMenu[] =
1832     {
1833       "Help",
1834       "Apply",
1835       (char *) NULL
1836     };
1837
1838   static const ModeType
1839     AnnotateCommands[] =
1840     {
1841       AnnotateNameCommand,
1842       AnnotateFontColorCommand,
1843       AnnotateBackgroundColorCommand,
1844       AnnotateRotateCommand,
1845       AnnotateHelpCommand,
1846       AnnotateDismissCommand
1847     },
1848     TextCommands[] =
1849     {
1850       TextHelpCommand,
1851       TextApplyCommand
1852     };
1853
1854   static MagickBooleanType
1855     transparent_box = MagickTrue,
1856     transparent_pen = MagickFalse;
1857
1858   static MagickRealType
1859     degrees = 0.0;
1860
1861   static unsigned int
1862     box_id = MaxNumberPens-2,
1863     font_id = 0,
1864     pen_id = 0;
1865
1866   char
1867     command[MaxTextExtent],
1868     text[MaxTextExtent];
1869
1870   const char
1871     *ColorMenu[MaxNumberPens+1];
1872
1873   Cursor
1874     cursor;
1875
1876   GC
1877     annotate_context;
1878
1879   int
1880     id,
1881     pen_number,
1882     status,
1883     x,
1884     y;
1885
1886   KeySym
1887     key_symbol;
1888
1889   register char
1890     *p;
1891
1892   register ssize_t
1893     i;
1894
1895   unsigned int
1896     height,
1897     width;
1898
1899   size_t
1900     state;
1901
1902   XAnnotateInfo
1903     *annotate_info,
1904     *previous_info;
1905
1906   XColor
1907     color;
1908
1909   XFontStruct
1910     *font_info;
1911
1912   XEvent
1913     event,
1914     text_event;
1915
1916   /*
1917     Map Command widget.
1918   */
1919   (void) CloneString(&windows->command.name,"Annotate");
1920   windows->command.data=4;
1921   (void) XCommandWidget(display,windows,AnnotateMenu,(XEvent *) NULL);
1922   (void) XMapRaised(display,windows->command.id);
1923   XClientMessage(display,windows->image.id,windows->im_protocols,
1924     windows->im_update_widget,CurrentTime);
1925   /*
1926     Track pointer until button 1 is pressed.
1927   */
1928   XQueryPosition(display,windows->image.id,&x,&y);
1929   (void) XSelectInput(display,windows->image.id,
1930     windows->image.attributes.event_mask | PointerMotionMask);
1931   cursor=XCreateFontCursor(display,XC_left_side);
1932   (void) XCheckDefineCursor(display,windows->image.id,cursor);
1933   state=DefaultState;
1934   do
1935   {
1936     if (windows->info.mapped != MagickFalse)
1937       {
1938         /*
1939           Display pointer position.
1940         */
1941         (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
1942           x+windows->image.x,y+windows->image.y);
1943         XInfoWidget(display,windows,text);
1944       }
1945     /*
1946       Wait for next event.
1947     */
1948     XScreenEvent(display,windows,&event,exception);
1949     if (event.xany.window == windows->command.id)
1950       {
1951         /*
1952           Select a command from the Command widget.
1953         */
1954         id=XCommandWidget(display,windows,AnnotateMenu,&event);
1955         (void) XCheckDefineCursor(display,windows->image.id,cursor);
1956         if (id < 0)
1957           continue;
1958         switch (AnnotateCommands[id])
1959         {
1960           case AnnotateNameCommand:
1961           {
1962             const char
1963               *FontMenu[MaxNumberFonts];
1964
1965             int
1966               font_number;
1967
1968             /*
1969               Initialize menu selections.
1970             */
1971             for (i=0; i < MaxNumberFonts; i++)
1972               FontMenu[i]=resource_info->font_name[i];
1973             FontMenu[MaxNumberFonts-2]="Browser...";
1974             FontMenu[MaxNumberFonts-1]=(const char *) NULL;
1975             /*
1976               Select a font name from the pop-up menu.
1977             */
1978             font_number=XMenuWidget(display,windows,AnnotateMenu[id],
1979               (const char **) FontMenu,command);
1980             if (font_number < 0)
1981               break;
1982             if (font_number == (MaxNumberFonts-2))
1983               {
1984                 static char
1985                   font_name[MaxTextExtent] = "fixed";
1986
1987                 /*
1988                   Select a font name from a browser.
1989                 */
1990                 resource_info->font_name[font_number]=font_name;
1991                 XFontBrowserWidget(display,windows,"Select",font_name);
1992                 if (*font_name == '\0')
1993                   break;
1994               }
1995             /*
1996               Initialize font info.
1997             */
1998             font_info=XLoadQueryFont(display,resource_info->font_name[
1999               font_number]);
2000             if (font_info == (XFontStruct *) NULL)
2001               {
2002                 XNoticeWidget(display,windows,"Unable to load font:",
2003                   resource_info->font_name[font_number]);
2004                 break;
2005               }
2006             font_id=(unsigned int) font_number;
2007             (void) XFreeFont(display,font_info);
2008             break;
2009           }
2010           case AnnotateFontColorCommand:
2011           {
2012             /*
2013               Initialize menu selections.
2014             */
2015             for (i=0; i < (int) (MaxNumberPens-2); i++)
2016               ColorMenu[i]=resource_info->pen_colors[i];
2017             ColorMenu[MaxNumberPens-2]="transparent";
2018             ColorMenu[MaxNumberPens-1]="Browser...";
2019             ColorMenu[MaxNumberPens]=(const char *) NULL;
2020             /*
2021               Select a pen color from the pop-up menu.
2022             */
2023             pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
2024               (const char **) ColorMenu,command);
2025             if (pen_number < 0)
2026               break;
2027             transparent_pen=pen_number == (MaxNumberPens-2) ? MagickTrue :
2028               MagickFalse;
2029             if (transparent_pen != MagickFalse)
2030               break;
2031             if (pen_number == (MaxNumberPens-1))
2032               {
2033                 static char
2034                   color_name[MaxTextExtent] = "gray";
2035
2036                 /*
2037                   Select a pen color from a dialog.
2038                 */
2039                 resource_info->pen_colors[pen_number]=color_name;
2040                 XColorBrowserWidget(display,windows,"Select",color_name);
2041                 if (*color_name == '\0')
2042                   break;
2043               }
2044             /*
2045               Set pen color.
2046             */
2047             (void) XParseColor(display,windows->map_info->colormap,
2048               resource_info->pen_colors[pen_number],&color);
2049             XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
2050               (unsigned int) MaxColors,&color);
2051             windows->pixel_info->pen_colors[pen_number]=color;
2052             pen_id=(unsigned int) pen_number;
2053             break;
2054           }
2055           case AnnotateBackgroundColorCommand:
2056           {
2057             /*
2058               Initialize menu selections.
2059             */
2060             for (i=0; i < (int) (MaxNumberPens-2); i++)
2061               ColorMenu[i]=resource_info->pen_colors[i];
2062             ColorMenu[MaxNumberPens-2]="transparent";
2063             ColorMenu[MaxNumberPens-1]="Browser...";
2064             ColorMenu[MaxNumberPens]=(const char *) NULL;
2065             /*
2066               Select a pen color from the pop-up menu.
2067             */
2068             pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
2069               (const char **) ColorMenu,command);
2070             if (pen_number < 0)
2071               break;
2072             transparent_box=pen_number == (MaxNumberPens-2) ? MagickTrue :
2073               MagickFalse;
2074             if (transparent_box != MagickFalse)
2075               break;
2076             if (pen_number == (MaxNumberPens-1))
2077               {
2078                 static char
2079                   color_name[MaxTextExtent] = "gray";
2080
2081                 /*
2082                   Select a pen color from a dialog.
2083                 */
2084                 resource_info->pen_colors[pen_number]=color_name;
2085                 XColorBrowserWidget(display,windows,"Select",color_name);
2086                 if (*color_name == '\0')
2087                   break;
2088               }
2089             /*
2090               Set pen color.
2091             */
2092             (void) XParseColor(display,windows->map_info->colormap,
2093               resource_info->pen_colors[pen_number],&color);
2094             XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
2095               (unsigned int) MaxColors,&color);
2096             windows->pixel_info->pen_colors[pen_number]=color;
2097             box_id=(unsigned int) pen_number;
2098             break;
2099           }
2100           case AnnotateRotateCommand:
2101           {
2102             int
2103               entry;
2104
2105             static char
2106               angle[MaxTextExtent] = "30.0";
2107
2108             static const char
2109               *RotateMenu[] =
2110               {
2111                 "-90",
2112                 "-45",
2113                 "-30",
2114                 "0",
2115                 "30",
2116                 "45",
2117                 "90",
2118                 "180",
2119                 "Dialog...",
2120                 (char *) NULL,
2121               };
2122
2123             /*
2124               Select a command from the pop-up menu.
2125             */
2126             entry=XMenuWidget(display,windows,AnnotateMenu[id],RotateMenu,
2127               command);
2128             if (entry < 0)
2129               break;
2130             if (entry != 8)
2131               {
2132                 degrees=StringToDouble(RotateMenu[entry],(char **) NULL);
2133                 break;
2134               }
2135             (void) XDialogWidget(display,windows,"OK","Enter rotation angle:",
2136               angle);
2137             if (*angle == '\0')
2138               break;
2139             degrees=StringToDouble(angle,(char **) NULL);
2140             break;
2141           }
2142           case AnnotateHelpCommand:
2143           {
2144             XTextViewWidget(display,resource_info,windows,MagickFalse,
2145               "Help Viewer - Image Annotation",ImageAnnotateHelp);
2146             break;
2147           }
2148           case AnnotateDismissCommand:
2149           {
2150             /*
2151               Prematurely exit.
2152             */
2153             state|=EscapeState;
2154             state|=ExitState;
2155             break;
2156           }
2157           default:
2158             break;
2159         }
2160         continue;
2161       }
2162     switch (event.type)
2163     {
2164       case ButtonPress:
2165       {
2166         if (event.xbutton.button != Button1)
2167           break;
2168         if (event.xbutton.window != windows->image.id)
2169           break;
2170         /*
2171           Change to text entering mode.
2172         */
2173         x=event.xbutton.x;
2174         y=event.xbutton.y;
2175         state|=ExitState;
2176         break;
2177       }
2178       case ButtonRelease:
2179         break;
2180       case Expose:
2181         break;
2182       case KeyPress:
2183       {
2184         if (event.xkey.window != windows->image.id)
2185           break;
2186         /*
2187           Respond to a user key press.
2188         */
2189         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2190           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2191         switch ((int) key_symbol)
2192         {
2193           case XK_Escape:
2194           case XK_F20:
2195           {
2196             /*
2197               Prematurely exit.
2198             */
2199             state|=EscapeState;
2200             state|=ExitState;
2201             break;
2202           }
2203           case XK_F1:
2204           case XK_Help:
2205           {
2206             XTextViewWidget(display,resource_info,windows,MagickFalse,
2207               "Help Viewer - Image Annotation",ImageAnnotateHelp);
2208             break;
2209           }
2210           default:
2211           {
2212             (void) XBell(display,0);
2213             break;
2214           }
2215         }
2216         break;
2217       }
2218       case MotionNotify:
2219       {
2220         /*
2221           Map and unmap Info widget as cursor crosses its boundaries.
2222         */
2223         x=event.xmotion.x;
2224         y=event.xmotion.y;
2225         if (windows->info.mapped != MagickFalse)
2226           {
2227             if ((x < (int) (windows->info.x+windows->info.width)) &&
2228                 (y < (int) (windows->info.y+windows->info.height)))
2229               (void) XWithdrawWindow(display,windows->info.id,
2230                 windows->info.screen);
2231           }
2232         else
2233           if ((x > (int) (windows->info.x+windows->info.width)) ||
2234               (y > (int) (windows->info.y+windows->info.height)))
2235             (void) XMapWindow(display,windows->info.id);
2236         break;
2237       }
2238       default:
2239         break;
2240     }
2241   } while ((state & ExitState) == 0);
2242   (void) XSelectInput(display,windows->image.id,
2243     windows->image.attributes.event_mask);
2244   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
2245   if ((state & EscapeState) != 0)
2246     return(MagickTrue);
2247   /*
2248     Set font info and check boundary conditions.
2249   */
2250   font_info=XLoadQueryFont(display,resource_info->font_name[font_id]);
2251   if (font_info == (XFontStruct *) NULL)
2252     {
2253       XNoticeWidget(display,windows,"Unable to load font:",
2254         resource_info->font_name[font_id]);
2255       font_info=windows->font_info;
2256     }
2257   if ((x+font_info->max_bounds.width) >= (int) windows->image.width)
2258     x=(int) windows->image.width-font_info->max_bounds.width;
2259   if (y < (int) (font_info->ascent+font_info->descent))
2260     y=(int) font_info->ascent+font_info->descent;
2261   if (((int) font_info->max_bounds.width > (int) windows->image.width) ||
2262       ((font_info->ascent+font_info->descent) >= (int) windows->image.height))
2263     return(MagickFalse);
2264   /*
2265     Initialize annotate structure.
2266   */
2267   annotate_info=(XAnnotateInfo *) AcquireMagickMemory(sizeof(*annotate_info));
2268   if (annotate_info == (XAnnotateInfo *) NULL)
2269     return(MagickFalse);
2270   XGetAnnotateInfo(annotate_info);
2271   annotate_info->x=x;
2272   annotate_info->y=y;
2273   if ((transparent_box == MagickFalse) && (transparent_pen == MagickFalse))
2274     annotate_info->stencil=OpaqueStencil;
2275   else
2276     if (transparent_box == MagickFalse)
2277       annotate_info->stencil=BackgroundStencil;
2278     else
2279       annotate_info->stencil=ForegroundStencil;
2280   annotate_info->height=(unsigned int) font_info->ascent+font_info->descent;
2281   annotate_info->degrees=degrees;
2282   annotate_info->font_info=font_info;
2283   annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2284     windows->image.width/MagickMax((ssize_t) font_info->min_bounds.width,1)+2UL,
2285     sizeof(*annotate_info->text));
2286   if (annotate_info->text == (char *) NULL)
2287     return(MagickFalse);
2288   /*
2289     Create cursor and set graphic context.
2290   */
2291   cursor=XCreateFontCursor(display,XC_pencil);
2292   (void) XCheckDefineCursor(display,windows->image.id,cursor);
2293   annotate_context=windows->image.annotate_context;
2294   (void) XSetFont(display,annotate_context,font_info->fid);
2295   (void) XSetBackground(display,annotate_context,
2296     windows->pixel_info->pen_colors[box_id].pixel);
2297   (void) XSetForeground(display,annotate_context,
2298     windows->pixel_info->pen_colors[pen_id].pixel);
2299   /*
2300     Begin annotating the image with text.
2301   */
2302   (void) CloneString(&windows->command.name,"Text");
2303   windows->command.data=0;
2304   (void) XCommandWidget(display,windows,TextMenu,(XEvent *) NULL);
2305   state=DefaultState;
2306   (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2307   text_event.xexpose.width=(int) font_info->max_bounds.width;
2308   text_event.xexpose.height=font_info->max_bounds.ascent+
2309     font_info->max_bounds.descent;
2310   p=annotate_info->text;
2311   do
2312   {
2313     /*
2314       Display text cursor.
2315     */
2316     *p='\0';
2317     (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2318     /*
2319       Wait for next event.
2320     */
2321     XScreenEvent(display,windows,&event,exception);
2322     if (event.xany.window == windows->command.id)
2323       {
2324         /*
2325           Select a command from the Command widget.
2326         */
2327         (void) XSetBackground(display,annotate_context,
2328           windows->pixel_info->background_color.pixel);
2329         (void) XSetForeground(display,annotate_context,
2330           windows->pixel_info->foreground_color.pixel);
2331         id=XCommandWidget(display,windows,AnnotateMenu,&event);
2332         (void) XSetBackground(display,annotate_context,
2333           windows->pixel_info->pen_colors[box_id].pixel);
2334         (void) XSetForeground(display,annotate_context,
2335           windows->pixel_info->pen_colors[pen_id].pixel);
2336         if (id < 0)
2337           continue;
2338         switch (TextCommands[id])
2339         {
2340           case TextHelpCommand:
2341           {
2342             XTextViewWidget(display,resource_info,windows,MagickFalse,
2343               "Help Viewer - Image Annotation",ImageAnnotateHelp);
2344             (void) XCheckDefineCursor(display,windows->image.id,cursor);
2345             break;
2346           }
2347           case TextApplyCommand:
2348           {
2349             /*
2350               Finished annotating.
2351             */
2352             annotate_info->width=(unsigned int) XTextWidth(font_info,
2353               annotate_info->text,(int) strlen(annotate_info->text));
2354             XRefreshWindow(display,&windows->image,&text_event);
2355             state|=ExitState;
2356             break;
2357           }
2358           default:
2359             break;
2360         }
2361         continue;
2362       }
2363     /*
2364       Erase text cursor.
2365     */
2366     text_event.xexpose.x=x;
2367     text_event.xexpose.y=y-font_info->max_bounds.ascent;
2368     (void) XClearArea(display,windows->image.id,x,text_event.xexpose.y,
2369       (unsigned int) text_event.xexpose.width,(unsigned int)
2370       text_event.xexpose.height,MagickFalse);
2371     XRefreshWindow(display,&windows->image,&text_event);
2372     switch (event.type)
2373     {
2374       case ButtonPress:
2375       {
2376         if (event.xbutton.window != windows->image.id)
2377           break;
2378         if (event.xbutton.button == Button2)
2379           {
2380             /*
2381               Request primary selection.
2382             */
2383             (void) XConvertSelection(display,XA_PRIMARY,XA_STRING,XA_STRING,
2384               windows->image.id,CurrentTime);
2385             break;
2386           }
2387         break;
2388       }
2389       case Expose:
2390       {
2391         if (event.xexpose.count == 0)
2392           {
2393             XAnnotateInfo
2394               *text_info;
2395
2396             /*
2397               Refresh Image window.
2398             */
2399             XRefreshWindow(display,&windows->image,(XEvent *) NULL);
2400             text_info=annotate_info;
2401             while (text_info != (XAnnotateInfo *) NULL)
2402             {
2403               if (annotate_info->stencil == ForegroundStencil)
2404                 (void) XDrawString(display,windows->image.id,annotate_context,
2405                   text_info->x,text_info->y,text_info->text,
2406                   (int) strlen(text_info->text));
2407               else
2408                 (void) XDrawImageString(display,windows->image.id,
2409                   annotate_context,text_info->x,text_info->y,text_info->text,
2410                   (int) strlen(text_info->text));
2411               text_info=text_info->previous;
2412             }
2413             (void) XDrawString(display,windows->image.id,annotate_context,
2414               x,y,"_",1);
2415           }
2416         break;
2417       }
2418       case KeyPress:
2419       {
2420         int
2421           length;
2422
2423         if (event.xkey.window != windows->image.id)
2424           break;
2425         /*
2426           Respond to a user key press.
2427         */
2428         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
2429           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2430         *(command+length)='\0';
2431         if (((event.xkey.state & ControlMask) != 0) ||
2432             ((event.xkey.state & Mod1Mask) != 0))
2433           state|=ModifierState;
2434         if ((state & ModifierState) != 0)
2435           switch ((int) key_symbol)
2436           {
2437             case XK_u:
2438             case XK_U:
2439             {
2440               key_symbol=DeleteCommand;
2441               break;
2442             }
2443             default:
2444               break;
2445           }
2446         switch ((int) key_symbol)
2447         {
2448           case XK_BackSpace:
2449           {
2450             /*
2451               Erase one character.
2452             */
2453             if (p == annotate_info->text)
2454               {
2455                 if (annotate_info->previous == (XAnnotateInfo *) NULL)
2456                   break;
2457                 else
2458                   {
2459                     /*
2460                       Go to end of the previous line of text.
2461                     */
2462                     annotate_info=annotate_info->previous;
2463                     p=annotate_info->text;
2464                     x=annotate_info->x+annotate_info->width;
2465                     y=annotate_info->y;
2466                     if (annotate_info->width != 0)
2467                       p+=strlen(annotate_info->text);
2468                     break;
2469                   }
2470               }
2471             p--;
2472             x-=XTextWidth(font_info,p,1);
2473             text_event.xexpose.x=x;
2474             text_event.xexpose.y=y-font_info->max_bounds.ascent;
2475             XRefreshWindow(display,&windows->image,&text_event);
2476             break;
2477           }
2478           case XK_bracketleft:
2479           {
2480             key_symbol=XK_Escape;
2481             break;
2482           }
2483           case DeleteCommand:
2484           {
2485             /*
2486               Erase the entire line of text.
2487             */
2488             while (p != annotate_info->text)
2489             {
2490               p--;
2491               x-=XTextWidth(font_info,p,1);
2492               text_event.xexpose.x=x;
2493               XRefreshWindow(display,&windows->image,&text_event);
2494             }
2495             break;
2496           }
2497           case XK_Escape:
2498           case XK_F20:
2499           {
2500             /*
2501               Finished annotating.
2502             */
2503             annotate_info->width=(unsigned int) XTextWidth(font_info,
2504               annotate_info->text,(int) strlen(annotate_info->text));
2505             XRefreshWindow(display,&windows->image,&text_event);
2506             state|=ExitState;
2507             break;
2508           }
2509           default:
2510           {
2511             /*
2512               Draw a single character on the Image window.
2513             */
2514             if ((state & ModifierState) != 0)
2515               break;
2516             if (*command == '\0')
2517               break;
2518             *p=(*command);
2519             if (annotate_info->stencil == ForegroundStencil)
2520               (void) XDrawString(display,windows->image.id,annotate_context,
2521                 x,y,p,1);
2522             else
2523               (void) XDrawImageString(display,windows->image.id,
2524                 annotate_context,x,y,p,1);
2525             x+=XTextWidth(font_info,p,1);
2526             p++;
2527             if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2528               break;
2529           }
2530           case XK_Return:
2531           case XK_KP_Enter:
2532           {
2533             /*
2534               Advance to the next line of text.
2535             */
2536             *p='\0';
2537             annotate_info->width=(unsigned int) XTextWidth(font_info,
2538               annotate_info->text,(int) strlen(annotate_info->text));
2539             if (annotate_info->next != (XAnnotateInfo *) NULL)
2540               {
2541                 /*
2542                   Line of text already exists.
2543                 */
2544                 annotate_info=annotate_info->next;
2545                 x=annotate_info->x;
2546                 y=annotate_info->y;
2547                 p=annotate_info->text;
2548                 break;
2549               }
2550             annotate_info->next=(XAnnotateInfo *) AcquireMagickMemory(
2551               sizeof(*annotate_info->next));
2552             if (annotate_info->next == (XAnnotateInfo *) NULL)
2553               return(MagickFalse);
2554             *annotate_info->next=(*annotate_info);
2555             annotate_info->next->previous=annotate_info;
2556             annotate_info=annotate_info->next;
2557             annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2558               windows->image.width/MagickMax((ssize_t)
2559               font_info->min_bounds.width,1)+2UL,sizeof(*annotate_info->text));
2560             if (annotate_info->text == (char *) NULL)
2561               return(MagickFalse);
2562             annotate_info->y+=annotate_info->height;
2563             if (annotate_info->y > (int) windows->image.height)
2564               annotate_info->y=(int) annotate_info->height;
2565             annotate_info->next=(XAnnotateInfo *) NULL;
2566             x=annotate_info->x;
2567             y=annotate_info->y;
2568             p=annotate_info->text;
2569             break;
2570           }
2571         }
2572         break;
2573       }
2574       case KeyRelease:
2575       {
2576         /*
2577           Respond to a user key release.
2578         */
2579         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2580           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2581         state&=(~ModifierState);
2582         break;
2583       }
2584       case SelectionNotify:
2585       {
2586         Atom
2587           type;
2588
2589         int
2590           format;
2591
2592         unsigned char
2593           *data;
2594
2595         unsigned long
2596           after,
2597           length;
2598
2599         /*
2600           Obtain response from primary selection.
2601         */
2602         if (event.xselection.property == (Atom) None)
2603           break;
2604         status=XGetWindowProperty(display,event.xselection.requestor,
2605           event.xselection.property,0L,(long) MaxTextExtent,True,XA_STRING,
2606           &type,&format,&length,&after,&data);
2607         if ((status != Success) || (type != XA_STRING) || (format == 32) ||
2608             (length == 0))
2609           break;
2610         /*
2611           Annotate Image window with primary selection.
2612         */
2613         for (i=0; i < (ssize_t) length; i++)
2614         {
2615           if ((char) data[i] != '\n')
2616             {
2617               /*
2618                 Draw a single character on the Image window.
2619               */
2620               *p=(char) data[i];
2621               (void) XDrawString(display,windows->image.id,annotate_context,
2622                 x,y,p,1);
2623               x+=XTextWidth(font_info,p,1);
2624               p++;
2625               if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2626                 continue;
2627             }
2628           /*
2629             Advance to the next line of text.
2630           */
2631           *p='\0';
2632           annotate_info->width=(unsigned int) XTextWidth(font_info,
2633             annotate_info->text,(int) strlen(annotate_info->text));
2634           if (annotate_info->next != (XAnnotateInfo *) NULL)
2635             {
2636               /*
2637                 Line of text already exists.
2638               */
2639               annotate_info=annotate_info->next;
2640               x=annotate_info->x;
2641               y=annotate_info->y;
2642               p=annotate_info->text;
2643               continue;
2644             }
2645           annotate_info->next=(XAnnotateInfo *) AcquireMagickMemory(
2646             sizeof(*annotate_info->next));
2647           if (annotate_info->next == (XAnnotateInfo *) NULL)
2648             return(MagickFalse);
2649           *annotate_info->next=(*annotate_info);
2650           annotate_info->next->previous=annotate_info;
2651           annotate_info=annotate_info->next;
2652           annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2653             windows->image.width/MagickMax((ssize_t)
2654             font_info->min_bounds.width,1)+2UL,sizeof(*annotate_info->text));
2655           if (annotate_info->text == (char *) NULL)
2656             return(MagickFalse);
2657           annotate_info->y+=annotate_info->height;
2658           if (annotate_info->y > (int) windows->image.height)
2659             annotate_info->y=(int) annotate_info->height;
2660           annotate_info->next=(XAnnotateInfo *) NULL;
2661           x=annotate_info->x;
2662           y=annotate_info->y;
2663           p=annotate_info->text;
2664         }
2665         (void) XFree((void *) data);
2666         break;
2667       }
2668       default:
2669         break;
2670     }
2671   } while ((state & ExitState) == 0);
2672   (void) XFreeCursor(display,cursor);
2673   /*
2674     Annotation is relative to image configuration.
2675   */
2676   width=(unsigned int) image->columns;
2677   height=(unsigned int) image->rows;
2678   x=0;
2679   y=0;
2680   if (windows->image.crop_geometry != (char *) NULL)
2681     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
2682   /*
2683     Initialize annotated image.
2684   */
2685   XSetCursorState(display,windows,MagickTrue);
2686   XCheckRefreshWindows(display,windows);
2687   while (annotate_info != (XAnnotateInfo *) NULL)
2688   {
2689     if (annotate_info->width == 0)
2690       {
2691         /*
2692           No text on this line--  go to the next line of text.
2693         */
2694         previous_info=annotate_info->previous;
2695         annotate_info->text=(char *)
2696           RelinquishMagickMemory(annotate_info->text);
2697         annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2698         annotate_info=previous_info;
2699         continue;
2700       }
2701     /*
2702       Determine pixel index for box and pen color.
2703     */
2704     windows->pixel_info->box_color=windows->pixel_info->pen_colors[box_id];
2705     if (windows->pixel_info->colors != 0)
2706       for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
2707         if (windows->pixel_info->pixels[i] ==
2708             windows->pixel_info->pen_colors[box_id].pixel)
2709           {
2710             windows->pixel_info->box_index=(unsigned short) i;
2711             break;
2712           }
2713     windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
2714     if (windows->pixel_info->colors != 0)
2715       for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
2716         if (windows->pixel_info->pixels[i] ==
2717             windows->pixel_info->pen_colors[pen_id].pixel)
2718           {
2719             windows->pixel_info->pen_index=(unsigned short) i;
2720             break;
2721           }
2722     /*
2723       Define the annotate geometry string.
2724     */
2725     annotate_info->x=(int)
2726       width*(annotate_info->x+windows->image.x)/windows->image.ximage->width;
2727     annotate_info->y=(int) height*(annotate_info->y-font_info->ascent+
2728       windows->image.y)/windows->image.ximage->height;
2729     (void) FormatLocaleString(annotate_info->geometry,MaxTextExtent,
2730       "%ux%u%+d%+d",width*annotate_info->width/windows->image.ximage->width,
2731       height*annotate_info->height/windows->image.ximage->height,
2732       annotate_info->x+x,annotate_info->y+y);
2733     /*
2734       Annotate image with text.
2735     */
2736     status=XAnnotateImage(display,windows->pixel_info,annotate_info,image,
2737       exception);
2738     if (status == 0)
2739       return(MagickFalse);
2740     /*
2741       Free up memory.
2742     */
2743     previous_info=annotate_info->previous;
2744     annotate_info->text=DestroyString(annotate_info->text);
2745     annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2746     annotate_info=previous_info;
2747   }
2748   (void) XSetForeground(display,annotate_context,
2749     windows->pixel_info->foreground_color.pixel);
2750   (void) XSetBackground(display,annotate_context,
2751     windows->pixel_info->background_color.pixel);
2752   (void) XSetFont(display,annotate_context,windows->font_info->fid);
2753   XSetCursorState(display,windows,MagickFalse);
2754   (void) XFreeFont(display,font_info);
2755   /*
2756     Update image configuration.
2757   */
2758   XConfigureImageColormap(display,resource_info,windows,image,exception);
2759   (void) XConfigureImage(display,resource_info,windows,image,exception);
2760   return(MagickTrue);
2761 }
2762 \f
2763 /*
2764 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2765 %                                                                             %
2766 %                                                                             %
2767 %                                                                             %
2768 +   X B a c k g r o u n d I m a g e                                           %
2769 %                                                                             %
2770 %                                                                             %
2771 %                                                                             %
2772 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2773 %
2774 %  XBackgroundImage() displays the image in the background of a window.
2775 %
2776 %  The format of the XBackgroundImage method is:
2777 %
2778 %      MagickBooleanType XBackgroundImage(Display *display,
2779 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
2780 %        ExceptionInfo *exception)
2781 %
2782 %  A description of each parameter follows:
2783 %
2784 %    o display: Specifies a connection to an X server; returned from
2785 %      XOpenDisplay.
2786 %
2787 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2788 %
2789 %    o windows: Specifies a pointer to a XWindows structure.
2790 %
2791 %    o image: the image.
2792 %
2793 %    o exception: return any errors or warnings in this structure.
2794 %
2795 */
2796 static MagickBooleanType XBackgroundImage(Display *display,
2797   XResourceInfo *resource_info,XWindows *windows,Image **image,
2798   ExceptionInfo *exception)
2799 {
2800 #define BackgroundImageTag  "Background/Image"
2801
2802   int
2803     status;
2804
2805   static char
2806     window_id[MaxTextExtent] = "root";
2807
2808   XResourceInfo
2809     background_resources;
2810
2811   /*
2812     Put image in background.
2813   */
2814   status=XDialogWidget(display,windows,"Background",
2815     "Enter window id (id 0x00 selects window with pointer):",window_id);
2816   if (*window_id == '\0')
2817     return(MagickFalse);
2818   (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
2819     exception);
2820   XInfoWidget(display,windows,BackgroundImageTag);
2821   XSetCursorState(display,windows,MagickTrue);
2822   XCheckRefreshWindows(display,windows);
2823   background_resources=(*resource_info);
2824   background_resources.window_id=window_id;
2825   background_resources.backdrop=status != 0 ? MagickTrue : MagickFalse;
2826   status=XDisplayBackgroundImage(display,&background_resources,*image,
2827     exception);
2828   if (status != MagickFalse)
2829     XClientMessage(display,windows->image.id,windows->im_protocols,
2830       windows->im_retain_colors,CurrentTime);
2831   XSetCursorState(display,windows,MagickFalse);
2832   (void) XMagickCommand(display,resource_info,windows,UndoCommand,image,
2833     exception);
2834   return(MagickTrue);
2835 }
2836 \f
2837 /*
2838 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2839 %                                                                             %
2840 %                                                                             %
2841 %                                                                             %
2842 +   X C h o p I m a g e                                                       %
2843 %                                                                             %
2844 %                                                                             %
2845 %                                                                             %
2846 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2847 %
2848 %  XChopImage() chops the X image.
2849 %
2850 %  The format of the XChopImage method is:
2851 %
2852 %    MagickBooleanType XChopImage(Display *display,XResourceInfo *resource_info,
2853 %      XWindows *windows,Image **image,ExceptionInfo *exception)
2854 %
2855 %  A description of each parameter follows:
2856 %
2857 %    o display: Specifies a connection to an X server; returned from
2858 %      XOpenDisplay.
2859 %
2860 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2861 %
2862 %    o windows: Specifies a pointer to a XWindows structure.
2863 %
2864 %    o image: the image.
2865 %
2866 %    o exception: return any errors or warnings in this structure.
2867 %
2868 */
2869 static MagickBooleanType XChopImage(Display *display,
2870   XResourceInfo *resource_info,XWindows *windows,Image **image,
2871   ExceptionInfo *exception)
2872 {
2873   static const char
2874     *ChopMenu[] =
2875     {
2876       "Direction",
2877       "Help",
2878       "Dismiss",
2879       (char *) NULL
2880     };
2881
2882   static ModeType
2883     direction = HorizontalChopCommand;
2884
2885   static const ModeType
2886     ChopCommands[] =
2887     {
2888       ChopDirectionCommand,
2889       ChopHelpCommand,
2890       ChopDismissCommand
2891     },
2892     DirectionCommands[] =
2893     {
2894       HorizontalChopCommand,
2895       VerticalChopCommand
2896     };
2897
2898   char
2899     text[MaxTextExtent];
2900
2901   Image
2902     *chop_image;
2903
2904   int
2905     id,
2906     x,
2907     y;
2908
2909   MagickRealType
2910     scale_factor;
2911
2912   RectangleInfo
2913     chop_info;
2914
2915   unsigned int
2916     distance,
2917     height,
2918     width;
2919
2920   size_t
2921     state;
2922
2923   XEvent
2924     event;
2925
2926   XSegment
2927     segment_info;
2928
2929   /*
2930     Map Command widget.
2931   */
2932   (void) CloneString(&windows->command.name,"Chop");
2933   windows->command.data=1;
2934   (void) XCommandWidget(display,windows,ChopMenu,(XEvent *) NULL);
2935   (void) XMapRaised(display,windows->command.id);
2936   XClientMessage(display,windows->image.id,windows->im_protocols,
2937     windows->im_update_widget,CurrentTime);
2938   /*
2939     Track pointer until button 1 is pressed.
2940   */
2941   XQueryPosition(display,windows->image.id,&x,&y);
2942   (void) XSelectInput(display,windows->image.id,
2943     windows->image.attributes.event_mask | PointerMotionMask);
2944   state=DefaultState;
2945   do
2946   {
2947     if (windows->info.mapped != MagickFalse)
2948       {
2949         /*
2950           Display pointer position.
2951         */
2952         (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
2953           x+windows->image.x,y+windows->image.y);
2954         XInfoWidget(display,windows,text);
2955       }
2956     /*
2957       Wait for next event.
2958     */
2959     XScreenEvent(display,windows,&event,exception);
2960     if (event.xany.window == windows->command.id)
2961       {
2962         /*
2963           Select a command from the Command widget.
2964         */
2965         id=XCommandWidget(display,windows,ChopMenu,&event);
2966         if (id < 0)
2967           continue;
2968         switch (ChopCommands[id])
2969         {
2970           case ChopDirectionCommand:
2971           {
2972             char
2973               command[MaxTextExtent];
2974
2975             static const char
2976               *Directions[] =
2977               {
2978                 "horizontal",
2979                 "vertical",
2980                 (char *) NULL,
2981               };
2982
2983             /*
2984               Select a command from the pop-up menu.
2985             */
2986             id=XMenuWidget(display,windows,ChopMenu[id],Directions,command);
2987             if (id >= 0)
2988               direction=DirectionCommands[id];
2989             break;
2990           }
2991           case ChopHelpCommand:
2992           {
2993             XTextViewWidget(display,resource_info,windows,MagickFalse,
2994               "Help Viewer - Image Chop",ImageChopHelp);
2995             break;
2996           }
2997           case ChopDismissCommand:
2998           {
2999             /*
3000               Prematurely exit.
3001             */
3002             state|=EscapeState;
3003             state|=ExitState;
3004             break;
3005           }
3006           default:
3007             break;
3008         }
3009         continue;
3010       }
3011     switch (event.type)
3012     {
3013       case ButtonPress:
3014       {
3015         if (event.xbutton.button != Button1)
3016           break;
3017         if (event.xbutton.window != windows->image.id)
3018           break;
3019         /*
3020           User has committed to start point of chopping line.
3021         */
3022         segment_info.x1=(short int) event.xbutton.x;
3023         segment_info.x2=(short int) event.xbutton.x;
3024         segment_info.y1=(short int) event.xbutton.y;
3025         segment_info.y2=(short int) event.xbutton.y;
3026         state|=ExitState;
3027         break;
3028       }
3029       case ButtonRelease:
3030         break;
3031       case Expose:
3032         break;
3033       case KeyPress:
3034       {
3035         char
3036           command[MaxTextExtent];
3037
3038         KeySym
3039           key_symbol;
3040
3041         if (event.xkey.window != windows->image.id)
3042           break;
3043         /*
3044           Respond to a user key press.
3045         */
3046         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3047           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3048         switch ((int) key_symbol)
3049         {
3050           case XK_Escape:
3051           case XK_F20:
3052           {
3053             /*
3054               Prematurely exit.
3055             */
3056             state|=EscapeState;
3057             state|=ExitState;
3058             break;
3059           }
3060           case XK_F1:
3061           case XK_Help:
3062           {
3063             (void) XSetFunction(display,windows->image.highlight_context,
3064               GXcopy);
3065             XTextViewWidget(display,resource_info,windows,MagickFalse,
3066               "Help Viewer - Image Chop",ImageChopHelp);
3067             (void) XSetFunction(display,windows->image.highlight_context,
3068               GXinvert);
3069             break;
3070           }
3071           default:
3072           {
3073             (void) XBell(display,0);
3074             break;
3075           }
3076         }
3077         break;
3078       }
3079       case MotionNotify:
3080       {
3081         /*
3082           Map and unmap Info widget as text cursor crosses its boundaries.
3083         */
3084         x=event.xmotion.x;
3085         y=event.xmotion.y;
3086         if (windows->info.mapped != MagickFalse)
3087           {
3088             if ((x < (int) (windows->info.x+windows->info.width)) &&
3089                 (y < (int) (windows->info.y+windows->info.height)))
3090               (void) XWithdrawWindow(display,windows->info.id,
3091                 windows->info.screen);
3092           }
3093         else
3094           if ((x > (int) (windows->info.x+windows->info.width)) ||
3095               (y > (int) (windows->info.y+windows->info.height)))
3096             (void) XMapWindow(display,windows->info.id);
3097       }
3098     }
3099   } while ((state & ExitState) == 0);
3100   (void) XSelectInput(display,windows->image.id,
3101     windows->image.attributes.event_mask);
3102   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3103   if ((state & EscapeState) != 0)
3104     return(MagickTrue);
3105   /*
3106     Draw line as pointer moves until the mouse button is released.
3107   */
3108   chop_info.width=0;
3109   chop_info.height=0;
3110   chop_info.x=0;
3111   chop_info.y=0;
3112   distance=0;
3113   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
3114   state=DefaultState;
3115   do
3116   {
3117     if (distance > 9)
3118       {
3119         /*
3120           Display info and draw chopping line.
3121         */
3122         if (windows->info.mapped == MagickFalse)
3123           (void) XMapWindow(display,windows->info.id);
3124         (void) FormatLocaleString(text,MaxTextExtent,
3125           " %.20gx%.20g%+.20g%+.20g",(double) chop_info.width,(double)
3126           chop_info.height,(double) chop_info.x,(double) chop_info.y);
3127         XInfoWidget(display,windows,text);
3128         XHighlightLine(display,windows->image.id,
3129           windows->image.highlight_context,&segment_info);
3130       }
3131     else
3132       if (windows->info.mapped != MagickFalse)
3133         (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3134     /*
3135       Wait for next event.
3136     */
3137     XScreenEvent(display,windows,&event,exception);
3138     if (distance > 9)
3139       XHighlightLine(display,windows->image.id,
3140         windows->image.highlight_context,&segment_info);
3141     switch (event.type)
3142     {
3143       case ButtonPress:
3144       {
3145         segment_info.x2=(short int) event.xmotion.x;
3146         segment_info.y2=(short int) event.xmotion.y;
3147         break;
3148       }
3149       case ButtonRelease:
3150       {
3151         /*
3152           User has committed to chopping line.
3153         */
3154         segment_info.x2=(short int) event.xbutton.x;
3155         segment_info.y2=(short int) event.xbutton.y;
3156         state|=ExitState;
3157         break;
3158       }
3159       case Expose:
3160         break;
3161       case MotionNotify:
3162       {
3163         segment_info.x2=(short int) event.xmotion.x;
3164         segment_info.y2=(short int) event.xmotion.y;
3165       }
3166       default:
3167         break;
3168     }
3169     /*
3170       Check boundary conditions.
3171     */
3172     if (segment_info.x2 < 0)
3173       segment_info.x2=0;
3174     else
3175       if (segment_info.x2 > windows->image.ximage->width)
3176         segment_info.x2=windows->image.ximage->width;
3177     if (segment_info.y2 < 0)
3178       segment_info.y2=0;
3179     else
3180       if (segment_info.y2 > windows->image.ximage->height)
3181         segment_info.y2=windows->image.ximage->height;
3182     distance=(unsigned int)
3183       (((segment_info.x2-segment_info.x1)*(segment_info.x2-segment_info.x1))+
3184        ((segment_info.y2-segment_info.y1)*(segment_info.y2-segment_info.y1)));
3185     /*
3186       Compute chopping geometry.
3187     */
3188     if (direction == HorizontalChopCommand)
3189       {
3190         chop_info.width=(size_t) (segment_info.x2-segment_info.x1+1);
3191         chop_info.x=(ssize_t) windows->image.x+segment_info.x1;
3192         chop_info.height=0;
3193         chop_info.y=0;
3194         if (segment_info.x1 > (int) segment_info.x2)
3195           {
3196             chop_info.width=(size_t) (segment_info.x1-segment_info.x2+1);
3197             chop_info.x=(ssize_t) windows->image.x+segment_info.x2;
3198           }
3199       }
3200     else
3201       {
3202         chop_info.width=0;
3203         chop_info.height=(size_t) (segment_info.y2-segment_info.y1+1);
3204         chop_info.x=0;
3205         chop_info.y=(ssize_t) windows->image.y+segment_info.y1;
3206         if (segment_info.y1 > segment_info.y2)
3207           {
3208             chop_info.height=(size_t) (segment_info.y1-segment_info.y2+1);
3209             chop_info.y=(ssize_t) windows->image.y+segment_info.y2;
3210           }
3211       }
3212   } while ((state & ExitState) == 0);
3213   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
3214   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3215   if (distance <= 9)
3216     return(MagickTrue);
3217   /*
3218     Image chopping is relative to image configuration.
3219   */
3220   (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
3221     exception);
3222   XSetCursorState(display,windows,MagickTrue);
3223   XCheckRefreshWindows(display,windows);
3224   windows->image.window_changes.width=windows->image.ximage->width-
3225     (unsigned int) chop_info.width;
3226   windows->image.window_changes.height=windows->image.ximage->height-
3227     (unsigned int) chop_info.height;
3228   width=(unsigned int) (*image)->columns;
3229   height=(unsigned int) (*image)->rows;
3230   x=0;
3231   y=0;
3232   if (windows->image.crop_geometry != (char *) NULL)
3233     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
3234   scale_factor=(MagickRealType) width/windows->image.ximage->width;
3235   chop_info.x+=x;
3236   chop_info.x=(ssize_t) (scale_factor*chop_info.x+0.5);
3237   chop_info.width=(unsigned int) (scale_factor*chop_info.width+0.5);
3238   scale_factor=(MagickRealType) height/windows->image.ximage->height;
3239   chop_info.y+=y;
3240   chop_info.y=(ssize_t) (scale_factor*chop_info.y+0.5);
3241   chop_info.height=(unsigned int) (scale_factor*chop_info.height+0.5);
3242   /*
3243     Chop image.
3244   */
3245   chop_image=ChopImage(*image,&chop_info,exception);
3246   XSetCursorState(display,windows,MagickFalse);
3247   if (chop_image == (Image *) NULL)
3248     return(MagickFalse);
3249   *image=DestroyImage(*image);
3250   *image=chop_image;
3251   /*
3252     Update image configuration.
3253   */
3254   XConfigureImageColormap(display,resource_info,windows,*image,exception);
3255   (void) XConfigureImage(display,resource_info,windows,*image,exception);
3256   return(MagickTrue);
3257 }
3258 \f
3259 /*
3260 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3261 %                                                                             %
3262 %                                                                             %
3263 %                                                                             %
3264 +   X C o l o r E d i t I m a g e                                             %
3265 %                                                                             %
3266 %                                                                             %
3267 %                                                                             %
3268 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3269 %
3270 %  XColorEditImage() allows the user to interactively change the color of one
3271 %  pixel for a DirectColor image or one colormap entry for a PseudoClass image.
3272 %
3273 %  The format of the XColorEditImage method is:
3274 %
3275 %      MagickBooleanType XColorEditImage(Display *display,
3276 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
3277 %          ExceptionInfo *exception)
3278 %
3279 %  A description of each parameter follows:
3280 %
3281 %    o display: Specifies a connection to an X server;  returned from
3282 %      XOpenDisplay.
3283 %
3284 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3285 %
3286 %    o windows: Specifies a pointer to a XWindows structure.
3287 %
3288 %    o image: the image; returned from ReadImage.
3289 %
3290 %    o exception: return any errors or warnings in this structure.
3291 %
3292 */
3293 static MagickBooleanType XColorEditImage(Display *display,
3294   XResourceInfo *resource_info,XWindows *windows,Image **image,
3295   ExceptionInfo *exception)
3296 {
3297   static const char
3298     *ColorEditMenu[] =
3299     {
3300       "Method",
3301       "Pixel Color",
3302       "Border Color",
3303       "Fuzz",
3304       "Undo",
3305       "Help",
3306       "Dismiss",
3307       (char *) NULL
3308     };
3309
3310   static const ModeType
3311     ColorEditCommands[] =
3312     {
3313       ColorEditMethodCommand,
3314       ColorEditColorCommand,
3315       ColorEditBorderCommand,
3316       ColorEditFuzzCommand,
3317       ColorEditUndoCommand,
3318       ColorEditHelpCommand,
3319       ColorEditDismissCommand
3320     };
3321
3322   static PaintMethod
3323     method = PointMethod;
3324
3325   static unsigned int
3326     pen_id = 0;
3327
3328   static XColor
3329     border_color = { 0, 0, 0, 0, 0, 0 };
3330
3331   char
3332     command[MaxTextExtent],
3333     text[MaxTextExtent];
3334
3335   Cursor
3336     cursor;
3337
3338   int
3339     entry,
3340     id,
3341     x,
3342     x_offset,
3343     y,
3344     y_offset;
3345
3346   register Quantum
3347     *q;
3348
3349   register ssize_t
3350     i;
3351
3352   unsigned int
3353     height,
3354     width;
3355
3356   size_t
3357     state;
3358
3359   XColor
3360     color;
3361
3362   XEvent
3363     event;
3364
3365   /*
3366     Map Command widget.
3367   */
3368   (void) CloneString(&windows->command.name,"Color Edit");
3369   windows->command.data=4;
3370   (void) XCommandWidget(display,windows,ColorEditMenu,(XEvent *) NULL);
3371   (void) XMapRaised(display,windows->command.id);
3372   XClientMessage(display,windows->image.id,windows->im_protocols,
3373     windows->im_update_widget,CurrentTime);
3374   /*
3375     Make cursor.
3376   */
3377   cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
3378     resource_info->background_color,resource_info->foreground_color);
3379   (void) XCheckDefineCursor(display,windows->image.id,cursor);
3380   /*
3381     Track pointer until button 1 is pressed.
3382   */
3383   XQueryPosition(display,windows->image.id,&x,&y);
3384   (void) XSelectInput(display,windows->image.id,
3385     windows->image.attributes.event_mask | PointerMotionMask);
3386   state=DefaultState;
3387   do
3388   {
3389     if (windows->info.mapped != MagickFalse)
3390       {
3391         /*
3392           Display pointer position.
3393         */
3394         (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
3395           x+windows->image.x,y+windows->image.y);
3396         XInfoWidget(display,windows,text);
3397       }
3398     /*
3399       Wait for next event.
3400     */
3401     XScreenEvent(display,windows,&event,exception);
3402     if (event.xany.window == windows->command.id)
3403       {
3404         /*
3405           Select a command from the Command widget.
3406         */
3407         id=XCommandWidget(display,windows,ColorEditMenu,&event);
3408         if (id < 0)
3409           {
3410             (void) XCheckDefineCursor(display,windows->image.id,cursor);
3411             continue;
3412           }
3413         switch (ColorEditCommands[id])
3414         {
3415           case ColorEditMethodCommand:
3416           {
3417             char
3418               **methods;
3419
3420             /*
3421               Select a method from the pop-up menu.
3422             */
3423             methods=(char **) GetCommandOptions(MagickMethodOptions);
3424             if (methods == (char **) NULL)
3425               break;
3426             entry=XMenuWidget(display,windows,ColorEditMenu[id],
3427               (const char **) methods,command);
3428             if (entry >= 0)
3429               method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
3430                 MagickFalse,methods[entry]);
3431             methods=DestroyStringList(methods);
3432             break;
3433           }
3434           case ColorEditColorCommand:
3435           {
3436             const char
3437               *ColorMenu[MaxNumberPens];
3438
3439             int
3440               pen_number;
3441
3442             /*
3443               Initialize menu selections.
3444             */
3445             for (i=0; i < (int) (MaxNumberPens-2); i++)
3446               ColorMenu[i]=resource_info->pen_colors[i];
3447             ColorMenu[MaxNumberPens-2]="Browser...";
3448             ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3449             /*
3450               Select a pen color from the pop-up menu.
3451             */
3452             pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3453               (const char **) ColorMenu,command);
3454             if (pen_number < 0)
3455               break;
3456             if (pen_number == (MaxNumberPens-2))
3457               {
3458                 static char
3459                   color_name[MaxTextExtent] = "gray";
3460
3461                 /*
3462                   Select a pen color from a dialog.
3463                 */
3464                 resource_info->pen_colors[pen_number]=color_name;
3465                 XColorBrowserWidget(display,windows,"Select",color_name);
3466                 if (*color_name == '\0')
3467                   break;
3468               }
3469             /*
3470               Set pen color.
3471             */
3472             (void) XParseColor(display,windows->map_info->colormap,
3473               resource_info->pen_colors[pen_number],&color);
3474             XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
3475               (unsigned int) MaxColors,&color);
3476             windows->pixel_info->pen_colors[pen_number]=color;
3477             pen_id=(unsigned int) pen_number;
3478             break;
3479           }
3480           case ColorEditBorderCommand:
3481           {
3482             const char
3483               *ColorMenu[MaxNumberPens];
3484
3485             int
3486               pen_number;
3487
3488             /*
3489               Initialize menu selections.
3490             */
3491             for (i=0; i < (int) (MaxNumberPens-2); i++)
3492               ColorMenu[i]=resource_info->pen_colors[i];
3493             ColorMenu[MaxNumberPens-2]="Browser...";
3494             ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3495             /*
3496               Select a pen color from the pop-up menu.
3497             */
3498             pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3499               (const char **) ColorMenu,command);
3500             if (pen_number < 0)
3501               break;
3502             if (pen_number == (MaxNumberPens-2))
3503               {
3504                 static char
3505                   color_name[MaxTextExtent] = "gray";
3506
3507                 /*
3508                   Select a pen color from a dialog.
3509                 */
3510                 resource_info->pen_colors[pen_number]=color_name;
3511                 XColorBrowserWidget(display,windows,"Select",color_name);
3512                 if (*color_name == '\0')
3513                   break;
3514               }
3515             /*
3516               Set border color.
3517             */
3518             (void) XParseColor(display,windows->map_info->colormap,
3519               resource_info->pen_colors[pen_number],&border_color);
3520             break;
3521           }
3522           case ColorEditFuzzCommand:
3523           {
3524             static char
3525               fuzz[MaxTextExtent];
3526
3527             static const char
3528               *FuzzMenu[] =
3529               {
3530                 "0%",
3531                 "2%",
3532                 "5%",
3533                 "10%",
3534                 "15%",
3535                 "Dialog...",
3536                 (char *) NULL,
3537               };
3538
3539             /*
3540               Select a command from the pop-up menu.
3541             */
3542             entry=XMenuWidget(display,windows,ColorEditMenu[id],FuzzMenu,
3543               command);
3544             if (entry < 0)
3545               break;
3546             if (entry != 5)
3547               {
3548                 (*image)->fuzz=StringToDoubleInterval(FuzzMenu[entry],(double)
3549                   QuantumRange+1.0);
3550                 break;
3551               }
3552             (void) (void) CopyMagickString(fuzz,"20%",MaxTextExtent);
3553             (void) XDialogWidget(display,windows,"Ok",
3554               "Enter fuzz factor (0.0 - 99.9%):",fuzz);
3555             if (*fuzz == '\0')
3556               break;
3557             (void) ConcatenateMagickString(fuzz,"%",MaxTextExtent);
3558             (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+
3559               1.0);
3560             break;
3561           }
3562           case ColorEditUndoCommand:
3563           {
3564             (void) XMagickCommand(display,resource_info,windows,UndoCommand,
3565               image,exception);
3566             break;
3567           }
3568           case ColorEditHelpCommand:
3569           default:
3570           {
3571             XTextViewWidget(display,resource_info,windows,MagickFalse,
3572               "Help Viewer - Image Annotation",ImageColorEditHelp);
3573             break;
3574           }
3575           case ColorEditDismissCommand:
3576           {
3577             /*
3578               Prematurely exit.
3579             */
3580             state|=EscapeState;
3581             state|=ExitState;
3582             break;
3583           }
3584         }
3585         (void) XCheckDefineCursor(display,windows->image.id,cursor);
3586         continue;
3587       }
3588     switch (event.type)
3589     {
3590       case ButtonPress:
3591       {
3592         if (event.xbutton.button != Button1)
3593           break;
3594         if ((event.xbutton.window != windows->image.id) &&
3595             (event.xbutton.window != windows->magnify.id))
3596           break;
3597         /*
3598           exit loop.
3599         */
3600         x=event.xbutton.x;
3601         y=event.xbutton.y;
3602         (void) XMagickCommand(display,resource_info,windows,
3603           SaveToUndoBufferCommand,image,exception);
3604         state|=UpdateConfigurationState;
3605         break;
3606       }
3607       case ButtonRelease:
3608       {
3609         if (event.xbutton.button != Button1)
3610           break;
3611         if ((event.xbutton.window != windows->image.id) &&
3612             (event.xbutton.window != windows->magnify.id))
3613           break;
3614         /*
3615           Update colormap information.
3616         */
3617         x=event.xbutton.x;
3618         y=event.xbutton.y;
3619         XConfigureImageColormap(display,resource_info,windows,*image,exception);
3620         (void) XConfigureImage(display,resource_info,windows,*image,exception);
3621         XInfoWidget(display,windows,text);
3622         (void) XCheckDefineCursor(display,windows->image.id,cursor);
3623         state&=(~UpdateConfigurationState);
3624         break;
3625       }
3626       case Expose:
3627         break;
3628       case KeyPress:
3629       {
3630         KeySym
3631           key_symbol;
3632
3633         if (event.xkey.window == windows->magnify.id)
3634           {
3635             Window
3636               window;
3637
3638             window=windows->magnify.id;
3639             while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
3640           }
3641         if (event.xkey.window != windows->image.id)
3642           break;
3643         /*
3644           Respond to a user key press.
3645         */
3646         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3647           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3648         switch ((int) key_symbol)
3649         {
3650           case XK_Escape:
3651           case XK_F20:
3652           {
3653             /*
3654               Prematurely exit.
3655             */
3656             state|=ExitState;
3657             break;
3658           }
3659           case XK_F1:
3660           case XK_Help:
3661           {
3662             XTextViewWidget(display,resource_info,windows,MagickFalse,
3663               "Help Viewer - Image Annotation",ImageColorEditHelp);
3664             break;
3665           }
3666           default:
3667           {
3668             (void) XBell(display,0);
3669             break;
3670           }
3671         }
3672         break;
3673       }
3674       case MotionNotify:
3675       {
3676         /*
3677           Map and unmap Info widget as cursor crosses its boundaries.
3678         */
3679         x=event.xmotion.x;
3680         y=event.xmotion.y;
3681         if (windows->info.mapped != MagickFalse)
3682           {
3683             if ((x < (int) (windows->info.x+windows->info.width)) &&
3684                 (y < (int) (windows->info.y+windows->info.height)))
3685               (void) XWithdrawWindow(display,windows->info.id,
3686                 windows->info.screen);
3687           }
3688         else
3689           if ((x > (int) (windows->info.x+windows->info.width)) ||
3690               (y > (int) (windows->info.y+windows->info.height)))
3691             (void) XMapWindow(display,windows->info.id);
3692         break;
3693       }
3694       default:
3695         break;
3696     }
3697     if (event.xany.window == windows->magnify.id)
3698       {
3699         x=windows->magnify.x-windows->image.x;
3700         y=windows->magnify.y-windows->image.y;
3701       }
3702     x_offset=x;
3703     y_offset=y;
3704     if ((state & UpdateConfigurationState) != 0)
3705       {
3706         CacheView
3707           *image_view;
3708
3709         int
3710           x,
3711           y;
3712
3713         /*
3714           Pixel edit is relative to image configuration.
3715         */
3716         (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
3717           MagickTrue);
3718         color=windows->pixel_info->pen_colors[pen_id];
3719         XPutPixel(windows->image.ximage,x_offset,y_offset,color.pixel);
3720         width=(unsigned int) (*image)->columns;
3721         height=(unsigned int) (*image)->rows;
3722         x=0;
3723         y=0;
3724         if (windows->image.crop_geometry != (char *) NULL)
3725           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
3726             &width,&height);
3727         x_offset=(int)
3728           (width*(windows->image.x+x_offset)/windows->image.ximage->width+x);
3729         y_offset=(int)
3730           (height*(windows->image.y+y_offset)/windows->image.ximage->height+y);
3731         if ((x_offset < 0) || (y_offset < 0))
3732           continue;
3733         if ((x_offset >= (int) (*image)->columns) ||
3734             (y_offset >= (int) (*image)->rows))
3735           continue;
3736         image_view=AcquireCacheView(*image);
3737         switch (method)
3738         {
3739           case PointMethod:
3740           default:
3741           {
3742             /*
3743               Update color information using point algorithm.
3744             */
3745             if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
3746               return(MagickFalse);
3747             q=GetCacheViewAuthenticPixels(image_view,(ssize_t)x_offset,
3748               (ssize_t) y_offset,1,1,exception);
3749             if (q == (Quantum *) NULL)
3750               break;
3751             SetPixelRed(*image,ScaleShortToQuantum(color.red),q);
3752             SetPixelGreen(*image,ScaleShortToQuantum(color.green),q);
3753             SetPixelBlue(*image,ScaleShortToQuantum(color.blue),q);
3754             (void) SyncCacheViewAuthenticPixels(image_view,exception);
3755             break;
3756           }
3757           case ReplaceMethod:
3758           {
3759             PixelInfo
3760               pixel,
3761               target;
3762
3763             Quantum
3764               virtual_pixel[CompositePixelChannel];
3765
3766             /*
3767               Update color information using replace algorithm.
3768             */
3769             (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t) x_offset,
3770               (ssize_t) y_offset,virtual_pixel,exception);
3771             target.red=virtual_pixel[RedPixelChannel];
3772             target.green=virtual_pixel[GreenPixelChannel];
3773             target.blue=virtual_pixel[BluePixelChannel];
3774             target.alpha=virtual_pixel[AlphaPixelChannel];
3775             if ((*image)->storage_class == DirectClass)
3776               {
3777                 for (y=0; y < (int) (*image)->rows; y++)
3778                 {
3779                   q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3780                     (*image)->columns,1,exception);
3781                   if (q == (Quantum *) NULL)
3782                     break;
3783                   for (x=0; x < (int) (*image)->columns; x++)
3784                   {
3785                     GetPixelInfoPixel(*image,q,&pixel);
3786                     if (IsFuzzyEquivalencePixelInfo(&pixel,&target))
3787                       {
3788                         SetPixelRed(*image,ScaleShortToQuantum(
3789                           color.red),q);
3790                         SetPixelGreen(*image,ScaleShortToQuantum(
3791                           color.green),q);
3792                         SetPixelBlue(*image,ScaleShortToQuantum(
3793                           color.blue),q);
3794                       }
3795                     q+=GetPixelChannels(*image);
3796                   }
3797                   if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3798                     break;
3799                 }
3800               }
3801             else
3802               {
3803                 for (i=0; i < (ssize_t) (*image)->colors; i++)
3804                   if (IsFuzzyEquivalencePixelInfo((*image)->colormap+i,&target))
3805                     {
3806                       (*image)->colormap[i].red=ScaleShortToQuantum(
3807                         color.red);
3808                       (*image)->colormap[i].green=ScaleShortToQuantum(
3809                         color.green);
3810                       (*image)->colormap[i].blue=ScaleShortToQuantum(
3811                         color.blue);
3812                     }
3813                 (void) SyncImage(*image,exception);
3814               }
3815             break;
3816           }
3817           case FloodfillMethod:
3818           case FillToBorderMethod:
3819           {
3820             DrawInfo
3821               *draw_info;
3822
3823             PixelInfo
3824               target;
3825
3826             /*
3827               Update color information using floodfill algorithm.
3828             */
3829             (void) GetOneVirtualMagickPixel(*image,
3830               GetPixelCacheVirtualMethod(*image),(ssize_t) x_offset,(ssize_t)
3831               y_offset,&target,exception);
3832             if (method == FillToBorderMethod)
3833               {
3834                 target.red=(MagickRealType)
3835                   ScaleShortToQuantum(border_color.red);
3836                 target.green=(MagickRealType)
3837                   ScaleShortToQuantum(border_color.green);
3838                 target.blue=(MagickRealType)
3839                   ScaleShortToQuantum(border_color.blue);
3840               }
3841             draw_info=CloneDrawInfo(resource_info->image_info,
3842               (DrawInfo *) NULL);
3843             (void) QueryColorCompliance(resource_info->pen_colors[pen_id],
3844               AllCompliance,&draw_info->fill,exception);
3845             (void) FloodfillPaintImage(*image,draw_info,&target,(ssize_t)
3846               x_offset,(ssize_t) y_offset,method == FloodfillMethod ?
3847               MagickFalse : MagickTrue,exception);
3848             draw_info=DestroyDrawInfo(draw_info);
3849             break;
3850           }
3851           case ResetMethod:
3852           {
3853             /*
3854               Update color information using reset algorithm.
3855             */
3856             if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
3857               return(MagickFalse);
3858             for (y=0; y < (int) (*image)->rows; y++)
3859             {
3860               q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3861                 (*image)->columns,1,exception);
3862               if (q == (Quantum *) NULL)
3863                 break;
3864               for (x=0; x < (int) (*image)->columns; x++)
3865               {
3866                 SetPixelRed(*image,ScaleShortToQuantum(color.red),q);
3867                 SetPixelGreen(*image,ScaleShortToQuantum(color.green),q);
3868                 SetPixelBlue(*image,ScaleShortToQuantum(color.blue),q);
3869                 q+=GetPixelChannels(*image);
3870               }
3871               if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3872                 break;
3873             }
3874             break;
3875           }
3876         }
3877         image_view=DestroyCacheView(image_view);
3878         state&=(~UpdateConfigurationState);
3879       }
3880   } while ((state & ExitState) == 0);
3881   (void) XSelectInput(display,windows->image.id,
3882     windows->image.attributes.event_mask);
3883   XSetCursorState(display,windows,MagickFalse);
3884   (void) XFreeCursor(display,cursor);
3885   return(MagickTrue);
3886 }
3887 \f
3888 /*
3889 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3890 %                                                                             %
3891 %                                                                             %
3892 %                                                                             %
3893 +   X C o m p o s i t e I m a g e                                             %
3894 %                                                                             %
3895 %                                                                             %
3896 %                                                                             %
3897 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3898 %
3899 %  XCompositeImage() requests an image name from the user, reads the image and
3900 %  composites it with the X window image at a location the user chooses with
3901 %  the pointer.
3902 %
3903 %  The format of the XCompositeImage method is:
3904 %
3905 %      MagickBooleanType XCompositeImage(Display *display,
3906 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
3907 %        ExceptionInfo *exception)
3908 %
3909 %  A description of each parameter follows:
3910 %
3911 %    o display: Specifies a connection to an X server;  returned from
3912 %      XOpenDisplay.
3913 %
3914 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3915 %
3916 %    o windows: Specifies a pointer to a XWindows structure.
3917 %
3918 %    o image: the image; returned from ReadImage.
3919 %
3920 %    o exception: return any errors or warnings in this structure.
3921 %
3922 */
3923 static MagickBooleanType XCompositeImage(Display *display,
3924   XResourceInfo *resource_info,XWindows *windows,Image *image,
3925   ExceptionInfo *exception)
3926 {
3927   static char
3928     displacement_geometry[MaxTextExtent] = "30x30",
3929     filename[MaxTextExtent] = "\0";
3930
3931   static const char
3932     *CompositeMenu[] =
3933     {
3934       "Operators",
3935       "Dissolve",
3936       "Displace",
3937       "Help",
3938       "Dismiss",
3939       (char *) NULL
3940     };
3941
3942   static CompositeOperator
3943     compose = CopyCompositeOp;
3944
3945   static const ModeType
3946     CompositeCommands[] =
3947     {
3948       CompositeOperatorsCommand,
3949       CompositeDissolveCommand,
3950       CompositeDisplaceCommand,
3951       CompositeHelpCommand,
3952       CompositeDismissCommand
3953     };
3954
3955   char
3956     text[MaxTextExtent];
3957
3958   Cursor
3959     cursor;
3960
3961   Image
3962     *composite_image;
3963
3964   int
3965     entry,
3966     id,
3967     x,
3968     y;
3969
3970   MagickRealType
3971     blend,
3972     scale_factor;
3973
3974   RectangleInfo
3975     highlight_info,
3976     composite_info;
3977
3978   unsigned int
3979     height,
3980     width;
3981
3982   size_t
3983     state;
3984
3985   XEvent
3986     event;
3987
3988   /*
3989     Request image file name from user.
3990   */
3991   XFileBrowserWidget(display,windows,"Composite",filename);
3992   if (*filename == '\0')
3993     return(MagickTrue);
3994   /*
3995     Read image.
3996   */
3997   XSetCursorState(display,windows,MagickTrue);
3998   XCheckRefreshWindows(display,windows);
3999   (void) CopyMagickString(resource_info->image_info->filename,filename,
4000     MaxTextExtent);
4001   composite_image=ReadImage(resource_info->image_info,exception);
4002   CatchException(exception);
4003   XSetCursorState(display,windows,MagickFalse);
4004   if (composite_image == (Image *) NULL)
4005     return(MagickFalse);
4006   /*
4007     Map Command widget.
4008   */
4009   (void) CloneString(&windows->command.name,"Composite");
4010   windows->command.data=1;
4011   (void) XCommandWidget(display,windows,CompositeMenu,(XEvent *) NULL);
4012   (void) XMapRaised(display,windows->command.id);
4013   XClientMessage(display,windows->image.id,windows->im_protocols,
4014     windows->im_update_widget,CurrentTime);
4015   /*
4016     Track pointer until button 1 is pressed.
4017   */
4018   XQueryPosition(display,windows->image.id,&x,&y);
4019   (void) XSelectInput(display,windows->image.id,
4020     windows->image.attributes.event_mask | PointerMotionMask);
4021   composite_info.x=(ssize_t) windows->image.x+x;
4022   composite_info.y=(ssize_t) windows->image.y+y;
4023   composite_info.width=0;
4024   composite_info.height=0;
4025   cursor=XCreateFontCursor(display,XC_ul_angle);
4026   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
4027   blend=0.0;
4028   state=DefaultState;
4029   do
4030   {
4031     if (windows->info.mapped != MagickFalse)
4032       {
4033         /*
4034           Display pointer position.
4035         */
4036         (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
4037           (long) composite_info.x,(long) composite_info.y);
4038         XInfoWidget(display,windows,text);
4039       }
4040     highlight_info=composite_info;
4041     highlight_info.x=composite_info.x-windows->image.x;
4042     highlight_info.y=composite_info.y-windows->image.y;
4043     XHighlightRectangle(display,windows->image.id,
4044       windows->image.highlight_context,&highlight_info);
4045     /*
4046       Wait for next event.
4047     */
4048     XScreenEvent(display,windows,&event,exception);
4049     XHighlightRectangle(display,windows->image.id,
4050       windows->image.highlight_context,&highlight_info);
4051     if (event.xany.window == windows->command.id)
4052       {
4053         /*
4054           Select a command from the Command widget.
4055         */
4056         id=XCommandWidget(display,windows,CompositeMenu,&event);
4057         if (id < 0)
4058           continue;
4059         switch (CompositeCommands[id])
4060         {
4061           case CompositeOperatorsCommand:
4062           {
4063             char
4064               command[MaxTextExtent],
4065               **operators;
4066
4067             /*
4068               Select a command from the pop-up menu.
4069             */
4070             operators=GetCommandOptions(MagickComposeOptions);
4071             if (operators == (char **) NULL)
4072               break;
4073             entry=XMenuWidget(display,windows,CompositeMenu[id],
4074               (const char **) operators,command);
4075             if (entry >= 0)
4076               compose=(CompositeOperator) ParseCommandOption(
4077                 MagickComposeOptions,MagickFalse,operators[entry]);
4078             operators=DestroyStringList(operators);
4079             break;
4080           }
4081           case CompositeDissolveCommand:
4082           {
4083             static char
4084               factor[MaxTextExtent] = "20.0";
4085
4086             /*
4087               Dissolve the two images a given percent.
4088             */
4089             (void) XSetFunction(display,windows->image.highlight_context,
4090               GXcopy);
4091             (void) XDialogWidget(display,windows,"Dissolve",
4092               "Enter the blend factor (0.0 - 99.9%):",factor);
4093             (void) XSetFunction(display,windows->image.highlight_context,
4094               GXinvert);
4095             if (*factor == '\0')
4096               break;
4097             blend=StringToDouble(factor,(char **) NULL);
4098             compose=DissolveCompositeOp;
4099             break;
4100           }
4101           case CompositeDisplaceCommand:
4102           {
4103             /*
4104               Get horizontal and vertical scale displacement geometry.
4105             */
4106             (void) XSetFunction(display,windows->image.highlight_context,
4107               GXcopy);
4108             (void) XDialogWidget(display,windows,"Displace",
4109               "Enter the horizontal and vertical scale:",displacement_geometry);
4110             (void) XSetFunction(display,windows->image.highlight_context,
4111               GXinvert);
4112             if (*displacement_geometry == '\0')
4113               break;
4114             compose=DisplaceCompositeOp;
4115             break;
4116           }
4117           case CompositeHelpCommand:
4118           {
4119             (void) XSetFunction(display,windows->image.highlight_context,
4120               GXcopy);
4121             XTextViewWidget(display,resource_info,windows,MagickFalse,
4122               "Help Viewer - Image Composite",ImageCompositeHelp);
4123             (void) XSetFunction(display,windows->image.highlight_context,
4124               GXinvert);
4125             break;
4126           }
4127           case CompositeDismissCommand:
4128           {
4129             /*
4130               Prematurely exit.
4131             */
4132             state|=EscapeState;
4133             state|=ExitState;
4134             break;
4135           }
4136           default:
4137             break;
4138         }
4139         continue;
4140       }
4141     switch (event.type)
4142     {
4143       case ButtonPress:
4144       {
4145         if (image->debug != MagickFalse)
4146           (void) LogMagickEvent(X11Event,GetMagickModule(),
4147             "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
4148             event.xbutton.button,event.xbutton.x,event.xbutton.y);
4149         if (event.xbutton.button != Button1)
4150           break;
4151         if (event.xbutton.window != windows->image.id)
4152           break;
4153         /*
4154           Change cursor.
4155         */
4156         composite_info.width=composite_image->columns;
4157         composite_info.height=composite_image->rows;
4158         (void) XCheckDefineCursor(display,windows->image.id,cursor);
4159         composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4160         composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4161         break;
4162       }
4163       case ButtonRelease:
4164       {
4165         if (image->debug != MagickFalse)
4166           (void) LogMagickEvent(X11Event,GetMagickModule(),
4167             "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
4168             event.xbutton.button,event.xbutton.x,event.xbutton.y);
4169         if (event.xbutton.button != Button1)
4170           break;
4171         if (event.xbutton.window != windows->image.id)
4172           break;
4173         if ((composite_info.width != 0) && (composite_info.height != 0))
4174           {
4175             /*
4176               User has selected the location of the composite image.
4177             */
4178             composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4179             composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4180             state|=ExitState;
4181           }
4182         break;
4183       }
4184       case Expose:
4185         break;
4186       case KeyPress:
4187       {
4188         char
4189           command[MaxTextExtent];
4190
4191         KeySym
4192           key_symbol;
4193
4194         int
4195           length;
4196
4197         if (event.xkey.window != windows->image.id)
4198           break;
4199         /*
4200           Respond to a user key press.
4201         */
4202         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
4203           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4204         *(command+length)='\0';
4205         if (image->debug != MagickFalse)
4206           (void) LogMagickEvent(X11Event,GetMagickModule(),
4207             "Key press: 0x%lx (%s)",(unsigned long) key_symbol,command);
4208         switch ((int) key_symbol)
4209         {
4210           case XK_Escape:
4211           case XK_F20:
4212           {
4213             /*
4214               Prematurely exit.
4215             */
4216             composite_image=DestroyImage(composite_image);
4217             state|=EscapeState;
4218             state|=ExitState;
4219             break;
4220           }
4221           case XK_F1:
4222           case XK_Help:
4223           {
4224             (void) XSetFunction(display,windows->image.highlight_context,
4225               GXcopy);
4226             XTextViewWidget(display,resource_info,windows,MagickFalse,
4227               "Help Viewer - Image Composite",ImageCompositeHelp);
4228             (void) XSetFunction(display,windows->image.highlight_context,
4229               GXinvert);
4230             break;
4231           }
4232           default:
4233           {
4234             (void) XBell(display,0);
4235             break;
4236           }
4237         }
4238         break;
4239       }
4240       case MotionNotify:
4241       {
4242         /*
4243           Map and unmap Info widget as text cursor crosses its boundaries.
4244         */
4245         x=event.xmotion.x;
4246         y=event.xmotion.y;
4247         if (windows->info.mapped != MagickFalse)
4248           {
4249             if ((x < (int) (windows->info.x+windows->info.width)) &&
4250                 (y < (int) (windows->info.y+windows->info.height)))
4251               (void) XWithdrawWindow(display,windows->info.id,
4252                 windows->info.screen);
4253           }
4254         else
4255           if ((x > (int) (windows->info.x+windows->info.width)) ||
4256               (y > (int) (windows->info.y+windows->info.height)))
4257             (void) XMapWindow(display,windows->info.id);
4258         composite_info.x=(ssize_t) windows->image.x+x;
4259         composite_info.y=(ssize_t) windows->image.y+y;
4260         break;
4261       }
4262       default:
4263       {
4264         if (image->debug != MagickFalse)
4265           (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
4266             event.type);
4267         break;
4268       }
4269     }
4270   } while ((state & ExitState) == 0);
4271   (void) XSelectInput(display,windows->image.id,
4272     windows->image.attributes.event_mask);
4273   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
4274   XSetCursorState(display,windows,MagickFalse);
4275   (void) XFreeCursor(display,cursor);
4276   if ((state & EscapeState) != 0)
4277     return(MagickTrue);
4278   /*
4279     Image compositing is relative to image configuration.
4280   */
4281   XSetCursorState(display,windows,MagickTrue);
4282   XCheckRefreshWindows(display,windows);
4283   width=(unsigned int) image->columns;
4284   height=(unsigned int) image->rows;
4285   x=0;
4286   y=0;
4287   if (windows->image.crop_geometry != (char *) NULL)
4288     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
4289   scale_factor=(MagickRealType) width/windows->image.ximage->width;
4290   composite_info.x+=x;
4291   composite_info.x=(ssize_t) (scale_factor*composite_info.x+0.5);
4292   composite_info.width=(unsigned int) (scale_factor*composite_info.width+0.5);
4293   scale_factor=(MagickRealType) height/windows->image.ximage->height;
4294   composite_info.y+=y;
4295   composite_info.y=(ssize_t) (scale_factor*composite_info.y+0.5);
4296   composite_info.height=(unsigned int) (scale_factor*composite_info.height+0.5);
4297   if ((composite_info.width != composite_image->columns) ||
4298       (composite_info.height != composite_image->rows))
4299     {
4300       Image
4301         *resize_image;
4302
4303       /*
4304         Scale composite image.
4305       */
4306       resize_image=ResizeImage(composite_image,composite_info.width,
4307         composite_info.height,composite_image->filter,composite_image->blur,
4308         exception);
4309       composite_image=DestroyImage(composite_image);
4310       if (resize_image == (Image *) NULL)
4311         {
4312           XSetCursorState(display,windows,MagickFalse);
4313           return(MagickFalse);
4314         }
4315       composite_image=resize_image;
4316     }
4317   if (compose == DisplaceCompositeOp)
4318     (void) SetImageArtifact(composite_image,"compose:args",
4319       displacement_geometry);
4320   if (blend != 0.0)
4321     {
4322       CacheView
4323         *image_view;
4324
4325       int
4326         y;
4327
4328       Quantum
4329         opacity;
4330
4331       register int
4332         x;
4333
4334       register Quantum
4335         *q;
4336
4337       /*
4338         Create mattes for blending.
4339       */
4340       (void) SetImageAlphaChannel(composite_image,OpaqueAlphaChannel,exception);
4341       opacity=(Quantum) (ScaleQuantumToChar((Quantum) QuantumRange)-
4342         ((ssize_t) ScaleQuantumToChar((Quantum) QuantumRange)*blend)/100);
4343       if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
4344         return(MagickFalse);
4345       image->matte=MagickTrue;
4346       image_view=AcquireCacheView(image);
4347       for (y=0; y < (int) image->rows; y++)
4348       {
4349         q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,image->columns,1,
4350           exception);
4351         if (q == (Quantum *) NULL)
4352           break;
4353         for (x=0; x < (int) image->columns; x++)
4354         {
4355           SetPixelAlpha(image,opacity,q);
4356           q+=GetPixelChannels(image);
4357         }
4358         if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4359           break;
4360       }
4361       image_view=DestroyCacheView(image_view);
4362     }
4363   /*
4364     Composite image with X Image window.
4365   */
4366   (void) CompositeImage(image,compose,composite_image,composite_info.x,
4367     composite_info.y,exception);
4368   composite_image=DestroyImage(composite_image);
4369   XSetCursorState(display,windows,MagickFalse);
4370   /*
4371     Update image configuration.
4372   */
4373   XConfigureImageColormap(display,resource_info,windows,image,exception);
4374   (void) XConfigureImage(display,resource_info,windows,image,exception);
4375   return(MagickTrue);
4376 }
4377 \f
4378 /*
4379 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4380 %                                                                             %
4381 %                                                                             %
4382 %                                                                             %
4383 +   X C o n f i g u r e I m a g e                                             %
4384 %                                                                             %
4385 %                                                                             %
4386 %                                                                             %
4387 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4388 %
4389 %  XConfigureImage() creates a new X image.  It also notifies the window
4390 %  manager of the new image size and configures the transient widows.
4391 %
4392 %  The format of the XConfigureImage method is:
4393 %
4394 %      MagickBooleanType XConfigureImage(Display *display,
4395 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
4396 %        ExceptionInfo *exception)
4397 %
4398 %  A description of each parameter follows:
4399 %
4400 %    o display: Specifies a connection to an X server; returned from
4401 %      XOpenDisplay.
4402 %
4403 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4404 %
4405 %    o windows: Specifies a pointer to a XWindows structure.
4406 %
4407 %    o image: the image.
4408 %
4409 %    o exception: return any errors or warnings in this structure.
4410 %
4411 %    o exception: return any errors or warnings in this structure.
4412 %
4413 */
4414 static MagickBooleanType XConfigureImage(Display *display,
4415   XResourceInfo *resource_info,XWindows *windows,Image *image,
4416   ExceptionInfo *exception)
4417 {
4418   char
4419     geometry[MaxTextExtent];
4420
4421   MagickStatusType
4422     status;
4423
4424   size_t
4425     mask,
4426     height,
4427     width;
4428
4429   ssize_t
4430     x,
4431     y;
4432
4433   XSizeHints
4434     *size_hints;
4435
4436   XWindowChanges
4437     window_changes;
4438
4439   /*
4440     Dismiss if window dimensions are zero.
4441   */
4442   width=(unsigned int) windows->image.window_changes.width;
4443   height=(unsigned int) windows->image.window_changes.height;
4444   if (image->debug != MagickFalse)
4445     (void) LogMagickEvent(X11Event,GetMagickModule(),
4446       "Configure Image: %dx%d=>%.20gx%.20g",windows->image.ximage->width,
4447       windows->image.ximage->height,(double) width,(double) height);
4448   if ((width*height) == 0)
4449     return(MagickTrue);
4450   x=0;
4451   y=0;
4452   /*
4453     Resize image to fit Image window dimensions.
4454   */
4455   XSetCursorState(display,windows,MagickTrue);
4456   (void) XFlush(display);
4457   if (((int) width != windows->image.ximage->width) ||
4458       ((int) height != windows->image.ximage->height))
4459     image->taint=MagickTrue;
4460   windows->magnify.x=(int)
4461     width*windows->magnify.x/windows->image.ximage->width;
4462   windows->magnify.y=(int)
4463     height*windows->magnify.y/windows->image.ximage->height;
4464   windows->image.x=(int) (width*windows->image.x/windows->image.ximage->width);
4465   windows->image.y=(int)
4466     (height*windows->image.y/windows->image.ximage->height);
4467   status=XMakeImage(display,resource_info,&windows->image,image,
4468     (unsigned int) width,(unsigned int) height,exception);
4469   if (status == MagickFalse)
4470     XNoticeWidget(display,windows,"Unable to configure X image:",
4471       windows->image.name);
4472   /*
4473     Notify window manager of the new configuration.
4474   */
4475   if (resource_info->image_geometry != (char *) NULL)
4476     (void) FormatLocaleString(geometry,MaxTextExtent,"%s>!",
4477       resource_info->image_geometry);
4478   else
4479     (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>!",
4480       XDisplayWidth(display,windows->image.screen),
4481       XDisplayHeight(display,windows->image.screen));
4482   (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
4483   window_changes.width=(int) width;
4484   if (window_changes.width > XDisplayWidth(display,windows->image.screen))
4485     window_changes.width=XDisplayWidth(display,windows->image.screen);
4486   window_changes.height=(int) height;
4487   if (window_changes.height > XDisplayHeight(display,windows->image.screen))
4488     window_changes.height=XDisplayHeight(display,windows->image.screen);
4489   mask=(size_t) (CWWidth | CWHeight);
4490   if (resource_info->backdrop)
4491     {
4492       mask|=CWX | CWY;
4493       window_changes.x=(int)
4494         ((XDisplayWidth(display,windows->image.screen)/2)-(width/2));
4495       window_changes.y=(int)
4496         ((XDisplayHeight(display,windows->image.screen)/2)-(height/2));
4497     }
4498   (void) XReconfigureWMWindow(display,windows->image.id,windows->image.screen,
4499     (unsigned int) mask,&window_changes);
4500   (void) XClearWindow(display,windows->image.id);
4501   XRefreshWindow(display,&windows->image,(XEvent *) NULL);
4502   /*
4503     Update Magnify window configuration.
4504   */
4505   if (windows->magnify.mapped != MagickFalse)
4506     XMakeMagnifyImage(display,windows,exception);
4507   windows->pan.crop_geometry=windows->image.crop_geometry;
4508   XBestIconSize(display,&windows->pan,image);
4509   while (((windows->pan.width << 1) < MaxIconSize) &&
4510          ((windows->pan.height << 1) < MaxIconSize))
4511   {
4512     windows->pan.width<<=1;
4513     windows->pan.height<<=1;
4514   }
4515   if (windows->pan.geometry != (char *) NULL)
4516     (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
4517       &windows->pan.width,&windows->pan.height);
4518   window_changes.width=(int) windows->pan.width;
4519   window_changes.height=(int) windows->pan.height;
4520   size_hints=XAllocSizeHints();
4521   if (size_hints != (XSizeHints *) NULL)
4522     {
4523       /*
4524         Set new size hints.
4525       */
4526       size_hints->flags=PSize | PMinSize | PMaxSize;
4527       size_hints->width=window_changes.width;
4528       size_hints->height=window_changes.height;
4529       size_hints->min_width=size_hints->width;
4530       size_hints->min_height=size_hints->height;
4531       size_hints->max_width=size_hints->width;
4532       size_hints->max_height=size_hints->height;
4533       (void) XSetNormalHints(display,windows->pan.id,size_hints);
4534       (void) XFree((void *) size_hints);
4535     }
4536   (void) XReconfigureWMWindow(display,windows->pan.id,windows->pan.screen,
4537     (unsigned int) (CWWidth | CWHeight),&window_changes);
4538   /*
4539     Update icon window configuration.
4540   */
4541   windows->icon.crop_geometry=windows->image.crop_geometry;
4542   XBestIconSize(display,&windows->icon,image);
4543   window_changes.width=(int) windows->icon.width;
4544   window_changes.height=(int) windows->icon.height;
4545   (void) XReconfigureWMWindow(display,windows->icon.id,windows->icon.screen,
4546     (unsigned int) (CWWidth | CWHeight),&window_changes);
4547   XSetCursorState(display,windows,MagickFalse);
4548   return(status != 0 ? MagickTrue : MagickFalse);
4549 }
4550 \f
4551 /*
4552 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4553 %                                                                             %
4554 %                                                                             %
4555 %                                                                             %
4556 +   X C r o p I m a g e                                                       %
4557 %                                                                             %
4558 %                                                                             %
4559 %                                                                             %
4560 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4561 %
4562 %  XCropImage() allows the user to select a region of the image and crop, copy,
4563 %  or cut it.  For copy or cut, the image can subsequently be composited onto
4564 %  the image with XPasteImage.
4565 %
4566 %  The format of the XCropImage method is:
4567 %
4568 %      MagickBooleanType XCropImage(Display *display,
4569 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
4570 %        const ClipboardMode mode,ExceptionInfo *exception)
4571 %
4572 %  A description of each parameter follows:
4573 %
4574 %    o display: Specifies a connection to an X server; returned from
4575 %      XOpenDisplay.
4576 %
4577 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4578 %
4579 %    o windows: Specifies a pointer to a XWindows structure.
4580 %
4581 %    o image: the image; returned from ReadImage.
4582 %
4583 %    o mode: This unsigned value specified whether the image should be
4584 %      cropped, copied, or cut.
4585 %
4586 %    o exception: return any errors or warnings in this structure.
4587 %
4588 */
4589 static MagickBooleanType XCropImage(Display *display,
4590   XResourceInfo *resource_info,XWindows *windows,Image *image,
4591   const ClipboardMode mode,ExceptionInfo *exception)
4592 {
4593   static const char
4594     *CropModeMenu[] =
4595     {
4596       "Help",
4597       "Dismiss",
4598       (char *) NULL
4599     },
4600     *RectifyModeMenu[] =
4601     {
4602       "Crop",
4603       "Help",
4604       "Dismiss",
4605       (char *) NULL
4606     };
4607
4608   static const ModeType
4609     CropCommands[] =
4610     {
4611       CropHelpCommand,
4612       CropDismissCommand
4613     },
4614     RectifyCommands[] =
4615     {
4616       RectifyCopyCommand,
4617       RectifyHelpCommand,
4618       RectifyDismissCommand
4619     };
4620
4621   CacheView
4622     *image_view;
4623
4624   char
4625     command[MaxTextExtent],
4626     text[MaxTextExtent];
4627
4628   Cursor
4629     cursor;
4630
4631   int
4632     id,
4633     x,
4634     y;
4635
4636   KeySym
4637     key_symbol;
4638
4639   Image
4640     *crop_image;
4641
4642   MagickRealType
4643     scale_factor;
4644
4645   RectangleInfo
4646     crop_info,
4647     highlight_info;
4648
4649   register Quantum
4650     *q;
4651
4652   unsigned int
4653     height,
4654     width;
4655
4656   size_t
4657     state;
4658
4659   XEvent
4660     event;
4661
4662   /*
4663     Map Command widget.
4664   */
4665   switch (mode)
4666   {
4667     case CopyMode:
4668     {
4669       (void) CloneString(&windows->command.name,"Copy");
4670       break;
4671     }
4672     case CropMode:
4673     {
4674       (void) CloneString(&windows->command.name,"Crop");
4675       break;
4676     }
4677     case CutMode:
4678     {
4679       (void) CloneString(&windows->command.name,"Cut");
4680       break;
4681     }
4682   }
4683   RectifyModeMenu[0]=windows->command.name;
4684   windows->command.data=0;
4685   (void) XCommandWidget(display,windows,CropModeMenu,(XEvent *) NULL);
4686   (void) XMapRaised(display,windows->command.id);
4687   XClientMessage(display,windows->image.id,windows->im_protocols,
4688     windows->im_update_widget,CurrentTime);
4689   /*
4690     Track pointer until button 1 is pressed.
4691   */
4692   XQueryPosition(display,windows->image.id,&x,&y);
4693   (void) XSelectInput(display,windows->image.id,
4694     windows->image.attributes.event_mask | PointerMotionMask);
4695   crop_info.x=(ssize_t) windows->image.x+x;
4696   crop_info.y=(ssize_t) windows->image.y+y;
4697   crop_info.width=0;
4698   crop_info.height=0;
4699   cursor=XCreateFontCursor(display,XC_fleur);
4700   state=DefaultState;
4701   do
4702   {
4703     if (windows->info.mapped != MagickFalse)
4704       {
4705         /*
4706           Display pointer position.
4707         */
4708         (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
4709           (long) crop_info.x,(long) crop_info.y);
4710         XInfoWidget(display,windows,text);
4711       }
4712     /*
4713       Wait for next event.
4714     */
4715     XScreenEvent(display,windows,&event,exception);
4716     if (event.xany.window == windows->command.id)
4717       {
4718         /*
4719           Select a command from the Command widget.
4720         */
4721         id=XCommandWidget(display,windows,CropModeMenu,&event);
4722         if (id < 0)
4723           continue;
4724         switch (CropCommands[id])
4725         {
4726           case CropHelpCommand:
4727           {
4728             switch (mode)
4729             {
4730               case CopyMode:
4731               {
4732                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4733                   "Help Viewer - Image Copy",ImageCopyHelp);
4734                 break;
4735               }
4736               case CropMode:
4737               {
4738                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4739                   "Help Viewer - Image Crop",ImageCropHelp);
4740                 break;
4741               }
4742               case CutMode:
4743               {
4744                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4745                   "Help Viewer - Image Cut",ImageCutHelp);
4746                 break;
4747               }
4748             }
4749             break;
4750           }
4751           case CropDismissCommand:
4752           {
4753             /*
4754               Prematurely exit.
4755             */
4756             state|=EscapeState;
4757             state|=ExitState;
4758             break;
4759           }
4760           default:
4761             break;
4762         }
4763         continue;
4764       }
4765     switch (event.type)
4766     {
4767       case ButtonPress:
4768       {
4769         if (event.xbutton.button != Button1)
4770           break;
4771         if (event.xbutton.window != windows->image.id)
4772           break;
4773         /*
4774           Note first corner of cropping rectangle-- exit loop.
4775         */
4776         (void) XCheckDefineCursor(display,windows->image.id,cursor);
4777         crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4778         crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4779         state|=ExitState;
4780         break;
4781       }
4782       case ButtonRelease:
4783         break;
4784       case Expose:
4785         break;
4786       case KeyPress:
4787       {
4788         if (event.xkey.window != windows->image.id)
4789           break;
4790         /*
4791           Respond to a user key press.
4792         */
4793         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
4794           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4795         switch ((int) key_symbol)
4796         {
4797           case XK_Escape:
4798           case XK_F20:
4799           {
4800             /*
4801               Prematurely exit.
4802             */
4803             state|=EscapeState;
4804             state|=ExitState;
4805             break;
4806           }
4807           case XK_F1:
4808           case XK_Help:
4809           {
4810             switch (mode)
4811             {
4812               case CopyMode:
4813               {
4814                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4815                   "Help Viewer - Image Copy",ImageCopyHelp);
4816                 break;
4817               }
4818               case CropMode:
4819               {
4820                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4821                   "Help Viewer - Image Crop",ImageCropHelp);
4822                 break;
4823               }
4824               case CutMode:
4825               {
4826                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4827                   "Help Viewer - Image Cut",ImageCutHelp);
4828                 break;
4829               }
4830             }
4831             break;
4832           }
4833           default:
4834           {
4835             (void) XBell(display,0);
4836             break;
4837           }
4838         }
4839         break;
4840       }
4841       case MotionNotify:
4842       {
4843         if (event.xmotion.window != windows->image.id)
4844           break;
4845         /*
4846           Map and unmap Info widget as text cursor crosses its boundaries.
4847         */
4848         x=event.xmotion.x;
4849         y=event.xmotion.y;
4850         if (windows->info.mapped != MagickFalse)
4851           {
4852             if ((x < (int) (windows->info.x+windows->info.width)) &&
4853                 (y < (int) (windows->info.y+windows->info.height)))
4854               (void) XWithdrawWindow(display,windows->info.id,
4855                 windows->info.screen);
4856           }
4857         else
4858           if ((x > (int) (windows->info.x+windows->info.width)) ||
4859               (y > (int) (windows->info.y+windows->info.height)))
4860             (void) XMapWindow(display,windows->info.id);
4861         crop_info.x=(ssize_t) windows->image.x+x;
4862         crop_info.y=(ssize_t) windows->image.y+y;
4863         break;
4864       }
4865       default:
4866         break;
4867     }
4868   } while ((state & ExitState) == 0);
4869   (void) XSelectInput(display,windows->image.id,
4870     windows->image.attributes.event_mask);
4871   if ((state & EscapeState) != 0)
4872     {
4873       /*
4874         User want to exit without cropping.
4875       */
4876       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4877       (void) XFreeCursor(display,cursor);
4878       return(MagickTrue);
4879     }
4880   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
4881   do
4882   {
4883     /*
4884       Size rectangle as pointer moves until the mouse button is released.
4885     */
4886     x=(int) crop_info.x;
4887     y=(int) crop_info.y;
4888     crop_info.width=0;
4889     crop_info.height=0;
4890     state=DefaultState;
4891     do
4892     {
4893       highlight_info=crop_info;
4894       highlight_info.x=crop_info.x-windows->image.x;
4895       highlight_info.y=crop_info.y-windows->image.y;
4896       if ((highlight_info.width > 3) && (highlight_info.height > 3))
4897         {
4898           /*
4899             Display info and draw cropping rectangle.
4900           */
4901           if (windows->info.mapped == MagickFalse)
4902             (void) XMapWindow(display,windows->info.id);
4903           (void) FormatLocaleString(text,MaxTextExtent,
4904             " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
4905             crop_info.height,(double) crop_info.x,(double) crop_info.y);
4906           XInfoWidget(display,windows,text);
4907           XHighlightRectangle(display,windows->image.id,
4908             windows->image.highlight_context,&highlight_info);
4909         }
4910       else
4911         if (windows->info.mapped != MagickFalse)
4912           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4913       /*
4914         Wait for next event.
4915       */
4916       XScreenEvent(display,windows,&event,exception);
4917       if ((highlight_info.width > 3) && (highlight_info.height > 3))
4918         XHighlightRectangle(display,windows->image.id,
4919           windows->image.highlight_context,&highlight_info);
4920       switch (event.type)
4921       {
4922         case ButtonPress:
4923         {
4924           crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4925           crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4926           break;
4927         }
4928         case ButtonRelease:
4929         {
4930           /*
4931             User has committed to cropping rectangle.
4932           */
4933           crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4934           crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4935           XSetCursorState(display,windows,MagickFalse);
4936           state|=ExitState;
4937           windows->command.data=0;
4938           (void) XCommandWidget(display,windows,RectifyModeMenu,
4939             (XEvent *) NULL);
4940           break;
4941         }
4942         case Expose:
4943           break;
4944         case MotionNotify:
4945         {
4946           crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
4947           crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
4948         }
4949         default:
4950           break;
4951       }
4952       if ((((int) crop_info.x != x) && ((int) crop_info.y != y)) ||
4953           ((state & ExitState) != 0))
4954         {
4955           /*
4956             Check boundary conditions.
4957           */
4958           if (crop_info.x < 0)
4959             crop_info.x=0;
4960           else
4961             if (crop_info.x > (ssize_t) windows->image.ximage->width)
4962               crop_info.x=(ssize_t) windows->image.ximage->width;
4963           if ((int) crop_info.x < x)
4964             crop_info.width=(unsigned int) (x-crop_info.x);
4965           else
4966             {
4967               crop_info.width=(unsigned int) (crop_info.x-x);
4968               crop_info.x=(ssize_t) x;
4969             }
4970           if (crop_info.y < 0)
4971             crop_info.y=0;
4972           else
4973             if (crop_info.y > (ssize_t) windows->image.ximage->height)
4974               crop_info.y=(ssize_t) windows->image.ximage->height;
4975           if ((int) crop_info.y < y)
4976             crop_info.height=(unsigned int) (y-crop_info.y);
4977           else
4978             {
4979               crop_info.height=(unsigned int) (crop_info.y-y);
4980               crop_info.y=(ssize_t) y;
4981             }
4982         }
4983     } while ((state & ExitState) == 0);
4984     /*
4985       Wait for user to grab a corner of the rectangle or press return.
4986     */
4987     state=DefaultState;
4988     (void) XMapWindow(display,windows->info.id);
4989     do
4990     {
4991       if (windows->info.mapped != MagickFalse)
4992         {
4993           /*
4994             Display pointer position.
4995           */
4996           (void) FormatLocaleString(text,MaxTextExtent,
4997             " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
4998             crop_info.height,(double) crop_info.x,(double) crop_info.y);
4999           XInfoWidget(display,windows,text);
5000         }
5001       highlight_info=crop_info;
5002       highlight_info.x=crop_info.x-windows->image.x;
5003       highlight_info.y=crop_info.y-windows->image.y;
5004       if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
5005         {
5006           state|=EscapeState;
5007           state|=ExitState;
5008           break;
5009         }
5010       XHighlightRectangle(display,windows->image.id,
5011         windows->image.highlight_context,&highlight_info);
5012       XScreenEvent(display,windows,&event,exception);
5013       if (event.xany.window == windows->command.id)
5014         {
5015           /*
5016             Select a command from the Command widget.
5017           */
5018           (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
5019           id=XCommandWidget(display,windows,RectifyModeMenu,&event);
5020           (void) XSetFunction(display,windows->image.highlight_context,
5021             GXinvert);
5022           XHighlightRectangle(display,windows->image.id,
5023             windows->image.highlight_context,&highlight_info);
5024           if (id >= 0)
5025             switch (RectifyCommands[id])
5026             {
5027               case RectifyCopyCommand:
5028               {
5029                 state|=ExitState;
5030                 break;
5031               }
5032               case RectifyHelpCommand:
5033               {
5034                 (void) XSetFunction(display,windows->image.highlight_context,
5035                   GXcopy);
5036                 switch (mode)
5037                 {
5038                   case CopyMode:
5039                   {
5040                     XTextViewWidget(display,resource_info,windows,MagickFalse,
5041                       "Help Viewer - Image Copy",ImageCopyHelp);
5042                     break;
5043                   }
5044                   case CropMode:
5045                   {
5046                     XTextViewWidget(display,resource_info,windows,MagickFalse,
5047                       "Help Viewer - Image Crop",ImageCropHelp);
5048                     break;
5049                   }
5050                   case CutMode:
5051                   {
5052                     XTextViewWidget(display,resource_info,windows,MagickFalse,
5053                       "Help Viewer - Image Cut",ImageCutHelp);
5054                     break;
5055                   }
5056                 }
5057                 (void) XSetFunction(display,windows->image.highlight_context,
5058                   GXinvert);
5059                 break;
5060               }
5061               case RectifyDismissCommand:
5062               {
5063                 /*
5064                   Prematurely exit.
5065                 */
5066                 state|=EscapeState;
5067                 state|=ExitState;
5068                 break;
5069               }
5070               default:
5071                 break;
5072             }
5073           continue;
5074         }
5075       XHighlightRectangle(display,windows->image.id,
5076         windows->image.highlight_context,&highlight_info);
5077       switch (event.type)
5078       {
5079         case ButtonPress:
5080         {
5081           if (event.xbutton.button != Button1)
5082             break;
5083           if (event.xbutton.window != windows->image.id)
5084             break;
5085           x=windows->image.x+event.xbutton.x;
5086           y=windows->image.y+event.xbutton.y;
5087           if ((x < (int) (crop_info.x+RoiDelta)) &&
5088               (x > (int) (crop_info.x-RoiDelta)) &&
5089               (y < (int) (crop_info.y+RoiDelta)) &&
5090               (y > (int) (crop_info.y-RoiDelta)))
5091             {
5092               crop_info.x=(ssize_t) (crop_info.x+crop_info.width);
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+RoiDelta)) &&
5098               (x > (int) (crop_info.x-RoiDelta)) &&
5099               (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5100               (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5101             {
5102               crop_info.x=(ssize_t) (crop_info.x+crop_info.width);
5103               state|=UpdateConfigurationState;
5104               break;
5105             }
5106           if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5107               (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5108               (y < (int) (crop_info.y+RoiDelta)) &&
5109               (y > (int) (crop_info.y-RoiDelta)))
5110             {
5111               crop_info.y=(ssize_t) (crop_info.y+crop_info.height);
5112               state|=UpdateConfigurationState;
5113               break;
5114             }
5115           if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5116               (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5117               (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5118               (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5119             {
5120               state|=UpdateConfigurationState;
5121               break;
5122             }
5123         }
5124         case ButtonRelease:
5125         {
5126           if (event.xbutton.window == windows->pan.id)
5127             if ((highlight_info.x != crop_info.x-windows->image.x) ||
5128                 (highlight_info.y != crop_info.y-windows->image.y))
5129               XHighlightRectangle(display,windows->image.id,
5130                 windows->image.highlight_context,&highlight_info);
5131           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5132             event.xbutton.time);
5133           break;
5134         }
5135         case Expose:
5136         {
5137           if (event.xexpose.window == windows->image.id)
5138             if (event.xexpose.count == 0)
5139               {
5140                 event.xexpose.x=(int) highlight_info.x;
5141                 event.xexpose.y=(int) highlight_info.y;
5142                 event.xexpose.width=(int) highlight_info.width;
5143                 event.xexpose.height=(int) highlight_info.height;
5144                 XRefreshWindow(display,&windows->image,&event);
5145               }
5146           if (event.xexpose.window == windows->info.id)
5147             if (event.xexpose.count == 0)
5148               XInfoWidget(display,windows,text);
5149           break;
5150         }
5151         case KeyPress:
5152         {
5153           if (event.xkey.window != windows->image.id)
5154             break;
5155           /*
5156             Respond to a user key press.
5157           */
5158           (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5159             sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5160           switch ((int) key_symbol)
5161           {
5162             case XK_Escape:
5163             case XK_F20:
5164               state|=EscapeState;
5165             case XK_Return:
5166             {
5167               state|=ExitState;
5168               break;
5169             }
5170             case XK_Home:
5171             case XK_KP_Home:
5172             {
5173               crop_info.x=(ssize_t) (windows->image.width/2L-
5174                 crop_info.width/2L);
5175               crop_info.y=(ssize_t) (windows->image.height/2L-
5176                 crop_info.height/2L);
5177               break;
5178             }
5179             case XK_Left:
5180             case XK_KP_Left:
5181             {
5182               crop_info.x--;
5183               break;
5184             }
5185             case XK_Up:
5186             case XK_KP_Up:
5187             case XK_Next:
5188             {
5189               crop_info.y--;
5190               break;
5191             }
5192             case XK_Right:
5193             case XK_KP_Right:
5194             {
5195               crop_info.x++;
5196               break;
5197             }
5198             case XK_Prior:
5199             case XK_Down:
5200             case XK_KP_Down:
5201             {
5202               crop_info.y++;
5203               break;
5204             }
5205             case XK_F1:
5206             case XK_Help:
5207             {
5208               (void) XSetFunction(display,windows->image.highlight_context,
5209                 GXcopy);
5210               switch (mode)
5211               {
5212                 case CopyMode:
5213                 {
5214                   XTextViewWidget(display,resource_info,windows,MagickFalse,
5215                     "Help Viewer - Image Copy",ImageCopyHelp);
5216                   break;
5217                 }
5218                 case CropMode:
5219                 {
5220                   XTextViewWidget(display,resource_info,windows,MagickFalse,
5221                     "Help Viewer - Image Cropg",ImageCropHelp);
5222                   break;
5223                 }
5224                 case CutMode:
5225                 {
5226                   XTextViewWidget(display,resource_info,windows,MagickFalse,
5227                     "Help Viewer - Image Cutg",ImageCutHelp);
5228                   break;
5229                 }
5230               }
5231               (void) XSetFunction(display,windows->image.highlight_context,
5232                 GXinvert);
5233               break;
5234             }
5235             default:
5236             {
5237               (void) XBell(display,0);
5238               break;
5239             }
5240           }
5241           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5242             event.xkey.time);
5243           break;
5244         }
5245         case KeyRelease:
5246           break;
5247         case MotionNotify:
5248         {
5249           if (event.xmotion.window != windows->image.id)
5250             break;
5251           /*
5252             Map and unmap Info widget as text cursor crosses its boundaries.
5253           */
5254           x=event.xmotion.x;
5255           y=event.xmotion.y;
5256           if (windows->info.mapped != MagickFalse)
5257             {
5258               if ((x < (int) (windows->info.x+windows->info.width)) &&
5259                   (y < (int) (windows->info.y+windows->info.height)))
5260                 (void) XWithdrawWindow(display,windows->info.id,
5261                   windows->info.screen);
5262             }
5263           else
5264             if ((x > (int) (windows->info.x+windows->info.width)) ||
5265                 (y > (int) (windows->info.y+windows->info.height)))
5266               (void) XMapWindow(display,windows->info.id);
5267           crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
5268           crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
5269           break;
5270         }
5271         case SelectionRequest:
5272         {
5273           XSelectionEvent
5274             notify;
5275
5276           XSelectionRequestEvent
5277             *request;
5278
5279           /*
5280             Set primary selection.
5281           */
5282           (void) FormatLocaleString(text,MaxTextExtent,
5283             "%.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
5284             crop_info.height,(double) crop_info.x,(double) crop_info.y);
5285           request=(&(event.xselectionrequest));
5286           (void) XChangeProperty(request->display,request->requestor,
5287             request->property,request->target,8,PropModeReplace,
5288             (unsigned char *) text,(int) strlen(text));
5289           notify.type=SelectionNotify;
5290           notify.display=request->display;
5291           notify.requestor=request->requestor;
5292           notify.selection=request->selection;
5293           notify.target=request->target;
5294           notify.time=request->time;
5295           if (request->property == None)
5296             notify.property=request->target;
5297           else
5298             notify.property=request->property;
5299           (void) XSendEvent(request->display,request->requestor,False,0,
5300             (XEvent *) &notify);
5301         }
5302         default:
5303           break;
5304       }
5305       if ((state & UpdateConfigurationState) != 0)
5306         {
5307           (void) XPutBackEvent(display,&event);
5308           (void) XCheckDefineCursor(display,windows->image.id,cursor);
5309           break;
5310         }
5311     } while ((state & ExitState) == 0);
5312   } while ((state & ExitState) == 0);
5313   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
5314   XSetCursorState(display,windows,MagickFalse);
5315   if ((state & EscapeState) != 0)
5316     return(MagickTrue);
5317   if (mode == CropMode)
5318     if (((int) crop_info.width != windows->image.ximage->width) ||
5319         ((int) crop_info.height != windows->image.ximage->height))
5320       {
5321         /*
5322           Reconfigure Image window as defined by cropping rectangle.
5323         */
5324         XSetCropGeometry(display,windows,&crop_info,image);
5325         windows->image.window_changes.width=(int) crop_info.width;
5326         windows->image.window_changes.height=(int) crop_info.height;
5327         (void) XConfigureImage(display,resource_info,windows,image,exception);
5328         return(MagickTrue);
5329       }
5330   /*
5331     Copy image before applying image transforms.
5332   */
5333   XSetCursorState(display,windows,MagickTrue);
5334   XCheckRefreshWindows(display,windows);
5335   width=(unsigned int) image->columns;
5336   height=(unsigned int) image->rows;
5337   x=0;
5338   y=0;
5339   if (windows->image.crop_geometry != (char *) NULL)
5340     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
5341   scale_factor=(MagickRealType) width/windows->image.ximage->width;
5342   crop_info.x+=x;
5343   crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
5344   crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
5345   scale_factor=(MagickRealType) height/windows->image.ximage->height;
5346   crop_info.y+=y;
5347   crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
5348   crop_info.height=(unsigned int) (scale_factor*crop_info.height+0.5);
5349   crop_image=CropImage(image,&crop_info,exception);
5350   XSetCursorState(display,windows,MagickFalse);
5351   if (crop_image == (Image *) NULL)
5352     return(MagickFalse);
5353   if (resource_info->copy_image != (Image *) NULL)
5354     resource_info->copy_image=DestroyImage(resource_info->copy_image);
5355   resource_info->copy_image=crop_image;
5356   if (mode == CopyMode)
5357     {
5358       (void) XConfigureImage(display,resource_info,windows,image,exception);
5359       return(MagickTrue);
5360     }
5361   /*
5362     Cut image.
5363   */
5364   if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
5365     return(MagickFalse);
5366   image->matte=MagickTrue;
5367   image_view=AcquireCacheView(image);
5368   for (y=0; y < (int) crop_info.height; y++)
5369   {
5370     q=GetCacheViewAuthenticPixels(image_view,crop_info.x,y+crop_info.y,
5371       crop_info.width,1,exception);
5372     if (q == (Quantum *) NULL)
5373       break;
5374     for (x=0; x < (int) crop_info.width; x++)
5375     {
5376       SetPixelAlpha(image,TransparentAlpha,q);
5377       q+=GetPixelChannels(image);
5378     }
5379     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
5380       break;
5381   }
5382   image_view=DestroyCacheView(image_view);
5383   /*
5384     Update image configuration.
5385   */
5386   XConfigureImageColormap(display,resource_info,windows,image,exception);
5387   (void) XConfigureImage(display,resource_info,windows,image,exception);
5388   return(MagickTrue);
5389 }
5390 \f
5391 /*
5392 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5393 %                                                                             %
5394 %                                                                             %
5395 %                                                                             %
5396 +   X D r a w I m a g e                                                       %
5397 %                                                                             %
5398 %                                                                             %
5399 %                                                                             %
5400 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5401 %
5402 %  XDrawEditImage() draws a graphic element (point, line, rectangle, etc.) on
5403 %  the image.
5404 %
5405 %  The format of the XDrawEditImage method is:
5406 %
5407 %      MagickBooleanType XDrawEditImage(Display *display,
5408 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
5409 %        ExceptionInfo *exception)
5410 %
5411 %  A description of each parameter follows:
5412 %
5413 %    o display: Specifies a connection to an X server; returned from
5414 %      XOpenDisplay.
5415 %
5416 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
5417 %
5418 %    o windows: Specifies a pointer to a XWindows structure.
5419 %
5420 %    o image: the image.
5421 %
5422 %    o exception: return any errors or warnings in this structure.
5423 %
5424 */
5425 static MagickBooleanType XDrawEditImage(Display *display,
5426   XResourceInfo *resource_info,XWindows *windows,Image **image,
5427   ExceptionInfo *exception)
5428 {
5429   static const char
5430     *DrawMenu[] =
5431     {
5432       "Element",
5433       "Color",
5434       "Stipple",
5435       "Width",
5436       "Undo",
5437       "Help",
5438       "Dismiss",
5439       (char *) NULL
5440     };
5441
5442   static ElementType
5443     element = PointElement;
5444
5445   static const ModeType
5446     DrawCommands[] =
5447     {
5448       DrawElementCommand,
5449       DrawColorCommand,
5450       DrawStippleCommand,
5451       DrawWidthCommand,
5452       DrawUndoCommand,
5453       DrawHelpCommand,
5454       DrawDismissCommand
5455     };
5456
5457   static Pixmap
5458     stipple = (Pixmap) NULL;
5459
5460   static unsigned int
5461     pen_id = 0,
5462     line_width = 1;
5463
5464   char
5465     command[MaxTextExtent],
5466     text[MaxTextExtent];
5467
5468   Cursor
5469     cursor;
5470
5471   int
5472     entry,
5473     id,
5474     number_coordinates,
5475     x,
5476     y;
5477
5478   MagickRealType
5479     degrees;
5480
5481   MagickStatusType
5482     status;
5483
5484   RectangleInfo
5485     rectangle_info;
5486
5487   register int
5488     i;
5489
5490   unsigned int
5491     distance,
5492     height,
5493     max_coordinates,
5494     width;
5495
5496   size_t
5497     state;
5498
5499   Window
5500     root_window;
5501
5502   XDrawInfo
5503     draw_info;
5504
5505   XEvent
5506     event;
5507
5508   XPoint
5509     *coordinate_info;
5510
5511   XSegment
5512     line_info;
5513
5514   /*
5515     Allocate polygon info.
5516   */
5517   max_coordinates=2048;
5518   coordinate_info=(XPoint *) AcquireQuantumMemory((size_t) max_coordinates,
5519     sizeof(*coordinate_info));
5520   if (coordinate_info == (XPoint *) NULL)
5521     {
5522       (void) ThrowMagickException(exception,GetMagickModule(),
5523         ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
5524       return(MagickFalse);
5525     }
5526   /*
5527     Map Command widget.
5528   */
5529   (void) CloneString(&windows->command.name,"Draw");
5530   windows->command.data=4;
5531   (void) XCommandWidget(display,windows,DrawMenu,(XEvent *) NULL);
5532   (void) XMapRaised(display,windows->command.id);
5533   XClientMessage(display,windows->image.id,windows->im_protocols,
5534     windows->im_update_widget,CurrentTime);
5535   /*
5536     Wait for first button press.
5537   */
5538   root_window=XRootWindow(display,XDefaultScreen(display));
5539   draw_info.stencil=OpaqueStencil;
5540   status=MagickTrue;
5541   cursor=XCreateFontCursor(display,XC_tcross);
5542   for ( ; ; )
5543   {
5544     XQueryPosition(display,windows->image.id,&x,&y);
5545     (void) XSelectInput(display,windows->image.id,
5546       windows->image.attributes.event_mask | PointerMotionMask);
5547     (void) XCheckDefineCursor(display,windows->image.id,cursor);
5548     state=DefaultState;
5549     do
5550     {
5551       if (windows->info.mapped != MagickFalse)
5552         {
5553           /*
5554             Display pointer position.
5555           */
5556           (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
5557             x+windows->image.x,y+windows->image.y);
5558           XInfoWidget(display,windows,text);
5559         }
5560       /*
5561         Wait for next event.
5562       */
5563       XScreenEvent(display,windows,&event,exception);
5564       if (event.xany.window == windows->command.id)
5565         {
5566           /*
5567             Select a command from the Command widget.
5568           */
5569           id=XCommandWidget(display,windows,DrawMenu,&event);
5570           if (id < 0)
5571             continue;
5572           switch (DrawCommands[id])
5573           {
5574             case DrawElementCommand:
5575             {
5576               static const char
5577                 *Elements[] =
5578                 {
5579                   "point",
5580                   "line",
5581                   "rectangle",
5582                   "fill rectangle",
5583                   "circle",
5584                   "fill circle",
5585                   "ellipse",
5586                   "fill ellipse",
5587                   "polygon",
5588                   "fill polygon",
5589                   (char *) NULL,
5590                 };
5591
5592               /*
5593                 Select a command from the pop-up menu.
5594               */
5595               element=(ElementType) (XMenuWidget(display,windows,
5596                 DrawMenu[id],Elements,command)+1);
5597               break;
5598             }
5599             case DrawColorCommand:
5600             {
5601               const char
5602                 *ColorMenu[MaxNumberPens+1];
5603
5604               int
5605                 pen_number;
5606
5607               MagickBooleanType
5608                 transparent;
5609
5610               XColor
5611                 color;
5612
5613               /*
5614                 Initialize menu selections.
5615               */
5616               for (i=0; i < (int) (MaxNumberPens-2); i++)
5617                 ColorMenu[i]=resource_info->pen_colors[i];
5618               ColorMenu[MaxNumberPens-2]="transparent";
5619               ColorMenu[MaxNumberPens-1]="Browser...";
5620               ColorMenu[MaxNumberPens]=(char *) NULL;
5621               /*
5622                 Select a pen color from the pop-up menu.
5623               */
5624               pen_number=XMenuWidget(display,windows,DrawMenu[id],
5625                 (const char **) ColorMenu,command);
5626               if (pen_number < 0)
5627                 break;
5628               transparent=pen_number == (MaxNumberPens-2) ? MagickTrue :
5629                 MagickFalse;
5630               if (transparent != MagickFalse)
5631                 {
5632                   draw_info.stencil=TransparentStencil;
5633                   break;
5634                 }
5635               if (pen_number == (MaxNumberPens-1))
5636                 {
5637                   static char
5638                     color_name[MaxTextExtent] = "gray";
5639
5640                   /*
5641                     Select a pen color from a dialog.
5642                   */
5643                   resource_info->pen_colors[pen_number]=color_name;
5644                   XColorBrowserWidget(display,windows,"Select",color_name);
5645                   if (*color_name == '\0')
5646                     break;
5647                 }
5648               /*
5649                 Set pen color.
5650               */
5651               (void) XParseColor(display,windows->map_info->colormap,
5652                 resource_info->pen_colors[pen_number],&color);
5653               XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
5654                 (unsigned int) MaxColors,&color);
5655               windows->pixel_info->pen_colors[pen_number]=color;
5656               pen_id=(unsigned int) pen_number;
5657               draw_info.stencil=OpaqueStencil;
5658               break;
5659             }
5660             case DrawStippleCommand:
5661             {
5662               Image
5663                 *stipple_image;
5664
5665               ImageInfo
5666                 *image_info;
5667
5668               int
5669                 status;
5670
5671               static char
5672                 filename[MaxTextExtent] = "\0";
5673
5674               static const char
5675                 *StipplesMenu[] =
5676                 {
5677                   "Brick",
5678                   "Diagonal",
5679                   "Scales",
5680                   "Vertical",
5681                   "Wavy",
5682                   "Translucent",
5683                   "Opaque",
5684                   (char *) NULL,
5685                   (char *) NULL,
5686                 };
5687
5688               /*
5689                 Select a command from the pop-up menu.
5690               */
5691               StipplesMenu[7]="Open...";
5692               entry=XMenuWidget(display,windows,DrawMenu[id],StipplesMenu,
5693                 command);
5694               if (entry < 0)
5695                 break;
5696               if (stipple != (Pixmap) NULL)
5697                 (void) XFreePixmap(display,stipple);
5698               stipple=(Pixmap) NULL;
5699               if (entry != 7)
5700                 {
5701                   switch (entry)
5702                   {
5703                     case 0:
5704                     {
5705                       stipple=XCreateBitmapFromData(display,root_window,
5706                         (char *) BricksBitmap,BricksWidth,BricksHeight);
5707                       break;
5708                     }
5709                     case 1:
5710                     {
5711                       stipple=XCreateBitmapFromData(display,root_window,
5712                         (char *) DiagonalBitmap,DiagonalWidth,DiagonalHeight);
5713                       break;
5714                     }
5715                     case 2:
5716                     {
5717                       stipple=XCreateBitmapFromData(display,root_window,
5718                         (char *) ScalesBitmap,ScalesWidth,ScalesHeight);
5719                       break;
5720                     }
5721                     case 3:
5722                     {
5723                       stipple=XCreateBitmapFromData(display,root_window,
5724                         (char *) VerticalBitmap,VerticalWidth,VerticalHeight);
5725                       break;
5726                     }
5727                     case 4:
5728                     {
5729                       stipple=XCreateBitmapFromData(display,root_window,
5730                         (char *) WavyBitmap,WavyWidth,WavyHeight);
5731                       break;
5732                     }
5733                     case 5:
5734                     {
5735                       stipple=XCreateBitmapFromData(display,root_window,
5736                         (char *) HighlightBitmap,HighlightWidth,
5737                         HighlightHeight);
5738                       break;
5739                     }
5740                     case 6:
5741                     default:
5742                     {
5743                       stipple=XCreateBitmapFromData(display,root_window,
5744                         (char *) OpaqueBitmap,OpaqueWidth,OpaqueHeight);
5745                       break;
5746                     }
5747                   }
5748                   break;
5749                 }
5750               XFileBrowserWidget(display,windows,"Stipple",filename);
5751               if (*filename == '\0')
5752                 break;
5753               /*
5754                 Read image.
5755               */
5756               XSetCursorState(display,windows,MagickTrue);
5757               XCheckRefreshWindows(display,windows);
5758               image_info=AcquireImageInfo();
5759               (void) CopyMagickString(image_info->filename,filename,
5760                 MaxTextExtent);
5761               stipple_image=ReadImage(image_info,exception);
5762               CatchException(exception);
5763               XSetCursorState(display,windows,MagickFalse);
5764               if (stipple_image == (Image *) NULL)
5765                 break;
5766               (void) AcquireUniqueFileResource(filename);
5767               (void) FormatLocaleString(stipple_image->filename,MaxTextExtent,
5768                 "xbm:%s",filename);
5769               (void) WriteImage(image_info,stipple_image,exception);
5770               stipple_image=DestroyImage(stipple_image);
5771               image_info=DestroyImageInfo(image_info);
5772               status=XReadBitmapFile(display,root_window,filename,&width,
5773                 &height,&stipple,&x,&y);
5774               (void) RelinquishUniqueFileResource(filename);
5775               if ((status != BitmapSuccess) != 0)
5776                 XNoticeWidget(display,windows,"Unable to read X bitmap image:",
5777                   filename);
5778               break;
5779             }
5780             case DrawWidthCommand:
5781             {
5782               static char
5783                 width[MaxTextExtent] = "0";
5784
5785               static const char
5786                 *WidthsMenu[] =
5787                 {
5788                   "1",
5789                   "2",
5790                   "4",
5791                   "8",
5792                   "16",
5793                   "Dialog...",
5794                   (char *) NULL,
5795                 };
5796
5797               /*
5798                 Select a command from the pop-up menu.
5799               */
5800               entry=XMenuWidget(display,windows,DrawMenu[id],WidthsMenu,
5801                 command);
5802               if (entry < 0)
5803                 break;
5804               if (entry != 5)
5805                 {
5806                   line_width=(unsigned int) StringToUnsignedLong(
5807                     WidthsMenu[entry]);
5808                   break;
5809                 }
5810               (void) XDialogWidget(display,windows,"Ok","Enter line width:",
5811                 width);
5812               if (*width == '\0')
5813                 break;
5814               line_width=(unsigned int) StringToUnsignedLong(width);
5815               break;
5816             }
5817             case DrawUndoCommand:
5818             {
5819               (void) XMagickCommand(display,resource_info,windows,UndoCommand,
5820                 image,exception);
5821               break;
5822             }
5823             case DrawHelpCommand:
5824             {
5825               XTextViewWidget(display,resource_info,windows,MagickFalse,
5826                 "Help Viewer - Image Rotation",ImageDrawHelp);
5827               (void) XCheckDefineCursor(display,windows->image.id,cursor);
5828               break;
5829             }
5830             case DrawDismissCommand:
5831             {
5832               /*
5833                 Prematurely exit.
5834               */
5835               state|=EscapeState;
5836               state|=ExitState;
5837               break;
5838             }
5839             default:
5840               break;
5841           }
5842           (void) XCheckDefineCursor(display,windows->image.id,cursor);
5843           continue;
5844         }
5845       switch (event.type)
5846       {
5847         case ButtonPress:
5848         {
5849           if (event.xbutton.button != Button1)
5850             break;
5851           if (event.xbutton.window != windows->image.id)
5852             break;
5853           /*
5854             exit loop.
5855           */
5856           x=event.xbutton.x;
5857           y=event.xbutton.y;
5858           state|=ExitState;
5859           break;
5860         }
5861         case ButtonRelease:
5862           break;
5863         case Expose:
5864           break;
5865         case KeyPress:
5866         {
5867           KeySym
5868             key_symbol;
5869
5870           if (event.xkey.window != windows->image.id)
5871             break;
5872           /*
5873             Respond to a user key press.
5874           */
5875           (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5876             sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5877           switch ((int) key_symbol)
5878           {
5879             case XK_Escape:
5880             case XK_F20:
5881             {
5882               /*
5883                 Prematurely exit.
5884               */
5885               state|=EscapeState;
5886               state|=ExitState;
5887               break;
5888             }
5889             case XK_F1:
5890             case XK_Help:
5891             {
5892               XTextViewWidget(display,resource_info,windows,MagickFalse,
5893                 "Help Viewer - Image Rotation",ImageDrawHelp);
5894               break;
5895             }
5896             default:
5897             {
5898               (void) XBell(display,0);
5899               break;
5900             }
5901           }
5902           break;
5903         }
5904         case MotionNotify:
5905         {
5906           /*
5907             Map and unmap Info widget as text cursor crosses its boundaries.
5908           */
5909           x=event.xmotion.x;
5910           y=event.xmotion.y;
5911           if (windows->info.mapped != MagickFalse)
5912             {
5913               if ((x < (int) (windows->info.x+windows->info.width)) &&
5914                   (y < (int) (windows->info.y+windows->info.height)))
5915                 (void) XWithdrawWindow(display,windows->info.id,
5916                   windows->info.screen);
5917             }
5918           else
5919             if ((x > (int) (windows->info.x+windows->info.width)) ||
5920                 (y > (int) (windows->info.y+windows->info.height)))
5921               (void) XMapWindow(display,windows->info.id);
5922           break;
5923         }
5924       }
5925     } while ((state & ExitState) == 0);
5926     (void) XSelectInput(display,windows->image.id,
5927       windows->image.attributes.event_mask);
5928     (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
5929     if ((state & EscapeState) != 0)
5930       break;
5931     /*
5932       Draw element as pointer moves until the button is released.
5933     */
5934     distance=0;
5935     degrees=0.0;
5936     line_info.x1=x;
5937     line_info.y1=y;
5938     line_info.x2=x;
5939     line_info.y2=y;
5940     rectangle_info.x=(ssize_t) x;
5941     rectangle_info.y=(ssize_t) y;
5942     rectangle_info.width=0;
5943     rectangle_info.height=0;
5944     number_coordinates=1;
5945     coordinate_info->x=x;
5946     coordinate_info->y=y;
5947     (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
5948     state=DefaultState;
5949     do
5950     {
5951       switch (element)
5952       {
5953         case PointElement:
5954         default:
5955         {
5956           if (number_coordinates > 1)
5957             {
5958               (void) XDrawLines(display,windows->image.id,
5959                 windows->image.highlight_context,coordinate_info,
5960                 number_coordinates,CoordModeOrigin);
5961               (void) FormatLocaleString(text,MaxTextExtent," %+d%+d",
5962                 coordinate_info[number_coordinates-1].x,
5963                 coordinate_info[number_coordinates-1].y);
5964               XInfoWidget(display,windows,text);
5965             }
5966           break;
5967         }
5968         case LineElement:
5969         {
5970           if (distance > 9)
5971             {
5972               /*
5973                 Display angle of the line.
5974               */
5975               degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
5976                 line_info.y1),(double) (line_info.x2-line_info.x1)));
5977               (void) FormatLocaleString(text,MaxTextExtent," %g",
5978                 (double) degrees);
5979               XInfoWidget(display,windows,text);
5980               XHighlightLine(display,windows->image.id,
5981                 windows->image.highlight_context,&line_info);
5982             }
5983           else
5984             if (windows->info.mapped != MagickFalse)
5985               (void) XWithdrawWindow(display,windows->info.id,
5986                 windows->info.screen);
5987           break;
5988         }
5989         case RectangleElement:
5990         case FillRectangleElement:
5991         {
5992           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
5993             {
5994               /*
5995                 Display info and draw drawing rectangle.
5996               */
5997               (void) FormatLocaleString(text,MaxTextExtent,
5998                 " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
5999                 (double) rectangle_info.height,(double) rectangle_info.x,
6000                 (double) rectangle_info.y);
6001               XInfoWidget(display,windows,text);
6002               XHighlightRectangle(display,windows->image.id,
6003                 windows->image.highlight_context,&rectangle_info);
6004             }
6005           else
6006             if (windows->info.mapped != MagickFalse)
6007               (void) XWithdrawWindow(display,windows->info.id,
6008                 windows->info.screen);
6009           break;
6010         }
6011         case CircleElement:
6012         case FillCircleElement:
6013         case EllipseElement:
6014         case FillEllipseElement:
6015         {
6016           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6017             {
6018               /*
6019                 Display info and draw drawing rectangle.
6020               */
6021               (void) FormatLocaleString(text,MaxTextExtent,
6022                 " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
6023                 (double) rectangle_info.height,(double) rectangle_info.x,
6024                 (double) rectangle_info.y);
6025               XInfoWidget(display,windows,text);
6026               XHighlightEllipse(display,windows->image.id,
6027                 windows->image.highlight_context,&rectangle_info);
6028             }
6029           else
6030             if (windows->info.mapped != MagickFalse)
6031               (void) XWithdrawWindow(display,windows->info.id,
6032                 windows->info.screen);
6033           break;
6034         }
6035         case PolygonElement:
6036         case FillPolygonElement:
6037         {
6038           if (number_coordinates > 1)
6039             (void) XDrawLines(display,windows->image.id,
6040               windows->image.highlight_context,coordinate_info,
6041               number_coordinates,CoordModeOrigin);
6042           if (distance > 9)
6043             {
6044               /*
6045                 Display angle of the line.
6046               */
6047               degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
6048                 line_info.y1),(double) (line_info.x2-line_info.x1)));
6049               (void) FormatLocaleString(text,MaxTextExtent," %g",
6050                 (double) degrees);
6051               XInfoWidget(display,windows,text);
6052               XHighlightLine(display,windows->image.id,
6053                 windows->image.highlight_context,&line_info);
6054             }
6055           else
6056             if (windows->info.mapped != MagickFalse)
6057               (void) XWithdrawWindow(display,windows->info.id,
6058                 windows->info.screen);
6059           break;
6060         }
6061       }
6062       /*
6063         Wait for next event.
6064       */
6065       XScreenEvent(display,windows,&event,exception);
6066       switch (element)
6067       {
6068         case PointElement:
6069         default:
6070         {
6071           if (number_coordinates > 1)
6072             (void) XDrawLines(display,windows->image.id,
6073               windows->image.highlight_context,coordinate_info,
6074               number_coordinates,CoordModeOrigin);
6075           break;
6076         }
6077         case LineElement:
6078         {
6079           if (distance > 9)
6080             XHighlightLine(display,windows->image.id,
6081               windows->image.highlight_context,&line_info);
6082           break;
6083         }
6084         case RectangleElement:
6085         case FillRectangleElement:
6086         {
6087           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6088             XHighlightRectangle(display,windows->image.id,
6089               windows->image.highlight_context,&rectangle_info);
6090           break;
6091         }
6092         case CircleElement:
6093         case FillCircleElement:
6094         case EllipseElement:
6095         case FillEllipseElement:
6096         {
6097           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6098             XHighlightEllipse(display,windows->image.id,
6099               windows->image.highlight_context,&rectangle_info);
6100           break;
6101         }
6102         case PolygonElement:
6103         case FillPolygonElement:
6104         {
6105           if (number_coordinates > 1)
6106             (void) XDrawLines(display,windows->image.id,
6107               windows->image.highlight_context,coordinate_info,
6108               number_coordinates,CoordModeOrigin);
6109           if (distance > 9)
6110             XHighlightLine(display,windows->image.id,
6111               windows->image.highlight_context,&line_info);
6112           break;
6113         }
6114       }
6115       switch (event.type)
6116       {
6117         case ButtonPress:
6118           break;
6119         case ButtonRelease:
6120         {
6121           /*
6122             User has committed to element.
6123           */
6124           line_info.x2=event.xbutton.x;
6125           line_info.y2=event.xbutton.y;
6126           rectangle_info.x=(ssize_t) event.xbutton.x;
6127           rectangle_info.y=(ssize_t) event.xbutton.y;
6128           coordinate_info[number_coordinates].x=event.xbutton.x;
6129           coordinate_info[number_coordinates].y=event.xbutton.y;
6130           if (((element != PolygonElement) &&
6131                (element != FillPolygonElement)) || (distance <= 9))
6132             {
6133               state|=ExitState;
6134               break;
6135             }
6136           number_coordinates++;
6137           if (number_coordinates < (int) max_coordinates)
6138             {
6139               line_info.x1=event.xbutton.x;
6140               line_info.y1=event.xbutton.y;
6141               break;
6142             }
6143           max_coordinates<<=1;
6144           coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6145             max_coordinates,sizeof(*coordinate_info));
6146           if (coordinate_info == (XPoint *) NULL)
6147             (void) ThrowMagickException(exception,GetMagickModule(),
6148               ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6149           break;
6150         }
6151         case Expose:
6152           break;
6153         case MotionNotify:
6154         {
6155           if (event.xmotion.window != windows->image.id)
6156             break;
6157           if (element != PointElement)
6158             {
6159               line_info.x2=event.xmotion.x;
6160               line_info.y2=event.xmotion.y;
6161               rectangle_info.x=(ssize_t) event.xmotion.x;
6162               rectangle_info.y=(ssize_t) event.xmotion.y;
6163               break;
6164             }
6165           coordinate_info[number_coordinates].x=event.xbutton.x;
6166           coordinate_info[number_coordinates].y=event.xbutton.y;
6167           number_coordinates++;
6168           if (number_coordinates < (int) max_coordinates)
6169             break;
6170           max_coordinates<<=1;
6171           coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6172             max_coordinates,sizeof(*coordinate_info));
6173           if (coordinate_info == (XPoint *) NULL)
6174             (void) ThrowMagickException(exception,GetMagickModule(),
6175               ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6176           break;
6177         }
6178         default:
6179           break;
6180       }
6181       /*
6182         Check boundary conditions.
6183       */
6184       if (line_info.x2 < 0)
6185         line_info.x2=0;
6186       else
6187         if (line_info.x2 > (int) windows->image.width)
6188           line_info.x2=(short) windows->image.width;
6189       if (line_info.y2 < 0)
6190         line_info.y2=0;
6191       else
6192         if (line_info.y2 > (int) windows->image.height)
6193           line_info.y2=(short) windows->image.height;
6194       distance=(unsigned int)
6195         (((line_info.x2-line_info.x1+1)*(line_info.x2-line_info.x1+1))+
6196          ((line_info.y2-line_info.y1+1)*(line_info.y2-line_info.y1+1)));
6197       if ((((int) rectangle_info.x != x) && ((int) rectangle_info.y != y)) ||
6198           ((state & ExitState) != 0))
6199         {
6200           if (rectangle_info.x < 0)
6201             rectangle_info.x=0;
6202           else
6203             if (rectangle_info.x > (ssize_t) windows->image.width)
6204               rectangle_info.x=(ssize_t) windows->image.width;
6205           if ((int) rectangle_info.x < x)
6206             rectangle_info.width=(unsigned int) (x-rectangle_info.x);
6207           else
6208             {
6209               rectangle_info.width=(unsigned int) (rectangle_info.x-x);
6210               rectangle_info.x=(ssize_t) x;
6211             }
6212           if (rectangle_info.y < 0)
6213             rectangle_info.y=0;
6214           else
6215             if (rectangle_info.y > (ssize_t) windows->image.height)
6216               rectangle_info.y=(ssize_t) windows->image.height;
6217           if ((int) rectangle_info.y < y)
6218             rectangle_info.height=(unsigned int) (y-rectangle_info.y);
6219           else
6220             {
6221               rectangle_info.height=(unsigned int) (rectangle_info.y-y);
6222               rectangle_info.y=(ssize_t) y;
6223             }
6224         }
6225     } while ((state & ExitState) == 0);
6226     (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
6227     if ((element == PointElement) || (element == PolygonElement) ||
6228         (element == FillPolygonElement))
6229       {
6230         /*
6231           Determine polygon bounding box.
6232         */
6233         rectangle_info.x=(ssize_t) coordinate_info->x;
6234         rectangle_info.y=(ssize_t) coordinate_info->y;
6235         x=coordinate_info->x;
6236         y=coordinate_info->y;
6237         for (i=1; i < number_coordinates; i++)
6238         {
6239           if (coordinate_info[i].x > x)
6240             x=coordinate_info[i].x;
6241           if (coordinate_info[i].y > y)
6242             y=coordinate_info[i].y;
6243           if ((ssize_t) coordinate_info[i].x < rectangle_info.x)
6244             rectangle_info.x=MagickMax((ssize_t) coordinate_info[i].x,0);
6245           if ((ssize_t) coordinate_info[i].y < rectangle_info.y)
6246             rectangle_info.y=MagickMax((ssize_t) coordinate_info[i].y,0);
6247         }
6248         rectangle_info.width=(size_t) (x-rectangle_info.x);
6249         rectangle_info.height=(size_t) (y-rectangle_info.y);
6250         for (i=0; i < number_coordinates; i++)
6251         {
6252           coordinate_info[i].x-=rectangle_info.x;
6253           coordinate_info[i].y-=rectangle_info.y;
6254         }
6255       }
6256     else
6257       if (distance <= 9)
6258         continue;
6259       else
6260         if ((element == RectangleElement) ||
6261             (element == CircleElement) || (element == EllipseElement))
6262           {
6263             rectangle_info.width--;
6264             rectangle_info.height--;
6265           }
6266     /*
6267       Drawing is relative to image configuration.
6268     */
6269     draw_info.x=(int) rectangle_info.x;
6270     draw_info.y=(int) rectangle_info.y;
6271     (void) XMagickCommand(display,resource_info,windows,SaveToUndoBufferCommand,
6272       image,exception);
6273     width=(unsigned int) (*image)->columns;
6274     height=(unsigned int) (*image)->rows;
6275     x=0;
6276     y=0;
6277     if (windows->image.crop_geometry != (char *) NULL)
6278       (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
6279     draw_info.x+=windows->image.x-(line_width/2);
6280     if (draw_info.x < 0)
6281       draw_info.x=0;
6282     draw_info.x=(int) (width*draw_info.x/windows->image.ximage->width);
6283     draw_info.y+=windows->image.y-(line_width/2);
6284     if (draw_info.y < 0)
6285       draw_info.y=0;
6286     draw_info.y=(int) height*draw_info.y/windows->image.ximage->height;
6287     draw_info.width=(unsigned int) rectangle_info.width+(line_width << 1);
6288     if (draw_info.width > (unsigned int) (*image)->columns)
6289       draw_info.width=(unsigned int) (*image)->columns;
6290     draw_info.height=(unsigned int) rectangle_info.height+(line_width << 1);
6291     if (draw_info.height > (unsigned int) (*image)->rows)
6292       draw_info.height=(unsigned int) (*image)->rows;
6293     (void) FormatLocaleString(draw_info.geometry,MaxTextExtent,"%ux%u%+d%+d",
6294       width*draw_info.width/windows->image.ximage->width,
6295       height*draw_info.height/windows->image.ximage->height,
6296       draw_info.x+x,draw_info.y+y);
6297     /*
6298       Initialize drawing attributes.
6299     */
6300     draw_info.degrees=0.0;
6301     draw_info.element=element;
6302     draw_info.stipple=stipple;
6303     draw_info.line_width=line_width;
6304     draw_info.line_info=line_info;
6305     if (line_info.x1 > (int) (line_width/2))
6306       draw_info.line_info.x1=(short) line_width/2;
6307     if (line_info.y1 > (int) (line_width/2))
6308       draw_info.line_info.y1=(short) line_width/2;
6309     draw_info.line_info.x2=(short) (line_info.x2-line_info.x1+(line_width/2));
6310     draw_info.line_info.y2=(short) (line_info.y2-line_info.y1+(line_width/2));
6311     if ((draw_info.line_info.x2 < 0) && (draw_info.line_info.y2 < 0))
6312       {
6313         draw_info.line_info.x2=(-draw_info.line_info.x2);
6314         draw_info.line_info.y2=(-draw_info.line_info.y2);
6315       }
6316     if (draw_info.line_info.x2 < 0)
6317       {
6318         draw_info.line_info.x2=(-draw_info.line_info.x2);
6319         Swap(draw_info.line_info.x1,draw_info.line_info.x2);
6320       }
6321     if (draw_info.line_info.y2 < 0)
6322       {
6323         draw_info.line_info.y2=(-draw_info.line_info.y2);
6324         Swap(draw_info.line_info.y1,draw_info.line_info.y2);
6325       }
6326     draw_info.rectangle_info=rectangle_info;
6327     if (draw_info.rectangle_info.x > (ssize_t) (line_width/2))
6328       draw_info.rectangle_info.x=(ssize_t) line_width/2;
6329     if (draw_info.rectangle_info.y > (ssize_t) (line_width/2))
6330       draw_info.rectangle_info.y=(ssize_t) line_width/2;
6331     draw_info.number_coordinates=(unsigned int) number_coordinates;
6332     draw_info.coordinate_info=coordinate_info;
6333     windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
6334     /*
6335       Draw element on image.
6336     */
6337     XSetCursorState(display,windows,MagickTrue);
6338     XCheckRefreshWindows(display,windows);
6339     status=XDrawImage(display,windows->pixel_info,&draw_info,*image,exception);
6340     XSetCursorState(display,windows,MagickFalse);
6341     /*
6342       Update image colormap and return to image drawing.
6343     */
6344     XConfigureImageColormap(display,resource_info,windows,*image,exception);
6345     (void) XConfigureImage(display,resource_info,windows,*image,exception);
6346   }
6347   XSetCursorState(display,windows,MagickFalse);
6348   coordinate_info=(XPoint *) RelinquishMagickMemory(coordinate_info);
6349   return(status != 0 ? MagickTrue : MagickFalse);
6350 }
6351 \f
6352 /*
6353 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6354 %                                                                             %
6355 %                                                                             %
6356 %                                                                             %
6357 +   X D r a w P a n R e c t a n g l e                                         %
6358 %                                                                             %
6359 %                                                                             %
6360 %                                                                             %
6361 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6362 %
6363 %  XDrawPanRectangle() draws a rectangle in the pan window.  The pan window
6364 %  displays a zoom image and the rectangle shows which portion of the image is
6365 %  displayed in the Image window.
6366 %
6367 %  The format of the XDrawPanRectangle method is:
6368 %
6369 %      XDrawPanRectangle(Display *display,XWindows *windows)
6370 %
6371 %  A description of each parameter follows:
6372 %
6373 %    o display: Specifies a connection to an X server;  returned from
6374 %      XOpenDisplay.
6375 %
6376 %    o windows: Specifies a pointer to a XWindows structure.
6377 %
6378 */
6379 static void XDrawPanRectangle(Display *display,XWindows *windows)
6380 {
6381   MagickRealType
6382     scale_factor;
6383
6384   RectangleInfo
6385     highlight_info;
6386
6387   /*
6388     Determine dimensions of the panning rectangle.
6389   */
6390   scale_factor=(MagickRealType) windows->pan.width/windows->image.ximage->width;
6391   highlight_info.x=(ssize_t) (scale_factor*windows->image.x+0.5);
6392   highlight_info.width=(unsigned int) (scale_factor*windows->image.width+0.5);
6393   scale_factor=(MagickRealType)
6394     windows->pan.height/windows->image.ximage->height;
6395   highlight_info.y=(ssize_t) (scale_factor*windows->image.y+0.5);
6396   highlight_info.height=(unsigned int) (scale_factor*windows->image.height+0.5);
6397   /*
6398     Display the panning rectangle.
6399   */
6400   (void) XClearWindow(display,windows->pan.id);
6401   XHighlightRectangle(display,windows->pan.id,windows->pan.annotate_context,
6402     &highlight_info);
6403 }
6404 \f
6405 /*
6406 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6407 %                                                                             %
6408 %                                                                             %
6409 %                                                                             %
6410 +   X I m a g e C a c h e                                                     %
6411 %                                                                             %
6412 %                                                                             %
6413 %                                                                             %
6414 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6415 %
6416 %  XImageCache() handles the creation, manipulation, and destruction of the
6417 %  image cache (undo and redo buffers).
6418 %
6419 %  The format of the XImageCache method is:
6420 %
6421 %      void XImageCache(Display *display,XResourceInfo *resource_info,
6422 %        XWindows *windows,const CommandType command,Image **image,
6423 %        ExceptionInfo *exception)
6424 %
6425 %  A description of each parameter follows:
6426 %
6427 %    o display: Specifies a connection to an X server; returned from
6428 %      XOpenDisplay.
6429 %
6430 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6431 %
6432 %    o windows: Specifies a pointer to a XWindows structure.
6433 %
6434 %    o command: Specifies a command to perform.
6435 %
6436 %    o image: the image;  XImageCache may transform the image and return a new
6437 %      image pointer.
6438 %
6439 %    o exception: return any errors or warnings in this structure.
6440 %
6441 */
6442 static void XImageCache(Display *display,XResourceInfo *resource_info,
6443   XWindows *windows,const CommandType command,Image **image,
6444   ExceptionInfo *exception)
6445 {
6446   Image
6447     *cache_image;
6448
6449   static Image
6450     *redo_image = (Image *) NULL,
6451     *undo_image = (Image *) NULL;
6452
6453   switch (command)
6454   {
6455     case FreeBuffersCommand:
6456     {
6457       /*
6458         Free memory from the undo and redo cache.
6459       */
6460       while (undo_image != (Image *) NULL)
6461       {
6462         cache_image=undo_image;
6463         undo_image=GetPreviousImageInList(undo_image);
6464         cache_image->list=DestroyImage(cache_image->list);
6465         cache_image=DestroyImage(cache_image);
6466       }
6467       undo_image=NewImageList();
6468       if (redo_image != (Image *) NULL)
6469         redo_image=DestroyImage(redo_image);
6470       redo_image=NewImageList();
6471       return;
6472     }
6473     case UndoCommand:
6474     {
6475       char
6476         image_geometry[MaxTextExtent];
6477
6478       /*
6479         Undo the last image transformation.
6480       */
6481       if (undo_image == (Image *) NULL)
6482         {
6483           (void) XBell(display,0);
6484           return;
6485         }
6486       cache_image=undo_image;
6487       undo_image=GetPreviousImageInList(undo_image);
6488       windows->image.window_changes.width=(int) cache_image->columns;
6489       windows->image.window_changes.height=(int) cache_image->rows;
6490       (void) FormatLocaleString(image_geometry,MaxTextExtent,"%dx%d!",
6491         windows->image.ximage->width,windows->image.ximage->height);
6492       (void) TransformImage(image,windows->image.crop_geometry,image_geometry,
6493         exception);
6494       if (windows->image.crop_geometry != (char *) NULL)
6495         windows->image.crop_geometry=(char *) RelinquishMagickMemory(
6496           windows->image.crop_geometry);
6497       windows->image.crop_geometry=cache_image->geometry;
6498       if (redo_image != (Image *) NULL)
6499         redo_image=DestroyImage(redo_image);
6500       redo_image=(*image);
6501       *image=cache_image->list;
6502       cache_image=DestroyImage(cache_image);
6503       if (windows->image.orphan != MagickFalse)
6504         return;
6505       XConfigureImageColormap(display,resource_info,windows,*image,exception);
6506       (void) XConfigureImage(display,resource_info,windows,*image,exception);
6507       return;
6508     }
6509     case CutCommand:
6510     case PasteCommand:
6511     case ApplyCommand:
6512     case HalfSizeCommand:
6513     case OriginalSizeCommand:
6514     case DoubleSizeCommand:
6515     case ResizeCommand:
6516     case TrimCommand:
6517     case CropCommand:
6518     case ChopCommand:
6519     case FlipCommand:
6520     case FlopCommand:
6521     case RotateRightCommand:
6522     case RotateLeftCommand:
6523     case RotateCommand:
6524     case ShearCommand:
6525     case RollCommand:
6526     case NegateCommand:
6527     case ContrastStretchCommand:
6528     case SigmoidalContrastCommand:
6529     case NormalizeCommand:
6530     case EqualizeCommand:
6531     case HueCommand:
6532     case SaturationCommand:
6533     case BrightnessCommand:
6534     case GammaCommand:
6535     case SpiffCommand:
6536     case DullCommand:
6537     case GrayscaleCommand:
6538     case MapCommand:
6539     case QuantizeCommand:
6540     case DespeckleCommand:
6541     case EmbossCommand:
6542     case ReduceNoiseCommand:
6543     case AddNoiseCommand:
6544     case SharpenCommand:
6545     case BlurCommand:
6546     case ThresholdCommand:
6547     case EdgeDetectCommand:
6548     case SpreadCommand:
6549     case ShadeCommand:
6550     case RaiseCommand:
6551     case SegmentCommand:
6552     case SolarizeCommand:
6553     case SepiaToneCommand:
6554     case SwirlCommand:
6555     case ImplodeCommand:
6556     case VignetteCommand:
6557     case WaveCommand:
6558     case OilPaintCommand:
6559     case CharcoalDrawCommand:
6560     case AnnotateCommand:
6561     case AddBorderCommand:
6562     case AddFrameCommand:
6563     case CompositeCommand:
6564     case CommentCommand:
6565     case LaunchCommand:
6566     case RegionofInterestCommand:
6567     case SaveToUndoBufferCommand:
6568     case RedoCommand:
6569     {
6570       Image
6571         *previous_image;
6572
6573       ssize_t
6574         bytes;
6575
6576       bytes=(ssize_t) ((*image)->columns*(*image)->rows*sizeof(PixelInfo));
6577       if (undo_image != (Image *) NULL)
6578         {
6579           /*
6580             Ensure the undo cache has enough memory available.
6581           */
6582           previous_image=undo_image;
6583           while (previous_image != (Image *) NULL)
6584           {
6585             bytes+=previous_image->list->columns*previous_image->list->rows*
6586               sizeof(PixelInfo);
6587             if (bytes <= (ssize_t) (resource_info->undo_cache << 20))
6588               {
6589                 previous_image=GetPreviousImageInList(previous_image);
6590                 continue;
6591               }
6592             bytes-=previous_image->list->columns*previous_image->list->rows*
6593               sizeof(PixelInfo);
6594             if (previous_image == undo_image)
6595               undo_image=NewImageList();
6596             else
6597               previous_image->next->previous=NewImageList();
6598             break;
6599           }
6600           while (previous_image != (Image *) NULL)
6601           {
6602             /*
6603               Delete any excess memory from undo cache.
6604             */
6605             cache_image=previous_image;
6606             previous_image=GetPreviousImageInList(previous_image);
6607             cache_image->list=DestroyImage(cache_image->list);
6608             cache_image=DestroyImage(cache_image);
6609           }
6610         }
6611       if (bytes > (ssize_t) (resource_info->undo_cache << 20))
6612         break;
6613       /*
6614         Save image before transformations are applied.
6615       */
6616       cache_image=AcquireImage((ImageInfo *) NULL,exception);
6617       if (cache_image == (Image *) NULL)
6618         break;
6619       XSetCursorState(display,windows,MagickTrue);
6620       XCheckRefreshWindows(display,windows);
6621       cache_image->list=CloneImage(*image,0,0,MagickTrue,exception);
6622       XSetCursorState(display,windows,MagickFalse);
6623       if (cache_image->list == (Image *) NULL)
6624         {
6625           cache_image=DestroyImage(cache_image);
6626           break;
6627         }
6628       cache_image->columns=(size_t) windows->image.ximage->width;
6629       cache_image->rows=(size_t) windows->image.ximage->height;
6630       cache_image->geometry=windows->image.crop_geometry;
6631       if (windows->image.crop_geometry != (char *) NULL)
6632         {
6633           cache_image->geometry=AcquireString((char *) NULL);
6634           (void) CopyMagickString(cache_image->geometry,
6635             windows->image.crop_geometry,MaxTextExtent);
6636         }
6637       if (undo_image == (Image *) NULL)
6638         {
6639           undo_image=cache_image;
6640           break;
6641         }
6642       undo_image->next=cache_image;
6643       undo_image->next->previous=undo_image;
6644       undo_image=undo_image->next;
6645       break;
6646     }
6647     default:
6648       break;
6649   }
6650   if (command == RedoCommand)
6651     {
6652       /*
6653         Redo the last image transformation.
6654       */
6655       if (redo_image == (Image *) NULL)
6656         {
6657           (void) XBell(display,0);
6658           return;
6659         }
6660       windows->image.window_changes.width=(int) redo_image->columns;
6661       windows->image.window_changes.height=(int) redo_image->rows;
6662       if (windows->image.crop_geometry != (char *) NULL)
6663         windows->image.crop_geometry=(char *)
6664           RelinquishMagickMemory(windows->image.crop_geometry);
6665       windows->image.crop_geometry=redo_image->geometry;
6666       *image=DestroyImage(*image);
6667       *image=redo_image;
6668       redo_image=NewImageList();
6669       if (windows->image.orphan != MagickFalse)
6670         return;
6671       XConfigureImageColormap(display,resource_info,windows,*image,exception);
6672       (void) XConfigureImage(display,resource_info,windows,*image,exception);
6673       return;
6674     }
6675   if (command != InfoCommand)
6676     return;
6677   /*
6678     Display image info.
6679   */
6680   XSetCursorState(display,windows,MagickTrue);
6681   XCheckRefreshWindows(display,windows);
6682   XDisplayImageInfo(display,resource_info,windows,undo_image,*image,exception);
6683   XSetCursorState(display,windows,MagickFalse);
6684 }
6685 \f
6686 /*
6687 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6688 %                                                                             %
6689 %                                                                             %
6690 %                                                                             %
6691 +   X I m a g e W i n d o w C o m m a n d                                     %
6692 %                                                                             %
6693 %                                                                             %
6694 %                                                                             %
6695 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6696 %
6697 %  XImageWindowCommand() makes a transform to the image or Image window as
6698 %  specified by a user menu button or keyboard command.
6699 %
6700 %  The format of the XImageWindowCommand method is:
6701 %
6702 %      CommandType XImageWindowCommand(Display *display,
6703 %        XResourceInfo *resource_info,XWindows *windows,
6704 %        const MagickStatusType state,KeySym key_symbol,Image **image,
6705 %        ExceptionInfo *exception)
6706 %
6707 %  A description of each parameter follows:
6708 %
6709 %    o nexus:  Method XImageWindowCommand returns an image when the
6710 %      user chooses 'Open Image' from the command menu.  Otherwise a null
6711 %      image is returned.
6712 %
6713 %    o display: Specifies a connection to an X server; returned from
6714 %      XOpenDisplay.
6715 %
6716 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6717 %
6718 %    o windows: Specifies a pointer to a XWindows structure.
6719 %
6720 %    o state: key mask.
6721 %
6722 %    o key_symbol: Specifies a command to perform.
6723 %
6724 %    o image: the image;  XImageWIndowCommand may transform the image and
6725 %      return a new image pointer.
6726 %
6727 %    o exception: return any errors or warnings in this structure.
6728 %
6729 */
6730 static CommandType XImageWindowCommand(Display *display,
6731   XResourceInfo *resource_info,XWindows *windows,const MagickStatusType state,
6732   KeySym key_symbol,Image **image,ExceptionInfo *exception)
6733 {
6734   static char
6735     delta[MaxTextExtent] = "";
6736
6737   static const char
6738     Digits[] = "01234567890";
6739
6740   static KeySym
6741     last_symbol = XK_0;
6742
6743   if ((key_symbol >= XK_0) && (key_symbol <= XK_9))
6744     {
6745       if (((last_symbol < XK_0) || (last_symbol > XK_9)))
6746         {
6747           *delta='\0';
6748           resource_info->quantum=1;
6749         }
6750       last_symbol=key_symbol;
6751       delta[strlen(delta)+1]='\0';
6752       delta[strlen(delta)]=Digits[key_symbol-XK_0];
6753       resource_info->quantum=StringToLong(delta);
6754       return(NullCommand);
6755     }
6756   last_symbol=key_symbol;
6757   if (resource_info->immutable)
6758     {
6759       /*
6760         Virtual image window has a restricted command set.
6761       */
6762       switch (key_symbol)
6763       {
6764         case XK_question:
6765           return(InfoCommand);
6766         case XK_p:
6767         case XK_Print:
6768           return(PrintCommand);
6769         case XK_space:
6770           return(NextCommand);
6771         case XK_q:
6772         case XK_Escape:
6773           return(QuitCommand);
6774         default:
6775           break;
6776       }
6777       return(NullCommand);
6778     }
6779   switch ((int) key_symbol)
6780   {
6781     case XK_o:
6782     {
6783       if ((state & ControlMask) == 0)
6784         break;
6785       return(OpenCommand);
6786     }
6787     case XK_space:
6788       return(NextCommand);
6789     case XK_BackSpace:
6790       return(FormerCommand);
6791     case XK_s:
6792     {
6793       if ((state & Mod1Mask) != 0)
6794         return(SwirlCommand);
6795       if ((state & ControlMask) == 0)
6796         return(ShearCommand);
6797       return(SaveCommand);
6798     }
6799     case XK_p:
6800     case XK_Print:
6801     {
6802       if ((state & Mod1Mask) != 0)
6803         return(OilPaintCommand);
6804       if ((state & Mod4Mask) != 0)
6805         return(ColorCommand);
6806       if ((state & ControlMask) == 0)
6807         return(NullCommand);
6808       return(PrintCommand);
6809     }
6810     case XK_d:
6811     {
6812       if ((state & Mod4Mask) != 0)
6813         return(DrawCommand);
6814       if ((state & ControlMask) == 0)
6815         return(NullCommand);
6816       return(DeleteCommand);
6817     }
6818     case XK_Select:
6819     {
6820       if ((state & ControlMask) == 0)
6821         return(NullCommand);
6822       return(SelectCommand);
6823     }
6824     case XK_n:
6825     {
6826       if ((state & ControlMask) == 0)
6827         return(NullCommand);
6828       return(NewCommand);
6829     }
6830     case XK_q:
6831     case XK_Escape:
6832       return(QuitCommand);
6833     case XK_z:
6834     case XK_Undo:
6835     {
6836       if ((state & ControlMask) == 0)
6837         return(NullCommand);
6838       return(UndoCommand);
6839     }
6840     case XK_r:
6841     case XK_Redo:
6842     {
6843       if ((state & ControlMask) == 0)
6844         return(RollCommand);
6845       return(RedoCommand);
6846     }
6847     case XK_x:
6848     {
6849       if ((state & ControlMask) == 0)
6850         return(NullCommand);
6851       return(CutCommand);
6852     }
6853     case XK_c:
6854     {
6855       if ((state & Mod1Mask) != 0)
6856         return(CharcoalDrawCommand);
6857       if ((state & ControlMask) == 0)
6858         return(CropCommand);
6859       return(CopyCommand);
6860     }
6861     case XK_v:
6862     case XK_Insert:
6863     {
6864       if ((state & Mod4Mask) != 0)
6865         return(CompositeCommand);
6866       if ((state & ControlMask) == 0)
6867         return(FlipCommand);
6868       return(PasteCommand);
6869     }
6870     case XK_less:
6871       return(HalfSizeCommand);
6872     case XK_minus:
6873       return(OriginalSizeCommand);
6874     case XK_greater:
6875       return(DoubleSizeCommand);
6876     case XK_percent:
6877       return(ResizeCommand);
6878     case XK_at:
6879       return(RefreshCommand);
6880     case XK_bracketleft:
6881       return(ChopCommand);
6882     case XK_h:
6883       return(FlopCommand);
6884     case XK_slash:
6885       return(RotateRightCommand);
6886     case XK_backslash:
6887       return(RotateLeftCommand);
6888     case XK_asterisk:
6889       return(RotateCommand);
6890     case XK_t:
6891       return(TrimCommand);
6892     case XK_H:
6893       return(HueCommand);
6894     case XK_S:
6895       return(SaturationCommand);
6896     case XK_L:
6897       return(BrightnessCommand);
6898     case XK_G:
6899       return(GammaCommand);
6900     case XK_C:
6901       return(SpiffCommand);
6902     case XK_Z:
6903       return(DullCommand);
6904     case XK_N:
6905       return(NormalizeCommand);
6906     case XK_equal:
6907       return(EqualizeCommand);
6908     case XK_asciitilde:
6909       return(NegateCommand);
6910     case XK_period:
6911       return(GrayscaleCommand);
6912     case XK_numbersign:
6913       return(QuantizeCommand);
6914     case XK_F2:
6915       return(DespeckleCommand);
6916     case XK_F3:
6917       return(EmbossCommand);
6918     case XK_F4:
6919       return(ReduceNoiseCommand);
6920     case XK_F5:
6921       return(AddNoiseCommand);
6922     case XK_F6:
6923       return(SharpenCommand);
6924     case XK_F7:
6925       return(BlurCommand);
6926     case XK_F8:
6927       return(ThresholdCommand);
6928     case XK_F9:
6929       return(EdgeDetectCommand);
6930     case XK_F10:
6931       return(SpreadCommand);
6932     case XK_F11:
6933       return(ShadeCommand);
6934     case XK_F12:
6935       return(RaiseCommand);
6936     case XK_F13:
6937       return(SegmentCommand);
6938     case XK_i:
6939     {
6940       if ((state & Mod1Mask) == 0)
6941         return(NullCommand);
6942       return(ImplodeCommand);
6943     }
6944     case XK_w:
6945     {
6946       if ((state & Mod1Mask) == 0)
6947         return(NullCommand);
6948       return(WaveCommand);
6949     }
6950     case XK_m:
6951     {
6952       if ((state & Mod4Mask) == 0)
6953         return(NullCommand);
6954       return(MatteCommand);
6955     }
6956     case XK_b:
6957     {
6958       if ((state & Mod4Mask) == 0)
6959         return(NullCommand);
6960       return(AddBorderCommand);
6961     }
6962     case XK_f:
6963     {
6964       if ((state & Mod4Mask) == 0)
6965         return(NullCommand);
6966       return(AddFrameCommand);
6967     }
6968     case XK_exclam:
6969     {
6970       if ((state & Mod4Mask) == 0)
6971         return(NullCommand);
6972       return(CommentCommand);
6973     }
6974     case XK_a:
6975     {
6976       if ((state & Mod1Mask) != 0)
6977         return(ApplyCommand);
6978       if ((state & Mod4Mask) != 0)
6979         return(AnnotateCommand);
6980       if ((state & ControlMask) == 0)
6981         return(NullCommand);
6982       return(RegionofInterestCommand);
6983     }
6984     case XK_question:
6985       return(InfoCommand);
6986     case XK_plus:
6987       return(ZoomCommand);
6988     case XK_P:
6989     {
6990       if ((state & ShiftMask) == 0)
6991         return(NullCommand);
6992       return(ShowPreviewCommand);
6993     }
6994     case XK_Execute:
6995       return(LaunchCommand);
6996     case XK_F1:
6997       return(HelpCommand);
6998     case XK_Find:
6999       return(BrowseDocumentationCommand);
7000     case XK_Menu:
7001     {
7002       (void) XMapRaised(display,windows->command.id);
7003       return(NullCommand);
7004     }
7005     case XK_Next:
7006     case XK_Prior:
7007     case XK_Home:
7008     case XK_KP_Home:
7009     {
7010       XTranslateImage(display,windows,*image,key_symbol);
7011       return(NullCommand);
7012     }
7013     case XK_Up:
7014     case XK_KP_Up:
7015     case XK_Down:
7016     case XK_KP_Down:
7017     case XK_Left:
7018     case XK_KP_Left:
7019     case XK_Right:
7020     case XK_KP_Right:
7021     {
7022       if ((state & Mod1Mask) != 0)
7023         {
7024           RectangleInfo
7025             crop_info;
7026
7027           /*
7028             Trim one pixel from edge of image.
7029           */
7030           crop_info.x=0;
7031           crop_info.y=0;
7032           crop_info.width=(size_t) windows->image.ximage->width;
7033           crop_info.height=(size_t) windows->image.ximage->height;
7034           if ((key_symbol == XK_Up) || (key_symbol == XK_KP_Up))
7035             {
7036               if (resource_info->quantum >= (int) crop_info.height)
7037                 resource_info->quantum=(int) crop_info.height-1;
7038               crop_info.height-=resource_info->quantum;
7039             }
7040           if ((key_symbol == XK_Down) || (key_symbol == XK_KP_Down))
7041             {
7042               if (resource_info->quantum >= (int) (crop_info.height-crop_info.y))
7043                 resource_info->quantum=(int) (crop_info.height-crop_info.y-1);
7044               crop_info.y+=resource_info->quantum;
7045               crop_info.height-=resource_info->quantum;
7046             }
7047           if ((key_symbol == XK_Left) || (key_symbol == XK_KP_Left))
7048             {
7049               if (resource_info->quantum >= (int) crop_info.width)
7050                 resource_info->quantum=(int) crop_info.width-1;
7051               crop_info.width-=resource_info->quantum;
7052             }
7053           if ((key_symbol == XK_Right) || (key_symbol == XK_KP_Right))
7054             {
7055               if (resource_info->quantum >= (int) (crop_info.width-crop_info.x))
7056                 resource_info->quantum=(int) (crop_info.width-crop_info.x-1);
7057               crop_info.x+=resource_info->quantum;
7058               crop_info.width-=resource_info->quantum;
7059             }
7060           if ((int) (windows->image.x+windows->image.width) >
7061               (int) crop_info.width)
7062             windows->image.x=(int) (crop_info.width-windows->image.width);
7063           if ((int) (windows->image.y+windows->image.height) >
7064               (int) crop_info.height)
7065             windows->image.y=(int) (crop_info.height-windows->image.height);
7066           XSetCropGeometry(display,windows,&crop_info,*image);
7067           windows->image.window_changes.width=(int) crop_info.width;
7068           windows->image.window_changes.height=(int) crop_info.height;
7069           (void) XSetWindowBackgroundPixmap(display,windows->image.id,None);
7070           (void) XConfigureImage(display,resource_info,windows,*image,
7071             exception);
7072           return(NullCommand);
7073         }
7074       XTranslateImage(display,windows,*image,key_symbol);
7075       return(NullCommand);
7076     }
7077     default:
7078       return(NullCommand);
7079   }
7080   return(NullCommand);
7081 }
7082 \f
7083 /*
7084 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7085 %                                                                             %
7086 %                                                                             %
7087 %                                                                             %
7088 +   X M a g i c k C o m m a n d                                               %
7089 %                                                                             %
7090 %                                                                             %
7091 %                                                                             %
7092 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7093 %
7094 %  XMagickCommand() makes a transform to the image or Image window as
7095 %  specified by a user menu button or keyboard command.
7096 %
7097 %  The format of the XMagickCommand method is:
7098 %
7099 %      Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7100 %        XWindows *windows,const CommandType command,Image **image,
7101 %        ExceptionInfo *exception)
7102 %
7103 %  A description of each parameter follows:
7104 %
7105 %    o display: Specifies a connection to an X server; returned from
7106 %      XOpenDisplay.
7107 %
7108 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
7109 %
7110 %    o windows: Specifies a pointer to a XWindows structure.
7111 %
7112 %    o command: Specifies a command to perform.
7113 %
7114 %    o image: the image;  XMagickCommand may transform the image and return a
7115 %      new image pointer.
7116 %
7117 %    o exception: return any errors or warnings in this structure.
7118 %
7119 */
7120 static Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7121   XWindows *windows,const CommandType command,Image **image,
7122   ExceptionInfo *exception)
7123 {
7124   char
7125     filename[MaxTextExtent],
7126     geometry[MaxTextExtent],
7127     modulate_factors[MaxTextExtent];
7128
7129   GeometryInfo
7130     geometry_info;
7131
7132   Image
7133     *nexus;
7134
7135   ImageInfo
7136     *image_info;
7137
7138   int
7139     x,
7140     y;
7141
7142   MagickStatusType
7143     flags,
7144     status;
7145
7146   QuantizeInfo
7147     quantize_info;
7148
7149   RectangleInfo
7150     page_geometry;
7151
7152   register int
7153     i;
7154
7155   static char
7156     color[MaxTextExtent] = "gray";
7157
7158   unsigned int
7159     height,
7160     width;
7161
7162   /*
7163     Process user command.
7164   */
7165   XCheckRefreshWindows(display,windows);
7166   XImageCache(display,resource_info,windows,command,image,exception);
7167   nexus=NewImageList();
7168   windows->image.window_changes.width=windows->image.ximage->width;
7169   windows->image.window_changes.height=windows->image.ximage->height;
7170   image_info=CloneImageInfo(resource_info->image_info);
7171   SetGeometryInfo(&geometry_info);
7172   GetQuantizeInfo(&quantize_info);
7173   switch (command)
7174   {
7175     case OpenCommand:
7176     {
7177       /*
7178         Load image.
7179       */
7180       nexus=XOpenImage(display,resource_info,windows,MagickFalse);
7181       break;
7182     }
7183     case NextCommand:
7184     {
7185       /*
7186         Display next image.
7187       */
7188       for (i=0; i < resource_info->quantum; i++)
7189         XClientMessage(display,windows->image.id,windows->im_protocols,
7190           windows->im_next_image,CurrentTime);
7191       break;
7192     }
7193     case FormerCommand:
7194     {
7195       /*
7196         Display former image.
7197       */
7198       for (i=0; i < resource_info->quantum; i++)
7199         XClientMessage(display,windows->image.id,windows->im_protocols,
7200           windows->im_former_image,CurrentTime);
7201       break;
7202     }
7203     case SelectCommand:
7204     {
7205       int
7206         status;
7207
7208       /*
7209         Select image.
7210       */
7211       status=chdir(resource_info->home_directory);
7212       if (status == -1)
7213         (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
7214           "UnableToOpenFile","%s",resource_info->home_directory);
7215       nexus=XOpenImage(display,resource_info,windows,MagickTrue);
7216       break;
7217     }
7218     case SaveCommand:
7219     {
7220       /*
7221         Save image.
7222       */
7223       status=XSaveImage(display,resource_info,windows,*image,exception);
7224       if (status == MagickFalse)
7225         {
7226           char
7227             message[MaxTextExtent];
7228
7229           (void) FormatLocaleString(message,MaxTextExtent,"%s:%s",
7230             exception->reason != (char *) NULL ? exception->reason : "",
7231             exception->description != (char *) NULL ? exception->description :
7232             "");
7233           XNoticeWidget(display,windows,"Unable to save file:",message);
7234           break;
7235         }
7236       break;
7237     }
7238     case PrintCommand:
7239     {
7240       /*
7241         Print image.
7242       */
7243       status=XPrintImage(display,resource_info,windows,*image,exception);
7244       if (status == MagickFalse)
7245         {
7246           char
7247             message[MaxTextExtent];
7248
7249           (void) FormatLocaleString(message,MaxTextExtent,"%s:%s",
7250             exception->reason != (char *) NULL ? exception->reason : "",
7251             exception->description != (char *) NULL ? exception->description :
7252             "");
7253           XNoticeWidget(display,windows,"Unable to print file:",message);
7254           break;
7255         }
7256       break;
7257     }
7258     case DeleteCommand:
7259     {
7260       static char
7261         filename[MaxTextExtent] = "\0";
7262
7263       /*
7264         Delete image file.
7265       */
7266       XFileBrowserWidget(display,windows,"Delete",filename);
7267       if (*filename == '\0')
7268         break;
7269       status=remove_utf8(filename) != 0 ? MagickTrue : MagickFalse;
7270       if (status != MagickFalse)
7271         XNoticeWidget(display,windows,"Unable to delete image file:",filename);
7272       break;
7273     }
7274     case NewCommand:
7275     {
7276       int
7277         status;
7278
7279       static char
7280         color[MaxTextExtent] = "gray",
7281         geometry[MaxTextExtent] = "640x480";
7282
7283       static const char
7284         *format = "gradient";
7285
7286       /*
7287         Query user for canvas geometry.
7288       */
7289       status=XDialogWidget(display,windows,"New","Enter image geometry:",
7290         geometry);
7291       if (*geometry == '\0')
7292         break;
7293       if (status == 0)
7294         format="xc";
7295       XColorBrowserWidget(display,windows,"Select",color);
7296       if (*color == '\0')
7297         break;
7298       /*
7299         Create canvas.
7300       */
7301       (void) FormatLocaleString(image_info->filename,MaxTextExtent,
7302         "%s:%s",format,color);
7303       (void) CloneString(&image_info->size,geometry);
7304       nexus=ReadImage(image_info,exception);
7305       CatchException(exception);
7306       XClientMessage(display,windows->image.id,windows->im_protocols,
7307         windows->im_next_image,CurrentTime);
7308       break;
7309     }
7310     case VisualDirectoryCommand:
7311     {
7312       /*
7313         Visual Image directory.
7314       */
7315       nexus=XVisualDirectoryImage(display,resource_info,windows,exception);
7316       break;
7317     }
7318     case QuitCommand:
7319     {
7320       /*
7321         exit program.
7322       */
7323       if (resource_info->confirm_exit == MagickFalse)
7324         XClientMessage(display,windows->image.id,windows->im_protocols,
7325           windows->im_exit,CurrentTime);
7326       else
7327         {
7328           int
7329             status;
7330
7331           /*
7332             Confirm program exit.
7333           */
7334           status=XConfirmWidget(display,windows,"Do you really want to exit",
7335             resource_info->client_name);
7336           if (status > 0)
7337             XClientMessage(display,windows->image.id,windows->im_protocols,
7338               windows->im_exit,CurrentTime);
7339         }
7340       break;
7341     }
7342     case CutCommand:
7343     {
7344       /*
7345         Cut image.
7346       */
7347       (void) XCropImage(display,resource_info,windows,*image,CutMode,exception);
7348       break;
7349     }
7350     case CopyCommand:
7351     {
7352       /*
7353         Copy image.
7354       */
7355       (void) XCropImage(display,resource_info,windows,*image,CopyMode,
7356         exception);
7357       break;
7358     }
7359     case PasteCommand:
7360     {
7361       /*
7362         Paste image.
7363       */
7364       status=XPasteImage(display,resource_info,windows,*image,exception);
7365       if (status == MagickFalse)
7366         {
7367           XNoticeWidget(display,windows,"Unable to paste X image",
7368             (*image)->filename);
7369           break;
7370         }
7371       break;
7372     }
7373     case HalfSizeCommand:
7374     {
7375       /*
7376         Half image size.
7377       */
7378       windows->image.window_changes.width=windows->image.ximage->width/2;
7379       windows->image.window_changes.height=windows->image.ximage->height/2;
7380       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7381       break;
7382     }
7383     case OriginalSizeCommand:
7384     {
7385       /*
7386         Original image size.
7387       */
7388       windows->image.window_changes.width=(int) (*image)->columns;
7389       windows->image.window_changes.height=(int) (*image)->rows;
7390       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7391       break;
7392     }
7393     case DoubleSizeCommand:
7394     {
7395       /*
7396         Double the image size.
7397       */
7398       windows->image.window_changes.width=windows->image.ximage->width << 1;
7399       windows->image.window_changes.height=windows->image.ximage->height << 1;
7400       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7401       break;
7402     }
7403     case ResizeCommand:
7404     {
7405       int
7406         status;
7407
7408       size_t
7409         height,
7410         width;
7411
7412       ssize_t
7413         x,
7414         y;
7415
7416       /*
7417         Resize image.
7418       */
7419       width=(size_t) windows->image.ximage->width;
7420       height=(size_t) windows->image.ximage->height;
7421       x=0;
7422       y=0;
7423       (void) FormatLocaleString(geometry,MaxTextExtent,"%.20gx%.20g+0+0",
7424         (double) width,(double) height);
7425       status=XDialogWidget(display,windows,"Resize",
7426         "Enter resize geometry (e.g. 640x480, 200%):",geometry);
7427       if (*geometry == '\0')
7428         break;
7429       if (status == 0)
7430         (void) ConcatenateMagickString(geometry,"!",MaxTextExtent);
7431       (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
7432       windows->image.window_changes.width=(int) width;
7433       windows->image.window_changes.height=(int) height;
7434       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7435       break;
7436     }
7437     case ApplyCommand:
7438     {
7439       char
7440         image_geometry[MaxTextExtent];
7441
7442       if ((windows->image.crop_geometry == (char *) NULL) &&
7443           ((int) (*image)->columns == windows->image.ximage->width) &&
7444           ((int) (*image)->rows == windows->image.ximage->height))
7445         break;
7446       /*
7447         Apply size transforms to image.
7448       */
7449       XSetCursorState(display,windows,MagickTrue);
7450       XCheckRefreshWindows(display,windows);
7451       /*
7452         Crop and/or scale displayed image.
7453       */
7454       (void) FormatLocaleString(image_geometry,MaxTextExtent,"%dx%d!",
7455         windows->image.ximage->width,windows->image.ximage->height);
7456       (void) TransformImage(image,windows->image.crop_geometry,image_geometry,
7457         exception);
7458       if (windows->image.crop_geometry != (char *) NULL)
7459         windows->image.crop_geometry=(char *) RelinquishMagickMemory(
7460           windows->image.crop_geometry);
7461       windows->image.x=0;
7462       windows->image.y=0;
7463       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7464       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7465       break;
7466     }
7467     case RefreshCommand:
7468     {
7469       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7470       break;
7471     }
7472     case RestoreCommand:
7473     {
7474       /*
7475         Restore Image window to its original size.
7476       */
7477       if ((windows->image.width == (unsigned int) (*image)->columns) &&
7478           (windows->image.height == (unsigned int) (*image)->rows) &&
7479           (windows->image.crop_geometry == (char *) NULL))
7480         {
7481           (void) XBell(display,0);
7482           break;
7483         }
7484       windows->image.window_changes.width=(int) (*image)->columns;
7485       windows->image.window_changes.height=(int) (*image)->rows;
7486       if (windows->image.crop_geometry != (char *) NULL)
7487         {
7488           windows->image.crop_geometry=(char *)
7489             RelinquishMagickMemory(windows->image.crop_geometry);
7490           windows->image.crop_geometry=(char *) NULL;
7491           windows->image.x=0;
7492           windows->image.y=0;
7493         }
7494       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7495       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7496       break;
7497     }
7498     case CropCommand:
7499     {
7500       /*
7501         Crop image.
7502       */
7503       (void) XCropImage(display,resource_info,windows,*image,CropMode,
7504         exception);
7505       break;
7506     }
7507     case ChopCommand:
7508     {
7509       /*
7510         Chop image.
7511       */
7512       status=XChopImage(display,resource_info,windows,image,exception);
7513       if (status == MagickFalse)
7514         {
7515           XNoticeWidget(display,windows,"Unable to cut X image",
7516             (*image)->filename);
7517           break;
7518         }
7519       break;
7520     }
7521     case FlopCommand:
7522     {
7523       Image
7524         *flop_image;
7525
7526       /*
7527         Flop image scanlines.
7528       */
7529       XSetCursorState(display,windows,MagickTrue);
7530       XCheckRefreshWindows(display,windows);
7531       flop_image=FlopImage(*image,exception);
7532       if (flop_image != (Image *) NULL)
7533         {
7534           *image=DestroyImage(*image);
7535           *image=flop_image;
7536         }
7537       CatchException(exception);
7538       XSetCursorState(display,windows,MagickFalse);
7539       if (windows->image.crop_geometry != (char *) NULL)
7540         {
7541           /*
7542             Flop crop geometry.
7543           */
7544           width=(unsigned int) (*image)->columns;
7545           height=(unsigned int) (*image)->rows;
7546           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7547             &width,&height);
7548           (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
7549             "%ux%u%+d%+d",width,height,(int) (*image)->columns-(int) width-x,y);
7550         }
7551       if (windows->image.orphan != MagickFalse)
7552         break;
7553       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7554       break;
7555     }
7556     case FlipCommand:
7557     {
7558       Image
7559         *flip_image;
7560
7561       /*
7562         Flip image scanlines.
7563       */
7564       XSetCursorState(display,windows,MagickTrue);
7565       XCheckRefreshWindows(display,windows);
7566       flip_image=FlipImage(*image,exception);
7567       if (flip_image != (Image *) NULL)
7568         {
7569           *image=DestroyImage(*image);
7570           *image=flip_image;
7571         }
7572       CatchException(exception);
7573       XSetCursorState(display,windows,MagickFalse);
7574       if (windows->image.crop_geometry != (char *) NULL)
7575         {
7576           /*
7577             Flip crop geometry.
7578           */
7579           width=(unsigned int) (*image)->columns;
7580           height=(unsigned int) (*image)->rows;
7581           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7582             &width,&height);
7583           (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
7584             "%ux%u%+d%+d",width,height,x,(int) (*image)->rows-(int) height-y);
7585         }
7586       if (windows->image.orphan != MagickFalse)
7587         break;
7588       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7589       break;
7590     }
7591     case RotateRightCommand:
7592     {
7593       /*
7594         Rotate image 90 degrees clockwise.
7595       */
7596       status=XRotateImage(display,resource_info,windows,90.0,image,exception);
7597       if (status == MagickFalse)
7598         {
7599           XNoticeWidget(display,windows,"Unable to rotate X image",
7600             (*image)->filename);
7601           break;
7602         }
7603       break;
7604     }
7605     case RotateLeftCommand:
7606     {
7607       /*
7608         Rotate image 90 degrees counter-clockwise.
7609       */
7610       status=XRotateImage(display,resource_info,windows,-90.0,image,exception);
7611       if (status == MagickFalse)
7612         {
7613           XNoticeWidget(display,windows,"Unable to rotate X image",
7614             (*image)->filename);
7615           break;
7616         }
7617       break;
7618     }
7619     case RotateCommand:
7620     {
7621       /*
7622         Rotate image.
7623       */
7624       status=XRotateImage(display,resource_info,windows,0.0,image,exception);
7625       if (status == MagickFalse)
7626         {
7627           XNoticeWidget(display,windows,"Unable to rotate X image",
7628             (*image)->filename);
7629           break;
7630         }
7631       break;
7632     }
7633     case ShearCommand:
7634     {
7635       Image
7636         *shear_image;
7637
7638       static char
7639         geometry[MaxTextExtent] = "45.0x45.0";
7640
7641       /*
7642         Query user for shear color and geometry.
7643       */
7644       XColorBrowserWidget(display,windows,"Select",color);
7645       if (*color == '\0')
7646         break;
7647       (void) XDialogWidget(display,windows,"Shear","Enter shear geometry:",
7648         geometry);
7649       if (*geometry == '\0')
7650         break;
7651       /*
7652         Shear image.
7653       */
7654       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
7655         exception);
7656       XSetCursorState(display,windows,MagickTrue);
7657       XCheckRefreshWindows(display,windows);
7658       (void) QueryColorCompliance(color,AllCompliance,
7659         &(*image)->background_color,exception);
7660       flags=ParseGeometry(geometry,&geometry_info);
7661       if ((flags & SigmaValue) == 0)
7662         geometry_info.sigma=geometry_info.rho;
7663       shear_image=ShearImage(*image,geometry_info.rho,geometry_info.sigma,
7664         exception);
7665       if (shear_image != (Image *) NULL)
7666         {
7667           *image=DestroyImage(*image);
7668           *image=shear_image;
7669         }
7670       CatchException(exception);
7671       XSetCursorState(display,windows,MagickFalse);
7672       if (windows->image.orphan != MagickFalse)
7673         break;
7674       windows->image.window_changes.width=(int) (*image)->columns;
7675       windows->image.window_changes.height=(int) (*image)->rows;
7676       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7677       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7678       break;
7679     }
7680     case RollCommand:
7681     {
7682       Image
7683         *roll_image;
7684
7685       static char
7686         geometry[MaxTextExtent] = "+2+2";
7687
7688       /*
7689         Query user for the roll geometry.
7690       */
7691       (void) XDialogWidget(display,windows,"Roll","Enter roll geometry:",
7692         geometry);
7693       if (*geometry == '\0')
7694         break;
7695       /*
7696         Roll image.
7697       */
7698       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
7699         exception);
7700       XSetCursorState(display,windows,MagickTrue);
7701       XCheckRefreshWindows(display,windows);
7702       (void) ParsePageGeometry(*image,geometry,&page_geometry,
7703         exception);
7704       roll_image=RollImage(*image,page_geometry.x,page_geometry.y,
7705         exception);
7706       if (roll_image != (Image *) NULL)
7707         {
7708           *image=DestroyImage(*image);
7709           *image=roll_image;
7710         }
7711       CatchException(exception);
7712       XSetCursorState(display,windows,MagickFalse);
7713       if (windows->image.orphan != MagickFalse)
7714         break;
7715       windows->image.window_changes.width=(int) (*image)->columns;
7716       windows->image.window_changes.height=(int) (*image)->rows;
7717       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7718       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7719       break;
7720     }
7721     case TrimCommand:
7722     {
7723       static char
7724         fuzz[MaxTextExtent];
7725
7726       /*
7727         Query user for the fuzz factor.
7728       */
7729       (void) FormatLocaleString(fuzz,MaxTextExtent,"%g%%",100.0*
7730         (*image)->fuzz/(QuantumRange+1.0));
7731       (void) XDialogWidget(display,windows,"Trim","Enter fuzz factor:",fuzz);
7732       if (*fuzz == '\0')
7733         break;
7734       (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+1.0);
7735       /*
7736         Trim image.
7737       */
7738       status=XTrimImage(display,resource_info,windows,*image,exception);
7739       if (status == MagickFalse)
7740         {
7741           XNoticeWidget(display,windows,"Unable to trim X image",
7742             (*image)->filename);
7743           break;
7744         }
7745       break;
7746     }
7747     case HueCommand:
7748     {
7749       static char
7750         hue_percent[MaxTextExtent] = "110";
7751
7752       /*
7753         Query user for percent hue change.
7754       */
7755       (void) XDialogWidget(display,windows,"Apply",
7756         "Enter percent change in image hue (0-200):",hue_percent);
7757       if (*hue_percent == '\0')
7758         break;
7759       /*
7760         Vary the image hue.
7761       */
7762       XSetCursorState(display,windows,MagickTrue);
7763       XCheckRefreshWindows(display,windows);
7764       (void) CopyMagickString(modulate_factors,"100.0/100.0/",MaxTextExtent);
7765       (void) ConcatenateMagickString(modulate_factors,hue_percent,
7766         MaxTextExtent);
7767       (void) ModulateImage(*image,modulate_factors,exception);
7768       XSetCursorState(display,windows,MagickFalse);
7769       if (windows->image.orphan != MagickFalse)
7770         break;
7771       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7772       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7773       break;
7774     }
7775     case SaturationCommand:
7776     {
7777       static char
7778         saturation_percent[MaxTextExtent] = "110";
7779
7780       /*
7781         Query user for percent saturation change.
7782       */
7783       (void) XDialogWidget(display,windows,"Apply",
7784         "Enter percent change in color saturation (0-200):",saturation_percent);
7785       if (*saturation_percent == '\0')
7786         break;
7787       /*
7788         Vary color saturation.
7789       */
7790       XSetCursorState(display,windows,MagickTrue);
7791       XCheckRefreshWindows(display,windows);
7792       (void) CopyMagickString(modulate_factors,"100.0/",MaxTextExtent);
7793       (void) ConcatenateMagickString(modulate_factors,saturation_percent,
7794         MaxTextExtent);
7795       (void) ModulateImage(*image,modulate_factors,exception);
7796       XSetCursorState(display,windows,MagickFalse);
7797       if (windows->image.orphan != MagickFalse)
7798         break;
7799       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7800       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7801       break;
7802     }
7803     case BrightnessCommand:
7804     {
7805       static char
7806         brightness_percent[MaxTextExtent] = "110";
7807
7808       /*
7809         Query user for percent brightness change.
7810       */
7811       (void) XDialogWidget(display,windows,"Apply",
7812         "Enter percent change in color brightness (0-200):",brightness_percent);
7813       if (*brightness_percent == '\0')
7814         break;
7815       /*
7816         Vary the color brightness.
7817       */
7818       XSetCursorState(display,windows,MagickTrue);
7819       XCheckRefreshWindows(display,windows);
7820       (void) CopyMagickString(modulate_factors,brightness_percent,
7821         MaxTextExtent);
7822       (void) ModulateImage(*image,modulate_factors,exception);
7823       XSetCursorState(display,windows,MagickFalse);
7824       if (windows->image.orphan != MagickFalse)
7825         break;
7826       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7827       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7828       break;
7829     }
7830     case GammaCommand:
7831     {
7832       static char
7833         factor[MaxTextExtent] = "1.6";
7834
7835       /*
7836         Query user for gamma value.
7837       */
7838       (void) XDialogWidget(display,windows,"Gamma",
7839         "Enter gamma value (e.g. 1.2):",factor);
7840       if (*factor == '\0')
7841         break;
7842       /*
7843         Gamma correct image.
7844       */
7845       XSetCursorState(display,windows,MagickTrue);
7846       XCheckRefreshWindows(display,windows);
7847       (void) GammaImage(*image,atof(factor),exception);
7848       XSetCursorState(display,windows,MagickFalse);
7849       if (windows->image.orphan != MagickFalse)
7850         break;
7851       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7852       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7853       break;
7854     }
7855     case SpiffCommand:
7856     {
7857       /*
7858         Sharpen the image contrast.
7859       */
7860       XSetCursorState(display,windows,MagickTrue);
7861       XCheckRefreshWindows(display,windows);
7862       (void) ContrastImage(*image,MagickTrue,exception);
7863       XSetCursorState(display,windows,MagickFalse);
7864       if (windows->image.orphan != MagickFalse)
7865         break;
7866       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7867       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7868       break;
7869     }
7870     case DullCommand:
7871     {
7872       /*
7873         Dull the image contrast.
7874       */
7875       XSetCursorState(display,windows,MagickTrue);
7876       XCheckRefreshWindows(display,windows);
7877       (void) ContrastImage(*image,MagickFalse,exception);
7878       XSetCursorState(display,windows,MagickFalse);
7879       if (windows->image.orphan != MagickFalse)
7880         break;
7881       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7882       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7883       break;
7884     }
7885     case ContrastStretchCommand:
7886     {
7887       double
7888         black_point,
7889         white_point;
7890
7891       static char
7892         levels[MaxTextExtent] = "1%";
7893
7894       /*
7895         Query user for gamma value.
7896       */
7897       (void) XDialogWidget(display,windows,"Contrast Stretch",
7898         "Enter black and white points:",levels);
7899       if (*levels == '\0')
7900         break;
7901       /*
7902         Contrast stretch image.
7903       */
7904       XSetCursorState(display,windows,MagickTrue);
7905       XCheckRefreshWindows(display,windows);
7906       flags=ParseGeometry(levels,&geometry_info);
7907       black_point=geometry_info.rho;
7908       white_point=(flags & SigmaValue) != 0 ? geometry_info.sigma : black_point;
7909       if ((flags & PercentValue) != 0)
7910         {
7911           black_point*=(double) (*image)->columns*(*image)->rows/100.0;
7912           white_point*=(double) (*image)->columns*(*image)->rows/100.0;
7913         }
7914       white_point=(MagickRealType) (*image)->columns*(*image)->rows-white_point;
7915       (void) ContrastStretchImage(*image,black_point,white_point,
7916         exception);
7917       XSetCursorState(display,windows,MagickFalse);
7918       if (windows->image.orphan != MagickFalse)
7919         break;
7920       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7921       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7922       break;
7923     }
7924     case SigmoidalContrastCommand:
7925     {
7926       GeometryInfo
7927         geometry_info;
7928
7929       MagickStatusType
7930         flags;
7931
7932       static char
7933         levels[MaxTextExtent] = "3x50%";
7934
7935       /*
7936         Query user for gamma value.
7937       */
7938       (void) XDialogWidget(display,windows,"Sigmoidal Contrast",
7939         "Enter contrast and midpoint:",levels);
7940       if (*levels == '\0')
7941         break;
7942       /*
7943         Contrast stretch image.
7944       */
7945       XSetCursorState(display,windows,MagickTrue);
7946       XCheckRefreshWindows(display,windows);
7947       flags=ParseGeometry(levels,&geometry_info);
7948       if ((flags & SigmaValue) == 0)
7949         geometry_info.sigma=1.0*QuantumRange/2.0;
7950       if ((flags & PercentValue) != 0)
7951         geometry_info.sigma=1.0*QuantumRange*geometry_info.sigma/100.0;
7952       (void) SigmoidalContrastImage(*image,MagickTrue,geometry_info.rho,
7953         geometry_info.sigma,exception);
7954       XSetCursorState(display,windows,MagickFalse);
7955       if (windows->image.orphan != MagickFalse)
7956         break;
7957       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7958       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7959       break;
7960     }
7961     case NormalizeCommand:
7962     {
7963       /*
7964         Perform histogram normalization on the image.
7965       */
7966       XSetCursorState(display,windows,MagickTrue);
7967       XCheckRefreshWindows(display,windows);
7968       (void) NormalizeImage(*image,exception);
7969       XSetCursorState(display,windows,MagickFalse);
7970       if (windows->image.orphan != MagickFalse)
7971         break;
7972       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7973       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7974       break;
7975     }
7976     case EqualizeCommand:
7977     {
7978       /*
7979         Perform histogram equalization on the image.
7980       */
7981       XSetCursorState(display,windows,MagickTrue);
7982       XCheckRefreshWindows(display,windows);
7983       (void) EqualizeImage(*image,exception);
7984       XSetCursorState(display,windows,MagickFalse);
7985       if (windows->image.orphan != MagickFalse)
7986         break;
7987       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7988       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7989       break;
7990     }
7991     case NegateCommand:
7992     {
7993       /*
7994         Negate colors in image.
7995       */
7996       XSetCursorState(display,windows,MagickTrue);
7997       XCheckRefreshWindows(display,windows);
7998       (void) NegateImage(*image,MagickFalse,exception);
7999       XSetCursorState(display,windows,MagickFalse);
8000       if (windows->image.orphan != MagickFalse)
8001         break;
8002       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8003       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8004       break;
8005     }
8006     case GrayscaleCommand:
8007     {
8008       /*
8009         Convert image to grayscale.
8010       */
8011       XSetCursorState(display,windows,MagickTrue);
8012       XCheckRefreshWindows(display,windows);
8013       (void) SetImageType(*image,(*image)->matte == MagickFalse ?
8014         GrayscaleType : GrayscaleMatteType,exception);
8015       XSetCursorState(display,windows,MagickFalse);
8016       if (windows->image.orphan != MagickFalse)
8017         break;
8018       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8019       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8020       break;
8021     }
8022     case MapCommand:
8023     {
8024       Image
8025         *affinity_image;
8026
8027       static char
8028         filename[MaxTextExtent] = "\0";
8029
8030       /*
8031         Request image file name from user.
8032       */
8033       XFileBrowserWidget(display,windows,"Map",filename);
8034       if (*filename == '\0')
8035         break;
8036       /*
8037         Map image.
8038       */
8039       XSetCursorState(display,windows,MagickTrue);
8040       XCheckRefreshWindows(display,windows);
8041       (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
8042       affinity_image=ReadImage(image_info,exception);
8043       if (affinity_image != (Image *) NULL)
8044         {
8045           (void) RemapImage(&quantize_info,*image,affinity_image,exception);
8046           affinity_image=DestroyImage(affinity_image);
8047         }
8048       CatchException(exception);
8049       XSetCursorState(display,windows,MagickFalse);
8050       if (windows->image.orphan != MagickFalse)
8051         break;
8052       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8053       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8054       break;
8055     }
8056     case QuantizeCommand:
8057     {
8058       int
8059         status;
8060
8061       static char
8062         colors[MaxTextExtent] = "256";
8063
8064       /*
8065         Query user for maximum number of colors.
8066       */
8067       status=XDialogWidget(display,windows,"Quantize",
8068         "Maximum number of colors:",colors);
8069       if (*colors == '\0')
8070         break;
8071       /*
8072         Color reduce the image.
8073       */
8074       XSetCursorState(display,windows,MagickTrue);
8075       XCheckRefreshWindows(display,windows);
8076       quantize_info.number_colors=StringToUnsignedLong(colors);
8077       quantize_info.dither=status != 0 ? MagickTrue : MagickFalse;
8078       (void) QuantizeImage(&quantize_info,*image,exception);
8079       XSetCursorState(display,windows,MagickFalse);
8080       if (windows->image.orphan != MagickFalse)
8081         break;
8082       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8083       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8084       break;
8085     }
8086     case DespeckleCommand:
8087     {
8088       Image
8089         *despeckle_image;
8090
8091       /*
8092         Despeckle image.
8093       */
8094       XSetCursorState(display,windows,MagickTrue);
8095       XCheckRefreshWindows(display,windows);
8096       despeckle_image=DespeckleImage(*image,exception);
8097       if (despeckle_image != (Image *) NULL)
8098         {
8099           *image=DestroyImage(*image);
8100           *image=despeckle_image;
8101         }
8102       CatchException(exception);
8103       XSetCursorState(display,windows,MagickFalse);
8104       if (windows->image.orphan != MagickFalse)
8105         break;
8106       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8107       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8108       break;
8109     }
8110     case EmbossCommand:
8111     {
8112       Image
8113         *emboss_image;
8114
8115       static char
8116         radius[MaxTextExtent] = "0.0x1.0";
8117
8118       /*
8119         Query user for emboss radius.
8120       */
8121       (void) XDialogWidget(display,windows,"Emboss",
8122         "Enter the emboss radius and standard deviation:",radius);
8123       if (*radius == '\0')
8124         break;
8125       /*
8126         Reduce noise in the image.
8127       */
8128       XSetCursorState(display,windows,MagickTrue);
8129       XCheckRefreshWindows(display,windows);
8130       flags=ParseGeometry(radius,&geometry_info);
8131       if ((flags & SigmaValue) == 0)
8132         geometry_info.sigma=1.0;
8133       emboss_image=EmbossImage(*image,geometry_info.rho,geometry_info.sigma,
8134         exception);
8135       if (emboss_image != (Image *) NULL)
8136         {
8137           *image=DestroyImage(*image);
8138           *image=emboss_image;
8139         }
8140       CatchException(exception);
8141       XSetCursorState(display,windows,MagickFalse);
8142       if (windows->image.orphan != MagickFalse)
8143         break;
8144       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8145       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8146       break;
8147     }
8148     case ReduceNoiseCommand:
8149     {
8150       Image
8151         *noise_image;
8152
8153       static char
8154         radius[MaxTextExtent] = "0";
8155
8156       /*
8157         Query user for noise radius.
8158       */
8159       (void) XDialogWidget(display,windows,"Reduce Noise",
8160         "Enter the noise radius:",radius);
8161       if (*radius == '\0')
8162         break;
8163       /*
8164         Reduce noise in the image.
8165       */
8166       XSetCursorState(display,windows,MagickTrue);
8167       XCheckRefreshWindows(display,windows);
8168       flags=ParseGeometry(radius,&geometry_info);
8169       noise_image=StatisticImage(*image,NonpeakStatistic,(size_t)
8170         geometry_info.rho,(size_t) geometry_info.rho,exception);
8171       if (noise_image != (Image *) NULL)
8172         {
8173           *image=DestroyImage(*image);
8174           *image=noise_image;
8175         }
8176       CatchException(exception);
8177       XSetCursorState(display,windows,MagickFalse);
8178       if (windows->image.orphan != MagickFalse)
8179         break;
8180       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8181       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8182       break;
8183     }
8184     case AddNoiseCommand:
8185     {
8186       char
8187         **noises;
8188
8189       Image
8190         *noise_image;
8191
8192       static char
8193         noise_type[MaxTextExtent] = "Gaussian";
8194
8195       /*
8196         Add noise to the image.
8197       */
8198       noises=GetCommandOptions(MagickNoiseOptions);
8199       if (noises == (char **) NULL)
8200         break;
8201       XListBrowserWidget(display,windows,&windows->widget,
8202         (const char **) noises,"Add Noise",
8203         "Select a type of noise to add to your image:",noise_type);
8204       noises=DestroyStringList(noises);
8205       if (*noise_type == '\0')
8206         break;
8207       XSetCursorState(display,windows,MagickTrue);
8208       XCheckRefreshWindows(display,windows);
8209       noise_image=AddNoiseImage(*image,(NoiseType) ParseCommandOption(
8210         MagickNoiseOptions,MagickFalse,noise_type),1.0,exception);
8211       if (noise_image != (Image *) NULL)
8212         {
8213           *image=DestroyImage(*image);
8214           *image=noise_image;
8215         }
8216       CatchException(exception);
8217       XSetCursorState(display,windows,MagickFalse);
8218       if (windows->image.orphan != MagickFalse)
8219         break;
8220       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8221       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8222       break;
8223     }
8224     case SharpenCommand:
8225     {
8226       Image
8227         *sharp_image;
8228
8229       static char
8230         radius[MaxTextExtent] = "0.0x1.0";
8231
8232       /*
8233         Query user for sharpen radius.
8234       */
8235       (void) XDialogWidget(display,windows,"Sharpen",
8236         "Enter the sharpen radius and standard deviation:",radius);
8237       if (*radius == '\0')
8238         break;
8239       /*
8240         Sharpen image scanlines.
8241       */
8242       XSetCursorState(display,windows,MagickTrue);
8243       XCheckRefreshWindows(display,windows);
8244       flags=ParseGeometry(radius,&geometry_info);
8245       sharp_image=SharpenImage(*image,geometry_info.rho,geometry_info.sigma,
8246         geometry_info.xi,exception);
8247       if (sharp_image != (Image *) NULL)
8248         {
8249           *image=DestroyImage(*image);
8250           *image=sharp_image;
8251         }
8252       CatchException(exception);
8253       XSetCursorState(display,windows,MagickFalse);
8254       if (windows->image.orphan != MagickFalse)
8255         break;
8256       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8257       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8258       break;
8259     }
8260     case BlurCommand:
8261     {
8262       Image
8263         *blur_image;
8264
8265       static char
8266         radius[MaxTextExtent] = "0.0x1.0";
8267
8268       /*
8269         Query user for blur radius.
8270       */
8271       (void) XDialogWidget(display,windows,"Blur",
8272         "Enter the blur radius and standard deviation:",radius);
8273       if (*radius == '\0')
8274         break;
8275       /*
8276         Blur an image.
8277       */
8278       XSetCursorState(display,windows,MagickTrue);
8279       XCheckRefreshWindows(display,windows);
8280       flags=ParseGeometry(radius,&geometry_info);
8281       blur_image=BlurImage(*image,geometry_info.rho,geometry_info.sigma,
8282         geometry_info.xi,exception);
8283       if (blur_image != (Image *) NULL)
8284         {
8285           *image=DestroyImage(*image);
8286           *image=blur_image;
8287         }
8288       CatchException(exception);
8289       XSetCursorState(display,windows,MagickFalse);
8290       if (windows->image.orphan != MagickFalse)
8291         break;
8292       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8293       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8294       break;
8295     }
8296     case ThresholdCommand:
8297     {
8298       double
8299         threshold;
8300
8301       static char
8302         factor[MaxTextExtent] = "128";
8303
8304       /*
8305         Query user for threshold value.
8306       */
8307       (void) XDialogWidget(display,windows,"Threshold",
8308         "Enter threshold value:",factor);
8309       if (*factor == '\0')
8310         break;
8311       /*
8312         Gamma correct image.
8313       */
8314       XSetCursorState(display,windows,MagickTrue);
8315       XCheckRefreshWindows(display,windows);
8316       threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8317       (void) BilevelImage(*image,threshold,exception);
8318       XSetCursorState(display,windows,MagickFalse);
8319       if (windows->image.orphan != MagickFalse)
8320         break;
8321       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8322       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8323       break;
8324     }
8325     case EdgeDetectCommand:
8326     {
8327       Image
8328         *edge_image;
8329
8330       static char
8331         radius[MaxTextExtent] = "0";
8332
8333       /*
8334         Query user for edge factor.
8335       */
8336       (void) XDialogWidget(display,windows,"Detect Edges",
8337         "Enter the edge detect radius:",radius);
8338       if (*radius == '\0')
8339         break;
8340       /*
8341         Detect edge in image.
8342       */
8343       XSetCursorState(display,windows,MagickTrue);
8344       XCheckRefreshWindows(display,windows);
8345       flags=ParseGeometry(radius,&geometry_info);
8346       edge_image=EdgeImage(*image,geometry_info.rho,geometry_info.sigma,
8347         exception);
8348       if (edge_image != (Image *) NULL)
8349         {
8350           *image=DestroyImage(*image);
8351           *image=edge_image;
8352         }
8353       CatchException(exception);
8354       XSetCursorState(display,windows,MagickFalse);
8355       if (windows->image.orphan != MagickFalse)
8356         break;
8357       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8358       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8359       break;
8360     }
8361     case SpreadCommand:
8362     {
8363       Image
8364         *spread_image;
8365
8366       static char
8367         amount[MaxTextExtent] = "2";
8368
8369       /*
8370         Query user for spread amount.
8371       */
8372       (void) XDialogWidget(display,windows,"Spread",
8373         "Enter the displacement amount:",amount);
8374       if (*amount == '\0')
8375         break;
8376       /*
8377         Displace image pixels by a random amount.
8378       */
8379       XSetCursorState(display,windows,MagickTrue);
8380       XCheckRefreshWindows(display,windows);
8381       flags=ParseGeometry(amount,&geometry_info);
8382       spread_image=EdgeImage(*image,geometry_info.rho,geometry_info.sigma,
8383         exception);
8384       if (spread_image != (Image *) NULL)
8385         {
8386           *image=DestroyImage(*image);
8387           *image=spread_image;
8388         }
8389       CatchException(exception);
8390       XSetCursorState(display,windows,MagickFalse);
8391       if (windows->image.orphan != MagickFalse)
8392         break;
8393       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8394       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8395       break;
8396     }
8397     case ShadeCommand:
8398     {
8399       Image
8400         *shade_image;
8401
8402       int
8403         status;
8404
8405       static char
8406         geometry[MaxTextExtent] = "30x30";
8407
8408       /*
8409         Query user for the shade geometry.
8410       */
8411       status=XDialogWidget(display,windows,"Shade",
8412         "Enter the azimuth and elevation of the light source:",geometry);
8413       if (*geometry == '\0')
8414         break;
8415       /*
8416         Shade image pixels.
8417       */
8418       XSetCursorState(display,windows,MagickTrue);
8419       XCheckRefreshWindows(display,windows);
8420       flags=ParseGeometry(geometry,&geometry_info);
8421       if ((flags & SigmaValue) == 0)
8422         geometry_info.sigma=1.0;
8423       shade_image=ShadeImage(*image,status != 0 ? MagickFalse : MagickTrue,
8424         geometry_info.rho,geometry_info.sigma,exception);
8425       if (shade_image != (Image *) NULL)
8426         {
8427           *image=DestroyImage(*image);
8428           *image=shade_image;
8429         }
8430       CatchException(exception);
8431       XSetCursorState(display,windows,MagickFalse);
8432       if (windows->image.orphan != MagickFalse)
8433         break;
8434       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8435       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8436       break;
8437     }
8438     case RaiseCommand:
8439     {
8440       static char
8441         bevel_width[MaxTextExtent] = "10";
8442
8443       /*
8444         Query user for bevel width.
8445       */
8446       (void) XDialogWidget(display,windows,"Raise","Bevel width:",bevel_width);
8447       if (*bevel_width == '\0')
8448         break;
8449       /*
8450         Raise an image.
8451       */
8452       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8453         exception);
8454       XSetCursorState(display,windows,MagickTrue);
8455       XCheckRefreshWindows(display,windows);
8456       (void) ParsePageGeometry(*image,bevel_width,&page_geometry,
8457         exception);
8458       (void) RaiseImage(*image,&page_geometry,MagickTrue,exception);
8459       XSetCursorState(display,windows,MagickFalse);
8460       if (windows->image.orphan != MagickFalse)
8461         break;
8462       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8463       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8464       break;
8465     }
8466     case SegmentCommand:
8467     {
8468       static char
8469         threshold[MaxTextExtent] = "1.0x1.5";
8470
8471       /*
8472         Query user for smoothing threshold.
8473       */
8474       (void) XDialogWidget(display,windows,"Segment","Smooth threshold:",
8475         threshold);
8476       if (*threshold == '\0')
8477         break;
8478       /*
8479         Segment an image.
8480       */
8481       XSetCursorState(display,windows,MagickTrue);
8482       XCheckRefreshWindows(display,windows);
8483       flags=ParseGeometry(threshold,&geometry_info);
8484       if ((flags & SigmaValue) == 0)
8485         geometry_info.sigma=1.0;
8486       (void) SegmentImage(*image,RGBColorspace,MagickFalse,geometry_info.rho,
8487         geometry_info.sigma,exception);
8488       XSetCursorState(display,windows,MagickFalse);
8489       if (windows->image.orphan != MagickFalse)
8490         break;
8491       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8492       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8493       break;
8494     }
8495     case SepiaToneCommand:
8496     {
8497       double
8498         threshold;
8499
8500       Image
8501         *sepia_image;
8502
8503       static char
8504         factor[MaxTextExtent] = "80%";
8505
8506       /*
8507         Query user for sepia-tone factor.
8508       */
8509       (void) XDialogWidget(display,windows,"Sepia Tone",
8510         "Enter the sepia tone factor (0 - 99.9%):",factor);
8511       if (*factor == '\0')
8512         break;
8513       /*
8514         Sepia tone image pixels.
8515       */
8516       XSetCursorState(display,windows,MagickTrue);
8517       XCheckRefreshWindows(display,windows);
8518       threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8519       sepia_image=SepiaToneImage(*image,threshold,exception);
8520       if (sepia_image != (Image *) NULL)
8521         {
8522           *image=DestroyImage(*image);
8523           *image=sepia_image;
8524         }
8525       CatchException(exception);
8526       XSetCursorState(display,windows,MagickFalse);
8527       if (windows->image.orphan != MagickFalse)
8528         break;
8529       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8530       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8531       break;
8532     }
8533     case SolarizeCommand:
8534     {
8535       double
8536         threshold;
8537
8538       static char
8539         factor[MaxTextExtent] = "60%";
8540
8541       /*
8542         Query user for solarize factor.
8543       */
8544       (void) XDialogWidget(display,windows,"Solarize",
8545         "Enter the solarize factor (0 - 99.9%):",factor);
8546       if (*factor == '\0')
8547         break;
8548       /*
8549         Solarize image pixels.
8550       */
8551       XSetCursorState(display,windows,MagickTrue);
8552       XCheckRefreshWindows(display,windows);
8553       threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8554       (void) SolarizeImage(*image,threshold,exception);
8555       XSetCursorState(display,windows,MagickFalse);
8556       if (windows->image.orphan != MagickFalse)
8557         break;
8558       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8559       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8560       break;
8561     }
8562     case SwirlCommand:
8563     {
8564       Image
8565         *swirl_image;
8566
8567       static char
8568         degrees[MaxTextExtent] = "60";
8569
8570       /*
8571         Query user for swirl angle.
8572       */
8573       (void) XDialogWidget(display,windows,"Swirl","Enter the swirl angle:",
8574         degrees);
8575       if (*degrees == '\0')
8576         break;
8577       /*
8578         Swirl image pixels about the center.
8579       */
8580       XSetCursorState(display,windows,MagickTrue);
8581       XCheckRefreshWindows(display,windows);
8582       flags=ParseGeometry(degrees,&geometry_info);
8583       swirl_image=SwirlImage(*image,geometry_info.rho,(*image)->interpolate,
8584         exception);
8585       if (swirl_image != (Image *) NULL)
8586         {
8587           *image=DestroyImage(*image);
8588           *image=swirl_image;
8589         }
8590       CatchException(exception);
8591       XSetCursorState(display,windows,MagickFalse);
8592       if (windows->image.orphan != MagickFalse)
8593         break;
8594       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8595       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8596       break;
8597     }
8598     case ImplodeCommand:
8599     {
8600       Image
8601         *implode_image;
8602
8603       static char
8604         factor[MaxTextExtent] = "0.3";
8605
8606       /*
8607         Query user for implode factor.
8608       */
8609       (void) XDialogWidget(display,windows,"Implode",
8610         "Enter the implosion/explosion factor (-1.0 - 1.0):",factor);
8611       if (*factor == '\0')
8612         break;
8613       /*
8614         Implode image pixels about the center.
8615       */
8616       XSetCursorState(display,windows,MagickTrue);
8617       XCheckRefreshWindows(display,windows);
8618       flags=ParseGeometry(factor,&geometry_info);
8619       implode_image=ImplodeImage(*image,geometry_info.rho,(*image)->interpolate,
8620         exception);
8621       if (implode_image != (Image *) NULL)
8622         {
8623           *image=DestroyImage(*image);
8624           *image=implode_image;
8625         }
8626       CatchException(exception);
8627       XSetCursorState(display,windows,MagickFalse);
8628       if (windows->image.orphan != MagickFalse)
8629         break;
8630       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8631       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8632       break;
8633     }
8634     case VignetteCommand:
8635     {
8636       Image
8637         *vignette_image;
8638
8639       static char
8640         geometry[MaxTextExtent] = "0x20";
8641
8642       /*
8643         Query user for the vignette geometry.
8644       */
8645       (void) XDialogWidget(display,windows,"Vignette",
8646         "Enter the radius, sigma, and x and y offsets:",geometry);
8647       if (*geometry == '\0')
8648         break;
8649       /*
8650         Soften the edges of the image in vignette style
8651       */
8652       XSetCursorState(display,windows,MagickTrue);
8653       XCheckRefreshWindows(display,windows);
8654       flags=ParseGeometry(geometry,&geometry_info);
8655       if ((flags & SigmaValue) == 0)
8656         geometry_info.sigma=1.0;
8657       if ((flags & XiValue) == 0)
8658         geometry_info.xi=0.1*(*image)->columns;
8659       if ((flags & PsiValue) == 0)
8660         geometry_info.psi=0.1*(*image)->rows;
8661       vignette_image=VignetteImage(*image,geometry_info.rho,geometry_info.sigma,
8662         0.0,(ssize_t) ceil(geometry_info.xi-0.5),(ssize_t)
8663         ceil(geometry_info.psi-0.5),exception);
8664       if (vignette_image != (Image *) NULL)
8665         {
8666           *image=DestroyImage(*image);
8667           *image=vignette_image;
8668         }
8669       CatchException(exception);
8670       XSetCursorState(display,windows,MagickFalse);
8671       if (windows->image.orphan != MagickFalse)
8672         break;
8673       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8674       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8675       break;
8676     }
8677     case WaveCommand:
8678     {
8679       Image
8680         *wave_image;
8681
8682       static char
8683         geometry[MaxTextExtent] = "25x150";
8684
8685       /*
8686         Query user for the wave geometry.
8687       */
8688       (void) XDialogWidget(display,windows,"Wave",
8689         "Enter the amplitude and length of the wave:",geometry);
8690       if (*geometry == '\0')
8691         break;
8692       /*
8693         Alter an image along a sine wave.
8694       */
8695       XSetCursorState(display,windows,MagickTrue);
8696       XCheckRefreshWindows(display,windows);
8697       flags=ParseGeometry(geometry,&geometry_info);
8698       if ((flags & SigmaValue) == 0)
8699         geometry_info.sigma=1.0;
8700       wave_image=WaveImage(*image,geometry_info.rho,geometry_info.sigma,
8701         (*image)->interpolate,exception);
8702       if (wave_image != (Image *) NULL)
8703         {
8704           *image=DestroyImage(*image);
8705           *image=wave_image;
8706         }
8707       CatchException(exception);
8708       XSetCursorState(display,windows,MagickFalse);
8709       if (windows->image.orphan != MagickFalse)
8710         break;
8711       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8712       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8713       break;
8714     }
8715     case OilPaintCommand:
8716     {
8717       Image
8718         *paint_image;
8719
8720       static char
8721         radius[MaxTextExtent] = "0";
8722
8723       /*
8724         Query user for circular neighborhood radius.
8725       */
8726       (void) XDialogWidget(display,windows,"Oil Paint",
8727         "Enter the mask radius:",radius);
8728       if (*radius == '\0')
8729         break;
8730       /*
8731         OilPaint image scanlines.
8732       */
8733       XSetCursorState(display,windows,MagickTrue);
8734       XCheckRefreshWindows(display,windows);
8735       flags=ParseGeometry(radius,&geometry_info);
8736       paint_image=OilPaintImage(*image,geometry_info.rho,geometry_info.sigma,
8737         exception);
8738       if (paint_image != (Image *) NULL)
8739         {
8740           *image=DestroyImage(*image);
8741           *image=paint_image;
8742         }
8743       CatchException(exception);
8744       XSetCursorState(display,windows,MagickFalse);
8745       if (windows->image.orphan != MagickFalse)
8746         break;
8747       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8748       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8749       break;
8750     }
8751     case CharcoalDrawCommand:
8752     {
8753       Image
8754         *charcoal_image;
8755
8756       static char
8757         radius[MaxTextExtent] = "0x1";
8758
8759       /*
8760         Query user for charcoal radius.
8761       */
8762       (void) XDialogWidget(display,windows,"Charcoal Draw",
8763         "Enter the charcoal radius and sigma:",radius);
8764       if (*radius == '\0')
8765         break;
8766       /*
8767         Charcoal the image.
8768       */
8769       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8770         exception);
8771       XSetCursorState(display,windows,MagickTrue);
8772       XCheckRefreshWindows(display,windows);
8773       flags=ParseGeometry(radius,&geometry_info);
8774       if ((flags & SigmaValue) == 0)
8775         geometry_info.sigma=geometry_info.rho;
8776       charcoal_image=CharcoalImage(*image,geometry_info.rho,geometry_info.sigma,
8777         geometry_info.xi,exception);
8778       if (charcoal_image != (Image *) NULL)
8779         {
8780           *image=DestroyImage(*image);
8781           *image=charcoal_image;
8782         }
8783       CatchException(exception);
8784       XSetCursorState(display,windows,MagickFalse);
8785       if (windows->image.orphan != MagickFalse)
8786         break;
8787       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8788       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8789       break;
8790     }
8791     case AnnotateCommand:
8792     {
8793       /*
8794         Annotate the image with text.
8795       */
8796       status=XAnnotateEditImage(display,resource_info,windows,*image,exception);
8797       if (status == MagickFalse)
8798         {
8799           XNoticeWidget(display,windows,"Unable to annotate X image",
8800             (*image)->filename);
8801           break;
8802         }
8803       break;
8804     }
8805     case DrawCommand:
8806     {
8807       /*
8808         Draw image.
8809       */
8810       status=XDrawEditImage(display,resource_info,windows,image,exception);
8811       if (status == MagickFalse)
8812         {
8813           XNoticeWidget(display,windows,"Unable to draw on the X image",
8814             (*image)->filename);
8815           break;
8816         }
8817       break;
8818     }
8819     case ColorCommand:
8820     {
8821       /*
8822         Color edit.
8823       */
8824       status=XColorEditImage(display,resource_info,windows,image,exception);
8825       if (status == MagickFalse)
8826         {
8827           XNoticeWidget(display,windows,"Unable to pixel edit X image",
8828             (*image)->filename);
8829           break;
8830         }
8831       break;
8832     }
8833     case MatteCommand:
8834     {
8835       /*
8836         Matte edit.
8837       */
8838       status=XMatteEditImage(display,resource_info,windows,image,exception);
8839       if (status == MagickFalse)
8840         {
8841           XNoticeWidget(display,windows,"Unable to matte edit X image",
8842             (*image)->filename);
8843           break;
8844         }
8845       break;
8846     }
8847     case CompositeCommand:
8848     {
8849       /*
8850         Composite image.
8851       */
8852       status=XCompositeImage(display,resource_info,windows,*image,
8853         exception);
8854       if (status == MagickFalse)
8855         {
8856           XNoticeWidget(display,windows,"Unable to composite X image",
8857             (*image)->filename);
8858           break;
8859         }
8860       break;
8861     }
8862     case AddBorderCommand:
8863     {
8864       Image
8865         *border_image;
8866
8867       static char
8868         geometry[MaxTextExtent] = "6x6";
8869
8870       /*
8871         Query user for border color and geometry.
8872       */
8873       XColorBrowserWidget(display,windows,"Select",color);
8874       if (*color == '\0')
8875         break;
8876       (void) XDialogWidget(display,windows,"Add Border",
8877         "Enter border geometry:",geometry);
8878       if (*geometry == '\0')
8879         break;
8880       /*
8881         Add a border to the image.
8882       */
8883       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8884         exception);
8885       XSetCursorState(display,windows,MagickTrue);
8886       XCheckRefreshWindows(display,windows);
8887       (void) QueryColorCompliance(color,AllCompliance,&(*image)->border_color,
8888         exception);
8889       (void) ParsePageGeometry(*image,geometry,&page_geometry,
8890         exception);
8891       border_image=BorderImage(*image,&page_geometry,(*image)->compose,
8892         exception);
8893       if (border_image != (Image *) NULL)
8894         {
8895           *image=DestroyImage(*image);
8896           *image=border_image;
8897         }
8898       CatchException(exception);
8899       XSetCursorState(display,windows,MagickFalse);
8900       if (windows->image.orphan != MagickFalse)
8901         break;
8902       windows->image.window_changes.width=(int) (*image)->columns;
8903       windows->image.window_changes.height=(int) (*image)->rows;
8904       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8905       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8906       break;
8907     }
8908     case AddFrameCommand:
8909     {
8910       FrameInfo
8911         frame_info;
8912
8913       Image
8914         *frame_image;
8915
8916       static char
8917         geometry[MaxTextExtent] = "6x6";
8918
8919       /*
8920         Query user for frame color and geometry.
8921       */
8922       XColorBrowserWidget(display,windows,"Select",color);
8923       if (*color == '\0')
8924         break;
8925       (void) XDialogWidget(display,windows,"Add Frame","Enter frame geometry:",
8926         geometry);
8927       if (*geometry == '\0')
8928         break;
8929       /*
8930         Surround image with an ornamental border.
8931       */
8932       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8933         exception);
8934       XSetCursorState(display,windows,MagickTrue);
8935       XCheckRefreshWindows(display,windows);
8936       (void) QueryColorCompliance(color,AllCompliance,&(*image)->matte_color,
8937         exception);
8938       (void) ParsePageGeometry(*image,geometry,&page_geometry,
8939         exception);
8940       frame_info.width=page_geometry.width;
8941       frame_info.height=page_geometry.height;
8942       frame_info.outer_bevel=page_geometry.x;
8943       frame_info.inner_bevel=page_geometry.y;
8944       frame_info.x=(ssize_t) frame_info.width;
8945       frame_info.y=(ssize_t) frame_info.height;
8946       frame_info.width=(*image)->columns+2*frame_info.width;
8947       frame_info.height=(*image)->rows+2*frame_info.height;
8948       frame_image=FrameImage(*image,&frame_info,(*image)->compose,exception);
8949       if (frame_image != (Image *) NULL)
8950         {
8951           *image=DestroyImage(*image);
8952           *image=frame_image;
8953         }
8954       CatchException(exception);
8955       XSetCursorState(display,windows,MagickFalse);
8956       if (windows->image.orphan != MagickFalse)
8957         break;
8958       windows->image.window_changes.width=(int) (*image)->columns;
8959       windows->image.window_changes.height=(int) (*image)->rows;
8960       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8961       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8962       break;
8963     }
8964     case CommentCommand:
8965     {
8966       const char
8967         *value;
8968
8969       FILE
8970         *file;
8971
8972       int
8973         unique_file;
8974
8975       /*
8976         Edit image comment.
8977       */
8978       unique_file=AcquireUniqueFileResource(image_info->filename);
8979       if (unique_file == -1)
8980         XNoticeWidget(display,windows,"Unable to edit image comment",
8981           image_info->filename);
8982       value=GetImageProperty(*image,"comment",exception);
8983       if (value == (char *) NULL)
8984         unique_file=close(unique_file)-1;
8985       else
8986         {
8987           register const char
8988             *p;
8989
8990           file=fdopen(unique_file,"w");
8991           if (file == (FILE *) NULL)
8992             {
8993               XNoticeWidget(display,windows,"Unable to edit image comment",
8994                 image_info->filename);
8995               break;
8996             }
8997           for (p=value; *p != '\0'; p++)
8998             (void) fputc((int) *p,file);
8999           (void) fputc('\n',file);
9000           (void) fclose(file);
9001         }
9002       XSetCursorState(display,windows,MagickTrue);
9003       XCheckRefreshWindows(display,windows);
9004       status=InvokeDelegate(image_info,*image,"edit",(char *) NULL,
9005         exception);
9006       if (status == MagickFalse)
9007         XNoticeWidget(display,windows,"Unable to edit image comment",
9008           (char *) NULL);
9009       else
9010         {
9011           char
9012             *comment;
9013
9014           comment=FileToString(image_info->filename,~0UL,exception);
9015           if (comment != (char *) NULL)
9016             {
9017               (void) SetImageProperty(*image,"comment",comment,exception);
9018               (*image)->taint=MagickTrue;
9019             }
9020         }
9021       (void) RelinquishUniqueFileResource(image_info->filename);
9022       XSetCursorState(display,windows,MagickFalse);
9023       break;
9024     }
9025     case LaunchCommand:
9026     {
9027       /*
9028         Launch program.
9029       */
9030       XSetCursorState(display,windows,MagickTrue);
9031       XCheckRefreshWindows(display,windows);
9032       (void) AcquireUniqueFilename(filename);
9033       (void) FormatLocaleString((*image)->filename,MaxTextExtent,"launch:%s",
9034         filename);
9035       status=WriteImage(image_info,*image,exception);
9036       if (status == MagickFalse)
9037         XNoticeWidget(display,windows,"Unable to launch image editor",
9038           (char *) NULL);
9039       else
9040         {
9041           nexus=ReadImage(resource_info->image_info,exception);
9042           CatchException(exception);
9043           XClientMessage(display,windows->image.id,windows->im_protocols,
9044             windows->im_next_image,CurrentTime);
9045         }
9046       (void) RelinquishUniqueFileResource(filename);
9047       XSetCursorState(display,windows,MagickFalse);
9048       break;
9049     }
9050     case RegionofInterestCommand:
9051     {
9052       /*
9053         Apply an image processing technique to a region of interest.
9054       */
9055       (void) XROIImage(display,resource_info,windows,image,exception);
9056       break;
9057     }
9058     case InfoCommand:
9059       break;
9060     case ZoomCommand:
9061     {
9062       /*
9063         Zoom image.
9064       */
9065       if (windows->magnify.mapped != MagickFalse)
9066         (void) XRaiseWindow(display,windows->magnify.id);
9067       else
9068         {
9069           /*
9070             Make magnify image.
9071           */
9072           XSetCursorState(display,windows,MagickTrue);
9073           (void) XMapRaised(display,windows->magnify.id);
9074           XSetCursorState(display,windows,MagickFalse);
9075         }
9076       break;
9077     }
9078     case ShowPreviewCommand:
9079     {
9080       char
9081         **previews;
9082
9083       Image
9084         *preview_image;
9085
9086       static char
9087         preview_type[MaxTextExtent] = "Gamma";
9088
9089       /*
9090         Select preview type from menu.
9091       */
9092       previews=GetCommandOptions(MagickPreviewOptions);
9093       if (previews == (char **) NULL)
9094         break;
9095       XListBrowserWidget(display,windows,&windows->widget,
9096         (const char **) previews,"Preview",
9097         "Select an enhancement, effect, or F/X:",preview_type);
9098       previews=DestroyStringList(previews);
9099       if (*preview_type == '\0')
9100         break;
9101       /*
9102         Show image preview.
9103       */
9104       XSetCursorState(display,windows,MagickTrue);
9105       XCheckRefreshWindows(display,windows);
9106       image_info->preview_type=(PreviewType)
9107         ParseCommandOption(MagickPreviewOptions,MagickFalse,preview_type);
9108       image_info->group=(ssize_t) windows->image.id;
9109       (void) DeleteImageProperty(*image,"label");
9110       (void) SetImageProperty(*image,"label","Preview",exception);
9111       (void) AcquireUniqueFilename(filename);
9112       (void) FormatLocaleString((*image)->filename,MaxTextExtent,"preview:%s",
9113         filename);
9114       status=WriteImage(image_info,*image,exception);
9115       (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9116       preview_image=ReadImage(image_info,exception);
9117       (void) RelinquishUniqueFileResource(filename);
9118       if (preview_image == (Image *) NULL)
9119         break;
9120       (void) FormatLocaleString(preview_image->filename,MaxTextExtent,"show:%s",
9121         filename);
9122       status=WriteImage(image_info,preview_image,exception);
9123       preview_image=DestroyImage(preview_image);
9124       if (status == MagickFalse)
9125         XNoticeWidget(display,windows,"Unable to show image preview",
9126           (*image)->filename);
9127       XDelay(display,1500);
9128       XSetCursorState(display,windows,MagickFalse);
9129       break;
9130     }
9131     case ShowHistogramCommand:
9132     {
9133       Image
9134         *histogram_image;
9135
9136       /*
9137         Show image histogram.
9138       */
9139       XSetCursorState(display,windows,MagickTrue);
9140       XCheckRefreshWindows(display,windows);
9141       image_info->group=(ssize_t) windows->image.id;
9142       (void) DeleteImageProperty(*image,"label");
9143       (void) SetImageProperty(*image,"label","Histogram",exception);
9144       (void) AcquireUniqueFilename(filename);
9145       (void) FormatLocaleString((*image)->filename,MaxTextExtent,"histogram:%s",
9146         filename);
9147       status=WriteImage(image_info,*image,exception);
9148       (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9149       histogram_image=ReadImage(image_info,exception);
9150       (void) RelinquishUniqueFileResource(filename);
9151       if (histogram_image == (Image *) NULL)
9152         break;
9153       (void) FormatLocaleString(histogram_image->filename,MaxTextExtent,
9154         "show:%s",filename);
9155       status=WriteImage(image_info,histogram_image,exception);
9156       histogram_image=DestroyImage(histogram_image);
9157       if (status == MagickFalse)
9158         XNoticeWidget(display,windows,"Unable to show histogram",
9159           (*image)->filename);
9160       XDelay(display,1500);
9161       XSetCursorState(display,windows,MagickFalse);
9162       break;
9163     }
9164     case ShowMatteCommand:
9165     {
9166       Image
9167         *matte_image;
9168
9169       if ((*image)->matte == MagickFalse)
9170         {
9171           XNoticeWidget(display,windows,
9172             "Image does not have any matte information",(*image)->filename);
9173           break;
9174         }
9175       /*
9176         Show image matte.
9177       */
9178       XSetCursorState(display,windows,MagickTrue);
9179       XCheckRefreshWindows(display,windows);
9180       image_info->group=(ssize_t) windows->image.id;
9181       (void) DeleteImageProperty(*image,"label");
9182       (void) SetImageProperty(*image,"label","Matte",exception);
9183       (void) AcquireUniqueFilename(filename);
9184       (void) FormatLocaleString((*image)->filename,MaxTextExtent,"matte:%s",
9185         filename);
9186       status=WriteImage(image_info,*image,exception);
9187       (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9188       matte_image=ReadImage(image_info,exception);
9189       (void) RelinquishUniqueFileResource(filename);
9190       if (matte_image == (Image *) NULL)
9191         break;
9192       (void) FormatLocaleString(matte_image->filename,MaxTextExtent,"show:%s",
9193         filename);
9194       status=WriteImage(image_info,matte_image,exception);
9195       matte_image=DestroyImage(matte_image);
9196       if (status == MagickFalse)
9197         XNoticeWidget(display,windows,"Unable to show matte",
9198           (*image)->filename);
9199       XDelay(display,1500);
9200       XSetCursorState(display,windows,MagickFalse);
9201       break;
9202     }
9203     case BackgroundCommand:
9204     {
9205       /*
9206         Background image.
9207       */
9208       status=XBackgroundImage(display,resource_info,windows,image,exception);
9209       if (status == MagickFalse)
9210         break;
9211       nexus=CloneImage(*image,0,0,MagickTrue,exception);
9212       if (nexus != (Image *) NULL)
9213         XClientMessage(display,windows->image.id,windows->im_protocols,
9214           windows->im_next_image,CurrentTime);
9215       break;
9216     }
9217     case SlideShowCommand:
9218     {
9219       static char
9220         delay[MaxTextExtent] = "5";
9221
9222       /*
9223         Display next image after pausing.
9224       */
9225       (void) XDialogWidget(display,windows,"Slide Show",
9226         "Pause how many 1/100ths of a second between images:",delay);
9227       if (*delay == '\0')
9228         break;
9229       resource_info->delay=StringToUnsignedLong(delay);
9230       XClientMessage(display,windows->image.id,windows->im_protocols,
9231         windows->im_next_image,CurrentTime);
9232       break;
9233     }
9234     case PreferencesCommand:
9235     {
9236       /*
9237         Set user preferences.
9238       */
9239       status=XPreferencesWidget(display,resource_info,windows);
9240       if (status == MagickFalse)
9241         break;
9242       nexus=CloneImage(*image,0,0,MagickTrue,exception);
9243       if (nexus != (Image *) NULL)
9244         XClientMessage(display,windows->image.id,windows->im_protocols,
9245           windows->im_next_image,CurrentTime);
9246       break;
9247     }
9248     case HelpCommand:
9249     {
9250       /*
9251         User requested help.
9252       */
9253       XTextViewWidget(display,resource_info,windows,MagickFalse,
9254         "Help Viewer - Display",DisplayHelp);
9255       break;
9256     }
9257     case BrowseDocumentationCommand:
9258     {
9259       Atom
9260         mozilla_atom;
9261
9262       Window
9263         mozilla_window,
9264         root_window;
9265
9266       /*
9267         Browse the ImageMagick documentation.
9268       */
9269       root_window=XRootWindow(display,XDefaultScreen(display));
9270       mozilla_atom=XInternAtom(display,"_MOZILLA_VERSION",MagickFalse);
9271       mozilla_window=XWindowByProperty(display,root_window,mozilla_atom);
9272       if (mozilla_window != (Window) NULL)
9273         {
9274           char
9275             command[MaxTextExtent],
9276             *url;
9277
9278           /*
9279             Display documentation using Netscape remote control.
9280           */
9281           url=GetMagickHomeURL();
9282           (void) FormatLocaleString(command,MaxTextExtent,
9283             "openurl(%s,new-tab)",url);
9284           url=DestroyString(url);
9285           mozilla_atom=XInternAtom(display,"_MOZILLA_COMMAND",MagickFalse);
9286           (void) XChangeProperty(display,mozilla_window,mozilla_atom,XA_STRING,
9287             8,PropModeReplace,(unsigned char *) command,(int) strlen(command));
9288           XSetCursorState(display,windows,MagickFalse);
9289           break;
9290         }
9291       XSetCursorState(display,windows,MagickTrue);
9292       XCheckRefreshWindows(display,windows);
9293       status=InvokeDelegate(image_info,*image,"browse",(char *) NULL,
9294         exception);
9295       if (status == MagickFalse)
9296         XNoticeWidget(display,windows,"Unable to browse documentation",
9297           (char *) NULL);
9298       XDelay(display,1500);
9299       XSetCursorState(display,windows,MagickFalse);
9300       break;
9301     }
9302     case VersionCommand:
9303     {
9304       XNoticeWidget(display,windows,GetMagickVersion((size_t *) NULL),
9305         GetMagickCopyright());
9306       break;
9307     }
9308     case SaveToUndoBufferCommand:
9309       break;
9310     default:
9311     {
9312       (void) XBell(display,0);
9313       break;
9314     }
9315   }
9316   image_info=DestroyImageInfo(image_info);
9317   return(nexus);
9318 }
9319 \f
9320 /*
9321 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9322 %                                                                             %
9323 %                                                                             %
9324 %                                                                             %
9325 +   X M a g n i f y I m a g e                                                 %
9326 %                                                                             %
9327 %                                                                             %
9328 %                                                                             %
9329 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9330 %
9331 %  XMagnifyImage() magnifies portions of the image as indicated by the pointer.
9332 %  The magnified portion is displayed in a separate window.
9333 %
9334 %  The format of the XMagnifyImage method is:
9335 %
9336 %      void XMagnifyImage(Display *display,XWindows *windows,XEvent *event,
9337 %        ExceptionInfo *exception)
9338 %
9339 %  A description of each parameter follows:
9340 %
9341 %    o display: Specifies a connection to an X server;  returned from
9342 %      XOpenDisplay.
9343 %
9344 %    o windows: Specifies a pointer to a XWindows structure.
9345 %
9346 %    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
9347 %      the entire image is refreshed.
9348 %
9349 %    o exception: return any errors or warnings in this structure.
9350 %
9351 */
9352 static void XMagnifyImage(Display *display,XWindows *windows,XEvent *event,
9353   ExceptionInfo *exception)
9354 {
9355   char
9356     text[MaxTextExtent];
9357
9358   register int
9359     x,
9360     y;
9361
9362   size_t
9363     state;
9364
9365   /*
9366     Update magnified image until the mouse button is released.
9367   */
9368   (void) XCheckDefineCursor(display,windows->image.id,windows->magnify.cursor);
9369   state=DefaultState;
9370   x=event->xbutton.x;
9371   y=event->xbutton.y;
9372   windows->magnify.x=(int) windows->image.x+x;
9373   windows->magnify.y=(int) windows->image.y+y;
9374   do
9375   {
9376     /*
9377       Map and unmap Info widget as text cursor crosses its boundaries.
9378     */
9379     if (windows->info.mapped != MagickFalse)
9380       {
9381         if ((x < (int) (windows->info.x+windows->info.width)) &&
9382             (y < (int) (windows->info.y+windows->info.height)))
9383           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
9384       }
9385     else
9386       if ((x > (int) (windows->info.x+windows->info.width)) ||
9387           (y > (int) (windows->info.y+windows->info.height)))
9388         (void) XMapWindow(display,windows->info.id);
9389     if (windows->info.mapped != MagickFalse)
9390       {
9391         /*
9392           Display pointer position.
9393         */
9394         (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
9395           windows->magnify.x,windows->magnify.y);
9396         XInfoWidget(display,windows,text);
9397       }
9398     /*
9399       Wait for next event.
9400     */
9401     XScreenEvent(display,windows,event,exception);
9402     switch (event->type)
9403     {
9404       case ButtonPress:
9405         break;
9406       case ButtonRelease:
9407       {
9408         /*
9409           User has finished magnifying image.
9410         */
9411         x=event->xbutton.x;
9412         y=event->xbutton.y;
9413         state|=ExitState;
9414         break;
9415       }
9416       case Expose:
9417         break;
9418       case MotionNotify:
9419       {
9420         x=event->xmotion.x;
9421         y=event->xmotion.y;
9422         break;
9423       }
9424       default:
9425         break;
9426     }
9427     /*
9428       Check boundary conditions.
9429     */
9430     if (x < 0)
9431       x=0;
9432     else
9433       if (x >= (int) windows->image.width)
9434         x=(int) windows->image.width-1;
9435     if (y < 0)
9436       y=0;
9437     else
9438      if (y >= (int) windows->image.height)
9439        y=(int) windows->image.height-1;
9440   } while ((state & ExitState) == 0);
9441   /*
9442     Display magnified image.
9443   */
9444   XSetCursorState(display,windows,MagickFalse);
9445 }
9446 \f
9447 /*
9448 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9449 %                                                                             %
9450 %                                                                             %
9451 %                                                                             %
9452 +   X M a g n i f y W i n d o w C o m m a n d                                 %
9453 %                                                                             %
9454 %                                                                             %
9455 %                                                                             %
9456 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9457 %
9458 %  XMagnifyWindowCommand() moves the image within an Magnify window by one
9459 %  pixel as specified by the key symbol.
9460 %
9461 %  The format of the XMagnifyWindowCommand method is:
9462 %
9463 %      void XMagnifyWindowCommand(Display *display,XWindows *windows,
9464 %        const MagickStatusType state,const KeySym key_symbol,
9465 %        ExceptionInfo *exception)
9466 %
9467 %  A description of each parameter follows:
9468 %
9469 %    o display: Specifies a connection to an X server; returned from
9470 %      XOpenDisplay.
9471 %
9472 %    o windows: Specifies a pointer to a XWindows structure.
9473 %
9474 %    o state: key mask.
9475 %
9476 %    o key_symbol: Specifies a KeySym which indicates which side of the image
9477 %      to trim.
9478 %
9479 %    o exception: return any errors or warnings in this structure.
9480 %
9481 */
9482 static void XMagnifyWindowCommand(Display *display,XWindows *windows,
9483   const MagickStatusType state,const KeySym key_symbol,ExceptionInfo *exception)
9484 {
9485   unsigned int
9486     quantum;
9487
9488   /*
9489     User specified a magnify factor or position.
9490   */
9491   quantum=1;
9492   if ((state & Mod1Mask) != 0)
9493     quantum=10;
9494   switch ((int) key_symbol)
9495   {
9496     case QuitCommand:
9497     {
9498       (void) XWithdrawWindow(display,windows->magnify.id,
9499         windows->magnify.screen);
9500       break;
9501     }
9502     case XK_Home:
9503     case XK_KP_Home:
9504     {
9505       windows->magnify.x=(int) windows->image.width/2;
9506       windows->magnify.y=(int) windows->image.height/2;
9507       break;
9508     }
9509     case XK_Left:
9510     case XK_KP_Left:
9511     {
9512       if (windows->magnify.x > 0)
9513         windows->magnify.x-=quantum;
9514       break;
9515     }
9516     case XK_Up:
9517     case XK_KP_Up:
9518     {
9519       if (windows->magnify.y > 0)
9520         windows->magnify.y-=quantum;
9521       break;
9522     }
9523     case XK_Right:
9524     case XK_KP_Right:
9525     {
9526       if (windows->magnify.x < (int) (windows->image.ximage->width-1))
9527         windows->magnify.x+=quantum;
9528       break;
9529     }
9530     case XK_Down:
9531     case XK_KP_Down:
9532     {
9533       if (windows->magnify.y < (int) (windows->image.ximage->height-1))
9534         windows->magnify.y+=quantum;
9535       break;
9536     }
9537     case XK_0:
9538     case XK_1:
9539     case XK_2:
9540     case XK_3:
9541     case XK_4:
9542     case XK_5:
9543     case XK_6:
9544     case XK_7:
9545     case XK_8:
9546     case XK_9:
9547     {
9548       windows->magnify.data=(key_symbol-XK_0);
9549       break;
9550     }
9551     case XK_KP_0:
9552     case XK_KP_1:
9553     case XK_KP_2:
9554     case XK_KP_3:
9555     case XK_KP_4:
9556     case XK_KP_5:
9557     case XK_KP_6:
9558     case XK_KP_7:
9559     case XK_KP_8:
9560     case XK_KP_9:
9561     {
9562       windows->magnify.data=(key_symbol-XK_KP_0);
9563       break;
9564     }
9565     default:
9566       break;
9567   }
9568   XMakeMagnifyImage(display,windows,exception);
9569 }
9570 \f
9571 /*
9572 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9573 %                                                                             %
9574 %                                                                             %
9575 %                                                                             %
9576 +   X M a k e P a n I m a g e                                                 %
9577 %                                                                             %
9578 %                                                                             %
9579 %                                                                             %
9580 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9581 %
9582 %  XMakePanImage() creates a thumbnail of the image and displays it in the Pan
9583 %  icon window.
9584 %
9585 %  The format of the XMakePanImage method is:
9586 %
9587 %        void XMakePanImage(Display *display,XResourceInfo *resource_info,
9588 %          XWindows *windows,Image *image,ExceptionInfo *exception)
9589 %
9590 %  A description of each parameter follows:
9591 %
9592 %    o display: Specifies a connection to an X server;  returned from
9593 %      XOpenDisplay.
9594 %
9595 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9596 %
9597 %    o windows: Specifies a pointer to a XWindows structure.
9598 %
9599 %    o image: the image.
9600 %
9601 %    o exception: return any errors or warnings in this structure.
9602 %
9603 */
9604 static void XMakePanImage(Display *display,XResourceInfo *resource_info,
9605   XWindows *windows,Image *image,ExceptionInfo *exception)
9606 {
9607   MagickStatusType
9608     status;
9609
9610   /*
9611     Create and display image for panning icon.
9612   */
9613   XSetCursorState(display,windows,MagickTrue);
9614   XCheckRefreshWindows(display,windows);
9615   windows->pan.x=(int) windows->image.x;
9616   windows->pan.y=(int) windows->image.y;
9617   status=XMakeImage(display,resource_info,&windows->pan,image,
9618     windows->pan.width,windows->pan.height,exception);
9619   if (status == MagickFalse)
9620     ThrowXWindowFatalException(ResourceLimitError,
9621      "MemoryAllocationFailed",image->filename);
9622   (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
9623     windows->pan.pixmap);
9624   (void) XClearWindow(display,windows->pan.id);
9625   XDrawPanRectangle(display,windows);
9626   XSetCursorState(display,windows,MagickFalse);
9627 }
9628 \f
9629 /*
9630 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9631 %                                                                             %
9632 %                                                                             %
9633 %                                                                             %
9634 +   X M a t t a E d i t I m a g e                                             %
9635 %                                                                             %
9636 %                                                                             %
9637 %                                                                             %
9638 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9639 %
9640 %  XMatteEditImage() allows the user to interactively change the Matte channel
9641 %  of an image.  If the image is PseudoClass it is promoted to DirectClass
9642 %  before the matte information is stored.
9643 %
9644 %  The format of the XMatteEditImage method is:
9645 %
9646 %      MagickBooleanType XMatteEditImage(Display *display,
9647 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
9648 %        ExceptionInfo *exception)
9649 %
9650 %  A description of each parameter follows:
9651 %
9652 %    o display: Specifies a connection to an X server;  returned from
9653 %      XOpenDisplay.
9654 %
9655 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9656 %
9657 %    o windows: Specifies a pointer to a XWindows structure.
9658 %
9659 %    o image: the image; returned from ReadImage.
9660 %
9661 %    o exception: return any errors or warnings in this structure.
9662 %
9663 */
9664 static MagickBooleanType XMatteEditImage(Display *display,
9665   XResourceInfo *resource_info,XWindows *windows,Image **image,
9666   ExceptionInfo *exception)
9667 {
9668   static char
9669     matte[MaxTextExtent] = "0";
9670
9671   static const char
9672     *MatteEditMenu[] =
9673     {
9674       "Method",
9675       "Border Color",
9676       "Fuzz",
9677       "Matte Value",
9678       "Undo",
9679       "Help",
9680       "Dismiss",
9681       (char *) NULL
9682     };
9683
9684   static const ModeType
9685     MatteEditCommands[] =
9686     {
9687       MatteEditMethod,
9688       MatteEditBorderCommand,
9689       MatteEditFuzzCommand,
9690       MatteEditValueCommand,
9691       MatteEditUndoCommand,
9692       MatteEditHelpCommand,
9693       MatteEditDismissCommand
9694     };
9695
9696   static PaintMethod
9697     method = PointMethod;
9698
9699   static XColor
9700     border_color = { 0, 0, 0, 0, 0, 0 };
9701
9702   char
9703     command[MaxTextExtent],
9704     text[MaxTextExtent];
9705
9706   Cursor
9707     cursor;
9708
9709   int
9710     entry,
9711     id,
9712     x,
9713     x_offset,
9714     y,
9715     y_offset;
9716
9717   register int
9718     i;
9719
9720   register Quantum
9721     *q;
9722
9723   unsigned int
9724     height,
9725     width;
9726
9727   size_t
9728     state;
9729
9730   XEvent
9731     event;
9732
9733   /*
9734     Map Command widget.
9735   */
9736   (void) CloneString(&windows->command.name,"Matte Edit");
9737   windows->command.data=4;
9738   (void) XCommandWidget(display,windows,MatteEditMenu,(XEvent *) NULL);
9739   (void) XMapRaised(display,windows->command.id);
9740   XClientMessage(display,windows->image.id,windows->im_protocols,
9741     windows->im_update_widget,CurrentTime);
9742   /*
9743     Make cursor.
9744   */
9745   cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
9746     resource_info->background_color,resource_info->foreground_color);
9747   (void) XCheckDefineCursor(display,windows->image.id,cursor);
9748   /*
9749     Track pointer until button 1 is pressed.
9750   */
9751   XQueryPosition(display,windows->image.id,&x,&y);
9752   (void) XSelectInput(display,windows->image.id,
9753     windows->image.attributes.event_mask | PointerMotionMask);
9754   state=DefaultState;
9755   do
9756   {
9757     if (windows->info.mapped != MagickFalse)
9758       {
9759         /*
9760           Display pointer position.
9761         */
9762         (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
9763           x+windows->image.x,y+windows->image.y);
9764         XInfoWidget(display,windows,text);
9765       }
9766     /*
9767       Wait for next event.
9768     */
9769     XScreenEvent(display,windows,&event,exception);
9770     if (event.xany.window == windows->command.id)
9771       {
9772         /*
9773           Select a command from the Command widget.
9774         */
9775         id=XCommandWidget(display,windows,MatteEditMenu,&event);
9776         if (id < 0)
9777           {
9778             (void) XCheckDefineCursor(display,windows->image.id,cursor);
9779             continue;
9780           }
9781         switch (MatteEditCommands[id])
9782         {
9783           case MatteEditMethod:
9784           {
9785             char
9786               **methods;
9787
9788             /*
9789               Select a method from the pop-up menu.
9790             */
9791             methods=GetCommandOptions(MagickMethodOptions);
9792             if (methods == (char **) NULL)
9793               break;
9794             entry=XMenuWidget(display,windows,MatteEditMenu[id],
9795               (const char **) methods,command);
9796             if (entry >= 0)
9797               method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
9798                 MagickFalse,methods[entry]);
9799             methods=DestroyStringList(methods);
9800             break;
9801           }
9802           case MatteEditBorderCommand:
9803           {
9804             const char
9805               *ColorMenu[MaxNumberPens];
9806
9807             int
9808               pen_number;
9809
9810             /*
9811               Initialize menu selections.
9812             */
9813             for (i=0; i < (int) (MaxNumberPens-2); i++)
9814               ColorMenu[i]=resource_info->pen_colors[i];
9815             ColorMenu[MaxNumberPens-2]="Browser...";
9816             ColorMenu[MaxNumberPens-1]=(const char *) NULL;
9817             /*
9818               Select a pen color from the pop-up menu.
9819             */
9820             pen_number=XMenuWidget(display,windows,MatteEditMenu[id],
9821               (const char **) ColorMenu,command);
9822             if (pen_number < 0)
9823               break;
9824             if (pen_number == (MaxNumberPens-2))
9825               {
9826                 static char
9827                   color_name[MaxTextExtent] = "gray";
9828
9829                 /*
9830                   Select a pen color from a dialog.
9831                 */
9832                 resource_info->pen_colors[pen_number]=color_name;
9833                 XColorBrowserWidget(display,windows,"Select",color_name);
9834                 if (*color_name == '\0')
9835                   break;
9836               }
9837             /*
9838               Set border color.
9839             */
9840             (void) XParseColor(display,windows->map_info->colormap,
9841               resource_info->pen_colors[pen_number],&border_color);
9842             break;
9843           }
9844           case MatteEditFuzzCommand:
9845           {
9846             static char
9847               fuzz[MaxTextExtent];
9848
9849             static const char
9850               *FuzzMenu[] =
9851               {
9852                 "0%",
9853                 "2%",
9854                 "5%",
9855                 "10%",
9856                 "15%",
9857                 "Dialog...",
9858                 (char *) NULL,
9859               };
9860
9861             /*
9862               Select a command from the pop-up menu.
9863             */
9864             entry=XMenuWidget(display,windows,MatteEditMenu[id],FuzzMenu,
9865               command);
9866             if (entry < 0)
9867               break;
9868             if (entry != 5)
9869               {
9870                 (*image)->fuzz=StringToDoubleInterval(FuzzMenu[entry],(double)
9871                   QuantumRange+1.0);
9872                 break;
9873               }
9874             (void) CopyMagickString(fuzz,"20%",MaxTextExtent);
9875             (void) XDialogWidget(display,windows,"Ok",
9876               "Enter fuzz factor (0.0 - 99.9%):",fuzz);
9877             if (*fuzz == '\0')
9878               break;
9879             (void) ConcatenateMagickString(fuzz,"%",MaxTextExtent);
9880             (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+
9881               1.0);
9882             break;
9883           }
9884           case MatteEditValueCommand:
9885           {
9886             static char
9887               message[MaxTextExtent];
9888
9889             static const char
9890               *MatteMenu[] =
9891               {
9892                 "Opaque",
9893                 "Transparent",
9894                 "Dialog...",
9895                 (char *) NULL,
9896               };
9897
9898             /*
9899               Select a command from the pop-up menu.
9900             */
9901             entry=XMenuWidget(display,windows,MatteEditMenu[id],MatteMenu,
9902               command);
9903             if (entry < 0)
9904               break;
9905             if (entry != 2)
9906               {
9907                 (void) FormatLocaleString(matte,MaxTextExtent,QuantumFormat,
9908                   OpaqueAlpha);
9909                 if (LocaleCompare(MatteMenu[entry],"Transparent") == 0)
9910                   (void) FormatLocaleString(matte,MaxTextExtent,QuantumFormat,
9911                     (Quantum) TransparentAlpha);
9912                 break;
9913               }
9914             (void) FormatLocaleString(message,MaxTextExtent,
9915               "Enter matte value (0 - " QuantumFormat "):",(Quantum)
9916               QuantumRange);
9917             (void) XDialogWidget(display,windows,"Matte",message,matte);
9918             if (*matte == '\0')
9919               break;
9920             break;
9921           }
9922           case MatteEditUndoCommand:
9923           {
9924             (void) XMagickCommand(display,resource_info,windows,UndoCommand,
9925               image,exception);
9926             break;
9927           }
9928           case MatteEditHelpCommand:
9929           {
9930             XTextViewWidget(display,resource_info,windows,MagickFalse,
9931               "Help Viewer - Matte Edit",ImageMatteEditHelp);
9932             break;
9933           }
9934           case MatteEditDismissCommand:
9935           {
9936             /*
9937               Prematurely exit.
9938             */
9939             state|=EscapeState;
9940             state|=ExitState;
9941             break;
9942           }
9943           default:
9944             break;
9945         }
9946         (void) XCheckDefineCursor(display,windows->image.id,cursor);
9947         continue;
9948       }
9949     switch (event.type)
9950     {
9951       case ButtonPress:
9952       {
9953         if (event.xbutton.button != Button1)
9954           break;
9955         if ((event.xbutton.window != windows->image.id) &&
9956             (event.xbutton.window != windows->magnify.id))
9957           break;
9958         /*
9959           Update matte data.
9960         */
9961         x=event.xbutton.x;
9962         y=event.xbutton.y;
9963         (void) XMagickCommand(display,resource_info,windows,
9964           SaveToUndoBufferCommand,image,exception);
9965         state|=UpdateConfigurationState;
9966         break;
9967       }
9968       case ButtonRelease:
9969       {
9970         if (event.xbutton.button != Button1)
9971           break;
9972         if ((event.xbutton.window != windows->image.id) &&
9973             (event.xbutton.window != windows->magnify.id))
9974           break;
9975         /*
9976           Update colormap information.
9977         */
9978         x=event.xbutton.x;
9979         y=event.xbutton.y;
9980         XConfigureImageColormap(display,resource_info,windows,*image,exception);
9981         (void) XConfigureImage(display,resource_info,windows,*image,exception);
9982         XInfoWidget(display,windows,text);
9983         (void) XCheckDefineCursor(display,windows->image.id,cursor);
9984         state&=(~UpdateConfigurationState);
9985         break;
9986       }
9987       case Expose:
9988         break;
9989       case KeyPress:
9990       {
9991         char
9992           command[MaxTextExtent];
9993
9994         KeySym
9995           key_symbol;
9996
9997         if (event.xkey.window == windows->magnify.id)
9998           {
9999             Window
10000               window;
10001
10002             window=windows->magnify.id;
10003             while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
10004           }
10005         if (event.xkey.window != windows->image.id)
10006           break;
10007         /*
10008           Respond to a user key press.
10009         */
10010         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
10011           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
10012         switch ((int) key_symbol)
10013         {
10014           case XK_Escape:
10015           case XK_F20:
10016           {
10017             /*
10018               Prematurely exit.
10019             */
10020             state|=ExitState;
10021             break;
10022           }
10023           case XK_F1:
10024           case XK_Help:
10025           {
10026             XTextViewWidget(display,resource_info,windows,MagickFalse,
10027               "Help Viewer - Matte Edit",ImageMatteEditHelp);
10028             break;
10029           }
10030           default:
10031           {
10032             (void) XBell(display,0);
10033             break;
10034           }
10035         }
10036         break;
10037       }
10038       case MotionNotify:
10039       {
10040         /*
10041           Map and unmap Info widget as cursor crosses its boundaries.
10042         */
10043         x=event.xmotion.x;
10044         y=event.xmotion.y;
10045         if (windows->info.mapped != MagickFalse)
10046           {
10047             if ((x < (int) (windows->info.x+windows->info.width)) &&
10048                 (y < (int) (windows->info.y+windows->info.height)))
10049               (void) XWithdrawWindow(display,windows->info.id,
10050                 windows->info.screen);
10051           }
10052         else
10053           if ((x > (int) (windows->info.x+windows->info.width)) ||
10054               (y > (int) (windows->info.y+windows->info.height)))
10055             (void) XMapWindow(display,windows->info.id);
10056         break;
10057       }
10058       default:
10059         break;
10060     }
10061     if (event.xany.window == windows->magnify.id)
10062       {
10063         x=windows->magnify.x-windows->image.x;
10064         y=windows->magnify.y-windows->image.y;
10065       }
10066     x_offset=x;
10067     y_offset=y;
10068     if ((state & UpdateConfigurationState) != 0)
10069       {
10070         CacheView
10071           *image_view;
10072
10073         int
10074           x,
10075           y;
10076
10077         /*
10078           Matte edit is relative to image configuration.
10079         */
10080         (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
10081           MagickTrue);
10082         XPutPixel(windows->image.ximage,x_offset,y_offset,
10083           windows->pixel_info->background_color.pixel);
10084         width=(unsigned int) (*image)->columns;
10085         height=(unsigned int) (*image)->rows;
10086         x=0;
10087         y=0;
10088         if (windows->image.crop_geometry != (char *) NULL)
10089           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,
10090             &height);
10091         x_offset=(int) (width*(windows->image.x+x_offset)/
10092           windows->image.ximage->width+x);
10093         y_offset=(int) (height*(windows->image.y+y_offset)/
10094           windows->image.ximage->height+y);
10095         if ((x_offset < 0) || (y_offset < 0))
10096           continue;
10097         if ((x_offset >= (int) (*image)->columns) ||
10098             (y_offset >= (int) (*image)->rows))
10099           continue;
10100         if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
10101           return(MagickFalse);
10102         (*image)->matte=MagickTrue;
10103         image_view=AcquireCacheView(*image);
10104         switch (method)
10105         {
10106           case PointMethod:
10107           default:
10108           {
10109             /*
10110               Update matte information using point algorithm.
10111             */
10112             q=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,
10113               (ssize_t) y_offset,1,1,exception);
10114             if (q == (Quantum *) NULL)
10115               break;
10116             SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10117             (void) SyncCacheViewAuthenticPixels(image_view,exception);
10118             break;
10119           }
10120           case ReplaceMethod:
10121           {
10122             PixelInfo
10123               pixel,
10124               target;
10125
10126             Quantum
10127               virtual_pixel[CompositePixelChannel];
10128
10129             /*
10130               Update matte information using replace algorithm.
10131             */
10132             (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t) x_offset,
10133               (ssize_t) y_offset,virtual_pixel,exception);
10134             target.red=virtual_pixel[RedPixelChannel];
10135             target.green=virtual_pixel[GreenPixelChannel];
10136             target.blue=virtual_pixel[BluePixelChannel];
10137             target.alpha=virtual_pixel[AlphaPixelChannel];
10138             for (y=0; y < (int) (*image)->rows; y++)
10139             {
10140               q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10141                 (*image)->columns,1,exception);
10142               if (q == (Quantum *) NULL)
10143                 break;
10144               for (x=0; x < (int) (*image)->columns; x++)
10145               {
10146                 GetPixelInfoPixel(*image,q,&pixel);
10147                 if (IsFuzzyEquivalencePixelInfo(&pixel,&target))
10148                   SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10149                 q+=GetPixelChannels(*image);
10150               }
10151               if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
10152                 break;
10153             }
10154             break;
10155           }
10156           case FloodfillMethod:
10157           case FillToBorderMethod:
10158           {
10159             ChannelType
10160               channel_mask;
10161
10162             DrawInfo
10163               *draw_info;
10164
10165             PixelInfo
10166               target;
10167
10168             /*
10169               Update matte information using floodfill algorithm.
10170             */
10171             (void) GetOneVirtualMagickPixel(*image,
10172               GetPixelCacheVirtualMethod(*image),(ssize_t) x_offset,(ssize_t)
10173               y_offset,&target,exception);
10174             if (method == FillToBorderMethod)
10175               {
10176                 target.red=(MagickRealType) ScaleShortToQuantum(
10177                   border_color.red);
10178                 target.green=(MagickRealType) ScaleShortToQuantum(
10179                   border_color.green);
10180                 target.blue=(MagickRealType) ScaleShortToQuantum(
10181                   border_color.blue);
10182               }
10183             draw_info=CloneDrawInfo(resource_info->image_info,
10184               (DrawInfo *) NULL);
10185             draw_info->fill.alpha=ClampToQuantum(StringToDouble(matte,
10186               (char **) NULL));
10187             channel_mask=SetPixelChannelMask(*image,AlphaChannel); 
10188             (void) FloodfillPaintImage(*image,draw_info,&target,(ssize_t)
10189               x_offset,(ssize_t) y_offset,method == FloodfillMethod ?
10190               MagickFalse : MagickTrue,exception);
10191             (void) SetPixelChannelMapMask(*image,channel_mask);
10192             draw_info=DestroyDrawInfo(draw_info);
10193             break;
10194           }
10195           case ResetMethod:
10196           {
10197             /*
10198               Update matte information using reset algorithm.
10199             */
10200             if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
10201               return(MagickFalse);
10202             for (y=0; y < (int) (*image)->rows; y++)
10203             {
10204               q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10205                 (*image)->columns,1,exception);
10206               if (q == (Quantum *) NULL)
10207                 break;
10208               for (x=0; x < (int) (*image)->columns; x++)
10209               {
10210                 SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10211                 q+=GetPixelChannels(*image);
10212               }
10213               if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
10214                 break;
10215             }
10216             if (StringToLong(matte) == (long) OpaqueAlpha)
10217               (*image)->matte=MagickFalse;
10218             break;
10219           }
10220         }
10221         image_view=DestroyCacheView(image_view);
10222         state&=(~UpdateConfigurationState);
10223       }
10224   } while ((state & ExitState) == 0);
10225   (void) XSelectInput(display,windows->image.id,
10226     windows->image.attributes.event_mask);
10227   XSetCursorState(display,windows,MagickFalse);
10228   (void) XFreeCursor(display,cursor);
10229   return(MagickTrue);
10230 }
10231 \f
10232 /*
10233 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10234 %                                                                             %
10235 %                                                                             %
10236 %                                                                             %
10237 +   X O p e n I m a g e                                                       %
10238 %                                                                             %
10239 %                                                                             %
10240 %                                                                             %
10241 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10242 %
10243 %  XOpenImage() loads an image from a file.
10244 %
10245 %  The format of the XOpenImage method is:
10246 %
10247 %     Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10248 %       XWindows *windows,const unsigned int command)
10249 %
10250 %  A description of each parameter follows:
10251 %
10252 %    o display: Specifies a connection to an X server; returned from
10253 %      XOpenDisplay.
10254 %
10255 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10256 %
10257 %    o windows: Specifies a pointer to a XWindows structure.
10258 %
10259 %    o command: A value other than zero indicates that the file is selected
10260 %      from the command line argument list.
10261 %
10262 */
10263 static Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10264   XWindows *windows,const MagickBooleanType command)
10265 {
10266   const MagickInfo
10267     *magick_info;
10268
10269   ExceptionInfo
10270     *exception;
10271
10272   Image
10273     *nexus;
10274
10275   ImageInfo
10276     *image_info;
10277
10278   static char
10279     filename[MaxTextExtent] = "\0";
10280
10281   /*
10282     Request file name from user.
10283   */
10284   if (command == MagickFalse)
10285     XFileBrowserWidget(display,windows,"Open",filename);
10286   else
10287     {
10288       char
10289         **filelist,
10290         **files;
10291
10292       int
10293         count,
10294         status;
10295
10296       register int
10297         i,
10298         j;
10299
10300       /*
10301         Select next image from the command line.
10302       */
10303       status=XGetCommand(display,windows->image.id,&files,&count);
10304       if (status == 0)
10305         {
10306           ThrowXWindowFatalException(XServerError,"UnableToGetProperty","...");
10307           return((Image *) NULL);
10308         }
10309       filelist=(char **) AcquireQuantumMemory((size_t) count,sizeof(*filelist));
10310       if (filelist == (char **) NULL)
10311         {
10312           ThrowXWindowFatalException(ResourceLimitError,
10313             "MemoryAllocationFailed","...");
10314           (void) XFreeStringList(files);
10315           return((Image *) NULL);
10316         }
10317       j=0;
10318       for (i=1; i < count; i++)
10319         if (*files[i] != '-')
10320           filelist[j++]=files[i];
10321       filelist[j]=(char *) NULL;
10322       XListBrowserWidget(display,windows,&windows->widget,
10323         (const char **) filelist,"Load","Select Image to Load:",filename);
10324       filelist=(char **) RelinquishMagickMemory(filelist);
10325       (void) XFreeStringList(files);
10326     }
10327   if (*filename == '\0')
10328     return((Image *) NULL);
10329   image_info=CloneImageInfo(resource_info->image_info);
10330   (void) SetImageInfoProgressMonitor(image_info,(MagickProgressMonitor) NULL,
10331     (void *) NULL);
10332   (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
10333   exception=AcquireExceptionInfo();
10334   (void) SetImageInfo(image_info,0,exception);
10335   if (LocaleCompare(image_info->magick,"X") == 0)
10336     {
10337       char
10338         seconds[MaxTextExtent];
10339
10340       /*
10341         User may want to delay the X server screen grab.
10342       */
10343       (void) CopyMagickString(seconds,"0",MaxTextExtent);
10344       (void) XDialogWidget(display,windows,"Grab","Enter any delay in seconds:",
10345         seconds);
10346       if (*seconds == '\0')
10347         return((Image *) NULL);
10348       XDelay(display,(size_t) (1000*StringToLong(seconds)));
10349     }
10350   magick_info=GetMagickInfo(image_info->magick,exception);
10351   if ((magick_info != (const MagickInfo *) NULL) &&
10352       (magick_info->raw != MagickFalse))
10353     {
10354       char
10355         geometry[MaxTextExtent];
10356
10357       /*
10358         Request image size from the user.
10359       */
10360       (void) CopyMagickString(geometry,"512x512",MaxTextExtent);
10361       if (image_info->size != (char *) NULL)
10362         (void) CopyMagickString(geometry,image_info->size,MaxTextExtent);
10363       (void) XDialogWidget(display,windows,"Load","Enter the image geometry:",
10364         geometry);
10365       (void) CloneString(&image_info->size,geometry);
10366     }
10367   /*
10368     Load the image.
10369   */
10370   XSetCursorState(display,windows,MagickTrue);
10371   XCheckRefreshWindows(display,windows);
10372   (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
10373   nexus=ReadImage(image_info,exception);
10374   CatchException(exception);
10375   XSetCursorState(display,windows,MagickFalse);
10376   if (nexus != (Image *) NULL)
10377     XClientMessage(display,windows->image.id,windows->im_protocols,
10378       windows->im_next_image,CurrentTime);
10379   else
10380     {
10381       char
10382         *text,
10383         **textlist;
10384
10385       /*
10386         Unknown image format.
10387       */
10388       text=FileToString(filename,~0,exception);
10389       if (text == (char *) NULL)
10390         return((Image *) NULL);
10391       textlist=StringToList(text);
10392       if (textlist != (char **) NULL)
10393         {
10394           char
10395             title[MaxTextExtent];
10396
10397           register int
10398             i;
10399
10400           (void) FormatLocaleString(title,MaxTextExtent,
10401             "Unknown format: %s",filename);
10402           XTextViewWidget(display,resource_info,windows,MagickTrue,title,
10403             (const char **) textlist);
10404           for (i=0; textlist[i] != (char *) NULL; i++)
10405             textlist[i]=DestroyString(textlist[i]);
10406           textlist=(char **) RelinquishMagickMemory(textlist);
10407         }
10408       text=DestroyString(text);
10409     }
10410   exception=DestroyExceptionInfo(exception);
10411   image_info=DestroyImageInfo(image_info);
10412   return(nexus);
10413 }
10414 \f
10415 /*
10416 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10417 %                                                                             %
10418 %                                                                             %
10419 %                                                                             %
10420 +   X P a n I m a g e                                                         %
10421 %                                                                             %
10422 %                                                                             %
10423 %                                                                             %
10424 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10425 %
10426 %  XPanImage() pans the image until the mouse button is released.
10427 %
10428 %  The format of the XPanImage method is:
10429 %
10430 %      void XPanImage(Display *display,XWindows *windows,XEvent *event,
10431 %        ExceptionInfo *exception)
10432 %
10433 %  A description of each parameter follows:
10434 %
10435 %    o display: Specifies a connection to an X server;  returned from
10436 %      XOpenDisplay.
10437 %
10438 %    o windows: Specifies a pointer to a XWindows structure.
10439 %
10440 %    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
10441 %      the entire image is refreshed.
10442 %
10443 %    o exception: return any errors or warnings in this structure.
10444 %
10445 */
10446 static void XPanImage(Display *display,XWindows *windows,XEvent *event,
10447   ExceptionInfo *exception)
10448 {
10449   char
10450     text[MaxTextExtent];
10451
10452   Cursor
10453     cursor;
10454
10455   MagickRealType
10456     x_factor,
10457     y_factor;
10458
10459   RectangleInfo
10460     pan_info;
10461
10462   size_t
10463     state;
10464
10465   /*
10466     Define cursor.
10467   */
10468   if ((windows->image.ximage->width > (int) windows->image.width) &&
10469       (windows->image.ximage->height > (int) windows->image.height))
10470     cursor=XCreateFontCursor(display,XC_fleur);
10471   else
10472     if (windows->image.ximage->width > (int) windows->image.width)
10473       cursor=XCreateFontCursor(display,XC_sb_h_double_arrow);
10474     else
10475       if (windows->image.ximage->height > (int) windows->image.height)
10476         cursor=XCreateFontCursor(display,XC_sb_v_double_arrow);
10477       else
10478         cursor=XCreateFontCursor(display,XC_arrow);
10479   (void) XCheckDefineCursor(display,windows->pan.id,cursor);
10480   /*
10481     Pan image as pointer moves until the mouse button is released.
10482   */
10483   x_factor=(MagickRealType) windows->image.ximage->width/windows->pan.width;
10484   y_factor=(MagickRealType) windows->image.ximage->height/windows->pan.height;
10485   pan_info.width=windows->pan.width*windows->image.width/
10486     windows->image.ximage->width;
10487   pan_info.height=windows->pan.height*windows->image.height/
10488     windows->image.ximage->height;
10489   pan_info.x=0;
10490   pan_info.y=0;
10491   state=UpdateConfigurationState;
10492   do
10493   {
10494     switch (event->type)
10495     {
10496       case ButtonPress:
10497       {
10498         /*
10499           User choose an initial pan location.
10500         */
10501         pan_info.x=(ssize_t) event->xbutton.x;
10502         pan_info.y=(ssize_t) event->xbutton.y;
10503         state|=UpdateConfigurationState;
10504         break;
10505       }
10506       case ButtonRelease:
10507       {
10508         /*
10509           User has finished panning the image.
10510         */
10511         pan_info.x=(ssize_t) event->xbutton.x;
10512         pan_info.y=(ssize_t) event->xbutton.y;
10513         state|=UpdateConfigurationState | ExitState;
10514         break;
10515       }
10516       case MotionNotify:
10517       {
10518         pan_info.x=(ssize_t) event->xmotion.x;
10519         pan_info.y=(ssize_t) event->xmotion.y;
10520         state|=UpdateConfigurationState;
10521       }
10522       default:
10523         break;
10524     }
10525     if ((state & UpdateConfigurationState) != 0)
10526       {
10527         /*
10528           Check boundary conditions.
10529         */
10530         if (pan_info.x < (ssize_t) (pan_info.width/2))
10531           pan_info.x=0;
10532         else
10533           pan_info.x=(ssize_t) (x_factor*(pan_info.x-(pan_info.width/2)));
10534         if (pan_info.x < 0)
10535           pan_info.x=0;
10536         else
10537           if ((int) (pan_info.x+windows->image.width) >
10538               windows->image.ximage->width)
10539             pan_info.x=(ssize_t)
10540               (windows->image.ximage->width-windows->image.width);
10541         if (pan_info.y < (ssize_t) (pan_info.height/2))
10542           pan_info.y=0;
10543         else
10544           pan_info.y=(ssize_t) (y_factor*(pan_info.y-(pan_info.height/2)));
10545         if (pan_info.y < 0)
10546           pan_info.y=0;
10547         else
10548           if ((int) (pan_info.y+windows->image.height) >
10549               windows->image.ximage->height)
10550             pan_info.y=(ssize_t)
10551               (windows->image.ximage->height-windows->image.height);
10552         if ((windows->image.x != (int) pan_info.x) ||
10553             (windows->image.y != (int) pan_info.y))
10554           {
10555             /*
10556               Display image pan offset.
10557             */
10558             windows->image.x=(int) pan_info.x;
10559             windows->image.y=(int) pan_info.y;
10560             (void) FormatLocaleString(text,MaxTextExtent," %ux%u%+d%+d ",
10561               windows->image.width,windows->image.height,windows->image.x,
10562               windows->image.y);
10563             XInfoWidget(display,windows,text);
10564             /*
10565               Refresh Image window.
10566             */
10567             XDrawPanRectangle(display,windows);
10568             XRefreshWindow(display,&windows->image,(XEvent *) NULL);
10569           }
10570         state&=(~UpdateConfigurationState);
10571       }
10572     /*
10573       Wait for next event.
10574     */
10575     if ((state & ExitState) == 0)
10576       XScreenEvent(display,windows,event,exception);
10577   } while ((state & ExitState) == 0);
10578   /*
10579     Restore cursor.
10580   */
10581   (void) XCheckDefineCursor(display,windows->pan.id,windows->pan.cursor);
10582   (void) XFreeCursor(display,cursor);
10583   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
10584 }
10585 \f
10586 /*
10587 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10588 %                                                                             %
10589 %                                                                             %
10590 %                                                                             %
10591 +   X P a s t e I m a g e                                                     %
10592 %                                                                             %
10593 %                                                                             %
10594 %                                                                             %
10595 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10596 %
10597 %  XPasteImage() pastes an image previously saved with XCropImage in the X
10598 %  window image at a location the user chooses with the pointer.
10599 %
10600 %  The format of the XPasteImage method is:
10601 %
10602 %      MagickBooleanType XPasteImage(Display *display,
10603 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
10604 %        ExceptionInfo *exception)
10605 %
10606 %  A description of each parameter follows:
10607 %
10608 %    o display: Specifies a connection to an X server;  returned from
10609 %      XOpenDisplay.
10610 %
10611 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10612 %
10613 %    o windows: Specifies a pointer to a XWindows structure.
10614 %
10615 %    o image: the image; returned from ReadImage.
10616 %
10617 %    o exception: return any errors or warnings in this structure.
10618 %
10619 */
10620 static MagickBooleanType XPasteImage(Display *display,
10621   XResourceInfo *resource_info,XWindows *windows,Image *image,
10622   ExceptionInfo *exception)
10623 {
10624   static const char
10625     *PasteMenu[] =
10626     {
10627       "Operator",
10628       "Help",
10629       "Dismiss",
10630       (char *) NULL
10631     };
10632
10633   static const ModeType
10634     PasteCommands[] =
10635     {
10636       PasteOperatorsCommand,
10637       PasteHelpCommand,
10638       PasteDismissCommand
10639     };
10640
10641   static CompositeOperator
10642     compose = CopyCompositeOp;
10643
10644   char
10645     text[MaxTextExtent];
10646
10647   Cursor
10648     cursor;
10649
10650   Image
10651     *paste_image;
10652
10653   int
10654     entry,
10655     id,
10656     x,
10657     y;
10658
10659   MagickRealType
10660     scale_factor;
10661
10662   RectangleInfo
10663     highlight_info,
10664     paste_info;
10665
10666   unsigned int
10667     height,
10668     width;
10669
10670   size_t
10671     state;
10672
10673   XEvent
10674     event;
10675
10676   /*
10677     Copy image.
10678   */
10679   if (resource_info->copy_image == (Image *) NULL)
10680     return(MagickFalse);
10681   paste_image=CloneImage(resource_info->copy_image,0,0,MagickTrue,exception);
10682   /*
10683     Map Command widget.
10684   */
10685   (void) CloneString(&windows->command.name,"Paste");
10686   windows->command.data=1;
10687   (void) XCommandWidget(display,windows,PasteMenu,(XEvent *) NULL);
10688   (void) XMapRaised(display,windows->command.id);
10689   XClientMessage(display,windows->image.id,windows->im_protocols,
10690     windows->im_update_widget,CurrentTime);
10691   /*
10692     Track pointer until button 1 is pressed.
10693   */
10694   XSetCursorState(display,windows,MagickFalse);
10695   XQueryPosition(display,windows->image.id,&x,&y);
10696   (void) XSelectInput(display,windows->image.id,
10697     windows->image.attributes.event_mask | PointerMotionMask);
10698   paste_info.x=(ssize_t) windows->image.x+x;
10699   paste_info.y=(ssize_t) windows->image.y+y;
10700   paste_info.width=0;
10701   paste_info.height=0;
10702   cursor=XCreateFontCursor(display,XC_ul_angle);
10703   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
10704   state=DefaultState;
10705   do
10706   {
10707     if (windows->info.mapped != MagickFalse)
10708       {
10709         /*
10710           Display pointer position.
10711         */
10712         (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
10713           (long) paste_info.x,(long) paste_info.y);
10714         XInfoWidget(display,windows,text);
10715       }
10716     highlight_info=paste_info;
10717     highlight_info.x=paste_info.x-windows->image.x;
10718     highlight_info.y=paste_info.y-windows->image.y;
10719     XHighlightRectangle(display,windows->image.id,
10720       windows->image.highlight_context,&highlight_info);
10721     /*
10722       Wait for next event.
10723     */
10724     XScreenEvent(display,windows,&event,exception);
10725     XHighlightRectangle(display,windows->image.id,
10726       windows->image.highlight_context,&highlight_info);
10727     if (event.xany.window == windows->command.id)
10728       {
10729         /*
10730           Select a command from the Command widget.
10731         */
10732         id=XCommandWidget(display,windows,PasteMenu,&event);
10733         if (id < 0)
10734           continue;
10735         switch (PasteCommands[id])
10736         {
10737           case PasteOperatorsCommand:
10738           {
10739             char
10740               command[MaxTextExtent],
10741               **operators;
10742
10743             /*
10744               Select a command from the pop-up menu.
10745             */
10746             operators=GetCommandOptions(MagickComposeOptions);
10747             if (operators == (char **) NULL)
10748               break;
10749             entry=XMenuWidget(display,windows,PasteMenu[id],
10750               (const char **) operators,command);
10751             if (entry >= 0)
10752               compose=(CompositeOperator) ParseCommandOption(
10753                 MagickComposeOptions,MagickFalse,operators[entry]);
10754             operators=DestroyStringList(operators);
10755             break;
10756           }
10757           case PasteHelpCommand:
10758           {
10759             XTextViewWidget(display,resource_info,windows,MagickFalse,
10760               "Help Viewer - Image Composite",ImagePasteHelp);
10761             break;
10762           }
10763           case PasteDismissCommand:
10764           {
10765             /*
10766               Prematurely exit.
10767             */
10768             state|=EscapeState;
10769             state|=ExitState;
10770             break;
10771           }
10772           default:
10773             break;
10774         }
10775         continue;
10776       }
10777     switch (event.type)
10778     {
10779       case ButtonPress:
10780       {
10781         if (image->debug != MagickFalse)
10782           (void) LogMagickEvent(X11Event,GetMagickModule(),
10783             "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
10784             event.xbutton.button,event.xbutton.x,event.xbutton.y);
10785         if (event.xbutton.button != Button1)
10786           break;
10787         if (event.xbutton.window != windows->image.id)
10788           break;
10789         /*
10790           Paste rectangle is relative to image configuration.
10791         */
10792         width=(unsigned int) image->columns;
10793         height=(unsigned int) image->rows;
10794         x=0;
10795         y=0;
10796         if (windows->image.crop_geometry != (char *) NULL)
10797           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
10798             &width,&height);
10799         scale_factor=(MagickRealType) windows->image.ximage->width/width;
10800         paste_info.width=(unsigned int) (scale_factor*paste_image->columns+0.5);
10801         scale_factor=(MagickRealType) windows->image.ximage->height/height;
10802         paste_info.height=(unsigned int) (scale_factor*paste_image->rows+0.5);
10803         (void) XCheckDefineCursor(display,windows->image.id,cursor);
10804         paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10805         paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10806         break;
10807       }
10808       case ButtonRelease:
10809       {
10810         if (image->debug != MagickFalse)
10811           (void) LogMagickEvent(X11Event,GetMagickModule(),
10812             "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
10813             event.xbutton.button,event.xbutton.x,event.xbutton.y);
10814         if (event.xbutton.button != Button1)
10815           break;
10816         if (event.xbutton.window != windows->image.id)
10817           break;
10818         if ((paste_info.width != 0) && (paste_info.height != 0))
10819           {
10820             /*
10821               User has selected the location of the paste image.
10822             */
10823             paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10824             paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10825             state|=ExitState;
10826           }
10827         break;
10828       }
10829       case Expose:
10830         break;
10831       case KeyPress:
10832       {
10833         char
10834           command[MaxTextExtent];
10835
10836         KeySym
10837           key_symbol;
10838
10839         int
10840           length;
10841
10842         if (event.xkey.window != windows->image.id)
10843           break;
10844         /*
10845           Respond to a user key press.
10846         */
10847         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
10848           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
10849         *(command+length)='\0';
10850         if (image->debug != MagickFalse)
10851           (void) LogMagickEvent(X11Event,GetMagickModule(),
10852             "Key press: 0x%lx (%s)",(long) key_symbol,command);
10853         switch ((int) key_symbol)
10854         {
10855           case XK_Escape:
10856           case XK_F20:
10857           {
10858             /*
10859               Prematurely exit.
10860             */
10861             paste_image=DestroyImage(paste_image);
10862             state|=EscapeState;
10863             state|=ExitState;
10864             break;
10865           }
10866           case XK_F1:
10867           case XK_Help:
10868           {
10869             (void) XSetFunction(display,windows->image.highlight_context,
10870               GXcopy);
10871             XTextViewWidget(display,resource_info,windows,MagickFalse,
10872               "Help Viewer - Image Composite",ImagePasteHelp);
10873             (void) XSetFunction(display,windows->image.highlight_context,
10874               GXinvert);
10875             break;
10876           }
10877           default:
10878           {
10879             (void) XBell(display,0);
10880             break;
10881           }
10882         }
10883         break;
10884       }
10885       case MotionNotify:
10886       {
10887         /*
10888           Map and unmap Info widget as text cursor crosses its boundaries.
10889         */
10890         x=event.xmotion.x;
10891         y=event.xmotion.y;
10892         if (windows->info.mapped != MagickFalse)
10893           {
10894             if ((x < (int) (windows->info.x+windows->info.width)) &&
10895                 (y < (int) (windows->info.y+windows->info.height)))
10896               (void) XWithdrawWindow(display,windows->info.id,
10897                 windows->info.screen);
10898           }
10899         else
10900           if ((x > (int) (windows->info.x+windows->info.width)) ||
10901               (y > (int) (windows->info.y+windows->info.height)))
10902             (void) XMapWindow(display,windows->info.id);
10903         paste_info.x=(ssize_t) windows->image.x+x;
10904         paste_info.y=(ssize_t) windows->image.y+y;
10905         break;
10906       }
10907       default:
10908       {
10909         if (image->debug != MagickFalse)
10910           (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
10911             event.type);
10912         break;
10913       }
10914     }
10915   } while ((state & ExitState) == 0);
10916   (void) XSelectInput(display,windows->image.id,
10917     windows->image.attributes.event_mask);
10918   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
10919   XSetCursorState(display,windows,MagickFalse);
10920   (void) XFreeCursor(display,cursor);
10921   if ((state & EscapeState) != 0)
10922     return(MagickTrue);
10923   /*
10924     Image pasting is relative to image configuration.
10925   */
10926   XSetCursorState(display,windows,MagickTrue);
10927   XCheckRefreshWindows(display,windows);
10928   width=(unsigned int) image->columns;
10929   height=(unsigned int) image->rows;
10930   x=0;
10931   y=0;
10932   if (windows->image.crop_geometry != (char *) NULL)
10933     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
10934   scale_factor=(MagickRealType) width/windows->image.ximage->width;
10935   paste_info.x+=x;
10936   paste_info.x=(ssize_t) (scale_factor*paste_info.x+0.5);
10937   paste_info.width=(unsigned int) (scale_factor*paste_info.width+0.5);
10938   scale_factor=(MagickRealType) height/windows->image.ximage->height;
10939   paste_info.y+=y;
10940   paste_info.y=(ssize_t) (scale_factor*paste_info.y*scale_factor+0.5);
10941   paste_info.height=(unsigned int) (scale_factor*paste_info.height+0.5);
10942   /*
10943     Paste image with X Image window.
10944   */
10945   (void) CompositeImage(image,compose,paste_image,paste_info.x,paste_info.y,
10946     exception);
10947   paste_image=DestroyImage(paste_image);
10948   XSetCursorState(display,windows,MagickFalse);
10949   /*
10950     Update image colormap.
10951   */
10952   XConfigureImageColormap(display,resource_info,windows,image,exception);
10953   (void) XConfigureImage(display,resource_info,windows,image,exception);
10954   return(MagickTrue);
10955 }
10956 \f
10957 /*
10958 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10959 %                                                                             %
10960 %                                                                             %
10961 %                                                                             %
10962 +   X P r i n t I m a g e                                                     %
10963 %                                                                             %
10964 %                                                                             %
10965 %                                                                             %
10966 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10967 %
10968 %  XPrintImage() prints an image to a Postscript printer.
10969 %
10970 %  The format of the XPrintImage method is:
10971 %
10972 %      MagickBooleanType XPrintImage(Display *display,
10973 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
10974 %        ExceptionInfo *exception)
10975 %
10976 %  A description of each parameter follows:
10977 %
10978 %    o display: Specifies a connection to an X server; returned from
10979 %      XOpenDisplay.
10980 %
10981 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10982 %
10983 %    o windows: Specifies a pointer to a XWindows structure.
10984 %
10985 %    o image: the image.
10986 %
10987 %    o exception: return any errors or warnings in this structure.
10988 %
10989 */
10990 static MagickBooleanType XPrintImage(Display *display,
10991   XResourceInfo *resource_info,XWindows *windows,Image *image,
10992   ExceptionInfo *exception)
10993 {
10994   char
10995     filename[MaxTextExtent],
10996     geometry[MaxTextExtent];
10997
10998   Image
10999     *print_image;
11000
11001   ImageInfo
11002     *image_info;
11003
11004   MagickStatusType
11005     status;
11006
11007   /*
11008     Request Postscript page geometry from user.
11009   */
11010   image_info=CloneImageInfo(resource_info->image_info);
11011   (void) FormatLocaleString(geometry,MaxTextExtent,"Letter");
11012   if (image_info->page != (char *) NULL)
11013     (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
11014   XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
11015     "Select Postscript Page Geometry:",geometry);
11016   if (*geometry == '\0')
11017     return(MagickTrue);
11018   image_info->page=GetPageGeometry(geometry);
11019   /*
11020     Apply image transforms.
11021   */
11022   XSetCursorState(display,windows,MagickTrue);
11023   XCheckRefreshWindows(display,windows);
11024   print_image=CloneImage(image,0,0,MagickTrue,exception);
11025   if (print_image == (Image *) NULL)
11026     return(MagickFalse);
11027   (void) FormatLocaleString(geometry,MaxTextExtent,"%dx%d!",
11028     windows->image.ximage->width,windows->image.ximage->height);
11029   (void) TransformImage(&print_image,windows->image.crop_geometry,geometry,
11030     exception);
11031   /*
11032     Print image.
11033   */
11034   (void) AcquireUniqueFilename(filename);
11035   (void) FormatLocaleString(print_image->filename,MaxTextExtent,"print:%s",
11036     filename);
11037   status=WriteImage(image_info,print_image,exception);
11038   (void) RelinquishUniqueFileResource(filename);
11039   print_image=DestroyImage(print_image);
11040   image_info=DestroyImageInfo(image_info);
11041   XSetCursorState(display,windows,MagickFalse);
11042   return(status != 0 ? MagickTrue : MagickFalse);
11043 }
11044 \f
11045 /*
11046 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11047 %                                                                             %
11048 %                                                                             %
11049 %                                                                             %
11050 +   X R O I I m a g e                                                         %
11051 %                                                                             %
11052 %                                                                             %
11053 %                                                                             %
11054 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11055 %
11056 %  XROIImage() applies an image processing technique to a region of interest.
11057 %
11058 %  The format of the XROIImage method is:
11059 %
11060 %      MagickBooleanType XROIImage(Display *display,
11061 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
11062 %        ExceptionInfo *exception)
11063 %
11064 %  A description of each parameter follows:
11065 %
11066 %    o display: Specifies a connection to an X server; returned from
11067 %      XOpenDisplay.
11068 %
11069 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
11070 %
11071 %    o windows: Specifies a pointer to a XWindows structure.
11072 %
11073 %    o image: the image; returned from ReadImage.
11074 %
11075 %    o exception: return any errors or warnings in this structure.
11076 %
11077 */
11078 static MagickBooleanType XROIImage(Display *display,
11079   XResourceInfo *resource_info,XWindows *windows,Image **image,
11080   ExceptionInfo *exception)
11081 {
11082 #define ApplyMenus  7
11083
11084   static const char
11085     *ROIMenu[] =
11086     {
11087       "Help",
11088       "Dismiss",
11089       (char *) NULL
11090     },
11091     *ApplyMenu[] =
11092     {
11093       "File",
11094       "Edit",
11095       "Transform",
11096       "Enhance",
11097       "Effects",
11098       "F/X",
11099       "Miscellany",
11100       "Help",
11101       "Dismiss",
11102       (char *) NULL
11103     },
11104     *FileMenu[] =
11105     {
11106       "Save...",
11107       "Print...",
11108       (char *) NULL
11109     },
11110     *EditMenu[] =
11111     {
11112       "Undo",
11113       "Redo",
11114       (char *) NULL
11115     },
11116     *TransformMenu[] =
11117     {
11118       "Flop",
11119       "Flip",
11120       "Rotate Right",
11121       "Rotate Left",
11122       (char *) NULL
11123     },
11124     *EnhanceMenu[] =
11125     {
11126       "Hue...",
11127       "Saturation...",
11128       "Brightness...",
11129       "Gamma...",
11130       "Spiff",
11131       "Dull",
11132       "Contrast Stretch...",
11133       "Sigmoidal Contrast...",
11134       "Normalize",
11135       "Equalize",
11136       "Negate",
11137       "Grayscale",
11138       "Map...",
11139       "Quantize...",
11140       (char *) NULL
11141     },
11142     *EffectsMenu[] =
11143     {
11144       "Despeckle",
11145       "Emboss",
11146       "Reduce Noise",
11147       "Add Noise",
11148       "Sharpen...",
11149       "Blur...",
11150       "Threshold...",
11151       "Edge Detect...",
11152       "Spread...",
11153       "Shade...",
11154       "Raise...",
11155       "Segment...",
11156       (char *) NULL
11157     },
11158     *FXMenu[] =
11159     {
11160       "Solarize...",
11161       "Sepia Tone...",
11162       "Swirl...",
11163       "Implode...",
11164       "Vignette...",
11165       "Wave...",
11166       "Oil Paint...",
11167       "Charcoal Draw...",
11168       (char *) NULL
11169     },
11170     *MiscellanyMenu[] =
11171     {
11172       "Image Info",
11173       "Zoom Image",
11174       "Show Preview...",
11175       "Show Histogram",
11176       "Show Matte",
11177       (char *) NULL
11178     };
11179
11180   static const char
11181     **Menus[ApplyMenus] =
11182     {
11183       FileMenu,
11184       EditMenu,
11185       TransformMenu,
11186       EnhanceMenu,
11187       EffectsMenu,
11188       FXMenu,
11189       MiscellanyMenu
11190     };
11191
11192   static const CommandType
11193     ApplyCommands[] =
11194     {
11195       NullCommand,
11196       NullCommand,
11197       NullCommand,
11198       NullCommand,
11199       NullCommand,
11200       NullCommand,
11201       NullCommand,
11202       HelpCommand,
11203       QuitCommand
11204     },
11205     FileCommands[] =
11206     {
11207       SaveCommand,
11208       PrintCommand
11209     },
11210     EditCommands[] =
11211     {
11212       UndoCommand,
11213       RedoCommand
11214     },
11215     TransformCommands[] =
11216     {
11217       FlopCommand,
11218       FlipCommand,
11219       RotateRightCommand,
11220       RotateLeftCommand
11221     },
11222     EnhanceCommands[] =
11223     {
11224       HueCommand,
11225       SaturationCommand,
11226       BrightnessCommand,
11227       GammaCommand,
11228       SpiffCommand,
11229       DullCommand,
11230       ContrastStretchCommand,
11231       SigmoidalContrastCommand,
11232       NormalizeCommand,
11233       EqualizeCommand,
11234       NegateCommand,
11235       GrayscaleCommand,
11236       MapCommand,
11237       QuantizeCommand
11238     },
11239     EffectsCommands[] =
11240     {
11241       DespeckleCommand,
11242       EmbossCommand,
11243       ReduceNoiseCommand,
11244       AddNoiseCommand,
11245       SharpenCommand,
11246       BlurCommand,
11247       EdgeDetectCommand,
11248       SpreadCommand,
11249       ShadeCommand,
11250       RaiseCommand,
11251       SegmentCommand
11252     },
11253     FXCommands[] =
11254     {
11255       SolarizeCommand,
11256       SepiaToneCommand,
11257       SwirlCommand,
11258       ImplodeCommand,
11259       VignetteCommand,
11260       WaveCommand,
11261       OilPaintCommand,
11262       CharcoalDrawCommand
11263     },
11264     MiscellanyCommands[] =
11265     {
11266       InfoCommand,
11267       ZoomCommand,
11268       ShowPreviewCommand,
11269       ShowHistogramCommand,
11270       ShowMatteCommand
11271     },
11272     ROICommands[] =
11273     {
11274       ROIHelpCommand,
11275       ROIDismissCommand
11276     };
11277
11278   static const CommandType
11279     *Commands[ApplyMenus] =
11280     {
11281       FileCommands,
11282       EditCommands,
11283       TransformCommands,
11284       EnhanceCommands,
11285       EffectsCommands,
11286       FXCommands,
11287       MiscellanyCommands
11288     };
11289
11290   char
11291     command[MaxTextExtent],
11292     text[MaxTextExtent];
11293
11294   CommandType
11295     command_type;
11296
11297   Cursor
11298     cursor;
11299
11300   Image
11301     *roi_image;
11302
11303   int
11304     entry,
11305     id,
11306     x,
11307     y;
11308
11309   MagickRealType
11310     scale_factor;
11311
11312   MagickProgressMonitor
11313     progress_monitor;
11314
11315   RectangleInfo
11316     crop_info,
11317     highlight_info,
11318     roi_info;
11319
11320   unsigned int
11321     height,
11322     width;
11323
11324   size_t
11325     state;
11326
11327   XEvent
11328     event;
11329
11330   /*
11331     Map Command widget.
11332   */
11333   (void) CloneString(&windows->command.name,"ROI");
11334   windows->command.data=0;
11335   (void) XCommandWidget(display,windows,ROIMenu,(XEvent *) NULL);
11336   (void) XMapRaised(display,windows->command.id);
11337   XClientMessage(display,windows->image.id,windows->im_protocols,
11338     windows->im_update_widget,CurrentTime);
11339   /*
11340     Track pointer until button 1 is pressed.
11341   */
11342   XQueryPosition(display,windows->image.id,&x,&y);
11343   (void) XSelectInput(display,windows->image.id,
11344     windows->image.attributes.event_mask | PointerMotionMask);
11345   roi_info.x=(ssize_t) windows->image.x+x;
11346   roi_info.y=(ssize_t) windows->image.y+y;
11347   roi_info.width=0;
11348   roi_info.height=0;
11349   cursor=XCreateFontCursor(display,XC_fleur);
11350   state=DefaultState;
11351   do
11352   {
11353     if (windows->info.mapped != MagickFalse)
11354       {
11355         /*
11356           Display pointer position.
11357         */
11358         (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
11359           (long) roi_info.x,(long) roi_info.y);
11360         XInfoWidget(display,windows,text);
11361       }
11362     /*
11363       Wait for next event.
11364     */
11365     XScreenEvent(display,windows,&event,exception);
11366     if (event.xany.window == windows->command.id)
11367       {
11368         /*
11369           Select a command from the Command widget.
11370         */
11371         id=XCommandWidget(display,windows,ROIMenu,&event);
11372         if (id < 0)
11373           continue;
11374         switch (ROICommands[id])
11375         {
11376           case ROIHelpCommand:
11377           {
11378             XTextViewWidget(display,resource_info,windows,MagickFalse,
11379               "Help Viewer - Region of Interest",ImageROIHelp);
11380             break;
11381           }
11382           case ROIDismissCommand:
11383           {
11384             /*
11385               Prematurely exit.
11386             */
11387             state|=EscapeState;
11388             state|=ExitState;
11389             break;
11390           }
11391           default:
11392             break;
11393         }
11394         continue;
11395       }
11396     switch (event.type)
11397     {
11398       case ButtonPress:
11399       {
11400         if (event.xbutton.button != Button1)
11401           break;
11402         if (event.xbutton.window != windows->image.id)
11403           break;
11404         /*
11405           Note first corner of region of interest rectangle-- exit loop.
11406         */
11407         (void) XCheckDefineCursor(display,windows->image.id,cursor);
11408         roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11409         roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11410         state|=ExitState;
11411         break;
11412       }
11413       case ButtonRelease:
11414         break;
11415       case Expose:
11416         break;
11417       case KeyPress:
11418       {
11419         KeySym
11420           key_symbol;
11421
11422         if (event.xkey.window != windows->image.id)
11423           break;
11424         /*
11425           Respond to a user key press.
11426         */
11427         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11428           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11429         switch ((int) key_symbol)
11430         {
11431           case XK_Escape:
11432           case XK_F20:
11433           {
11434             /*
11435               Prematurely exit.
11436             */
11437             state|=EscapeState;
11438             state|=ExitState;
11439             break;
11440           }
11441           case XK_F1:
11442           case XK_Help:
11443           {
11444             XTextViewWidget(display,resource_info,windows,MagickFalse,
11445               "Help Viewer - Region of Interest",ImageROIHelp);
11446             break;
11447           }
11448           default:
11449           {
11450             (void) XBell(display,0);
11451             break;
11452           }
11453         }
11454         break;
11455       }
11456       case MotionNotify:
11457       {
11458         /*
11459           Map and unmap Info widget as text cursor crosses its boundaries.
11460         */
11461         x=event.xmotion.x;
11462         y=event.xmotion.y;
11463         if (windows->info.mapped != MagickFalse)
11464           {
11465             if ((x < (int) (windows->info.x+windows->info.width)) &&
11466                 (y < (int) (windows->info.y+windows->info.height)))
11467               (void) XWithdrawWindow(display,windows->info.id,
11468                 windows->info.screen);
11469           }
11470         else
11471           if ((x > (int) (windows->info.x+windows->info.width)) ||
11472               (y > (int) (windows->info.y+windows->info.height)))
11473             (void) XMapWindow(display,windows->info.id);
11474         roi_info.x=(ssize_t) windows->image.x+x;
11475         roi_info.y=(ssize_t) windows->image.y+y;
11476         break;
11477       }
11478       default:
11479         break;
11480     }
11481   } while ((state & ExitState) == 0);
11482   (void) XSelectInput(display,windows->image.id,
11483     windows->image.attributes.event_mask);
11484   if ((state & EscapeState) != 0)
11485     {
11486       /*
11487         User want to exit without region of interest.
11488       */
11489       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11490       (void) XFreeCursor(display,cursor);
11491       return(MagickTrue);
11492     }
11493   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
11494   do
11495   {
11496     /*
11497       Size rectangle as pointer moves until the mouse button is released.
11498     */
11499     x=(int) roi_info.x;
11500     y=(int) roi_info.y;
11501     roi_info.width=0;
11502     roi_info.height=0;
11503     state=DefaultState;
11504     do
11505     {
11506       highlight_info=roi_info;
11507       highlight_info.x=roi_info.x-windows->image.x;
11508       highlight_info.y=roi_info.y-windows->image.y;
11509       if ((highlight_info.width > 3) && (highlight_info.height > 3))
11510         {
11511           /*
11512             Display info and draw region of interest rectangle.
11513           */
11514           if (windows->info.mapped == MagickFalse)
11515             (void) XMapWindow(display,windows->info.id);
11516           (void) FormatLocaleString(text,MaxTextExtent,
11517             " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11518             roi_info.height,(double) roi_info.x,(double) roi_info.y);
11519           XInfoWidget(display,windows,text);
11520           XHighlightRectangle(display,windows->image.id,
11521             windows->image.highlight_context,&highlight_info);
11522         }
11523       else
11524         if (windows->info.mapped != MagickFalse)
11525           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11526       /*
11527         Wait for next event.
11528       */
11529       XScreenEvent(display,windows,&event,exception);
11530       if ((highlight_info.width > 3) && (highlight_info.height > 3))
11531         XHighlightRectangle(display,windows->image.id,
11532           windows->image.highlight_context,&highlight_info);
11533       switch (event.type)
11534       {
11535         case ButtonPress:
11536         {
11537           roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11538           roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11539           break;
11540         }
11541         case ButtonRelease:
11542         {
11543           /*
11544             User has committed to region of interest rectangle.
11545           */
11546           roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11547           roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11548           XSetCursorState(display,windows,MagickFalse);
11549           state|=ExitState;
11550           if (LocaleCompare(windows->command.name,"Apply") == 0)
11551             break;
11552           (void) CloneString(&windows->command.name,"Apply");
11553           windows->command.data=ApplyMenus;
11554           (void) XCommandWidget(display,windows,ApplyMenu,(XEvent *) NULL);
11555           break;
11556         }
11557         case Expose:
11558           break;
11559         case MotionNotify:
11560         {
11561           roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11562           roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11563         }
11564         default:
11565           break;
11566       }
11567       if ((((int) roi_info.x != x) && ((int) roi_info.y != y)) ||
11568           ((state & ExitState) != 0))
11569         {
11570           /*
11571             Check boundary conditions.
11572           */
11573           if (roi_info.x < 0)
11574             roi_info.x=0;
11575           else
11576             if (roi_info.x > (ssize_t) windows->image.ximage->width)
11577               roi_info.x=(ssize_t) windows->image.ximage->width;
11578           if ((int) roi_info.x < x)
11579             roi_info.width=(unsigned int) (x-roi_info.x);
11580           else
11581             {
11582               roi_info.width=(unsigned int) (roi_info.x-x);
11583               roi_info.x=(ssize_t) x;
11584             }
11585           if (roi_info.y < 0)
11586             roi_info.y=0;
11587           else
11588             if (roi_info.y > (ssize_t) windows->image.ximage->height)
11589               roi_info.y=(ssize_t) windows->image.ximage->height;
11590           if ((int) roi_info.y < y)
11591             roi_info.height=(unsigned int) (y-roi_info.y);
11592           else
11593             {
11594               roi_info.height=(unsigned int) (roi_info.y-y);
11595               roi_info.y=(ssize_t) y;
11596             }
11597         }
11598     } while ((state & ExitState) == 0);
11599     /*
11600       Wait for user to grab a corner of the rectangle or press return.
11601     */
11602     state=DefaultState;
11603     command_type=NullCommand;
11604     (void) XMapWindow(display,windows->info.id);
11605     do
11606     {
11607       if (windows->info.mapped != MagickFalse)
11608         {
11609           /*
11610             Display pointer position.
11611           */
11612           (void) FormatLocaleString(text,MaxTextExtent,
11613             " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11614             roi_info.height,(double) roi_info.x,(double) roi_info.y);
11615           XInfoWidget(display,windows,text);
11616         }
11617       highlight_info=roi_info;
11618       highlight_info.x=roi_info.x-windows->image.x;
11619       highlight_info.y=roi_info.y-windows->image.y;
11620       if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
11621         {
11622           state|=EscapeState;
11623           state|=ExitState;
11624           break;
11625         }
11626       if ((state & UpdateRegionState) != 0)
11627         {
11628           (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11629           switch (command_type)
11630           {
11631             case UndoCommand:
11632             case RedoCommand:
11633             {
11634               (void) XMagickCommand(display,resource_info,windows,command_type,
11635                 image,exception);
11636               break;
11637             }
11638             default:
11639             {
11640               /*
11641                 Region of interest is relative to image configuration.
11642               */
11643               progress_monitor=SetImageProgressMonitor(*image,
11644                 (MagickProgressMonitor) NULL,(*image)->client_data);
11645               crop_info=roi_info;
11646               width=(unsigned int) (*image)->columns;
11647               height=(unsigned int) (*image)->rows;
11648               x=0;
11649               y=0;
11650               if (windows->image.crop_geometry != (char *) NULL)
11651                 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
11652                   &width,&height);
11653               scale_factor=(MagickRealType) width/windows->image.ximage->width;
11654               crop_info.x+=x;
11655               crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
11656               crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
11657               scale_factor=(MagickRealType)
11658                 height/windows->image.ximage->height;
11659               crop_info.y+=y;
11660               crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
11661               crop_info.height=(unsigned int)
11662                 (scale_factor*crop_info.height+0.5);
11663               roi_image=CropImage(*image,&crop_info,exception);
11664               (void) SetImageProgressMonitor(*image,progress_monitor,
11665                 (*image)->client_data);
11666               if (roi_image == (Image *) NULL)
11667                 continue;
11668               /*
11669                 Apply image processing technique to the region of interest.
11670               */
11671               windows->image.orphan=MagickTrue;
11672               (void) XMagickCommand(display,resource_info,windows,command_type,
11673                 &roi_image,exception);
11674               progress_monitor=SetImageProgressMonitor(*image,
11675                 (MagickProgressMonitor) NULL,(*image)->client_data);
11676               (void) XMagickCommand(display,resource_info,windows,
11677                 SaveToUndoBufferCommand,image,exception);
11678               windows->image.orphan=MagickFalse;
11679               (void) CompositeImage(*image,CopyCompositeOp,roi_image,
11680                 crop_info.x,crop_info.y,exception);
11681               roi_image=DestroyImage(roi_image);
11682               (void) SetImageProgressMonitor(*image,progress_monitor,
11683                 (*image)->client_data);
11684               break;
11685             }
11686           }
11687           if (command_type != InfoCommand)
11688             {
11689               XConfigureImageColormap(display,resource_info,windows,*image,
11690                 exception);
11691               (void) XConfigureImage(display,resource_info,windows,*image,
11692                 exception);
11693             }
11694           XCheckRefreshWindows(display,windows);
11695           XInfoWidget(display,windows,text);
11696           (void) XSetFunction(display,windows->image.highlight_context,
11697             GXinvert);
11698           state&=(~UpdateRegionState);
11699         }
11700       XHighlightRectangle(display,windows->image.id,
11701         windows->image.highlight_context,&highlight_info);
11702       XScreenEvent(display,windows,&event,exception);
11703       if (event.xany.window == windows->command.id)
11704         {
11705           /*
11706             Select a command from the Command widget.
11707           */
11708           (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11709           command_type=NullCommand;
11710           id=XCommandWidget(display,windows,ApplyMenu,&event);
11711           if (id >= 0)
11712             {
11713               (void) CopyMagickString(command,ApplyMenu[id],MaxTextExtent);
11714               command_type=ApplyCommands[id];
11715               if (id < ApplyMenus)
11716                 {
11717                   /*
11718                     Select a command from a pop-up menu.
11719                   */
11720                   entry=XMenuWidget(display,windows,ApplyMenu[id],
11721                     (const char **) Menus[id],command);
11722                   if (entry >= 0)
11723                     {
11724                       (void) CopyMagickString(command,Menus[id][entry],
11725                         MaxTextExtent);
11726                       command_type=Commands[id][entry];
11727                     }
11728                 }
11729             }
11730           (void) XSetFunction(display,windows->image.highlight_context,
11731             GXinvert);
11732           XHighlightRectangle(display,windows->image.id,
11733             windows->image.highlight_context,&highlight_info);
11734           if (command_type == HelpCommand)
11735             {
11736               (void) XSetFunction(display,windows->image.highlight_context,
11737                 GXcopy);
11738               XTextViewWidget(display,resource_info,windows,MagickFalse,
11739                 "Help Viewer - Region of Interest",ImageROIHelp);
11740               (void) XSetFunction(display,windows->image.highlight_context,
11741                 GXinvert);
11742               continue;
11743             }
11744           if (command_type == QuitCommand)
11745             {
11746               /*
11747                 exit.
11748               */
11749               state|=EscapeState;
11750               state|=ExitState;
11751               continue;
11752             }
11753           if (command_type != NullCommand)
11754             state|=UpdateRegionState;
11755           continue;
11756         }
11757       XHighlightRectangle(display,windows->image.id,
11758         windows->image.highlight_context,&highlight_info);
11759       switch (event.type)
11760       {
11761         case ButtonPress:
11762         {
11763           x=windows->image.x;
11764           y=windows->image.y;
11765           if (event.xbutton.button != Button1)
11766             break;
11767           if (event.xbutton.window != windows->image.id)
11768             break;
11769           x=windows->image.x+event.xbutton.x;
11770           y=windows->image.y+event.xbutton.y;
11771           if ((x < (int) (roi_info.x+RoiDelta)) &&
11772               (x > (int) (roi_info.x-RoiDelta)) &&
11773               (y < (int) (roi_info.y+RoiDelta)) &&
11774               (y > (int) (roi_info.y-RoiDelta)))
11775             {
11776               roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11777               roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11778               state|=UpdateConfigurationState;
11779               break;
11780             }
11781           if ((x < (int) (roi_info.x+RoiDelta)) &&
11782               (x > (int) (roi_info.x-RoiDelta)) &&
11783               (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11784               (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11785             {
11786               roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11787               state|=UpdateConfigurationState;
11788               break;
11789             }
11790           if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11791               (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11792               (y < (int) (roi_info.y+RoiDelta)) &&
11793               (y > (int) (roi_info.y-RoiDelta)))
11794             {
11795               roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11796               state|=UpdateConfigurationState;
11797               break;
11798             }
11799           if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11800               (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11801               (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11802               (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11803             {
11804               state|=UpdateConfigurationState;
11805               break;
11806             }
11807         }
11808         case ButtonRelease:
11809         {
11810           if (event.xbutton.window == windows->pan.id)
11811             if ((highlight_info.x != crop_info.x-windows->image.x) ||
11812                 (highlight_info.y != crop_info.y-windows->image.y))
11813               XHighlightRectangle(display,windows->image.id,
11814                 windows->image.highlight_context,&highlight_info);
11815           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11816             event.xbutton.time);
11817           break;
11818         }
11819         case Expose:
11820         {
11821           if (event.xexpose.window == windows->image.id)
11822             if (event.xexpose.count == 0)
11823               {
11824                 event.xexpose.x=(int) highlight_info.x;
11825                 event.xexpose.y=(int) highlight_info.y;
11826                 event.xexpose.width=(int) highlight_info.width;
11827                 event.xexpose.height=(int) highlight_info.height;
11828                 XRefreshWindow(display,&windows->image,&event);
11829               }
11830           if (event.xexpose.window == windows->info.id)
11831             if (event.xexpose.count == 0)
11832               XInfoWidget(display,windows,text);
11833           break;
11834         }
11835         case KeyPress:
11836         {
11837           KeySym
11838             key_symbol;
11839
11840           if (event.xkey.window != windows->image.id)
11841             break;
11842           /*
11843             Respond to a user key press.
11844           */
11845           (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11846             sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11847           switch ((int) key_symbol)
11848           {
11849             case XK_Shift_L:
11850             case XK_Shift_R:
11851               break;
11852             case XK_Escape:
11853             case XK_F20:
11854               state|=EscapeState;
11855             case XK_Return:
11856             {
11857               state|=ExitState;
11858               break;
11859             }
11860             case XK_Home:
11861             case XK_KP_Home:
11862             {
11863               roi_info.x=(ssize_t) (windows->image.width/2L-roi_info.width/2L);
11864               roi_info.y=(ssize_t) (windows->image.height/2L-
11865                 roi_info.height/2L);
11866               break;
11867             }
11868             case XK_Left:
11869             case XK_KP_Left:
11870             {
11871               roi_info.x--;
11872               break;
11873             }
11874             case XK_Up:
11875             case XK_KP_Up:
11876             case XK_Next:
11877             {
11878               roi_info.y--;
11879               break;
11880             }
11881             case XK_Right:
11882             case XK_KP_Right:
11883             {
11884               roi_info.x++;
11885               break;
11886             }
11887             case XK_Prior:
11888             case XK_Down:
11889             case XK_KP_Down:
11890             {
11891               roi_info.y++;
11892               break;
11893             }
11894             case XK_F1:
11895             case XK_Help:
11896             {
11897               (void) XSetFunction(display,windows->image.highlight_context,
11898                 GXcopy);
11899               XTextViewWidget(display,resource_info,windows,MagickFalse,
11900                 "Help Viewer - Region of Interest",ImageROIHelp);
11901               (void) XSetFunction(display,windows->image.highlight_context,
11902                 GXinvert);
11903               break;
11904             }
11905             default:
11906             {
11907               command_type=XImageWindowCommand(display,resource_info,windows,
11908                 event.xkey.state,key_symbol,image,exception);
11909               if (command_type != NullCommand)
11910                 state|=UpdateRegionState;
11911               break;
11912             }
11913           }
11914           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11915             event.xkey.time);
11916           break;
11917         }
11918         case KeyRelease:
11919           break;
11920         case MotionNotify:
11921         {
11922           if (event.xbutton.window != windows->image.id)
11923             break;
11924           /*
11925             Map and unmap Info widget as text cursor crosses its boundaries.
11926           */
11927           x=event.xmotion.x;
11928           y=event.xmotion.y;
11929           if (windows->info.mapped != MagickFalse)
11930             {
11931               if ((x < (int) (windows->info.x+windows->info.width)) &&
11932                   (y < (int) (windows->info.y+windows->info.height)))
11933                 (void) XWithdrawWindow(display,windows->info.id,
11934                   windows->info.screen);
11935             }
11936           else
11937             if ((x > (int) (windows->info.x+windows->info.width)) ||
11938                 (y > (int) (windows->info.y+windows->info.height)))
11939               (void) XMapWindow(display,windows->info.id);
11940           roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11941           roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11942           break;
11943         }
11944         case SelectionRequest:
11945         {
11946           XSelectionEvent
11947             notify;
11948
11949           XSelectionRequestEvent
11950             *request;
11951
11952           /*
11953             Set primary selection.
11954           */
11955           (void) FormatLocaleString(text,MaxTextExtent,
11956             "%.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11957             roi_info.height,(double) roi_info.x,(double) roi_info.y);
11958           request=(&(event.xselectionrequest));
11959           (void) XChangeProperty(request->display,request->requestor,
11960             request->property,request->target,8,PropModeReplace,
11961             (unsigned char *) text,(int) strlen(text));
11962           notify.type=SelectionNotify;
11963           notify.display=request->display;
11964           notify.requestor=request->requestor;
11965           notify.selection=request->selection;
11966           notify.target=request->target;
11967           notify.time=request->time;
11968           if (request->property == None)
11969             notify.property=request->target;
11970           else
11971             notify.property=request->property;
11972           (void) XSendEvent(request->display,request->requestor,False,0,
11973             (XEvent *) &notify);
11974         }
11975         default:
11976           break;
11977       }
11978       if ((state & UpdateConfigurationState) != 0)
11979         {
11980           (void) XPutBackEvent(display,&event);
11981           (void) XCheckDefineCursor(display,windows->image.id,cursor);
11982           break;
11983         }
11984     } while ((state & ExitState) == 0);
11985   } while ((state & ExitState) == 0);
11986   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11987   XSetCursorState(display,windows,MagickFalse);
11988   if ((state & EscapeState) != 0)
11989     return(MagickTrue);
11990   return(MagickTrue);
11991 }
11992 \f
11993 /*
11994 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11995 %                                                                             %
11996 %                                                                             %
11997 %                                                                             %
11998 +   X R o t a t e I m a g e                                                   %
11999 %                                                                             %
12000 %                                                                             %
12001 %                                                                             %
12002 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12003 %
12004 %  XRotateImage() rotates the X image.  If the degrees parameter if zero, the
12005 %  rotation angle is computed from the slope of a line drawn by the user.
12006 %
12007 %  The format of the XRotateImage method is:
12008 %
12009 %      MagickBooleanType XRotateImage(Display *display,
12010 %        XResourceInfo *resource_info,XWindows *windows,double degrees,
12011 %        Image **image,ExceptionInfo *exception)
12012 %
12013 %  A description of each parameter follows:
12014 %
12015 %    o display: Specifies a connection to an X server; returned from
12016 %      XOpenDisplay.
12017 %
12018 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12019 %
12020 %    o windows: Specifies a pointer to a XWindows structure.
12021 %
12022 %    o degrees: Specifies the number of degrees to rotate the image.
12023 %
12024 %    o image: the image.
12025 %
12026 %    o exception: return any errors or warnings in this structure.
12027 %
12028 */
12029 static MagickBooleanType XRotateImage(Display *display,
12030   XResourceInfo *resource_info,XWindows *windows,double degrees,Image **image,
12031   ExceptionInfo *exception)
12032 {
12033   static const char
12034     *RotateMenu[] =
12035     {
12036       "Pixel Color",
12037       "Direction",
12038       "Help",
12039       "Dismiss",
12040       (char *) NULL
12041     };
12042
12043   static ModeType
12044     direction = HorizontalRotateCommand;
12045
12046   static const ModeType
12047     DirectionCommands[] =
12048     {
12049       HorizontalRotateCommand,
12050       VerticalRotateCommand
12051     },
12052     RotateCommands[] =
12053     {
12054       RotateColorCommand,
12055       RotateDirectionCommand,
12056       RotateHelpCommand,
12057       RotateDismissCommand
12058     };
12059
12060   static unsigned int
12061     pen_id = 0;
12062
12063   char
12064     command[MaxTextExtent],
12065     text[MaxTextExtent];
12066
12067   Image
12068     *rotate_image;
12069
12070   int
12071     id,
12072     x,
12073     y;
12074
12075   MagickRealType
12076     normalized_degrees;
12077
12078   register int
12079     i;
12080
12081   unsigned int
12082     height,
12083     rotations,
12084     width;
12085
12086   if (degrees == 0.0)
12087     {
12088       unsigned int
12089         distance;
12090
12091       size_t
12092         state;
12093
12094       XEvent
12095         event;
12096
12097       XSegment
12098         rotate_info;
12099
12100       /*
12101         Map Command widget.
12102       */
12103       (void) CloneString(&windows->command.name,"Rotate");
12104       windows->command.data=2;
12105       (void) XCommandWidget(display,windows,RotateMenu,(XEvent *) NULL);
12106       (void) XMapRaised(display,windows->command.id);
12107       XClientMessage(display,windows->image.id,windows->im_protocols,
12108         windows->im_update_widget,CurrentTime);
12109       /*
12110         Wait for first button press.
12111       */
12112       (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12113       XQueryPosition(display,windows->image.id,&x,&y);
12114       rotate_info.x1=x;
12115       rotate_info.y1=y;
12116       rotate_info.x2=x;
12117       rotate_info.y2=y;
12118       state=DefaultState;
12119       do
12120       {
12121         XHighlightLine(display,windows->image.id,
12122           windows->image.highlight_context,&rotate_info);
12123         /*
12124           Wait for next event.
12125         */
12126         XScreenEvent(display,windows,&event,exception);
12127         XHighlightLine(display,windows->image.id,
12128           windows->image.highlight_context,&rotate_info);
12129         if (event.xany.window == windows->command.id)
12130           {
12131             /*
12132               Select a command from the Command widget.
12133             */
12134             id=XCommandWidget(display,windows,RotateMenu,&event);
12135             if (id < 0)
12136               continue;
12137             (void) XSetFunction(display,windows->image.highlight_context,
12138               GXcopy);
12139             switch (RotateCommands[id])
12140             {
12141               case RotateColorCommand:
12142               {
12143                 const char
12144                   *ColorMenu[MaxNumberPens];
12145
12146                 int
12147                   pen_number;
12148
12149                 XColor
12150                   color;
12151
12152                 /*
12153                   Initialize menu selections.
12154                 */
12155                 for (i=0; i < (int) (MaxNumberPens-2); i++)
12156                   ColorMenu[i]=resource_info->pen_colors[i];
12157                 ColorMenu[MaxNumberPens-2]="Browser...";
12158                 ColorMenu[MaxNumberPens-1]=(const char *) NULL;
12159                 /*
12160                   Select a pen color from the pop-up menu.
12161                 */
12162                 pen_number=XMenuWidget(display,windows,RotateMenu[id],
12163                   (const char **) ColorMenu,command);
12164                 if (pen_number < 0)
12165                   break;
12166                 if (pen_number == (MaxNumberPens-2))
12167                   {
12168                     static char
12169                       color_name[MaxTextExtent] = "gray";
12170
12171                     /*
12172                       Select a pen color from a dialog.
12173                     */
12174                     resource_info->pen_colors[pen_number]=color_name;
12175                     XColorBrowserWidget(display,windows,"Select",color_name);
12176                     if (*color_name == '\0')
12177                       break;
12178                   }
12179                 /*
12180                   Set pen color.
12181                 */
12182                 (void) XParseColor(display,windows->map_info->colormap,
12183                   resource_info->pen_colors[pen_number],&color);
12184                 XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
12185                   (unsigned int) MaxColors,&color);
12186                 windows->pixel_info->pen_colors[pen_number]=color;
12187                 pen_id=(unsigned int) pen_number;
12188                 break;
12189               }
12190               case RotateDirectionCommand:
12191               {
12192                 static const char
12193                   *Directions[] =
12194                   {
12195                     "horizontal",
12196                     "vertical",
12197                     (char *) NULL,
12198                   };
12199
12200                 /*
12201                   Select a command from the pop-up menu.
12202                 */
12203                 id=XMenuWidget(display,windows,RotateMenu[id],
12204                   Directions,command);
12205                 if (id >= 0)
12206                   direction=DirectionCommands[id];
12207                 break;
12208               }
12209               case RotateHelpCommand:
12210               {
12211                 XTextViewWidget(display,resource_info,windows,MagickFalse,
12212                   "Help Viewer - Image Rotation",ImageRotateHelp);
12213                 break;
12214               }
12215               case RotateDismissCommand:
12216               {
12217                 /*
12218                   Prematurely exit.
12219                 */
12220                 state|=EscapeState;
12221                 state|=ExitState;
12222                 break;
12223               }
12224               default:
12225                 break;
12226             }
12227             (void) XSetFunction(display,windows->image.highlight_context,
12228               GXinvert);
12229             continue;
12230           }
12231         switch (event.type)
12232         {
12233           case ButtonPress:
12234           {
12235             if (event.xbutton.button != Button1)
12236               break;
12237             if (event.xbutton.window != windows->image.id)
12238               break;
12239             /*
12240               exit loop.
12241             */
12242             (void) XSetFunction(display,windows->image.highlight_context,
12243               GXcopy);
12244             rotate_info.x1=event.xbutton.x;
12245             rotate_info.y1=event.xbutton.y;
12246             state|=ExitState;
12247             break;
12248           }
12249           case ButtonRelease:
12250             break;
12251           case Expose:
12252             break;
12253           case KeyPress:
12254           {
12255             char
12256               command[MaxTextExtent];
12257
12258             KeySym
12259               key_symbol;
12260
12261             if (event.xkey.window != windows->image.id)
12262               break;
12263             /*
12264               Respond to a user key press.
12265             */
12266             (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
12267               sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12268             switch ((int) key_symbol)
12269             {
12270               case XK_Escape:
12271               case XK_F20:
12272               {
12273                 /*
12274                   Prematurely exit.
12275                 */
12276                 state|=EscapeState;
12277                 state|=ExitState;
12278                 break;
12279               }
12280               case XK_F1:
12281               case XK_Help:
12282               {
12283                 (void) XSetFunction(display,windows->image.highlight_context,
12284                   GXcopy);
12285                 XTextViewWidget(display,resource_info,windows,MagickFalse,
12286                   "Help Viewer - Image Rotation",ImageRotateHelp);
12287                 (void) XSetFunction(display,windows->image.highlight_context,
12288                   GXinvert);
12289                 break;
12290               }
12291               default:
12292               {
12293                 (void) XBell(display,0);
12294                 break;
12295               }
12296             }
12297             break;
12298           }
12299           case MotionNotify:
12300           {
12301             rotate_info.x1=event.xmotion.x;
12302             rotate_info.y1=event.xmotion.y;
12303           }
12304         }
12305         rotate_info.x2=rotate_info.x1;
12306         rotate_info.y2=rotate_info.y1;
12307         if (direction == HorizontalRotateCommand)
12308           rotate_info.x2+=32;
12309         else
12310           rotate_info.y2-=32;
12311       } while ((state & ExitState) == 0);
12312       (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12313       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12314       if ((state & EscapeState) != 0)
12315         return(MagickTrue);
12316       /*
12317         Draw line as pointer moves until the mouse button is released.
12318       */
12319       distance=0;
12320       (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12321       state=DefaultState;
12322       do
12323       {
12324         if (distance > 9)
12325           {
12326             /*
12327               Display info and draw rotation line.
12328             */
12329             if (windows->info.mapped == MagickFalse)
12330               (void) XMapWindow(display,windows->info.id);
12331             (void) FormatLocaleString(text,MaxTextExtent," %g",
12332               direction == VerticalRotateCommand ? degrees-90.0 : degrees);
12333             XInfoWidget(display,windows,text);
12334             XHighlightLine(display,windows->image.id,
12335               windows->image.highlight_context,&rotate_info);
12336           }
12337         else
12338           if (windows->info.mapped != MagickFalse)
12339             (void) XWithdrawWindow(display,windows->info.id,
12340               windows->info.screen);
12341         /*
12342           Wait for next event.
12343         */
12344         XScreenEvent(display,windows,&event,exception);
12345         if (distance > 9)
12346           XHighlightLine(display,windows->image.id,
12347             windows->image.highlight_context,&rotate_info);
12348         switch (event.type)
12349         {
12350           case ButtonPress:
12351             break;
12352           case ButtonRelease:
12353           {
12354             /*
12355               User has committed to rotation line.
12356             */
12357             rotate_info.x2=event.xbutton.x;
12358             rotate_info.y2=event.xbutton.y;
12359             state|=ExitState;
12360             break;
12361           }
12362           case Expose:
12363             break;
12364           case MotionNotify:
12365           {
12366             rotate_info.x2=event.xmotion.x;
12367             rotate_info.y2=event.xmotion.y;
12368           }
12369           default:
12370             break;
12371         }
12372         /*
12373           Check boundary conditions.
12374         */
12375         if (rotate_info.x2 < 0)
12376           rotate_info.x2=0;
12377         else
12378           if (rotate_info.x2 > (int) windows->image.width)
12379             rotate_info.x2=(short) windows->image.width;
12380         if (rotate_info.y2 < 0)
12381           rotate_info.y2=0;
12382         else
12383           if (rotate_info.y2 > (int) windows->image.height)
12384             rotate_info.y2=(short) windows->image.height;
12385         /*
12386           Compute rotation angle from the slope of the line.
12387         */
12388         degrees=0.0;
12389         distance=(unsigned int)
12390           ((rotate_info.x2-rotate_info.x1+1)*(rotate_info.x2-rotate_info.x1+1))+
12391           ((rotate_info.y2-rotate_info.y1+1)*(rotate_info.y2-rotate_info.y1+1));
12392         if (distance > 9)
12393           degrees=RadiansToDegrees(-atan2((double) (rotate_info.y2-
12394             rotate_info.y1),(double) (rotate_info.x2-rotate_info.x1)));
12395       } while ((state & ExitState) == 0);
12396       (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12397       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12398       if (distance <= 9)
12399         return(MagickTrue);
12400     }
12401   if (direction == VerticalRotateCommand)
12402     degrees-=90.0;
12403   if (degrees == 0.0)
12404     return(MagickTrue);
12405   /*
12406     Rotate image.
12407   */
12408   normalized_degrees=degrees;
12409   while (normalized_degrees < -45.0)
12410     normalized_degrees+=360.0;
12411   for (rotations=0; normalized_degrees > 45.0; rotations++)
12412     normalized_degrees-=90.0;
12413   if (normalized_degrees != 0.0)
12414     (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
12415       exception);
12416   XSetCursorState(display,windows,MagickTrue);
12417   XCheckRefreshWindows(display,windows);
12418   (*image)->background_color.red=ScaleShortToQuantum(
12419     windows->pixel_info->pen_colors[pen_id].red);
12420   (*image)->background_color.green=ScaleShortToQuantum(
12421     windows->pixel_info->pen_colors[pen_id].green);
12422   (*image)->background_color.blue=ScaleShortToQuantum(
12423     windows->pixel_info->pen_colors[pen_id].blue);
12424   rotate_image=RotateImage(*image,degrees,exception);
12425   XSetCursorState(display,windows,MagickFalse);
12426   if (rotate_image == (Image *) NULL)
12427     return(MagickFalse);
12428   *image=DestroyImage(*image);
12429   *image=rotate_image;
12430   if (windows->image.crop_geometry != (char *) NULL)
12431     {
12432       /*
12433         Rotate crop geometry.
12434       */
12435       width=(unsigned int) (*image)->columns;
12436       height=(unsigned int) (*image)->rows;
12437       (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12438       switch (rotations % 4)
12439       {
12440         default:
12441         case 0:
12442           break;
12443         case 1:
12444         {
12445           /*
12446             Rotate 90 degrees.
12447           */
12448           (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12449             "%ux%u%+d%+d",height,width,(int) (*image)->columns-
12450             (int) height-y,x);
12451           break;
12452         }
12453         case 2:
12454         {
12455           /*
12456             Rotate 180 degrees.
12457           */
12458           (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12459             "%ux%u%+d%+d",width,height,(int) width-x,(int) height-y);
12460           break;
12461         }
12462         case 3:
12463         {
12464           /*
12465             Rotate 270 degrees.
12466           */
12467           (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12468             "%ux%u%+d%+d",height,width,y,(int) (*image)->rows-(int) width-x);
12469           break;
12470         }
12471       }
12472     }
12473   if (windows->image.orphan != MagickFalse)
12474     return(MagickTrue);
12475   if (normalized_degrees != 0.0)
12476     {
12477       /*
12478         Update image colormap.
12479       */
12480       windows->image.window_changes.width=(int) (*image)->columns;
12481       windows->image.window_changes.height=(int) (*image)->rows;
12482       if (windows->image.crop_geometry != (char *) NULL)
12483         {
12484           /*
12485             Obtain dimensions of image from crop geometry.
12486           */
12487           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
12488             &width,&height);
12489           windows->image.window_changes.width=(int) width;
12490           windows->image.window_changes.height=(int) height;
12491         }
12492       XConfigureImageColormap(display,resource_info,windows,*image,exception);
12493     }
12494   else
12495     if (((rotations % 4) == 1) || ((rotations % 4) == 3))
12496       {
12497         windows->image.window_changes.width=windows->image.ximage->height;
12498         windows->image.window_changes.height=windows->image.ximage->width;
12499       }
12500   /*
12501     Update image configuration.
12502   */
12503   (void) XConfigureImage(display,resource_info,windows,*image,exception);
12504   return(MagickTrue);
12505 }
12506 \f
12507 /*
12508 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12509 %                                                                             %
12510 %                                                                             %
12511 %                                                                             %
12512 +   X S a v e I m a g e                                                       %
12513 %                                                                             %
12514 %                                                                             %
12515 %                                                                             %
12516 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12517 %
12518 %  XSaveImage() saves an image to a file.
12519 %
12520 %  The format of the XSaveImage method is:
12521 %
12522 %      MagickBooleanType XSaveImage(Display *display,
12523 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
12524 %        ExceptionInfo *exception)
12525 %
12526 %  A description of each parameter follows:
12527 %
12528 %    o display: Specifies a connection to an X server; returned from
12529 %      XOpenDisplay.
12530 %
12531 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12532 %
12533 %    o windows: Specifies a pointer to a XWindows structure.
12534 %
12535 %    o image: the image.
12536 %
12537 %    o exception: return any errors or warnings in this structure.
12538 %
12539 */
12540 static MagickBooleanType XSaveImage(Display *display,
12541   XResourceInfo *resource_info,XWindows *windows,Image *image,
12542   ExceptionInfo *exception)
12543 {
12544   char
12545     filename[MaxTextExtent],
12546     geometry[MaxTextExtent];
12547
12548   Image
12549     *save_image;
12550
12551   ImageInfo
12552     *image_info;
12553
12554   MagickStatusType
12555     status;
12556
12557   /*
12558     Request file name from user.
12559   */
12560   if (resource_info->write_filename != (char *) NULL)
12561     (void) CopyMagickString(filename,resource_info->write_filename,
12562       MaxTextExtent);
12563   else
12564     {
12565       char
12566         path[MaxTextExtent];
12567
12568       int
12569         status;
12570
12571       GetPathComponent(image->filename,HeadPath,path);
12572       GetPathComponent(image->filename,TailPath,filename);
12573       if (*path != '\0')
12574         {
12575           status=chdir(path);
12576           if (status == -1)
12577             (void) ThrowMagickException(exception,GetMagickModule(),
12578               FileOpenError,"UnableToOpenFile","%s",path);
12579         }
12580     }
12581   XFileBrowserWidget(display,windows,"Save",filename);
12582   if (*filename == '\0')
12583     return(MagickTrue);
12584   if (IsPathAccessible(filename) != MagickFalse)
12585     {
12586       int
12587         status;
12588
12589       /*
12590         File exists-- seek user's permission before overwriting.
12591       */
12592       status=XConfirmWidget(display,windows,"Overwrite",filename);
12593       if (status <= 0)
12594         return(MagickTrue);
12595     }
12596   image_info=CloneImageInfo(resource_info->image_info);
12597   (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
12598   (void) SetImageInfo(image_info,1,exception);
12599   if ((LocaleCompare(image_info->magick,"JPEG") == 0) ||
12600       (LocaleCompare(image_info->magick,"JPG") == 0))
12601     {
12602       char
12603         quality[MaxTextExtent];
12604
12605       int
12606         status;
12607
12608       /*
12609         Request JPEG quality from user.
12610       */
12611       (void) FormatLocaleString(quality,MaxTextExtent,"%.20g",(double)
12612         image->quality);
12613       status=XDialogWidget(display,windows,"Save","Enter JPEG quality:",
12614         quality);
12615       if (*quality == '\0')
12616         return(MagickTrue);
12617       image->quality=StringToUnsignedLong(quality);
12618       image_info->interlace=status != 0 ? NoInterlace : PlaneInterlace;
12619     }
12620   if ((LocaleCompare(image_info->magick,"EPS") == 0) ||
12621       (LocaleCompare(image_info->magick,"PDF") == 0) ||
12622       (LocaleCompare(image_info->magick,"PS") == 0) ||
12623       (LocaleCompare(image_info->magick,"PS2") == 0))
12624     {
12625       char
12626         geometry[MaxTextExtent];
12627
12628       /*
12629         Request page geometry from user.
12630       */
12631       (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
12632       if (LocaleCompare(image_info->magick,"PDF") == 0)
12633         (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
12634       if (image_info->page != (char *) NULL)
12635         (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
12636       XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
12637         "Select page geometry:",geometry);
12638       if (*geometry != '\0')
12639         image_info->page=GetPageGeometry(geometry);
12640     }
12641   /*
12642     Apply image transforms.
12643   */
12644   XSetCursorState(display,windows,MagickTrue);
12645   XCheckRefreshWindows(display,windows);
12646   save_image=CloneImage(image,0,0,MagickTrue,exception);
12647   if (save_image == (Image *) NULL)
12648     return(MagickFalse);
12649   (void) FormatLocaleString(geometry,MaxTextExtent,"%dx%d!",
12650     windows->image.ximage->width,windows->image.ximage->height);
12651   (void) TransformImage(&save_image,windows->image.crop_geometry,geometry,
12652     exception);
12653   /*
12654     Write image.
12655   */
12656   (void) CopyMagickString(save_image->filename,filename,MaxTextExtent);
12657   status=WriteImage(image_info,save_image,exception);
12658   if (status != MagickFalse)
12659     image->taint=MagickFalse;
12660   save_image=DestroyImage(save_image);
12661   image_info=DestroyImageInfo(image_info);
12662   XSetCursorState(display,windows,MagickFalse);
12663   return(status != 0 ? MagickTrue : MagickFalse);
12664 }
12665 \f
12666 /*
12667 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12668 %                                                                             %
12669 %                                                                             %
12670 %                                                                             %
12671 +   X S c r e e n E v e n t                                                   %
12672 %                                                                             %
12673 %                                                                             %
12674 %                                                                             %
12675 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12676 %
12677 %  XScreenEvent() handles global events associated with the Pan and Magnify
12678 %  windows.
12679 %
12680 %  The format of the XScreenEvent function is:
12681 %
12682 %      void XScreenEvent(Display *display,XWindows *windows,XEvent *event,
12683 %        ExceptionInfo *exception)
12684 %
12685 %  A description of each parameter follows:
12686 %
12687 %    o display: Specifies a pointer to the Display structure;  returned from
12688 %      XOpenDisplay.
12689 %
12690 %    o windows: Specifies a pointer to a XWindows structure.
12691 %
12692 %    o event: Specifies a pointer to a X11 XEvent structure.
12693 %
12694 %    o exception: return any errors or warnings in this structure.
12695 %
12696 */
12697
12698 #if defined(__cplusplus) || defined(c_plusplus)
12699 extern "C" {
12700 #endif
12701
12702 static int XPredicate(Display *magick_unused(display),XEvent *event,char *data)
12703 {
12704   register XWindows
12705     *windows;
12706
12707   windows=(XWindows *) data;
12708   if ((event->type == ClientMessage) &&
12709       (event->xclient.window == windows->image.id))
12710     return(MagickFalse);
12711   return(MagickTrue);
12712 }
12713
12714 #if defined(__cplusplus) || defined(c_plusplus)
12715 }
12716 #endif
12717
12718 static void XScreenEvent(Display *display,XWindows *windows,XEvent *event,
12719   ExceptionInfo *exception)
12720 {
12721   register int
12722     x,
12723     y;
12724
12725   (void) XIfEvent(display,event,XPredicate,(char *) windows);
12726   if (event->xany.window == windows->command.id)
12727     return;
12728   switch (event->type)
12729   {
12730     case ButtonPress:
12731     case ButtonRelease:
12732     {
12733       if ((event->xbutton.button == Button3) &&
12734           (event->xbutton.state & Mod1Mask))
12735         {
12736           /*
12737             Convert Alt-Button3 to Button2.
12738           */
12739           event->xbutton.button=Button2;
12740           event->xbutton.state&=(~Mod1Mask);
12741         }
12742       if (event->xbutton.window == windows->backdrop.id)
12743         {
12744           (void) XSetInputFocus(display,event->xbutton.window,RevertToParent,
12745             event->xbutton.time);
12746           break;
12747         }
12748       if (event->xbutton.window == windows->pan.id)
12749         {
12750           XPanImage(display,windows,event,exception);
12751           break;
12752         }
12753       if (event->xbutton.window == windows->image.id)
12754         if (event->xbutton.button == Button2)
12755           {
12756             /*
12757               Update magnified image.
12758             */
12759             x=event->xbutton.x;
12760             y=event->xbutton.y;
12761             if (x < 0)
12762               x=0;
12763             else
12764               if (x >= (int) windows->image.width)
12765                 x=(int) (windows->image.width-1);
12766             windows->magnify.x=(int) windows->image.x+x;
12767             if (y < 0)
12768               y=0;
12769             else
12770              if (y >= (int) windows->image.height)
12771                y=(int) (windows->image.height-1);
12772             windows->magnify.y=windows->image.y+y;
12773             if (windows->magnify.mapped == MagickFalse)
12774               (void) XMapRaised(display,windows->magnify.id);
12775             XMakeMagnifyImage(display,windows,exception);
12776             if (event->type == ButtonRelease)
12777               (void) XWithdrawWindow(display,windows->info.id,
12778                 windows->info.screen);
12779             break;
12780           }
12781       break;
12782     }
12783     case ClientMessage:
12784     {
12785       /*
12786         If client window delete message, exit.
12787       */
12788       if (event->xclient.message_type != windows->wm_protocols)
12789         break;
12790       if (*event->xclient.data.l != (long) windows->wm_delete_window)
12791         break;
12792       if (event->xclient.window == windows->magnify.id)
12793         {
12794           (void) XWithdrawWindow(display,windows->magnify.id,
12795             windows->magnify.screen);
12796           break;
12797         }
12798       break;
12799     }
12800     case ConfigureNotify:
12801     {
12802       if (event->xconfigure.window == windows->magnify.id)
12803         {
12804           unsigned int
12805             magnify;
12806
12807           /*
12808             Magnify window has a new configuration.
12809           */
12810           windows->magnify.width=(unsigned int) event->xconfigure.width;
12811           windows->magnify.height=(unsigned int) event->xconfigure.height;
12812           if (windows->magnify.mapped == MagickFalse)
12813             break;
12814           magnify=1;
12815           while ((int) magnify <= event->xconfigure.width)
12816             magnify<<=1;
12817           while ((int) magnify <= event->xconfigure.height)
12818             magnify<<=1;
12819           magnify>>=1;
12820           if (((int) magnify != event->xconfigure.width) ||
12821               ((int) magnify != event->xconfigure.height))
12822             {
12823               XWindowChanges
12824                 window_changes;
12825
12826               window_changes.width=(int) magnify;
12827               window_changes.height=(int) magnify;
12828               (void) XReconfigureWMWindow(display,windows->magnify.id,
12829                 windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
12830                 &window_changes);
12831               break;
12832             }
12833           XMakeMagnifyImage(display,windows,exception);
12834           break;
12835         }
12836       break;
12837     }
12838     case Expose:
12839     {
12840       if (event->xexpose.window == windows->image.id)
12841         {
12842           XRefreshWindow(display,&windows->image,event);
12843           break;
12844         }
12845       if (event->xexpose.window == windows->pan.id)
12846         if (event->xexpose.count == 0)
12847           {
12848             XDrawPanRectangle(display,windows);
12849             break;
12850           }
12851       if (event->xexpose.window == windows->magnify.id)
12852         if (event->xexpose.count == 0)
12853           {
12854             XMakeMagnifyImage(display,windows,exception);
12855             break;
12856           }
12857       break;
12858     }
12859     case KeyPress:
12860     {
12861       char
12862         command[MaxTextExtent];
12863
12864       KeySym
12865         key_symbol;
12866
12867       if (event->xkey.window != windows->magnify.id)
12868         break;
12869       /*
12870         Respond to a user key press.
12871       */
12872       (void) XLookupString((XKeyEvent *) &event->xkey,command,(int)
12873         sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12874       XMagnifyWindowCommand(display,windows,event->xkey.state,key_symbol,
12875         exception);
12876       break;
12877     }
12878     case MapNotify:
12879     {
12880       if (event->xmap.window == windows->magnify.id)
12881         {
12882           windows->magnify.mapped=MagickTrue;
12883           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12884           break;
12885         }
12886       if (event->xmap.window == windows->info.id)
12887         {
12888           windows->info.mapped=MagickTrue;
12889           break;
12890         }
12891       break;
12892     }
12893     case MotionNotify:
12894     {
12895       while (XCheckMaskEvent(display,ButtonMotionMask,event)) ;
12896       if (event->xmotion.window == windows->image.id)
12897         if (windows->magnify.mapped != MagickFalse)
12898           {
12899             /*
12900               Update magnified image.
12901             */
12902             x=event->xmotion.x;
12903             y=event->xmotion.y;
12904             if (x < 0)
12905               x=0;
12906             else
12907               if (x >= (int) windows->image.width)
12908                 x=(int) (windows->image.width-1);
12909             windows->magnify.x=(int) windows->image.x+x;
12910             if (y < 0)
12911               y=0;
12912             else
12913              if (y >= (int) windows->image.height)
12914                y=(int) (windows->image.height-1);
12915             windows->magnify.y=windows->image.y+y;
12916             XMakeMagnifyImage(display,windows,exception);
12917           }
12918       break;
12919     }
12920     case UnmapNotify:
12921     {
12922       if (event->xunmap.window == windows->magnify.id)
12923         {
12924           windows->magnify.mapped=MagickFalse;
12925           break;
12926         }
12927       if (event->xunmap.window == windows->info.id)
12928         {
12929           windows->info.mapped=MagickFalse;
12930           break;
12931         }
12932       break;
12933     }
12934     default:
12935       break;
12936   }
12937 }
12938 \f
12939 /*
12940 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12941 %                                                                             %
12942 %                                                                             %
12943 %                                                                             %
12944 +   X S e t C r o p G e o m e t r y                                           %
12945 %                                                                             %
12946 %                                                                             %
12947 %                                                                             %
12948 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12949 %
12950 %  XSetCropGeometry() accepts a cropping geometry relative to the Image window
12951 %  and translates it to a cropping geometry relative to the image.
12952 %
12953 %  The format of the XSetCropGeometry method is:
12954 %
12955 %      void XSetCropGeometry(Display *display,XWindows *windows,
12956 %        RectangleInfo *crop_info,Image *image)
12957 %
12958 %  A description of each parameter follows:
12959 %
12960 %    o display: Specifies a connection to an X server; returned from
12961 %      XOpenDisplay.
12962 %
12963 %    o windows: Specifies a pointer to a XWindows structure.
12964 %
12965 %    o crop_info:  A pointer to a RectangleInfo that defines a region of the
12966 %      Image window to crop.
12967 %
12968 %    o image: the image.
12969 %
12970 */
12971 static void XSetCropGeometry(Display *display,XWindows *windows,
12972   RectangleInfo *crop_info,Image *image)
12973 {
12974   char
12975     text[MaxTextExtent];
12976
12977   int
12978     x,
12979     y;
12980
12981   MagickRealType
12982     scale_factor;
12983
12984   unsigned int
12985     height,
12986     width;
12987
12988   if (windows->info.mapped != MagickFalse)
12989     {
12990       /*
12991         Display info on cropping rectangle.
12992       */
12993       (void) FormatLocaleString(text,MaxTextExtent," %.20gx%.20g%+.20g%+.20g",
12994         (double) crop_info->width,(double) crop_info->height,(double)
12995         crop_info->x,(double) crop_info->y);
12996       XInfoWidget(display,windows,text);
12997     }
12998   /*
12999     Cropping geometry is relative to any previous crop geometry.
13000   */
13001   x=0;
13002   y=0;
13003   width=(unsigned int) image->columns;
13004   height=(unsigned int) image->rows;
13005   if (windows->image.crop_geometry != (char *) NULL)
13006     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
13007   else
13008     windows->image.crop_geometry=AcquireString((char *) NULL);
13009   /*
13010     Define the crop geometry string from the cropping rectangle.
13011   */
13012   scale_factor=(MagickRealType) width/windows->image.ximage->width;
13013   if (crop_info->x > 0)
13014     x+=(int) (scale_factor*crop_info->x+0.5);
13015   width=(unsigned int) (scale_factor*crop_info->width+0.5);
13016   if (width == 0)
13017     width=1;
13018   scale_factor=(MagickRealType) height/windows->image.ximage->height;
13019   if (crop_info->y > 0)
13020     y+=(int) (scale_factor*crop_info->y+0.5);
13021   height=(unsigned int) (scale_factor*crop_info->height+0.5);
13022   if (height == 0)
13023     height=1;
13024   (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
13025     "%ux%u%+d%+d",width,height,x,y);
13026 }
13027 \f
13028 /*
13029 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13030 %                                                                             %
13031 %                                                                             %
13032 %                                                                             %
13033 +   X T i l e I m a g e                                                       %
13034 %                                                                             %
13035 %                                                                             %
13036 %                                                                             %
13037 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13038 %
13039 %  XTileImage() loads or deletes a selected tile from a visual image directory.
13040 %  The load or delete command is chosen from a menu.
13041 %
13042 %  The format of the XTileImage method is:
13043 %
13044 %      Image *XTileImage(Display *display,XResourceInfo *resource_info,
13045 %        XWindows *windows,Image *image,XEvent *event,ExceptionInfo *exception)
13046 %
13047 %  A description of each parameter follows:
13048 %
13049 %    o tile_image:  XTileImage reads or deletes the tile image
13050 %      and returns it.  A null image is returned if an error occurs.
13051 %
13052 %    o display: Specifies a connection to an X server;  returned from
13053 %      XOpenDisplay.
13054 %
13055 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13056 %
13057 %    o windows: Specifies a pointer to a XWindows structure.
13058 %
13059 %    o image: the image; returned from ReadImage.
13060 %
13061 %    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
13062 %      the entire image is refreshed.
13063 %
13064 %    o exception: return any errors or warnings in this structure.
13065 %
13066 */
13067 static Image *XTileImage(Display *display,XResourceInfo *resource_info,
13068   XWindows *windows,Image *image,XEvent *event,ExceptionInfo *exception)
13069 {
13070   static const char
13071     *VerbMenu[] =
13072     {
13073       "Load",
13074       "Next",
13075       "Former",
13076       "Delete",
13077       "Update",
13078       (char *) NULL,
13079     };
13080
13081   static const ModeType
13082     TileCommands[] =
13083     {
13084       TileLoadCommand,
13085       TileNextCommand,
13086       TileFormerCommand,
13087       TileDeleteCommand,
13088       TileUpdateCommand
13089     };
13090
13091   char
13092     command[MaxTextExtent],
13093     filename[MaxTextExtent];
13094
13095   Image
13096     *tile_image;
13097
13098   int
13099     id,
13100     status,
13101     tile,
13102     x,
13103     y;
13104
13105   MagickRealType
13106     scale_factor;
13107
13108   register char
13109     *p,
13110     *q;
13111
13112   register int
13113     i;
13114
13115   unsigned int
13116     height,
13117     width;
13118
13119   /*
13120     Tile image is relative to montage image configuration.
13121   */
13122   x=0;
13123   y=0;
13124   width=(unsigned int) image->columns;
13125   height=(unsigned int) image->rows;
13126   if (windows->image.crop_geometry != (char *) NULL)
13127     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
13128   scale_factor=(MagickRealType) width/windows->image.ximage->width;
13129   event->xbutton.x+=windows->image.x;
13130   event->xbutton.x=(int) (scale_factor*event->xbutton.x+x+0.5);
13131   scale_factor=(MagickRealType) height/windows->image.ximage->height;
13132   event->xbutton.y+=windows->image.y;
13133   event->xbutton.y=(int) (scale_factor*event->xbutton.y+y+0.5);
13134   /*
13135     Determine size and location of each tile in the visual image directory.
13136   */
13137   width=(unsigned int) image->columns;
13138   height=(unsigned int) image->rows;
13139   x=0;
13140   y=0;
13141   (void) XParseGeometry(image->montage,&x,&y,&width,&height);
13142   tile=((event->xbutton.y-y)/height)*(((int) image->columns-x)/width)+
13143     (event->xbutton.x-x)/width;
13144   if (tile < 0)
13145     {
13146       /*
13147         Button press is outside any tile.
13148       */
13149       (void) XBell(display,0);
13150       return((Image *) NULL);
13151     }
13152   /*
13153     Determine file name from the tile directory.
13154   */
13155   p=image->directory;
13156   for (i=tile; (i != 0) && (*p != '\0'); )
13157   {
13158     if (*p == '\n')
13159       i--;
13160     p++;
13161   }
13162   if (*p == '\0')
13163     {
13164       /*
13165         Button press is outside any tile.
13166       */
13167       (void) XBell(display,0);
13168       return((Image *) NULL);
13169     }
13170   /*
13171     Select a command from the pop-up menu.
13172   */
13173   id=XMenuWidget(display,windows,"Tile Verb",VerbMenu,command);
13174   if (id < 0)
13175     return((Image *) NULL);
13176   q=p;
13177   while ((*q != '\n') && (*q != '\0'))
13178     q++;
13179   (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13180   /*
13181     Perform command for the selected tile.
13182   */
13183   XSetCursorState(display,windows,MagickTrue);
13184   XCheckRefreshWindows(display,windows);
13185   tile_image=NewImageList();
13186   switch (TileCommands[id])
13187   {
13188     case TileLoadCommand:
13189     {
13190       /*
13191         Load tile image.
13192       */
13193       XCheckRefreshWindows(display,windows);
13194       (void) CopyMagickString(resource_info->image_info->magick,"MIFF",
13195         MaxTextExtent);
13196       (void) CopyMagickString(resource_info->image_info->filename,filename,
13197         MaxTextExtent);
13198       tile_image=ReadImage(resource_info->image_info,exception);
13199       CatchException(exception);
13200       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13201       break;
13202     }
13203     case TileNextCommand:
13204     {
13205       /*
13206         Display next image.
13207       */
13208       XClientMessage(display,windows->image.id,windows->im_protocols,
13209         windows->im_next_image,CurrentTime);
13210       break;
13211     }
13212     case TileFormerCommand:
13213     {
13214       /*
13215         Display former image.
13216       */
13217       XClientMessage(display,windows->image.id,windows->im_protocols,
13218         windows->im_former_image,CurrentTime);
13219       break;
13220     }
13221     case TileDeleteCommand:
13222     {
13223       /*
13224         Delete tile image.
13225       */
13226       if (IsPathAccessible(filename) == MagickFalse)
13227         {
13228           XNoticeWidget(display,windows,"Image file does not exist:",filename);
13229           break;
13230         }
13231       status=XConfirmWidget(display,windows,"Really delete tile",filename);
13232       if (status <= 0)
13233         break;
13234       status=remove_utf8(filename) != 0 ? MagickTrue : MagickFalse;
13235       if (status != MagickFalse)
13236         {
13237           XNoticeWidget(display,windows,"Unable to delete image file:",
13238             filename);
13239           break;
13240         }
13241     }
13242     case TileUpdateCommand:
13243     {
13244       int
13245         x_offset,
13246         y_offset;
13247
13248       PixelInfo
13249         pixel;
13250
13251       Quantum
13252         virtual_pixel[CompositePixelChannel];
13253
13254       register int
13255         j;
13256
13257       register Quantum
13258         *s;
13259
13260       /*
13261         Ensure all the images exist.
13262       */
13263       tile=0;
13264       GetPixelInfo(image,&pixel);
13265       for (p=image->directory; *p != '\0'; p++)
13266       {
13267         CacheView
13268           *image_view;
13269
13270         q=p;
13271         while ((*q != '\n') && (*q != '\0'))
13272           q++;
13273         (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13274         p=q;
13275         if (IsPathAccessible(filename) != MagickFalse)
13276           {
13277             tile++;
13278             continue;
13279           }
13280         /*
13281           Overwrite tile with background color.
13282         */
13283         x_offset=(int) (width*(tile % (((int) image->columns-x)/width))+x);
13284         y_offset=(int) (height*(tile/(((int) image->columns-x)/width))+y);
13285         image_view=AcquireCacheView(image);
13286         (void) GetOneCacheViewVirtualPixel(image_view,0,0,virtual_pixel,
13287           exception);
13288         pixel.red=virtual_pixel[RedPixelChannel];
13289         pixel.green=virtual_pixel[GreenPixelChannel];
13290         pixel.blue=virtual_pixel[BluePixelChannel];
13291         pixel.alpha=virtual_pixel[AlphaPixelChannel];
13292         for (i=0; i < (int) height; i++)
13293         {
13294           s=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,(ssize_t)
13295             y_offset+i,width,1,exception);
13296           if (s == (Quantum *) NULL)
13297             break;
13298           for (j=0; j < (int) width; j++)
13299           {
13300             SetPixelInfoPixel(image,&pixel,s);
13301             s+=GetPixelChannels(image);
13302           }
13303           if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
13304             break;
13305         }
13306         image_view=DestroyCacheView(image_view);
13307         tile++;
13308       }
13309       windows->image.window_changes.width=(int) image->columns;
13310       windows->image.window_changes.height=(int) image->rows;
13311       XConfigureImageColormap(display,resource_info,windows,image,exception);
13312       (void) XConfigureImage(display,resource_info,windows,image,exception);
13313       break;
13314     }
13315     default:
13316       break;
13317   }
13318   XSetCursorState(display,windows,MagickFalse);
13319   return(tile_image);
13320 }
13321 \f
13322 /*
13323 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13324 %                                                                             %
13325 %                                                                             %
13326 %                                                                             %
13327 +   X T r a n s l a t e I m a g e                                             %
13328 %                                                                             %
13329 %                                                                             %
13330 %                                                                             %
13331 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13332 %
13333 %  XTranslateImage() translates the image within an Image window by one pixel
13334 %  as specified by the key symbol.  If the image has a `montage string the
13335 %  translation is respect to the width and height contained within the string.
13336 %
13337 %  The format of the XTranslateImage method is:
13338 %
13339 %      void XTranslateImage(Display *display,XWindows *windows,
13340 %        Image *image,const KeySym key_symbol)
13341 %
13342 %  A description of each parameter follows:
13343 %
13344 %    o display: Specifies a connection to an X server; returned from
13345 %      XOpenDisplay.
13346 %
13347 %    o windows: Specifies a pointer to a XWindows structure.
13348 %
13349 %    o image: the image.
13350 %
13351 %    o key_symbol: Specifies a KeySym which indicates which side of the image
13352 %      to trim.
13353 %
13354 */
13355 static void XTranslateImage(Display *display,XWindows *windows,
13356   Image *image,const KeySym key_symbol)
13357 {
13358   char
13359     text[MaxTextExtent];
13360
13361   int
13362     x,
13363     y;
13364
13365   unsigned int
13366     x_offset,
13367     y_offset;
13368
13369   /*
13370     User specified a pan position offset.
13371   */
13372   x_offset=windows->image.width;
13373   y_offset=windows->image.height;
13374   if (image->montage != (char *) NULL)
13375     (void) XParseGeometry(image->montage,&x,&y,&x_offset,&y_offset);
13376   switch ((int) key_symbol)
13377   {
13378     case XK_Home:
13379     case XK_KP_Home:
13380     {
13381       windows->image.x=(int) windows->image.width/2;
13382       windows->image.y=(int) windows->image.height/2;
13383       break;
13384     }
13385     case XK_Left:
13386     case XK_KP_Left:
13387     {
13388       windows->image.x-=x_offset;
13389       break;
13390     }
13391     case XK_Next:
13392     case XK_Up:
13393     case XK_KP_Up:
13394     {
13395       windows->image.y-=y_offset;
13396       break;
13397     }
13398     case XK_Right:
13399     case XK_KP_Right:
13400     {
13401       windows->image.x+=x_offset;
13402       break;
13403     }
13404     case XK_Prior:
13405     case XK_Down:
13406     case XK_KP_Down:
13407     {
13408       windows->image.y+=y_offset;
13409       break;
13410     }
13411     default:
13412       return;
13413   }
13414   /*
13415     Check boundary conditions.
13416   */
13417   if (windows->image.x < 0)
13418     windows->image.x=0;
13419   else
13420     if ((int) (windows->image.x+windows->image.width) >
13421         windows->image.ximage->width)
13422       windows->image.x=(int) windows->image.ximage->width-windows->image.width;
13423   if (windows->image.y < 0)
13424     windows->image.y=0;
13425   else
13426     if ((int) (windows->image.y+windows->image.height) >
13427         windows->image.ximage->height)
13428       windows->image.y=(int) windows->image.ximage->height-windows->image.height;
13429   /*
13430     Refresh Image window.
13431   */
13432   (void) FormatLocaleString(text,MaxTextExtent," %ux%u%+d%+d ",
13433     windows->image.width,windows->image.height,windows->image.x,
13434     windows->image.y);
13435   XInfoWidget(display,windows,text);
13436   XCheckRefreshWindows(display,windows);
13437   XDrawPanRectangle(display,windows);
13438   XRefreshWindow(display,&windows->image,(XEvent *) NULL);
13439   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13440 }
13441 \f
13442 /*
13443 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13444 %                                                                             %
13445 %                                                                             %
13446 %                                                                             %
13447 +   X T r i m I m a g e                                                       %
13448 %                                                                             %
13449 %                                                                             %
13450 %                                                                             %
13451 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13452 %
13453 %  XTrimImage() trims the edges from the Image window.
13454 %
13455 %  The format of the XTrimImage method is:
13456 %
13457 %      MagickBooleanType XTrimImage(Display *display,
13458 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
13459 %        ExceptionInfo *exception)
13460 %
13461 %  A description of each parameter follows:
13462 %
13463 %    o display: Specifies a connection to an X server; returned from
13464 %      XOpenDisplay.
13465 %
13466 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13467 %
13468 %    o windows: Specifies a pointer to a XWindows structure.
13469 %
13470 %    o image: the image.
13471 %
13472 %    o exception: return any errors or warnings in this structure.
13473 %
13474 */
13475 static MagickBooleanType XTrimImage(Display *display,
13476   XResourceInfo *resource_info,XWindows *windows,Image *image,
13477   ExceptionInfo *exception)
13478 {
13479   RectangleInfo
13480     trim_info;
13481
13482   register int
13483     x,
13484     y;
13485
13486   size_t
13487     background,
13488     pixel;
13489
13490   /*
13491     Trim edges from image.
13492   */
13493   XSetCursorState(display,windows,MagickTrue);
13494   XCheckRefreshWindows(display,windows);
13495   /*
13496     Crop the left edge.
13497   */
13498   background=XGetPixel(windows->image.ximage,0,0);
13499   trim_info.width=(size_t) windows->image.ximage->width;
13500   for (x=0; x < windows->image.ximage->width; x++)
13501   {
13502     for (y=0; y < windows->image.ximage->height; y++)
13503     {
13504       pixel=XGetPixel(windows->image.ximage,x,y);
13505       if (pixel != background)
13506         break;
13507     }
13508     if (y < windows->image.ximage->height)
13509       break;
13510   }
13511   trim_info.x=(ssize_t) x;
13512   if (trim_info.x == (ssize_t) windows->image.ximage->width)
13513     {
13514       XSetCursorState(display,windows,MagickFalse);
13515       return(MagickFalse);
13516     }
13517   /*
13518     Crop the right edge.
13519   */
13520   background=XGetPixel(windows->image.ximage,windows->image.ximage->width-1,0);
13521   for (x=windows->image.ximage->width-1; x != 0; x--)
13522   {
13523     for (y=0; y < windows->image.ximage->height; y++)
13524     {
13525       pixel=XGetPixel(windows->image.ximage,x,y);
13526       if (pixel != background)
13527         break;
13528     }
13529     if (y < windows->image.ximage->height)
13530       break;
13531   }
13532   trim_info.width=(size_t) (x-trim_info.x+1);
13533   /*
13534     Crop the top edge.
13535   */
13536   background=XGetPixel(windows->image.ximage,0,0);
13537   trim_info.height=(size_t) windows->image.ximage->height;
13538   for (y=0; y < windows->image.ximage->height; y++)
13539   {
13540     for (x=0; x < windows->image.ximage->width; x++)
13541     {
13542       pixel=XGetPixel(windows->image.ximage,x,y);
13543       if (pixel != background)
13544         break;
13545     }
13546     if (x < windows->image.ximage->width)
13547       break;
13548   }
13549   trim_info.y=(ssize_t) y;
13550   /*
13551     Crop the bottom edge.
13552   */
13553   background=XGetPixel(windows->image.ximage,0,windows->image.ximage->height-1);
13554   for (y=windows->image.ximage->height-1; y != 0; y--)
13555   {
13556     for (x=0; x < windows->image.ximage->width; x++)
13557     {
13558       pixel=XGetPixel(windows->image.ximage,x,y);
13559       if (pixel != background)
13560         break;
13561     }
13562     if (x < windows->image.ximage->width)
13563       break;
13564   }
13565   trim_info.height=(size_t) y-trim_info.y+1;
13566   if (((unsigned int) trim_info.width != windows->image.width) ||
13567       ((unsigned int) trim_info.height != windows->image.height))
13568     {
13569       /*
13570         Reconfigure Image window as defined by the trimming rectangle.
13571       */
13572       XSetCropGeometry(display,windows,&trim_info,image);
13573       windows->image.window_changes.width=(int) trim_info.width;
13574       windows->image.window_changes.height=(int) trim_info.height;
13575       (void) XConfigureImage(display,resource_info,windows,image,exception);
13576     }
13577   XSetCursorState(display,windows,MagickFalse);
13578   return(MagickTrue);
13579 }
13580 \f
13581 /*
13582 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13583 %                                                                             %
13584 %                                                                             %
13585 %                                                                             %
13586 +   X V i s u a l D i r e c t o r y I m a g e                                 %
13587 %                                                                             %
13588 %                                                                             %
13589 %                                                                             %
13590 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13591 %
13592 %  XVisualDirectoryImage() creates a Visual Image Directory.
13593 %
13594 %  The format of the XVisualDirectoryImage method is:
13595 %
13596 %      Image *XVisualDirectoryImage(Display *display,
13597 %        XResourceInfo *resource_info,XWindows *windows,
13598 %        ExceptionInfo *exception)
13599 %
13600 %  A description of each parameter follows:
13601 %
13602 %    o display: Specifies a connection to an X server; returned from
13603 %      XOpenDisplay.
13604 %
13605 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13606 %
13607 %    o windows: Specifies a pointer to a XWindows structure.
13608 %
13609 %    o exception: return any errors or warnings in this structure.
13610 %
13611 */
13612 static Image *XVisualDirectoryImage(Display *display,
13613   XResourceInfo *resource_info,XWindows *windows,ExceptionInfo *exception)
13614 {
13615 #define TileImageTag  "Scale/Image"
13616 #define XClientName  "montage"
13617
13618   char
13619     **filelist;
13620
13621   Image
13622     *images,
13623     *montage_image,
13624     *next_image,
13625     *thumbnail_image;
13626
13627   ImageInfo
13628     *read_info;
13629
13630   int
13631     number_files;
13632
13633   MagickBooleanType
13634     backdrop;
13635
13636   MagickStatusType
13637     status;
13638
13639   MontageInfo
13640     *montage_info;
13641
13642   RectangleInfo
13643     geometry;
13644
13645   register int
13646     i;
13647
13648   static char
13649     filename[MaxTextExtent] = "\0",
13650     filenames[MaxTextExtent] = "*";
13651
13652   XResourceInfo
13653     background_resources;
13654
13655   /*
13656     Request file name from user.
13657   */
13658   XFileBrowserWidget(display,windows,"Directory",filenames);
13659   if (*filenames == '\0')
13660     return((Image *) NULL);
13661   /*
13662     Expand the filenames.
13663   */
13664   filelist=(char **) AcquireMagickMemory(sizeof(*filelist));
13665   if (filelist == (char **) NULL)
13666     {
13667       ThrowXWindowFatalException(ResourceLimitError,"MemoryAllocationFailed",
13668         filenames);
13669       return((Image *) NULL);
13670     }
13671   number_files=1;
13672   filelist[0]=filenames;
13673   status=ExpandFilenames(&number_files,&filelist);
13674   if ((status == MagickFalse) || (number_files == 0))
13675     {
13676       if (number_files == 0)
13677         ThrowXWindowFatalException(ImageError,"NoImagesWereFound",filenames)
13678       else
13679         ThrowXWindowFatalException(ResourceLimitError,"MemoryAllocationFailed",
13680           filenames);
13681       return((Image *) NULL);
13682     }
13683   /*
13684     Set image background resources.
13685   */
13686   background_resources=(*resource_info);
13687   background_resources.window_id=AcquireString("");
13688   (void) FormatLocaleString(background_resources.window_id,MaxTextExtent,
13689     "0x%lx",windows->image.id);
13690   background_resources.backdrop=MagickTrue;
13691   /*
13692     Read each image and convert them to a tile.
13693   */
13694   backdrop=(windows->visual_info->klass == TrueColor) ||
13695     (windows->visual_info->klass == DirectColor) ? MagickTrue : MagickFalse;
13696   read_info=CloneImageInfo(resource_info->image_info);
13697   (void) SetImageOption(read_info,"jpeg:size","120x120");
13698   (void) CloneString(&read_info->size,DefaultTileGeometry);
13699   (void) SetImageInfoProgressMonitor(read_info,(MagickProgressMonitor) NULL,
13700     (void *) NULL);
13701   images=NewImageList();
13702   XSetCursorState(display,windows,MagickTrue);
13703   XCheckRefreshWindows(display,windows);
13704   for (i=0; i < (int) number_files; i++)
13705   {
13706     (void) CopyMagickString(read_info->filename,filelist[i],MaxTextExtent);
13707     filelist[i]=DestroyString(filelist[i]);
13708     *read_info->magick='\0';
13709     next_image=ReadImage(read_info,exception);
13710     CatchException(exception);
13711     if (next_image != (Image *) NULL)
13712       {
13713         (void) DeleteImageProperty(next_image,"label");
13714         (void) SetImageProperty(next_image,"label",InterpretImageProperties(
13715           read_info,next_image,DefaultTileLabel,exception),exception);
13716         (void) ParseRegionGeometry(next_image,read_info->size,&geometry,
13717           exception);
13718         thumbnail_image=ThumbnailImage(next_image,geometry.width,
13719           geometry.height,exception);
13720         if (thumbnail_image != (Image *) NULL)
13721           {
13722             next_image=DestroyImage(next_image);
13723             next_image=thumbnail_image;
13724           }
13725         if (backdrop)
13726           {
13727             (void) XDisplayBackgroundImage(display,&background_resources,
13728               next_image,exception);
13729             XSetCursorState(display,windows,MagickTrue);
13730           }
13731         AppendImageToList(&images,next_image);
13732         if (images->progress_monitor != (MagickProgressMonitor) NULL)
13733           {
13734             MagickBooleanType
13735               proceed;
13736
13737             proceed=SetImageProgress(images,LoadImageTag,(MagickOffsetType) i,
13738               (MagickSizeType) number_files);
13739             if (proceed == MagickFalse)
13740               break;
13741           }
13742       }
13743   }
13744   filelist=(char **) RelinquishMagickMemory(filelist);
13745   if (images == (Image *) NULL)
13746     {
13747       read_info=DestroyImageInfo(read_info);
13748       XSetCursorState(display,windows,MagickFalse);
13749       ThrowXWindowFatalException(ImageError,"NoImagesWereLoaded",filenames);
13750       return((Image *) NULL);
13751     }
13752   /*
13753     Create the Visual Image Directory.
13754   */
13755   montage_info=CloneMontageInfo(read_info,(MontageInfo *) NULL);
13756   montage_info->pointsize=10;
13757   if (resource_info->font != (char *) NULL)
13758     (void) CloneString(&montage_info->font,resource_info->font);
13759   (void) CopyMagickString(montage_info->filename,filename,MaxTextExtent);
13760   montage_image=MontageImageList(read_info,montage_info,GetFirstImageInList(
13761     images),exception);
13762   images=DestroyImageList(images);
13763   montage_info=DestroyMontageInfo(montage_info);
13764   read_info=DestroyImageInfo(read_info);
13765   XSetCursorState(display,windows,MagickFalse);
13766   if (montage_image == (Image *) NULL)
13767     return(montage_image);
13768   XClientMessage(display,windows->image.id,windows->im_protocols,
13769     windows->im_next_image,CurrentTime);
13770   return(montage_image);
13771 }
13772 \f
13773 /*
13774 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13775 %                                                                             %
13776 %                                                                             %
13777 %                                                                             %
13778 %   X D i s p l a y B a c k g r o u n d I m a g e                             %
13779 %                                                                             %
13780 %                                                                             %
13781 %                                                                             %
13782 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13783 %
13784 %  XDisplayBackgroundImage() displays an image in the background of a window.
13785 %
13786 %  The format of the XDisplayBackgroundImage method is:
13787 %
13788 %      MagickBooleanType XDisplayBackgroundImage(Display *display,
13789 %        XResourceInfo *resource_info,Image *image,ExceptionInfo *exception)
13790 %
13791 %  A description of each parameter follows:
13792 %
13793 %    o display: Specifies a connection to an X server;  returned from
13794 %      XOpenDisplay.
13795 %
13796 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13797 %
13798 %    o image: the image.
13799 %
13800 %    o exception: return any errors or warnings in this structure.
13801 %
13802 */
13803 MagickExport MagickBooleanType XDisplayBackgroundImage(Display *display,
13804   XResourceInfo *resource_info,Image *image,ExceptionInfo *exception)
13805 {
13806   char
13807     geometry[MaxTextExtent],
13808     visual_type[MaxTextExtent];
13809
13810   int
13811     height,
13812     status,
13813     width;
13814
13815   RectangleInfo
13816     geometry_info;
13817
13818   static XPixelInfo
13819     pixel;
13820
13821   static XStandardColormap
13822     *map_info;
13823
13824   static XVisualInfo
13825     *visual_info = (XVisualInfo *) NULL;
13826
13827   static XWindowInfo
13828     window_info;
13829
13830   size_t
13831     delay;
13832
13833   Window
13834     root_window;
13835
13836   XGCValues
13837     context_values;
13838
13839   XResourceInfo
13840     resources;
13841
13842   XWindowAttributes
13843     window_attributes;
13844
13845   /*
13846     Determine target window.
13847   */
13848   assert(image != (Image *) NULL);
13849   assert(image->signature == MagickSignature);
13850   if (image->debug != MagickFalse)
13851     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
13852   resources=(*resource_info);
13853   window_info.id=(Window) NULL;
13854   root_window=XRootWindow(display,XDefaultScreen(display));
13855   if (LocaleCompare(resources.window_id,"root") == 0)
13856     window_info.id=root_window;
13857   else
13858     {
13859       if (isdigit((unsigned char) *resources.window_id) != 0)
13860         window_info.id=XWindowByID(display,root_window,
13861           (Window) strtol((char *) resources.window_id,(char **) NULL,0));
13862       if (window_info.id == (Window) NULL)
13863         window_info.id=XWindowByName(display,root_window,resources.window_id);
13864     }
13865   if (window_info.id == (Window) NULL)
13866     {
13867       ThrowXWindowFatalException(XServerError,"NoWindowWithSpecifiedIDExists",
13868         resources.window_id);
13869       return(MagickFalse);
13870     }
13871   /*
13872     Determine window visual id.
13873   */
13874   window_attributes.width=XDisplayWidth(display,XDefaultScreen(display));
13875   window_attributes.height=XDisplayHeight(display,XDefaultScreen(display));
13876   (void) CopyMagickString(visual_type,"default",MaxTextExtent);
13877   status=XGetWindowAttributes(display,window_info.id,&window_attributes);
13878   if (status != 0)
13879     (void) FormatLocaleString(visual_type,MaxTextExtent,"0x%lx",
13880       XVisualIDFromVisual(window_attributes.visual));
13881   if (visual_info == (XVisualInfo *) NULL)
13882     {
13883       /*
13884         Allocate standard colormap.
13885       */
13886       map_info=XAllocStandardColormap();
13887       if (map_info == (XStandardColormap *) NULL)
13888         ThrowXWindowFatalException(XServerFatalError,"MemoryAllocationFailed",
13889           image->filename);
13890       map_info->colormap=(Colormap) NULL;
13891       pixel.pixels=(unsigned long *) NULL;
13892       /*
13893         Initialize visual info.
13894       */
13895       resources.map_type=(char *) NULL;
13896       resources.visual_type=visual_type;
13897       visual_info=XBestVisualInfo(display,map_info,&resources);
13898       if (visual_info == (XVisualInfo *) NULL)
13899         ThrowXWindowFatalException(XServerFatalError,"UnableToGetVisual",
13900           resources.visual_type);
13901       /*
13902         Initialize window info.
13903       */
13904       window_info.ximage=(XImage *) NULL;
13905       window_info.matte_image=(XImage *) NULL;
13906       window_info.pixmap=(Pixmap) NULL;
13907       window_info.matte_pixmap=(Pixmap) NULL;
13908     }
13909   /*
13910     Free previous root colors.
13911   */
13912   if (window_info.id == root_window)
13913     (void) XDestroyWindowColors(display,root_window);
13914   /*
13915     Initialize Standard Colormap.
13916   */
13917   resources.colormap=SharedColormap;
13918   XMakeStandardColormap(display,visual_info,&resources,image,map_info,&pixel,
13919     exception);
13920   /*
13921     Graphic context superclass.
13922   */
13923   context_values.background=pixel.background_color.pixel;
13924   context_values.foreground=pixel.foreground_color.pixel;
13925   pixel.annotate_context=XCreateGC(display,window_info.id,
13926     (size_t) (GCBackground | GCForeground),&context_values);
13927   if (pixel.annotate_context == (GC) NULL)
13928     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
13929       image->filename);
13930   /*
13931     Initialize Image window attributes.
13932   */
13933   window_info.name=AcquireString("\0");
13934   window_info.icon_name=AcquireString("\0");
13935   XGetWindowInfo(display,visual_info,map_info,&pixel,(XFontStruct *) NULL,
13936     &resources,&window_info);
13937   /*
13938     Create the X image.
13939   */
13940   window_info.width=(unsigned int) image->columns;
13941   window_info.height=(unsigned int) image->rows;
13942   if ((image->columns != window_info.width) ||
13943       (image->rows != window_info.height))
13944     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13945       image->filename);
13946   (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>",
13947     window_attributes.width,window_attributes.height);
13948   geometry_info.width=window_info.width;
13949   geometry_info.height=window_info.height;
13950   geometry_info.x=(ssize_t) window_info.x;
13951   geometry_info.y=(ssize_t) window_info.y;
13952   (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
13953     &geometry_info.width,&geometry_info.height);
13954   window_info.width=(unsigned int) geometry_info.width;
13955   window_info.height=(unsigned int) geometry_info.height;
13956   window_info.x=(int) geometry_info.x;
13957   window_info.y=(int) geometry_info.y;
13958   status=XMakeImage(display,&resources,&window_info,image,window_info.width,
13959     window_info.height,exception);
13960   if (status == MagickFalse)
13961     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13962       image->filename);
13963   window_info.x=0;
13964   window_info.y=0;
13965   if (image->debug != MagickFalse)
13966     {
13967       (void) LogMagickEvent(X11Event,GetMagickModule(),
13968         "Image: %s[%.20g] %.20gx%.20g ",image->filename,(double) image->scene,
13969         (double) image->columns,(double) image->rows);
13970       if (image->colors != 0)
13971         (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
13972           image->colors);
13973       (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",image->magick);
13974     }
13975   /*
13976     Adjust image dimensions as specified by backdrop or geometry options.
13977   */
13978   width=(int) window_info.width;
13979   height=(int) window_info.height;
13980   if (resources.backdrop != MagickFalse)
13981     {
13982       /*
13983         Center image on window.
13984       */
13985       window_info.x=(window_attributes.width/2)-
13986         (window_info.ximage->width/2);
13987       window_info.y=(window_attributes.height/2)-
13988         (window_info.ximage->height/2);
13989       width=window_attributes.width;
13990       height=window_attributes.height;
13991     }
13992   if ((resources.image_geometry != (char *) NULL) &&
13993       (*resources.image_geometry != '\0'))
13994     {
13995       char
13996         default_geometry[MaxTextExtent];
13997
13998       int
13999         flags,
14000         gravity;
14001
14002       XSizeHints
14003         *size_hints;
14004
14005       /*
14006         User specified geometry.
14007       */
14008       size_hints=XAllocSizeHints();
14009       if (size_hints == (XSizeHints *) NULL)
14010         ThrowXWindowFatalException(ResourceLimitFatalError,
14011           "MemoryAllocationFailed",image->filename);
14012       size_hints->flags=0L;
14013       (void) FormatLocaleString(default_geometry,MaxTextExtent,"%dx%d",
14014         width,height);
14015       flags=XWMGeometry(display,visual_info->screen,resources.image_geometry,
14016         default_geometry,window_info.border_width,size_hints,&window_info.x,
14017         &window_info.y,&width,&height,&gravity);
14018       if (flags & (XValue | YValue))
14019         {
14020           width=window_attributes.width;
14021           height=window_attributes.height;
14022         }
14023       (void) XFree((void *) size_hints);
14024     }
14025   /*
14026     Create the X pixmap.
14027   */
14028   window_info.pixmap=XCreatePixmap(display,window_info.id,(unsigned int) width,
14029     (unsigned int) height,window_info.depth);
14030   if (window_info.pixmap == (Pixmap) NULL)
14031     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXPixmap",
14032       image->filename);
14033   /*
14034     Display pixmap on the window.
14035   */
14036   if (((unsigned int) width > window_info.width) ||
14037       ((unsigned int) height > window_info.height))
14038     (void) XFillRectangle(display,window_info.pixmap,
14039       window_info.annotate_context,0,0,(unsigned int) width,
14040       (unsigned int) height);
14041   (void) XPutImage(display,window_info.pixmap,window_info.annotate_context,
14042     window_info.ximage,0,0,window_info.x,window_info.y,(unsigned int)
14043     window_info.width,(unsigned int) window_info.height);
14044   (void) XSetWindowBackgroundPixmap(display,window_info.id,window_info.pixmap);
14045   (void) XClearWindow(display,window_info.id);
14046   delay=1000*image->delay/MagickMax(image->ticks_per_second,1L);
14047   XDelay(display,delay == 0UL ? 10UL : delay);
14048   (void) XSync(display,MagickFalse);
14049   return(window_info.id == root_window ? MagickTrue : MagickFalse);
14050 }
14051 \f
14052 /*
14053 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
14054 %                                                                             %
14055 %                                                                             %
14056 %                                                                             %
14057 +   X D i s p l a y I m a g e                                                 %
14058 %                                                                             %
14059 %                                                                             %
14060 %                                                                             %
14061 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
14062 %
14063 %  XDisplayImage() displays an image via X11.  A new image is created and
14064 %  returned if the user interactively transforms the displayed image.
14065 %
14066 %  The format of the XDisplayImage method is:
14067 %
14068 %      Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
14069 %        char **argv,int argc,Image **image,size_t *state,
14070 %        ExceptionInfo *exception)
14071 %
14072 %  A description of each parameter follows:
14073 %
14074 %    o nexus:  Method XDisplayImage returns an image when the
14075 %      user chooses 'Open Image' from the command menu or picks a tile
14076 %      from the image directory.  Otherwise a null image is returned.
14077 %
14078 %    o display: Specifies a connection to an X server;  returned from
14079 %      XOpenDisplay.
14080 %
14081 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
14082 %
14083 %    o argv: Specifies the application's argument list.
14084 %
14085 %    o argc: Specifies the number of arguments.
14086 %
14087 %    o image: Specifies an address to an address of an Image structure;
14088 %
14089 %    o exception: return any errors or warnings in this structure.
14090 %
14091 */
14092 MagickExport Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
14093   char **argv,int argc,Image **image,size_t *state,ExceptionInfo *exception)
14094 {
14095 #define MagnifySize  256  /* must be a power of 2 */
14096 #define MagickMenus  10
14097 #define MagickTitle  "Commands"
14098
14099   static const char
14100     *CommandMenu[] =
14101     {
14102       "File",
14103       "Edit",
14104       "View",
14105       "Transform",
14106       "Enhance",
14107       "Effects",
14108       "F/X",
14109       "Image Edit",
14110       "Miscellany",
14111       "Help",
14112       (char *) NULL
14113     },
14114     *FileMenu[] =
14115     {
14116       "Open...",
14117       "Next",
14118       "Former",
14119       "Select...",
14120       "Save...",
14121       "Print...",
14122       "Delete...",
14123       "New...",
14124       "Visual Directory...",
14125       "Quit",
14126       (char *) NULL
14127     },
14128     *EditMenu[] =
14129     {
14130       "Undo",
14131       "Redo",
14132       "Cut",
14133       "Copy",
14134       "Paste",
14135       (char *) NULL
14136     },
14137     *ViewMenu[] =
14138     {
14139       "Half Size",
14140       "Original Size",
14141       "Double Size",
14142       "Resize...",
14143       "Apply",
14144       "Refresh",
14145       "Restore",
14146       (char *) NULL
14147     },
14148     *TransformMenu[] =
14149     {
14150       "Crop",
14151       "Chop",
14152       "Flop",
14153       "Flip",
14154       "Rotate Right",
14155       "Rotate Left",
14156       "Rotate...",
14157       "Shear...",
14158       "Roll...",
14159       "Trim Edges",
14160       (char *) NULL
14161     },
14162     *EnhanceMenu[] =
14163     {
14164       "Hue...",
14165       "Saturation...",
14166       "Brightness...",
14167       "Gamma...",
14168       "Spiff",
14169       "Dull",
14170       "Contrast Stretch...",
14171       "Sigmoidal Contrast...",
14172       "Normalize",
14173       "Equalize",
14174       "Negate",
14175       "Grayscale",
14176       "Map...",
14177       "Quantize...",
14178       (char *) NULL
14179     },
14180     *EffectsMenu[] =
14181     {
14182       "Despeckle",
14183       "Emboss",
14184       "Reduce Noise",
14185       "Add Noise...",
14186       "Sharpen...",
14187       "Blur...",
14188       "Threshold...",
14189       "Edge Detect...",
14190       "Spread...",
14191       "Shade...",
14192       "Raise...",
14193       "Segment...",
14194       (char *) NULL
14195     },
14196     *FXMenu[] =
14197     {
14198       "Solarize...",
14199       "Sepia Tone...",
14200       "Swirl...",
14201       "Implode...",
14202       "Vignette...",
14203       "Wave...",
14204       "Oil Paint...",
14205       "Charcoal Draw...",
14206       (char *) NULL
14207     },
14208     *ImageEditMenu[] =
14209     {
14210       "Annotate...",
14211       "Draw...",
14212       "Color...",
14213       "Matte...",
14214       "Composite...",
14215       "Add Border...",
14216       "Add Frame...",
14217       "Comment...",
14218       "Launch...",
14219       "Region of Interest...",
14220       (char *) NULL
14221     },
14222     *MiscellanyMenu[] =
14223     {
14224       "Image Info",
14225       "Zoom Image",
14226       "Show Preview...",
14227       "Show Histogram",
14228       "Show Matte",
14229       "Background...",
14230       "Slide Show...",
14231       "Preferences...",
14232       (char *) NULL
14233     },
14234     *HelpMenu[] =
14235     {
14236       "Overview",
14237       "Browse Documentation",
14238       "About Display",
14239       (char *) NULL
14240     },
14241     *ShortCutsMenu[] =
14242     {
14243       "Next",
14244       "Former",
14245       "Open...",
14246       "Save...",
14247       "Print...",
14248       "Undo",
14249       "Restore",
14250       "Image Info",
14251       "Quit",
14252       (char *) NULL
14253     },
14254     *VirtualMenu[] =
14255     {
14256       "Image Info",
14257       "Print",
14258       "Next",
14259       "Quit",
14260       (char *) NULL
14261     };
14262
14263   static const char
14264     **Menus[MagickMenus] =
14265     {
14266       FileMenu,
14267       EditMenu,
14268       ViewMenu,
14269       TransformMenu,
14270       EnhanceMenu,
14271       EffectsMenu,
14272       FXMenu,
14273       ImageEditMenu,
14274       MiscellanyMenu,
14275       HelpMenu
14276     };
14277
14278   static CommandType
14279     CommandMenus[] =
14280     {
14281       NullCommand,
14282       NullCommand,
14283       NullCommand,
14284       NullCommand,
14285       NullCommand,
14286       NullCommand,
14287       NullCommand,
14288       NullCommand,
14289       NullCommand,
14290       NullCommand,
14291     },
14292     FileCommands[] =
14293     {
14294       OpenCommand,
14295       NextCommand,
14296       FormerCommand,
14297       SelectCommand,
14298       SaveCommand,
14299       PrintCommand,
14300       DeleteCommand,
14301       NewCommand,
14302       VisualDirectoryCommand,
14303       QuitCommand
14304     },
14305     EditCommands[] =
14306     {
14307       UndoCommand,
14308       RedoCommand,
14309       CutCommand,
14310       CopyCommand,
14311       PasteCommand
14312     },
14313     ViewCommands[] =
14314     {
14315       HalfSizeCommand,
14316       OriginalSizeCommand,
14317       DoubleSizeCommand,
14318       ResizeCommand,
14319       ApplyCommand,
14320       RefreshCommand,
14321       RestoreCommand
14322     },
14323     TransformCommands[] =
14324     {
14325       CropCommand,
14326       ChopCommand,
14327       FlopCommand,
14328       FlipCommand,
14329       RotateRightCommand,
14330       RotateLeftCommand,
14331       RotateCommand,
14332       ShearCommand,
14333       RollCommand,
14334       TrimCommand
14335     },
14336     EnhanceCommands[] =
14337     {
14338       HueCommand,
14339       SaturationCommand,
14340       BrightnessCommand,
14341       GammaCommand,
14342       SpiffCommand,
14343       DullCommand,
14344       ContrastStretchCommand,
14345       SigmoidalContrastCommand,
14346       NormalizeCommand,
14347       EqualizeCommand,
14348       NegateCommand,
14349       GrayscaleCommand,
14350       MapCommand,
14351       QuantizeCommand
14352     },
14353     EffectsCommands[] =
14354     {
14355       DespeckleCommand,
14356       EmbossCommand,
14357       ReduceNoiseCommand,
14358       AddNoiseCommand,
14359       SharpenCommand,
14360       BlurCommand,
14361       ThresholdCommand,
14362       EdgeDetectCommand,
14363       SpreadCommand,
14364       ShadeCommand,
14365       RaiseCommand,
14366       SegmentCommand
14367     },
14368     FXCommands[] =
14369     {
14370       SolarizeCommand,
14371       SepiaToneCommand,
14372       SwirlCommand,
14373       ImplodeCommand,
14374       VignetteCommand,
14375       WaveCommand,
14376       OilPaintCommand,
14377       CharcoalDrawCommand
14378     },
14379     ImageEditCommands[] =
14380     {
14381       AnnotateCommand,
14382       DrawCommand,
14383       ColorCommand,
14384       MatteCommand,
14385       CompositeCommand,
14386       AddBorderCommand,
14387       AddFrameCommand,
14388       CommentCommand,
14389       LaunchCommand,
14390       RegionofInterestCommand
14391     },
14392     MiscellanyCommands[] =
14393     {
14394       InfoCommand,
14395       ZoomCommand,
14396       ShowPreviewCommand,
14397       ShowHistogramCommand,
14398       ShowMatteCommand,
14399       BackgroundCommand,
14400       SlideShowCommand,
14401       PreferencesCommand
14402     },
14403     HelpCommands[] =
14404     {
14405       HelpCommand,
14406       BrowseDocumentationCommand,
14407       VersionCommand
14408     },
14409     ShortCutsCommands[] =
14410     {
14411       NextCommand,
14412       FormerCommand,
14413       OpenCommand,
14414       SaveCommand,
14415       PrintCommand,
14416       UndoCommand,
14417       RestoreCommand,
14418       InfoCommand,
14419       QuitCommand
14420     },
14421     VirtualCommands[] =
14422     {
14423       InfoCommand,
14424       PrintCommand,
14425       NextCommand,
14426       QuitCommand
14427     };
14428
14429   static CommandType
14430     *Commands[MagickMenus] =
14431     {
14432       FileCommands,
14433       EditCommands,
14434       ViewCommands,
14435       TransformCommands,
14436       EnhanceCommands,
14437       EffectsCommands,
14438       FXCommands,
14439       ImageEditCommands,
14440       MiscellanyCommands,
14441       HelpCommands
14442     };
14443
14444   char
14445     command[MaxTextExtent],
14446     *directory,
14447     geometry[MaxTextExtent],
14448     resource_name[MaxTextExtent];
14449
14450   CommandType
14451     command_type;
14452
14453   Image
14454     *display_image,
14455     *nexus;
14456
14457   int
14458     entry,
14459     id;
14460
14461   KeySym
14462     key_symbol;
14463
14464   MagickStatusType
14465     context_mask,
14466     status;
14467
14468   RectangleInfo
14469     geometry_info;
14470
14471   register int
14472     i;
14473
14474   static char
14475     working_directory[MaxTextExtent];
14476
14477   static XPoint
14478     vid_info;
14479
14480   static XWindowInfo
14481     *magick_windows[MaxXWindows];
14482
14483   static unsigned int
14484     number_windows;
14485
14486   struct stat
14487     attributes;
14488
14489   time_t
14490     timer,
14491     timestamp,
14492     update_time;
14493
14494   unsigned int
14495     height,
14496     width;
14497
14498   size_t
14499     delay;
14500
14501   WarningHandler
14502     warning_handler;
14503
14504   Window
14505     root_window;
14506
14507   XClassHint
14508     *class_hints;
14509
14510   XEvent
14511     event;
14512
14513   XFontStruct
14514     *font_info;
14515
14516   XGCValues
14517     context_values;
14518
14519   XPixelInfo
14520     *icon_pixel,
14521     *pixel;
14522
14523   XResourceInfo
14524     *icon_resources;
14525
14526   XStandardColormap
14527     *icon_map,
14528     *map_info;
14529
14530   XVisualInfo
14531     *icon_visual,
14532     *visual_info;
14533
14534   XWindowChanges
14535     window_changes;
14536
14537   XWindows
14538     *windows;
14539
14540   XWMHints
14541     *manager_hints;
14542
14543   assert(image != (Image **) NULL);
14544   assert((*image)->signature == MagickSignature);
14545   if ((*image)->debug != MagickFalse)
14546     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*image)->filename);
14547   display_image=(*image);
14548   warning_handler=(WarningHandler) NULL;
14549   windows=XSetWindows((XWindows *) ~0);
14550   if (windows != (XWindows *) NULL)
14551     {
14552       int
14553         status;
14554
14555       status=chdir(working_directory);
14556       if (status == -1)
14557         (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
14558           "UnableToOpenFile","%s",working_directory);
14559       warning_handler=resource_info->display_warnings ?
14560         SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
14561       warning_handler=resource_info->display_warnings ?
14562         SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
14563     }
14564   else
14565     {
14566       /*
14567         Allocate windows structure.
14568       */
14569       resource_info->colors=display_image->colors;
14570       windows=XSetWindows(XInitializeWindows(display,resource_info));
14571       if (windows == (XWindows *) NULL)
14572         ThrowXWindowFatalException(XServerFatalError,"UnableToCreateWindow",
14573           (*image)->filename);
14574       /*
14575         Initialize window id's.
14576       */
14577       number_windows=0;
14578       magick_windows[number_windows++]=(&windows->icon);
14579       magick_windows[number_windows++]=(&windows->backdrop);
14580       magick_windows[number_windows++]=(&windows->image);
14581       magick_windows[number_windows++]=(&windows->info);
14582       magick_windows[number_windows++]=(&windows->command);
14583       magick_windows[number_windows++]=(&windows->widget);
14584       magick_windows[number_windows++]=(&windows->popup);
14585       magick_windows[number_windows++]=(&windows->magnify);
14586       magick_windows[number_windows++]=(&windows->pan);
14587       for (i=0; i < (int) number_windows; i++)
14588         magick_windows[i]->id=(Window) NULL;
14589       vid_info.x=0;
14590       vid_info.y=0;
14591     }
14592   /*
14593     Initialize font info.
14594   */
14595   if (windows->font_info != (XFontStruct *) NULL)
14596     (void) XFreeFont(display,windows->font_info);
14597   windows->font_info=XBestFont(display,resource_info,MagickFalse);
14598   if (windows->font_info == (XFontStruct *) NULL)
14599     ThrowXWindowFatalException(XServerFatalError,"UnableToLoadFont",
14600       resource_info->font);
14601   /*
14602     Initialize Standard Colormap.
14603   */
14604   map_info=windows->map_info;
14605   icon_map=windows->icon_map;
14606   visual_info=windows->visual_info;
14607   icon_visual=windows->icon_visual;
14608   pixel=windows->pixel_info;
14609   icon_pixel=windows->icon_pixel;
14610   font_info=windows->font_info;
14611   icon_resources=windows->icon_resources;
14612   class_hints=windows->class_hints;
14613   manager_hints=windows->manager_hints;
14614   root_window=XRootWindow(display,visual_info->screen);
14615   nexus=NewImageList();
14616   if (display_image->debug != MagickFalse)
14617     {
14618       (void) LogMagickEvent(X11Event,GetMagickModule(),
14619         "Image: %s[%.20g] %.20gx%.20g ",display_image->filename,
14620         (double) display_image->scene,(double) display_image->columns,
14621         (double) display_image->rows);
14622       if (display_image->colors != 0)
14623         (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
14624           display_image->colors);
14625       (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",
14626         display_image->magick);
14627     }
14628   XMakeStandardColormap(display,visual_info,resource_info,display_image,
14629     map_info,pixel,exception);
14630   display_image->taint=MagickFalse;
14631   /*
14632     Initialize graphic context.
14633   */
14634   windows->context.id=(Window) NULL;
14635   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14636     resource_info,&windows->context);
14637   (void) CloneString(&class_hints->res_name,resource_info->client_name);
14638   (void) CloneString(&class_hints->res_class,resource_info->client_name);
14639   class_hints->res_class[0]=(char) toupper((int) class_hints->res_class[0]);
14640   manager_hints->flags=InputHint | StateHint;
14641   manager_hints->input=MagickFalse;
14642   manager_hints->initial_state=WithdrawnState;
14643   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14644     &windows->context);
14645   if (display_image->debug != MagickFalse)
14646     (void) LogMagickEvent(X11Event,GetMagickModule(),
14647       "Window id: 0x%lx (context)",windows->context.id);
14648   context_values.background=pixel->background_color.pixel;
14649   context_values.font=font_info->fid;
14650   context_values.foreground=pixel->foreground_color.pixel;
14651   context_values.graphics_exposures=MagickFalse;
14652   context_mask=(MagickStatusType)
14653     (GCBackground | GCFont | GCForeground | GCGraphicsExposures);
14654   if (pixel->annotate_context != (GC) NULL)
14655     (void) XFreeGC(display,pixel->annotate_context);
14656   pixel->annotate_context=XCreateGC(display,windows->context.id,
14657     context_mask,&context_values);
14658   if (pixel->annotate_context == (GC) NULL)
14659     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14660       display_image->filename);
14661   context_values.background=pixel->depth_color.pixel;
14662   if (pixel->widget_context != (GC) NULL)
14663     (void) XFreeGC(display,pixel->widget_context);
14664   pixel->widget_context=XCreateGC(display,windows->context.id,context_mask,
14665     &context_values);
14666   if (pixel->widget_context == (GC) NULL)
14667     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14668       display_image->filename);
14669   context_values.background=pixel->foreground_color.pixel;
14670   context_values.foreground=pixel->background_color.pixel;
14671   context_values.plane_mask=context_values.background ^
14672     context_values.foreground;
14673   if (pixel->highlight_context != (GC) NULL)
14674     (void) XFreeGC(display,pixel->highlight_context);
14675   pixel->highlight_context=XCreateGC(display,windows->context.id,
14676     (size_t) (context_mask | GCPlaneMask),&context_values);
14677   if (pixel->highlight_context == (GC) NULL)
14678     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14679       display_image->filename);
14680   (void) XDestroyWindow(display,windows->context.id);
14681   /*
14682     Initialize icon window.
14683   */
14684   XGetWindowInfo(display,icon_visual,icon_map,icon_pixel,(XFontStruct *) NULL,
14685     icon_resources,&windows->icon);
14686   windows->icon.geometry=resource_info->icon_geometry;
14687   XBestIconSize(display,&windows->icon,display_image);
14688   windows->icon.attributes.colormap=XDefaultColormap(display,
14689     icon_visual->screen);
14690   windows->icon.attributes.event_mask=ExposureMask | StructureNotifyMask;
14691   manager_hints->flags=InputHint | StateHint;
14692   manager_hints->input=MagickFalse;
14693   manager_hints->initial_state=IconicState;
14694   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14695     &windows->icon);
14696   if (display_image->debug != MagickFalse)
14697     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (icon)",
14698       windows->icon.id);
14699   /*
14700     Initialize graphic context for icon window.
14701   */
14702   if (icon_pixel->annotate_context != (GC) NULL)
14703     (void) XFreeGC(display,icon_pixel->annotate_context);
14704   context_values.background=icon_pixel->background_color.pixel;
14705   context_values.foreground=icon_pixel->foreground_color.pixel;
14706   icon_pixel->annotate_context=XCreateGC(display,windows->icon.id,
14707     (size_t) (GCBackground | GCForeground),&context_values);
14708   if (icon_pixel->annotate_context == (GC) NULL)
14709     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14710       display_image->filename);
14711   windows->icon.annotate_context=icon_pixel->annotate_context;
14712   /*
14713     Initialize Image window.
14714   */
14715   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14716     &windows->image);
14717   windows->image.shape=MagickTrue;  /* non-rectangular shape hint */
14718   if (resource_info->use_shared_memory == MagickFalse)
14719     windows->image.shared_memory=MagickFalse;
14720   if ((resource_info->title != (char *) NULL) && !(*state & MontageImageState))
14721     {
14722       char
14723         *title;
14724
14725       title=InterpretImageProperties(resource_info->image_info,display_image,
14726         resource_info->title,exception);
14727       (void) CopyMagickString(windows->image.name,title,MaxTextExtent);
14728       (void) CopyMagickString(windows->image.icon_name,title,MaxTextExtent);
14729       title=DestroyString(title);
14730     }
14731   else
14732     {
14733       char
14734         filename[MaxTextExtent];
14735
14736       /*
14737         Window name is the base of the filename.
14738       */
14739       GetPathComponent(display_image->magick_filename,TailPath,filename);
14740       if (display_image->scene == 0)
14741         (void) FormatLocaleString(windows->image.name,MaxTextExtent,
14742           "%s: %s",MagickPackageName,filename);
14743       else
14744         (void) FormatLocaleString(windows->image.name,MaxTextExtent,
14745           "%s: %s[scene: %.20g frames: %.20g]",MagickPackageName,filename,
14746           (double) display_image->scene,(double) GetImageListLength(
14747           display_image));
14748       (void) CopyMagickString(windows->image.icon_name,filename,MaxTextExtent);
14749     }
14750   if (resource_info->immutable)
14751     windows->image.immutable=MagickTrue;
14752   windows->image.use_pixmap=resource_info->use_pixmap;
14753   windows->image.geometry=resource_info->image_geometry;
14754   (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>!",
14755     XDisplayWidth(display,visual_info->screen),
14756     XDisplayHeight(display,visual_info->screen));
14757   geometry_info.width=display_image->columns;
14758   geometry_info.height=display_image->rows;
14759   geometry_info.x=0;
14760   geometry_info.y=0;
14761   (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
14762     &geometry_info.width,&geometry_info.height);
14763   windows->image.width=(unsigned int) geometry_info.width;
14764   windows->image.height=(unsigned int) geometry_info.height;
14765   windows->image.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14766     ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14767     KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14768     PropertyChangeMask | StructureNotifyMask | SubstructureNotifyMask;
14769   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14770     resource_info,&windows->backdrop);
14771   if ((resource_info->backdrop) || (windows->backdrop.id != (Window) NULL))
14772     {
14773       /*
14774         Initialize backdrop window.
14775       */
14776       windows->backdrop.x=0;
14777       windows->backdrop.y=0;
14778       (void) CloneString(&windows->backdrop.name,"Backdrop");
14779       windows->backdrop.flags=(size_t) (USSize | USPosition);
14780       windows->backdrop.width=(unsigned int)
14781         XDisplayWidth(display,visual_info->screen);
14782       windows->backdrop.height=(unsigned int)
14783         XDisplayHeight(display,visual_info->screen);
14784       windows->backdrop.border_width=0;
14785       windows->backdrop.immutable=MagickTrue;
14786       windows->backdrop.attributes.do_not_propagate_mask=ButtonPressMask |
14787         ButtonReleaseMask;
14788       windows->backdrop.attributes.event_mask=ButtonPressMask | KeyPressMask |
14789         StructureNotifyMask;
14790       manager_hints->flags=IconWindowHint | InputHint | StateHint;
14791       manager_hints->icon_window=windows->icon.id;
14792       manager_hints->input=MagickTrue;
14793       manager_hints->initial_state=resource_info->iconic ? IconicState :
14794         NormalState;
14795       XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14796         &windows->backdrop);
14797       if (display_image->debug != MagickFalse)
14798         (void) LogMagickEvent(X11Event,GetMagickModule(),
14799           "Window id: 0x%lx (backdrop)",windows->backdrop.id);
14800       (void) XMapWindow(display,windows->backdrop.id);
14801       (void) XClearWindow(display,windows->backdrop.id);
14802       if (windows->image.id != (Window) NULL)
14803         {
14804           (void) XDestroyWindow(display,windows->image.id);
14805           windows->image.id=(Window) NULL;
14806         }
14807       /*
14808         Position image in the center the backdrop.
14809       */
14810       windows->image.flags|=USPosition;
14811       windows->image.x=(XDisplayWidth(display,visual_info->screen)/2)-
14812         (windows->image.width/2);
14813       windows->image.y=(XDisplayHeight(display,visual_info->screen)/2)-
14814         (windows->image.height/2);
14815     }
14816   manager_hints->flags=IconWindowHint | InputHint | StateHint;
14817   manager_hints->icon_window=windows->icon.id;
14818   manager_hints->input=MagickTrue;
14819   manager_hints->initial_state=resource_info->iconic ? IconicState :
14820     NormalState;
14821   if (windows->group_leader.id != (Window) NULL)
14822     {
14823       /*
14824         Follow the leader.
14825       */
14826       manager_hints->flags|=WindowGroupHint;
14827       manager_hints->window_group=windows->group_leader.id;
14828       (void) XSelectInput(display,windows->group_leader.id,StructureNotifyMask);
14829       if (display_image->debug != MagickFalse)
14830         (void) LogMagickEvent(X11Event,GetMagickModule(),
14831           "Window id: 0x%lx (group leader)",windows->group_leader.id);
14832     }
14833   XMakeWindow(display,
14834     (Window) (resource_info->backdrop ? windows->backdrop.id : root_window),
14835     argv,argc,class_hints,manager_hints,&windows->image);
14836   (void) XChangeProperty(display,windows->image.id,windows->im_protocols,
14837     XA_STRING,8,PropModeReplace,(unsigned char *) NULL,0);
14838   if (windows->group_leader.id != (Window) NULL)
14839     (void) XSetTransientForHint(display,windows->image.id,
14840       windows->group_leader.id);
14841   if (display_image->debug != MagickFalse)
14842     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (image)",
14843       windows->image.id);
14844   /*
14845     Initialize Info widget.
14846   */
14847   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14848     &windows->info);
14849   (void) CloneString(&windows->info.name,"Info");
14850   (void) CloneString(&windows->info.icon_name,"Info");
14851   windows->info.border_width=1;
14852   windows->info.x=2;
14853   windows->info.y=2;
14854   windows->info.flags|=PPosition;
14855   windows->info.attributes.win_gravity=UnmapGravity;
14856   windows->info.attributes.event_mask=ButtonPressMask | ExposureMask |
14857     StructureNotifyMask;
14858   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14859   manager_hints->input=MagickFalse;
14860   manager_hints->initial_state=NormalState;
14861   manager_hints->window_group=windows->image.id;
14862   XMakeWindow(display,windows->image.id,argv,argc,class_hints,manager_hints,
14863     &windows->info);
14864   windows->info.highlight_stipple=XCreateBitmapFromData(display,
14865     windows->info.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14866   windows->info.shadow_stipple=XCreateBitmapFromData(display,
14867     windows->info.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14868   (void) XSetTransientForHint(display,windows->info.id,windows->image.id);
14869   if (windows->image.mapped != MagickFalse)
14870     (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14871   if (display_image->debug != MagickFalse)
14872     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (info)",
14873       windows->info.id);
14874   /*
14875     Initialize Command widget.
14876   */
14877   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14878     resource_info,&windows->command);
14879   windows->command.data=MagickMenus;
14880   (void) XCommandWidget(display,windows,CommandMenu,(XEvent *) NULL);
14881   (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.command",
14882     resource_info->client_name);
14883   windows->command.geometry=XGetResourceClass(resource_info->resource_database,
14884     resource_name,"geometry",(char *) NULL);
14885   (void) CloneString(&windows->command.name,MagickTitle);
14886   windows->command.border_width=0;
14887   windows->command.flags|=PPosition;
14888   windows->command.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14889     ButtonReleaseMask | EnterWindowMask | ExposureMask | LeaveWindowMask |
14890     OwnerGrabButtonMask | StructureNotifyMask;
14891   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14892   manager_hints->input=MagickTrue;
14893   manager_hints->initial_state=NormalState;
14894   manager_hints->window_group=windows->image.id;
14895   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14896     &windows->command);
14897   windows->command.highlight_stipple=XCreateBitmapFromData(display,
14898     windows->command.id,(char *) HighlightBitmap,HighlightWidth,
14899     HighlightHeight);
14900   windows->command.shadow_stipple=XCreateBitmapFromData(display,
14901     windows->command.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14902   (void) XSetTransientForHint(display,windows->command.id,windows->image.id);
14903   if (windows->command.mapped != MagickFalse)
14904     (void) XMapRaised(display,windows->command.id);
14905   if (display_image->debug != MagickFalse)
14906     (void) LogMagickEvent(X11Event,GetMagickModule(),
14907       "Window id: 0x%lx (command)",windows->command.id);
14908   /*
14909     Initialize Widget window.
14910   */
14911   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14912     resource_info,&windows->widget);
14913   (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.widget",
14914     resource_info->client_name);
14915   windows->widget.geometry=XGetResourceClass(resource_info->resource_database,
14916     resource_name,"geometry",(char *) NULL);
14917   windows->widget.border_width=0;
14918   windows->widget.flags|=PPosition;
14919   windows->widget.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14920     ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14921     KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14922     StructureNotifyMask;
14923   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14924   manager_hints->input=MagickTrue;
14925   manager_hints->initial_state=NormalState;
14926   manager_hints->window_group=windows->image.id;
14927   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14928     &windows->widget);
14929   windows->widget.highlight_stipple=XCreateBitmapFromData(display,
14930     windows->widget.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14931   windows->widget.shadow_stipple=XCreateBitmapFromData(display,
14932     windows->widget.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14933   (void) XSetTransientForHint(display,windows->widget.id,windows->image.id);
14934   if (display_image->debug != MagickFalse)
14935     (void) LogMagickEvent(X11Event,GetMagickModule(),
14936       "Window id: 0x%lx (widget)",windows->widget.id);
14937   /*
14938     Initialize popup window.
14939   */
14940   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14941     resource_info,&windows->popup);
14942   windows->popup.border_width=0;
14943   windows->popup.flags|=PPosition;
14944   windows->popup.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14945     ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14946     KeyReleaseMask | LeaveWindowMask | StructureNotifyMask;
14947   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14948   manager_hints->input=MagickTrue;
14949   manager_hints->initial_state=NormalState;
14950   manager_hints->window_group=windows->image.id;
14951   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14952     &windows->popup);
14953   windows->popup.highlight_stipple=XCreateBitmapFromData(display,
14954     windows->popup.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14955   windows->popup.shadow_stipple=XCreateBitmapFromData(display,
14956     windows->popup.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14957   (void) XSetTransientForHint(display,windows->popup.id,windows->image.id);
14958   if (display_image->debug != MagickFalse)
14959     (void) LogMagickEvent(X11Event,GetMagickModule(),
14960       "Window id: 0x%lx (pop up)",windows->popup.id);
14961   /*
14962     Initialize Magnify window and cursor.
14963   */
14964   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14965     resource_info,&windows->magnify);
14966   if (resource_info->use_shared_memory == MagickFalse)
14967     windows->magnify.shared_memory=MagickFalse;
14968   (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.magnify",
14969     resource_info->client_name);
14970   windows->magnify.geometry=XGetResourceClass(resource_info->resource_database,
14971     resource_name,"geometry",(char *) NULL);
14972   (void) FormatLocaleString(windows->magnify.name,MaxTextExtent,"Magnify %uX",
14973     resource_info->magnify);
14974   if (windows->magnify.cursor != (Cursor) NULL)
14975     (void) XFreeCursor(display,windows->magnify.cursor);
14976   windows->magnify.cursor=XMakeCursor(display,windows->image.id,
14977     map_info->colormap,resource_info->background_color,
14978     resource_info->foreground_color);
14979   if (windows->magnify.cursor == (Cursor) NULL)
14980     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateCursor",
14981       display_image->filename);
14982   windows->magnify.width=MagnifySize;
14983   windows->magnify.height=MagnifySize;
14984   windows->magnify.flags|=PPosition;
14985   windows->magnify.min_width=MagnifySize;
14986   windows->magnify.min_height=MagnifySize;
14987   windows->magnify.width_inc=MagnifySize;
14988   windows->magnify.height_inc=MagnifySize;
14989   windows->magnify.data=resource_info->magnify;
14990   windows->magnify.attributes.cursor=windows->magnify.cursor;
14991   windows->magnify.attributes.event_mask=ButtonPressMask | ButtonReleaseMask |
14992     ExposureMask | KeyPressMask | KeyReleaseMask | OwnerGrabButtonMask |
14993     StructureNotifyMask;
14994   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14995   manager_hints->input=MagickTrue;
14996   manager_hints->initial_state=NormalState;
14997   manager_hints->window_group=windows->image.id;
14998   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14999     &windows->magnify);
15000   if (display_image->debug != MagickFalse)
15001     (void) LogMagickEvent(X11Event,GetMagickModule(),
15002       "Window id: 0x%lx (magnify)",windows->magnify.id);
15003   (void) XSetTransientForHint(display,windows->magnify.id,windows->image.id);
15004   /*
15005     Initialize panning window.
15006   */
15007   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
15008     resource_info,&windows->pan);
15009   (void) CloneString(&windows->pan.name,"Pan Icon");
15010   windows->pan.width=windows->icon.width;
15011   windows->pan.height=windows->icon.height;
15012   (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.pan",
15013     resource_info->client_name);
15014   windows->pan.geometry=XGetResourceClass(resource_info->resource_database,
15015     resource_name,"geometry",(char *) NULL);
15016   (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
15017     &windows->pan.width,&windows->pan.height);
15018   windows->pan.flags|=PPosition;
15019   windows->pan.immutable=MagickTrue;
15020   windows->pan.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
15021     ButtonReleaseMask | ExposureMask | KeyPressMask | KeyReleaseMask |
15022     StructureNotifyMask;
15023   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
15024   manager_hints->input=MagickFalse;
15025   manager_hints->initial_state=NormalState;
15026   manager_hints->window_group=windows->image.id;
15027   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
15028     &windows->pan);
15029   if (display_image->debug != MagickFalse)
15030     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (pan)",
15031       windows->pan.id);
15032   (void) XSetTransientForHint(display,windows->pan.id,windows->image.id);
15033   if (windows->info.mapped != MagickFalse)
15034     (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15035   if ((windows->image.mapped == MagickFalse) ||
15036       (windows->backdrop.id != (Window) NULL))
15037     (void) XMapWindow(display,windows->image.id);
15038   /*
15039     Set our progress monitor and warning handlers.
15040   */
15041   if (warning_handler == (WarningHandler) NULL)
15042     {
15043       warning_handler=resource_info->display_warnings ?
15044         SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
15045       warning_handler=resource_info->display_warnings ?
15046         SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
15047     }
15048   /*
15049     Initialize Image and Magnify X images.
15050   */
15051   windows->image.x=0;
15052   windows->image.y=0;
15053   windows->magnify.shape=MagickFalse;
15054   width=(unsigned int) display_image->columns;
15055   height=(unsigned int) display_image->rows;
15056   if ((display_image->columns != width) || (display_image->rows != height))
15057     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15058       display_image->filename);
15059   status=XMakeImage(display,resource_info,&windows->image,display_image,
15060     width,height,exception);
15061   if (status == MagickFalse)
15062     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15063       display_image->filename);
15064   status=XMakeImage(display,resource_info,&windows->magnify,(Image *) NULL,
15065     windows->magnify.width,windows->magnify.height,exception);
15066   if (status == MagickFalse)
15067     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15068       display_image->filename);
15069   if (windows->magnify.mapped != MagickFalse)
15070     (void) XMapRaised(display,windows->magnify.id);
15071   if (windows->pan.mapped != MagickFalse)
15072     (void) XMapRaised(display,windows->pan.id);
15073   windows->image.window_changes.width=(int) display_image->columns;
15074   windows->image.window_changes.height=(int) display_image->rows;
15075   (void) XConfigureImage(display,resource_info,windows,display_image,exception);
15076   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15077   (void) XSync(display,MagickFalse);
15078   /*
15079     Respond to events.
15080   */
15081   delay=display_image->delay/MagickMax(display_image->ticks_per_second,1L);
15082   timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15083   update_time=0;
15084   if (resource_info->update != MagickFalse)
15085     {
15086       MagickBooleanType
15087         status;
15088
15089       /*
15090         Determine when file data was last modified.
15091       */
15092       status=GetPathAttributes(display_image->filename,&attributes);
15093       if (status != MagickFalse)
15094         update_time=attributes.st_mtime;
15095     }
15096   *state&=(~FormerImageState);
15097   *state&=(~MontageImageState);
15098   *state&=(~NextImageState);
15099   do
15100   {
15101     /*
15102       Handle a window event.
15103     */
15104     if (windows->image.mapped != MagickFalse)
15105       if ((display_image->delay != 0) || (resource_info->update != 0))
15106         {
15107           if (timer < time((time_t *) NULL))
15108             {
15109               if (resource_info->update == MagickFalse)
15110                 *state|=NextImageState | ExitState;
15111               else
15112                 {
15113                   MagickBooleanType
15114                     status;
15115
15116                   /*
15117                     Determine if image file was modified.
15118                   */
15119                   status=GetPathAttributes(display_image->filename,&attributes);
15120                   if (status != MagickFalse)
15121                     if (update_time != attributes.st_mtime)
15122                       {
15123                         /*
15124                           Redisplay image.
15125                         */
15126                         (void) FormatLocaleString(
15127                           resource_info->image_info->filename,MaxTextExtent,
15128                           "%s:%s",display_image->magick,
15129                           display_image->filename);
15130                         nexus=ReadImage(resource_info->image_info,exception);
15131                         if (nexus != (Image *) NULL)
15132                           {
15133                             nexus=DestroyImage(nexus);
15134                             *state|=NextImageState | ExitState;
15135                           }
15136                       }
15137                   delay=display_image->delay/MagickMax(
15138                     display_image->ticks_per_second,1L);
15139                   timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15140                 }
15141             }
15142           if (XEventsQueued(display,QueuedAfterFlush) == 0)
15143             {
15144               /*
15145                 Do not block if delay > 0.
15146               */
15147               XDelay(display,SuspendTime << 2);
15148               continue;
15149             }
15150         }
15151     timestamp=time((time_t *) NULL);
15152     (void) XNextEvent(display,&event);
15153     if (windows->image.stasis == MagickFalse)
15154       windows->image.stasis=(time((time_t *) NULL)-timestamp) > 0 ?
15155         MagickTrue : MagickFalse;
15156     if (windows->magnify.stasis == MagickFalse)
15157       windows->magnify.stasis=(time((time_t *) NULL)-timestamp) > 0 ?
15158         MagickTrue : MagickFalse;
15159     if (event.xany.window == windows->command.id)
15160       {
15161         /*
15162           Select a command from the Command widget.
15163         */
15164         id=XCommandWidget(display,windows,CommandMenu,&event);
15165         if (id < 0)
15166           continue;
15167         (void) CopyMagickString(command,CommandMenu[id],MaxTextExtent);
15168         command_type=CommandMenus[id];
15169         if (id < MagickMenus)
15170           {
15171             /*
15172               Select a command from a pop-up menu.
15173             */
15174             entry=XMenuWidget(display,windows,CommandMenu[id],Menus[id],
15175               command);
15176             if (entry < 0)
15177               continue;
15178             (void) CopyMagickString(command,Menus[id][entry],MaxTextExtent);
15179             command_type=Commands[id][entry];
15180           }
15181         if (command_type != NullCommand)
15182           nexus=XMagickCommand(display,resource_info,windows,command_type,
15183             &display_image,exception);
15184         continue;
15185       }
15186     switch (event.type)
15187     {
15188       case ButtonPress:
15189       {
15190         if (display_image->debug != MagickFalse)
15191           (void) LogMagickEvent(X11Event,GetMagickModule(),
15192             "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
15193             event.xbutton.button,event.xbutton.x,event.xbutton.y);
15194         if ((event.xbutton.button == Button3) &&
15195             (event.xbutton.state & Mod1Mask))
15196           {
15197             /*
15198               Convert Alt-Button3 to Button2.
15199             */
15200             event.xbutton.button=Button2;
15201             event.xbutton.state&=(~Mod1Mask);
15202           }
15203         if (event.xbutton.window == windows->backdrop.id)
15204           {
15205             (void) XSetInputFocus(display,event.xbutton.window,RevertToParent,
15206               event.xbutton.time);
15207             break;
15208           }
15209         if (event.xbutton.window == windows->image.id)
15210           {
15211             switch (event.xbutton.button)
15212             {
15213               case Button1:
15214               {
15215                 if (resource_info->immutable)
15216                   {
15217                     /*
15218                       Select a command from the Virtual menu.
15219                     */
15220                     entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15221                       command);
15222                     if (entry >= 0)
15223                       nexus=XMagickCommand(display,resource_info,windows,
15224                         VirtualCommands[entry],&display_image,exception);
15225                     break;
15226                   }
15227                 /*
15228                   Map/unmap Command widget.
15229                 */
15230                 if (windows->command.mapped != MagickFalse)
15231                   (void) XWithdrawWindow(display,windows->command.id,
15232                     windows->command.screen);
15233                 else
15234                   {
15235                     (void) XCommandWidget(display,windows,CommandMenu,
15236                       (XEvent *) NULL);
15237                     (void) XMapRaised(display,windows->command.id);
15238                   }
15239                 break;
15240               }
15241               case Button2:
15242               {
15243                 /*
15244                   User pressed the image magnify button.
15245                 */
15246                 (void) XMagickCommand(display,resource_info,windows,ZoomCommand,
15247                   &display_image,exception);
15248                 XMagnifyImage(display,windows,&event,exception);
15249                 break;
15250               }
15251               case Button3:
15252               {
15253                 if (resource_info->immutable)
15254                   {
15255                     /*
15256                       Select a command from the Virtual menu.
15257                     */
15258                     entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15259                       command);
15260                     if (entry >= 0)
15261                       nexus=XMagickCommand(display,resource_info,windows,
15262                         VirtualCommands[entry],&display_image,exception);
15263                     break;
15264                   }
15265                 if (display_image->montage != (char *) NULL)
15266                   {
15267                     /*
15268                       Open or delete a tile from a visual image directory.
15269                     */
15270                     nexus=XTileImage(display,resource_info,windows,
15271                       display_image,&event,exception);
15272                     if (nexus != (Image *) NULL)
15273                       *state|=MontageImageState | NextImageState | ExitState;
15274                     vid_info.x=(short int) windows->image.x;
15275                     vid_info.y=(short int) windows->image.y;
15276                     break;
15277                   }
15278                 /*
15279                   Select a command from the Short Cuts menu.
15280                 */
15281                 entry=XMenuWidget(display,windows,"Short Cuts",ShortCutsMenu,
15282                   command);
15283                 if (entry >= 0)
15284                   nexus=XMagickCommand(display,resource_info,windows,
15285                     ShortCutsCommands[entry],&display_image,exception);
15286                 break;
15287               }
15288               case Button4:
15289               {
15290                 /*
15291                   Wheel up.
15292                 */
15293                 XTranslateImage(display,windows,*image,XK_Up);
15294                 break;
15295               }
15296               case Button5:
15297               {
15298                 /*
15299                   Wheel down.
15300                 */
15301                 XTranslateImage(display,windows,*image,XK_Down);
15302                 break;
15303               }
15304               default:
15305                 break;
15306             }
15307             break;
15308           }
15309         if (event.xbutton.window == windows->magnify.id)
15310           {
15311             int
15312               factor;
15313
15314             static const char
15315               *MagnifyMenu[] =
15316               {
15317                 "2",
15318                 "4",
15319                 "5",
15320                 "6",
15321                 "7",
15322                 "8",
15323                 "9",
15324                 "3",
15325                 (char *) NULL,
15326               };
15327
15328             static KeySym
15329               MagnifyCommands[] =
15330               {
15331                 XK_2,
15332                 XK_4,
15333                 XK_5,
15334                 XK_6,
15335                 XK_7,
15336                 XK_8,
15337                 XK_9,
15338                 XK_3
15339               };
15340
15341             /*
15342               Select a magnify factor from the pop-up menu.
15343             */
15344             factor=XMenuWidget(display,windows,"Magnify",MagnifyMenu,command);
15345             if (factor >= 0)
15346               XMagnifyWindowCommand(display,windows,0,MagnifyCommands[factor],
15347                 exception);
15348             break;
15349           }
15350         if (event.xbutton.window == windows->pan.id)
15351           {
15352             switch (event.xbutton.button)
15353             {
15354               case Button4:
15355               {
15356                 /*
15357                   Wheel up.
15358                 */
15359                 XTranslateImage(display,windows,*image,XK_Up);
15360                 break;
15361               }
15362               case Button5:
15363               {
15364                 /*
15365                   Wheel down.
15366                 */
15367                 XTranslateImage(display,windows,*image,XK_Down);
15368                 break;
15369               }
15370               default:
15371               {
15372                 XPanImage(display,windows,&event,exception);
15373                 break;
15374               }
15375             }
15376             break;
15377           }
15378         delay=display_image->delay/MagickMax(display_image->ticks_per_second,
15379           1L);
15380         timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15381         break;
15382       }
15383       case ButtonRelease:
15384       {
15385         if (display_image->debug != MagickFalse)
15386           (void) LogMagickEvent(X11Event,GetMagickModule(),
15387             "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
15388             event.xbutton.button,event.xbutton.x,event.xbutton.y);
15389         break;
15390       }
15391       case ClientMessage:
15392       {
15393         if (display_image->debug != MagickFalse)
15394           (void) LogMagickEvent(X11Event,GetMagickModule(),
15395             "Client Message: 0x%lx 0x%lx %d 0x%lx",event.xclient.window,
15396             event.xclient.message_type,event.xclient.format,(unsigned long)
15397             event.xclient.data.l[0]);
15398         if (event.xclient.message_type == windows->im_protocols)
15399           {
15400             if (*event.xclient.data.l == (long) windows->im_update_widget)
15401               {
15402                 (void) CloneString(&windows->command.name,MagickTitle);
15403                 windows->command.data=MagickMenus;
15404                 (void) XCommandWidget(display,windows,CommandMenu,
15405                   (XEvent *) NULL);
15406                 break;
15407               }
15408             if (*event.xclient.data.l == (long) windows->im_update_colormap)
15409               {
15410                 /*
15411                   Update graphic context and window colormap.
15412                 */
15413                 for (i=0; i < (int) number_windows; i++)
15414                 {
15415                   if (magick_windows[i]->id == windows->icon.id)
15416                     continue;
15417                   context_values.background=pixel->background_color.pixel;
15418                   context_values.foreground=pixel->foreground_color.pixel;
15419                   (void) XChangeGC(display,magick_windows[i]->annotate_context,
15420                     context_mask,&context_values);
15421                   (void) XChangeGC(display,magick_windows[i]->widget_context,
15422                     context_mask,&context_values);
15423                   context_values.background=pixel->foreground_color.pixel;
15424                   context_values.foreground=pixel->background_color.pixel;
15425                   context_values.plane_mask=context_values.background ^
15426                     context_values.foreground;
15427                   (void) XChangeGC(display,magick_windows[i]->highlight_context,
15428                     (size_t) (context_mask | GCPlaneMask),
15429                     &context_values);
15430                   magick_windows[i]->attributes.background_pixel=
15431                     pixel->background_color.pixel;
15432                   magick_windows[i]->attributes.border_pixel=
15433                     pixel->border_color.pixel;
15434                   magick_windows[i]->attributes.colormap=map_info->colormap;
15435                   (void) XChangeWindowAttributes(display,magick_windows[i]->id,
15436                     (unsigned long) magick_windows[i]->mask,
15437                     &magick_windows[i]->attributes);
15438                 }
15439                 if (windows->pan.mapped != MagickFalse)
15440                   {
15441                     (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
15442                       windows->pan.pixmap);
15443                     (void) XClearWindow(display,windows->pan.id);
15444                     XDrawPanRectangle(display,windows);
15445                   }
15446                 if (windows->backdrop.id != (Window) NULL)
15447                   (void) XInstallColormap(display,map_info->colormap);
15448                 break;
15449               }
15450             if (*event.xclient.data.l == (long) windows->im_former_image)
15451               {
15452                 *state|=FormerImageState | ExitState;
15453                 break;
15454               }
15455             if (*event.xclient.data.l == (long) windows->im_next_image)
15456               {
15457                 *state|=NextImageState | ExitState;
15458                 break;
15459               }
15460             if (*event.xclient.data.l == (long) windows->im_retain_colors)
15461               {
15462                 *state|=RetainColorsState;
15463                 break;
15464               }
15465             if (*event.xclient.data.l == (long) windows->im_exit)
15466               {
15467                 *state|=ExitState;
15468                 break;
15469               }
15470             break;
15471           }
15472         if (event.xclient.message_type == windows->dnd_protocols)
15473           {
15474             Atom
15475               selection,
15476               type;
15477
15478             int
15479               format,
15480               status;
15481
15482             unsigned char
15483               *data;
15484
15485             unsigned long
15486               after,
15487               length;
15488
15489             /*
15490               Display image named by the Drag-and-Drop selection.
15491             */
15492             if ((*event.xclient.data.l != 2) && (*event.xclient.data.l != 128))
15493               break;
15494             selection=XInternAtom(display,"DndSelection",MagickFalse);
15495             status=XGetWindowProperty(display,root_window,selection,0L,(long)
15496               MaxTextExtent,MagickFalse,(Atom) AnyPropertyType,&type,&format,
15497               &length,&after,&data);
15498             if ((status != Success) || (length == 0))
15499               break;
15500             if (*event.xclient.data.l == 2)
15501               {
15502                 /*
15503                   Offix DND.
15504                 */
15505                 (void) CopyMagickString(resource_info->image_info->filename,
15506                   (char *) data,MaxTextExtent);
15507               }
15508             else
15509               {
15510                 /*
15511                   XDND.
15512                 */
15513                 if (strncmp((char *) data, "file:", 5) != 0)
15514                   {
15515                     (void) XFree((void *) data);
15516                     break;
15517                   }
15518                 (void) CopyMagickString(resource_info->image_info->filename,
15519                   ((char *) data)+5,MaxTextExtent);
15520               }
15521             nexus=ReadImage(resource_info->image_info,exception);
15522             CatchException(exception);
15523             if (nexus != (Image *) NULL)
15524               *state|=NextImageState | ExitState;
15525             (void) XFree((void *) data);
15526             break;
15527           }
15528         /*
15529           If client window delete message, exit.
15530         */
15531         if (event.xclient.message_type != windows->wm_protocols)
15532           break;
15533         if (*event.xclient.data.l != (long) windows->wm_delete_window)
15534           break;
15535         (void) XWithdrawWindow(display,event.xclient.window,
15536           visual_info->screen);
15537         if (event.xclient.window == windows->image.id)
15538           {
15539             *state|=ExitState;
15540             break;
15541           }
15542         if (event.xclient.window == windows->pan.id)
15543           {
15544             /*
15545               Restore original image size when pan window is deleted.
15546             */
15547             windows->image.window_changes.width=windows->image.ximage->width;
15548             windows->image.window_changes.height=windows->image.ximage->height;
15549             (void) XConfigureImage(display,resource_info,windows,
15550               display_image,exception);
15551           }
15552         break;
15553       }
15554       case ConfigureNotify:
15555       {
15556         if (display_image->debug != MagickFalse)
15557           (void) LogMagickEvent(X11Event,GetMagickModule(),
15558             "Configure Notify: 0x%lx %dx%d+%d+%d %d",event.xconfigure.window,
15559             event.xconfigure.width,event.xconfigure.height,event.xconfigure.x,
15560             event.xconfigure.y,event.xconfigure.send_event);
15561         if (event.xconfigure.window == windows->image.id)
15562           {
15563             /*
15564               Image window has a new configuration.
15565             */
15566             if (event.xconfigure.send_event != 0)
15567               {
15568                 XWindowChanges
15569                   window_changes;
15570
15571                 /*
15572                   Position the transient windows relative of the Image window.
15573                 */
15574                 if (windows->command.geometry == (char *) NULL)
15575                   if (windows->command.mapped == MagickFalse)
15576                     {
15577                       windows->command.x=event.xconfigure.x-
15578                         windows->command.width-25;
15579                       windows->command.y=event.xconfigure.y;
15580                       XConstrainWindowPosition(display,&windows->command);
15581                       window_changes.x=windows->command.x;
15582                       window_changes.y=windows->command.y;
15583                       (void) XReconfigureWMWindow(display,windows->command.id,
15584                         windows->command.screen,(unsigned int) (CWX | CWY),
15585                         &window_changes);
15586                     }
15587                 if (windows->widget.geometry == (char *) NULL)
15588                   if (windows->widget.mapped == MagickFalse)
15589                     {
15590                       windows->widget.x=event.xconfigure.x+
15591                         event.xconfigure.width/10;
15592                       windows->widget.y=event.xconfigure.y+
15593                         event.xconfigure.height/10;
15594                       XConstrainWindowPosition(display,&windows->widget);
15595                       window_changes.x=windows->widget.x;
15596                       window_changes.y=windows->widget.y;
15597                       (void) XReconfigureWMWindow(display,windows->widget.id,
15598                         windows->widget.screen,(unsigned int) (CWX | CWY),
15599                         &window_changes);
15600                     }
15601                 if (windows->magnify.geometry == (char *) NULL)
15602                   if (windows->magnify.mapped == MagickFalse)
15603                     {
15604                       windows->magnify.x=event.xconfigure.x+
15605                         event.xconfigure.width+25;
15606                       windows->magnify.y=event.xconfigure.y;
15607                       XConstrainWindowPosition(display,&windows->magnify);
15608                       window_changes.x=windows->magnify.x;
15609                       window_changes.y=windows->magnify.y;
15610                       (void) XReconfigureWMWindow(display,windows->magnify.id,
15611                         windows->magnify.screen,(unsigned int) (CWX | CWY),
15612                         &window_changes);
15613                     }
15614                 if (windows->pan.geometry == (char *) NULL)
15615                   if (windows->pan.mapped == MagickFalse)
15616                     {
15617                       windows->pan.x=event.xconfigure.x+
15618                         event.xconfigure.width+25;
15619                       windows->pan.y=event.xconfigure.y+
15620                         windows->magnify.height+50;
15621                       XConstrainWindowPosition(display,&windows->pan);
15622                       window_changes.x=windows->pan.x;
15623                       window_changes.y=windows->pan.y;
15624                       (void) XReconfigureWMWindow(display,windows->pan.id,
15625                         windows->pan.screen,(unsigned int) (CWX | CWY),
15626                         &window_changes);
15627                     }
15628               }
15629             if ((event.xconfigure.width == (int) windows->image.width) &&
15630                 (event.xconfigure.height == (int) windows->image.height))
15631               break;
15632             windows->image.width=(unsigned int) event.xconfigure.width;
15633             windows->image.height=(unsigned int) event.xconfigure.height;
15634             windows->image.x=0;
15635             windows->image.y=0;
15636             if (display_image->montage != (char *) NULL)
15637               {
15638                 windows->image.x=vid_info.x;
15639                 windows->image.y=vid_info.y;
15640               }
15641             if ((windows->image.mapped != MagickFalse) &&
15642                 (windows->image.stasis != MagickFalse))
15643               {
15644                 /*
15645                   Update image window configuration.
15646                 */
15647                 windows->image.window_changes.width=event.xconfigure.width;
15648                 windows->image.window_changes.height=event.xconfigure.height;
15649                 (void) XConfigureImage(display,resource_info,windows,
15650                   display_image,exception);
15651               }
15652             /*
15653               Update pan window configuration.
15654             */
15655             if ((event.xconfigure.width < windows->image.ximage->width) ||
15656                 (event.xconfigure.height < windows->image.ximage->height))
15657               {
15658                 (void) XMapRaised(display,windows->pan.id);
15659                 XDrawPanRectangle(display,windows);
15660               }
15661             else
15662               if (windows->pan.mapped != MagickFalse)
15663                 (void) XWithdrawWindow(display,windows->pan.id,
15664                   windows->pan.screen);
15665             break;
15666           }
15667         if (event.xconfigure.window == windows->magnify.id)
15668           {
15669             unsigned int
15670               magnify;
15671
15672             /*
15673               Magnify window has a new configuration.
15674             */
15675             windows->magnify.width=(unsigned int) event.xconfigure.width;
15676             windows->magnify.height=(unsigned int) event.xconfigure.height;
15677             if (windows->magnify.mapped == MagickFalse)
15678               break;
15679             magnify=1;
15680             while ((int) magnify <= event.xconfigure.width)
15681               magnify<<=1;
15682             while ((int) magnify <= event.xconfigure.height)
15683               magnify<<=1;
15684             magnify>>=1;
15685             if (((int) magnify != event.xconfigure.width) ||
15686                 ((int) magnify != event.xconfigure.height))
15687               {
15688                 window_changes.width=(int) magnify;
15689                 window_changes.height=(int) magnify;
15690                 (void) XReconfigureWMWindow(display,windows->magnify.id,
15691                   windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
15692                   &window_changes);
15693                 break;
15694               }
15695             if ((windows->magnify.mapped != MagickFalse) &&
15696                 (windows->magnify.stasis != MagickFalse))
15697               {
15698                 status=XMakeImage(display,resource_info,&windows->magnify,
15699                   display_image,windows->magnify.width,windows->magnify.height,
15700                   exception);
15701                 XMakeMagnifyImage(display,windows,exception);
15702               }
15703             break;
15704           }
15705         if ((windows->magnify.mapped != MagickFalse) &&
15706             (event.xconfigure.window == windows->pan.id))
15707           {
15708             /*
15709               Pan icon window has a new configuration.
15710             */
15711             if (event.xconfigure.send_event != 0)
15712               {
15713                 windows->pan.x=event.xconfigure.x;
15714                 windows->pan.y=event.xconfigure.y;
15715               }
15716             windows->pan.width=(unsigned int) event.xconfigure.width;
15717             windows->pan.height=(unsigned int) event.xconfigure.height;
15718             break;
15719           }
15720         if (event.xconfigure.window == windows->icon.id)
15721           {
15722             /*
15723               Icon window has a new configuration.
15724             */
15725             windows->icon.width=(unsigned int) event.xconfigure.width;
15726             windows->icon.height=(unsigned int) event.xconfigure.height;
15727             break;
15728           }
15729         break;
15730       }
15731       case DestroyNotify:
15732       {
15733         /*
15734           Group leader has exited.
15735         */
15736         if (display_image->debug != MagickFalse)
15737           (void) LogMagickEvent(X11Event,GetMagickModule(),
15738             "Destroy Notify: 0x%lx",event.xdestroywindow.window);
15739         if (event.xdestroywindow.window == windows->group_leader.id)
15740           {
15741             *state|=ExitState;
15742             break;
15743           }
15744         break;
15745       }
15746       case EnterNotify:
15747       {
15748         /*
15749           Selectively install colormap.
15750         */
15751         if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15752           if (event.xcrossing.mode != NotifyUngrab)
15753             XInstallColormap(display,map_info->colormap);
15754         break;
15755       }
15756       case Expose:
15757       {
15758         if (display_image->debug != MagickFalse)
15759           (void) LogMagickEvent(X11Event,GetMagickModule(),
15760             "Expose: 0x%lx %dx%d+%d+%d",event.xexpose.window,
15761             event.xexpose.width,event.xexpose.height,event.xexpose.x,
15762             event.xexpose.y);
15763         /*
15764           Refresh windows that are now exposed.
15765         */
15766         if ((event.xexpose.window == windows->image.id) &&
15767             (windows->image.mapped != MagickFalse))
15768           {
15769             XRefreshWindow(display,&windows->image,&event);
15770             delay=display_image->delay/MagickMax(
15771               display_image->ticks_per_second,1L);
15772             timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15773             break;
15774           }
15775         if ((event.xexpose.window == windows->magnify.id) &&
15776             (windows->magnify.mapped != MagickFalse))
15777           {
15778             XMakeMagnifyImage(display,windows,exception);
15779             break;
15780           }
15781         if (event.xexpose.window == windows->pan.id)
15782           {
15783             XDrawPanRectangle(display,windows);
15784             break;
15785           }
15786         if (event.xexpose.window == windows->icon.id)
15787           {
15788             XRefreshWindow(display,&windows->icon,&event);
15789             break;
15790           }
15791         break;
15792       }
15793       case KeyPress:
15794       {
15795         int
15796           length;
15797
15798         /*
15799           Respond to a user key press.
15800         */
15801         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
15802           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15803         *(command+length)='\0';
15804         if (display_image->debug != MagickFalse)
15805           (void) LogMagickEvent(X11Event,GetMagickModule(),
15806             "Key press: %d 0x%lx (%s)",event.xkey.state,(unsigned long)
15807             key_symbol,command);
15808         if (event.xkey.window == windows->image.id)
15809           {
15810             command_type=XImageWindowCommand(display,resource_info,windows,
15811               event.xkey.state,key_symbol,&display_image,exception);
15812             if (command_type != NullCommand)
15813               nexus=XMagickCommand(display,resource_info,windows,command_type,
15814                 &display_image,exception);
15815           }
15816         if (event.xkey.window == windows->magnify.id)
15817           XMagnifyWindowCommand(display,windows,event.xkey.state,key_symbol,
15818             exception);
15819         if (event.xkey.window == windows->pan.id)
15820           {
15821             if ((key_symbol == XK_q) || (key_symbol == XK_Escape))
15822               (void) XWithdrawWindow(display,windows->pan.id,
15823                 windows->pan.screen);
15824             else
15825               if ((key_symbol == XK_F1) || (key_symbol == XK_Help))
15826                 XTextViewWidget(display,resource_info,windows,MagickFalse,
15827                   "Help Viewer - Image Pan",ImagePanHelp);
15828               else
15829                 XTranslateImage(display,windows,*image,key_symbol);
15830           }
15831         delay=display_image->delay/MagickMax(
15832           display_image->ticks_per_second,1L);
15833         timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15834         break;
15835       }
15836       case KeyRelease:
15837       {
15838         /*
15839           Respond to a user key release.
15840         */
15841         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
15842           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15843         if (display_image->debug != MagickFalse)
15844           (void) LogMagickEvent(X11Event,GetMagickModule(),
15845             "Key release: 0x%lx (%c)",(unsigned long) key_symbol,*command);
15846         break;
15847       }
15848       case LeaveNotify:
15849       {
15850         /*
15851           Selectively uninstall colormap.
15852         */
15853         if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15854           if (event.xcrossing.mode != NotifyUngrab)
15855             XUninstallColormap(display,map_info->colormap);
15856         break;
15857       }
15858       case MapNotify:
15859       {
15860         if (display_image->debug != MagickFalse)
15861           (void) LogMagickEvent(X11Event,GetMagickModule(),"Map Notify: 0x%lx",
15862             event.xmap.window);
15863         if (event.xmap.window == windows->backdrop.id)
15864           {
15865             (void) XSetInputFocus(display,event.xmap.window,RevertToParent,
15866               CurrentTime);
15867             windows->backdrop.mapped=MagickTrue;
15868             break;
15869           }
15870         if (event.xmap.window == windows->image.id)
15871           {
15872             if (windows->backdrop.id != (Window) NULL)
15873               (void) XInstallColormap(display,map_info->colormap);
15874             if (LocaleCompare(display_image->magick,"LOGO") == 0)
15875               {
15876                 if (LocaleCompare(display_image->filename,"LOGO") == 0)
15877                   nexus=XOpenImage(display,resource_info,windows,MagickFalse);
15878               }
15879             if (((int) windows->image.width < windows->image.ximage->width) ||
15880                 ((int) windows->image.height < windows->image.ximage->height))
15881               (void) XMapRaised(display,windows->pan.id);
15882             windows->image.mapped=MagickTrue;
15883             break;
15884           }
15885         if (event.xmap.window == windows->magnify.id)
15886           {
15887             XMakeMagnifyImage(display,windows,exception);
15888             windows->magnify.mapped=MagickTrue;
15889             (void) XWithdrawWindow(display,windows->info.id,
15890               windows->info.screen);
15891             break;
15892           }
15893         if (event.xmap.window == windows->pan.id)
15894           {
15895             XMakePanImage(display,resource_info,windows,display_image,
15896               exception);
15897             windows->pan.mapped=MagickTrue;
15898             break;
15899           }
15900         if (event.xmap.window == windows->info.id)
15901           {
15902             windows->info.mapped=MagickTrue;
15903             break;
15904           }
15905         if (event.xmap.window == windows->icon.id)
15906           {
15907             MagickBooleanType
15908               taint;
15909
15910             /*
15911               Create an icon image.
15912             */
15913             taint=display_image->taint;
15914             XMakeStandardColormap(display,icon_visual,icon_resources,
15915               display_image,icon_map,icon_pixel,exception);
15916             (void) XMakeImage(display,icon_resources,&windows->icon,
15917               display_image,windows->icon.width,windows->icon.height,
15918               exception);
15919             display_image->taint=taint;
15920             (void) XSetWindowBackgroundPixmap(display,windows->icon.id,
15921               windows->icon.pixmap);
15922             (void) XClearWindow(display,windows->icon.id);
15923             (void) XWithdrawWindow(display,windows->info.id,
15924               windows->info.screen);
15925             windows->icon.mapped=MagickTrue;
15926             break;
15927           }
15928         if (event.xmap.window == windows->command.id)
15929           {
15930             windows->command.mapped=MagickTrue;
15931             break;
15932           }
15933         if (event.xmap.window == windows->popup.id)
15934           {
15935             windows->popup.mapped=MagickTrue;
15936             break;
15937           }
15938         if (event.xmap.window == windows->widget.id)
15939           {
15940             windows->widget.mapped=MagickTrue;
15941             break;
15942           }
15943         break;
15944       }
15945       case MappingNotify:
15946       {
15947         (void) XRefreshKeyboardMapping(&event.xmapping);
15948         break;
15949       }
15950       case NoExpose:
15951         break;
15952       case PropertyNotify:
15953       {
15954         Atom
15955           type;
15956
15957         int
15958           format,
15959           status;
15960
15961         unsigned char
15962           *data;
15963
15964         unsigned long
15965           after,
15966           length;
15967
15968         if (display_image->debug != MagickFalse)
15969           (void) LogMagickEvent(X11Event,GetMagickModule(),
15970             "Property Notify: 0x%lx 0x%lx %d",event.xproperty.window,
15971             event.xproperty.atom,event.xproperty.state);
15972         if (event.xproperty.atom != windows->im_remote_command)
15973           break;
15974         /*
15975           Display image named by the remote command protocol.
15976         */
15977         status=XGetWindowProperty(display,event.xproperty.window,
15978           event.xproperty.atom,0L,(long) MaxTextExtent,MagickFalse,(Atom)
15979           AnyPropertyType,&type,&format,&length,&after,&data);
15980         if ((status != Success) || (length == 0))
15981           break;
15982         if (LocaleCompare((char *) data,"-quit") == 0)
15983           {
15984             XClientMessage(display,windows->image.id,windows->im_protocols,
15985               windows->im_exit,CurrentTime);
15986             (void) XFree((void *) data);
15987             break;
15988           }
15989         (void) CopyMagickString(resource_info->image_info->filename,
15990           (char *) data,MaxTextExtent);
15991         (void) XFree((void *) data);
15992         nexus=ReadImage(resource_info->image_info,exception);
15993         CatchException(exception);
15994         if (nexus != (Image *) NULL)
15995           *state|=NextImageState | ExitState;
15996         break;
15997       }
15998       case ReparentNotify:
15999       {
16000         if (display_image->debug != MagickFalse)
16001           (void) LogMagickEvent(X11Event,GetMagickModule(),
16002             "Reparent Notify: 0x%lx=>0x%lx",event.xreparent.parent,
16003             event.xreparent.window);
16004         break;
16005       }
16006       case UnmapNotify:
16007       {
16008         if (display_image->debug != MagickFalse)
16009           (void) LogMagickEvent(X11Event,GetMagickModule(),
16010             "Unmap Notify: 0x%lx",event.xunmap.window);
16011         if (event.xunmap.window == windows->backdrop.id)
16012           {
16013             windows->backdrop.mapped=MagickFalse;
16014             break;
16015           }
16016         if (event.xunmap.window == windows->image.id)
16017           {
16018             windows->image.mapped=MagickFalse;
16019             break;
16020           }
16021         if (event.xunmap.window == windows->magnify.id)
16022           {
16023             windows->magnify.mapped=MagickFalse;
16024             break;
16025           }
16026         if (event.xunmap.window == windows->pan.id)
16027           {
16028             windows->pan.mapped=MagickFalse;
16029             break;
16030           }
16031         if (event.xunmap.window == windows->info.id)
16032           {
16033             windows->info.mapped=MagickFalse;
16034             break;
16035           }
16036         if (event.xunmap.window == windows->icon.id)
16037           {
16038             if (map_info->colormap == icon_map->colormap)
16039               XConfigureImageColormap(display,resource_info,windows,
16040                 display_image,exception);
16041             (void) XFreeStandardColormap(display,icon_visual,icon_map,
16042               icon_pixel);
16043             windows->icon.mapped=MagickFalse;
16044             break;
16045           }
16046         if (event.xunmap.window == windows->command.id)
16047           {
16048             windows->command.mapped=MagickFalse;
16049             break;
16050           }
16051         if (event.xunmap.window == windows->popup.id)
16052           {
16053             if (windows->backdrop.id != (Window) NULL)
16054               (void) XSetInputFocus(display,windows->image.id,RevertToParent,
16055                 CurrentTime);
16056             windows->popup.mapped=MagickFalse;
16057             break;
16058           }
16059         if (event.xunmap.window == windows->widget.id)
16060           {
16061             if (windows->backdrop.id != (Window) NULL)
16062               (void) XSetInputFocus(display,windows->image.id,RevertToParent,
16063                 CurrentTime);
16064             windows->widget.mapped=MagickFalse;
16065             break;
16066           }
16067         break;
16068       }
16069       default:
16070       {
16071         if (display_image->debug != MagickFalse)
16072           (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
16073             event.type);
16074         break;
16075       }
16076     }
16077   } while (!(*state & ExitState));
16078   if ((*state & ExitState) == 0)
16079     (void) XMagickCommand(display,resource_info,windows,FreeBuffersCommand,
16080       &display_image,exception);
16081   else
16082     if (resource_info->confirm_edit != MagickFalse)
16083       {
16084         /*
16085           Query user if image has changed.
16086         */
16087         if ((resource_info->immutable == MagickFalse) &&
16088             (display_image->taint != MagickFalse))
16089           {
16090             int
16091               status;
16092
16093             status=XConfirmWidget(display,windows,"Your image changed.",
16094               "Do you want to save it");
16095             if (status == 0)
16096               *state&=(~ExitState);
16097             else
16098               if (status > 0)
16099                 (void) XMagickCommand(display,resource_info,windows,SaveCommand,
16100                   &display_image,exception);
16101           }
16102       }
16103   if ((windows->visual_info->klass == GrayScale) ||
16104       (windows->visual_info->klass == PseudoColor) ||
16105       (windows->visual_info->klass == DirectColor))
16106     {
16107       /*
16108         Withdraw pan and Magnify window.
16109       */
16110       if (windows->info.mapped != MagickFalse)
16111         (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
16112       if (windows->magnify.mapped != MagickFalse)
16113         (void) XWithdrawWindow(display,windows->magnify.id,
16114           windows->magnify.screen);
16115       if (windows->command.mapped != MagickFalse)
16116         (void) XWithdrawWindow(display,windows->command.id,
16117           windows->command.screen);
16118     }
16119   if (windows->pan.mapped != MagickFalse)
16120     (void) XWithdrawWindow(display,windows->pan.id,windows->pan.screen);
16121   if (resource_info->backdrop == MagickFalse)
16122     if (windows->backdrop.mapped)
16123       {
16124         (void) XWithdrawWindow(display,windows->backdrop.id,
16125           windows->backdrop.screen);
16126         (void) XDestroyWindow(display,windows->backdrop.id);
16127         windows->backdrop.id=(Window) NULL;
16128         (void) XWithdrawWindow(display,windows->image.id,
16129           windows->image.screen);
16130         (void) XDestroyWindow(display,windows->image.id);
16131         windows->image.id=(Window) NULL;
16132       }
16133   XSetCursorState(display,windows,MagickTrue);
16134   XCheckRefreshWindows(display,windows);
16135   if (((*state & FormerImageState) != 0) || ((*state & NextImageState) != 0))
16136     *state&=(~ExitState);
16137   if (*state & ExitState)
16138     {
16139       /*
16140         Free Standard Colormap.
16141       */
16142       (void) XFreeStandardColormap(display,icon_visual,icon_map,icon_pixel);
16143       if (resource_info->map_type == (char *) NULL)
16144         (void) XFreeStandardColormap(display,visual_info,map_info,pixel);
16145       /*
16146         Free X resources.
16147       */
16148       if (resource_info->copy_image != (Image *) NULL)
16149         {
16150           resource_info->copy_image=DestroyImage(resource_info->copy_image);
16151           resource_info->copy_image=NewImageList();
16152         }
16153       DestroyXResources();
16154     }
16155   (void) XSync(display,MagickFalse);
16156   /*
16157     Restore our progress monitor and warning handlers.
16158   */
16159   (void) SetErrorHandler(warning_handler);
16160   (void) SetWarningHandler(warning_handler);
16161   /*
16162     Change to home directory.
16163   */
16164   directory=getcwd(working_directory,MaxTextExtent);
16165   (void) directory;
16166   {
16167     int
16168       status;
16169
16170     status=chdir(resource_info->home_directory);
16171     if (status == -1)
16172       (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
16173         "UnableToOpenFile","%s",resource_info->home_directory);
16174   }
16175   *image=display_image;
16176   return(nexus);
16177 }
16178 #else
16179 \f
16180 /*
16181 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16182 %                                                                             %
16183 %                                                                             %
16184 %                                                                             %
16185 +   D i s p l a y I m a g e s                                                 %
16186 %                                                                             %
16187 %                                                                             %
16188 %                                                                             %
16189 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16190 %
16191 %  DisplayImages() displays an image sequence to any X window screen.  It
16192 %  returns a value other than 0 if successful.  Check the exception member
16193 %  of image to determine the reason for any failure.
16194 %
16195 %  The format of the DisplayImages method is:
16196 %
16197 %      MagickBooleanType DisplayImages(const ImageInfo *image_info,
16198 %        Image *images,ExceptionInfo *exception)
16199 %
16200 %  A description of each parameter follows:
16201 %
16202 %    o image_info: the image info.
16203 %
16204 %    o image: the image.
16205 %
16206 %    o exception: return any errors or warnings in this structure.
16207 %
16208 */
16209 MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
16210   Image *image,ExceptionInfo *exception)
16211 {
16212   assert(image_info != (const ImageInfo *) NULL);
16213   assert(image_info->signature == MagickSignature);
16214   assert(image != (Image *) NULL);
16215   assert(image->signature == MagickSignature);
16216   if (image->debug != MagickFalse)
16217     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
16218   (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16219     "DelegateLibrarySupportNotBuiltIn","`%s' (X11)",image->filename);
16220   return(MagickFalse);
16221 }
16222 \f
16223 /*
16224 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16225 %                                                                             %
16226 %                                                                             %
16227 %                                                                             %
16228 +   R e m o t e D i s p l a y C o m m a n d                                   %
16229 %                                                                             %
16230 %                                                                             %
16231 %                                                                             %
16232 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16233 %
16234 %  RemoteDisplayCommand() encourages a remote display program to display the
16235 %  specified image filename.
16236 %
16237 %  The format of the RemoteDisplayCommand method is:
16238 %
16239 %      MagickBooleanType RemoteDisplayCommand(const ImageInfo *image,
16240 %        const char *window,const char *filename,ExceptionInfo *exception)
16241 %
16242 %  A description of each parameter follows:
16243 %
16244 %    o image_info: the image info.
16245 %
16246 %    o window: Specifies the name or id of an X window.
16247 %
16248 %    o filename: the name of the image filename to display.
16249 %
16250 %    o exception: return any errors or warnings in this structure.
16251 %
16252 */
16253 MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
16254   const char *window,const char *filename,ExceptionInfo *exception)
16255 {
16256   assert(image_info != (const ImageInfo *) NULL);
16257   assert(image_info->signature == MagickSignature);
16258   assert(filename != (char *) NULL);
16259   (void) window;
16260   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
16261   (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16262     "DelegateLibrarySupportNotBuiltIn","`%s' (X11)",image_info->filename);
16263   return(MagickFalse);
16264 }
16265 #endif