]> 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-2012 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) GetOneVirtualPixelInfo(*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         if ((*image)->matte == MagickFalse)
10103           (void) SetImageAlphaChannel(*image,OpaqueAlphaChannel,exception);
10104         image_view=AcquireCacheView(*image);
10105         switch (method)
10106         {
10107           case PointMethod:
10108           default:
10109           {
10110             /*
10111               Update matte information using point algorithm.
10112             */
10113             q=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,
10114               (ssize_t) y_offset,1,1,exception);
10115             if (q == (Quantum *) NULL)
10116               break;
10117             SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10118             (void) SyncCacheViewAuthenticPixels(image_view,exception);
10119             break;
10120           }
10121           case ReplaceMethod:
10122           {
10123             PixelInfo
10124               pixel,
10125               target;
10126
10127             Quantum
10128               virtual_pixel[CompositePixelChannel];
10129
10130             /*
10131               Update matte information using replace algorithm.
10132             */
10133             (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t) x_offset,
10134               (ssize_t) y_offset,virtual_pixel,exception);
10135             target.red=virtual_pixel[RedPixelChannel];
10136             target.green=virtual_pixel[GreenPixelChannel];
10137             target.blue=virtual_pixel[BluePixelChannel];
10138             target.alpha=virtual_pixel[AlphaPixelChannel];
10139             for (y=0; y < (int) (*image)->rows; y++)
10140             {
10141               q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10142                 (*image)->columns,1,exception);
10143               if (q == (Quantum *) NULL)
10144                 break;
10145               for (x=0; x < (int) (*image)->columns; x++)
10146               {
10147                 GetPixelInfoPixel(*image,q,&pixel);
10148                 if (IsFuzzyEquivalencePixelInfo(&pixel,&target))
10149                   SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10150                 q+=GetPixelChannels(*image);
10151               }
10152               if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
10153                 break;
10154             }
10155             break;
10156           }
10157           case FloodfillMethod:
10158           case FillToBorderMethod:
10159           {
10160             ChannelType
10161               channel_mask;
10162
10163             DrawInfo
10164               *draw_info;
10165
10166             PixelInfo
10167               target;
10168
10169             /*
10170               Update matte information using floodfill algorithm.
10171             */
10172             (void) GetOneVirtualPixelInfo(*image,
10173               GetPixelCacheVirtualMethod(*image),(ssize_t) x_offset,(ssize_t)
10174               y_offset,&target,exception);
10175             if (method == FillToBorderMethod)
10176               {
10177                 target.red=(MagickRealType) ScaleShortToQuantum(
10178                   border_color.red);
10179                 target.green=(MagickRealType) ScaleShortToQuantum(
10180                   border_color.green);
10181                 target.blue=(MagickRealType) ScaleShortToQuantum(
10182                   border_color.blue);
10183               }
10184             draw_info=CloneDrawInfo(resource_info->image_info,
10185               (DrawInfo *) NULL);
10186             draw_info->fill.alpha=ClampToQuantum(StringToDouble(matte,
10187               (char **) NULL));
10188             channel_mask=SetPixelChannelMask(*image,AlphaChannel); 
10189             (void) FloodfillPaintImage(*image,draw_info,&target,(ssize_t)
10190               x_offset,(ssize_t) y_offset,method == FloodfillMethod ?
10191               MagickFalse : MagickTrue,exception);
10192             (void) SetPixelChannelMapMask(*image,channel_mask);
10193             draw_info=DestroyDrawInfo(draw_info);
10194             break;
10195           }
10196           case ResetMethod:
10197           {
10198             /*
10199               Update matte information using reset algorithm.
10200             */
10201             if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
10202               return(MagickFalse);
10203             for (y=0; y < (int) (*image)->rows; y++)
10204             {
10205               q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10206                 (*image)->columns,1,exception);
10207               if (q == (Quantum *) NULL)
10208                 break;
10209               for (x=0; x < (int) (*image)->columns; x++)
10210               {
10211                 SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10212                 q+=GetPixelChannels(*image);
10213               }
10214               if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
10215                 break;
10216             }
10217             if (StringToLong(matte) == (long) OpaqueAlpha)
10218               (*image)->matte=MagickFalse;
10219             break;
10220           }
10221         }
10222         image_view=DestroyCacheView(image_view);
10223         state&=(~UpdateConfigurationState);
10224       }
10225   } while ((state & ExitState) == 0);
10226   (void) XSelectInput(display,windows->image.id,
10227     windows->image.attributes.event_mask);
10228   XSetCursorState(display,windows,MagickFalse);
10229   (void) XFreeCursor(display,cursor);
10230   return(MagickTrue);
10231 }
10232 \f
10233 /*
10234 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10235 %                                                                             %
10236 %                                                                             %
10237 %                                                                             %
10238 +   X O p e n I m a g e                                                       %
10239 %                                                                             %
10240 %                                                                             %
10241 %                                                                             %
10242 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10243 %
10244 %  XOpenImage() loads an image from a file.
10245 %
10246 %  The format of the XOpenImage method is:
10247 %
10248 %     Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10249 %       XWindows *windows,const unsigned int command)
10250 %
10251 %  A description of each parameter follows:
10252 %
10253 %    o display: Specifies a connection to an X server; returned from
10254 %      XOpenDisplay.
10255 %
10256 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10257 %
10258 %    o windows: Specifies a pointer to a XWindows structure.
10259 %
10260 %    o command: A value other than zero indicates that the file is selected
10261 %      from the command line argument list.
10262 %
10263 */
10264 static Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10265   XWindows *windows,const MagickBooleanType command)
10266 {
10267   const MagickInfo
10268     *magick_info;
10269
10270   ExceptionInfo
10271     *exception;
10272
10273   Image
10274     *nexus;
10275
10276   ImageInfo
10277     *image_info;
10278
10279   static char
10280     filename[MaxTextExtent] = "\0";
10281
10282   /*
10283     Request file name from user.
10284   */
10285   if (command == MagickFalse)
10286     XFileBrowserWidget(display,windows,"Open",filename);
10287   else
10288     {
10289       char
10290         **filelist,
10291         **files;
10292
10293       int
10294         count,
10295         status;
10296
10297       register int
10298         i,
10299         j;
10300
10301       /*
10302         Select next image from the command line.
10303       */
10304       status=XGetCommand(display,windows->image.id,&files,&count);
10305       if (status == 0)
10306         {
10307           ThrowXWindowFatalException(XServerError,"UnableToGetProperty","...");
10308           return((Image *) NULL);
10309         }
10310       filelist=(char **) AcquireQuantumMemory((size_t) count,sizeof(*filelist));
10311       if (filelist == (char **) NULL)
10312         {
10313           ThrowXWindowFatalException(ResourceLimitError,
10314             "MemoryAllocationFailed","...");
10315           (void) XFreeStringList(files);
10316           return((Image *) NULL);
10317         }
10318       j=0;
10319       for (i=1; i < count; i++)
10320         if (*files[i] != '-')
10321           filelist[j++]=files[i];
10322       filelist[j]=(char *) NULL;
10323       XListBrowserWidget(display,windows,&windows->widget,
10324         (const char **) filelist,"Load","Select Image to Load:",filename);
10325       filelist=(char **) RelinquishMagickMemory(filelist);
10326       (void) XFreeStringList(files);
10327     }
10328   if (*filename == '\0')
10329     return((Image *) NULL);
10330   image_info=CloneImageInfo(resource_info->image_info);
10331   (void) SetImageInfoProgressMonitor(image_info,(MagickProgressMonitor) NULL,
10332     (void *) NULL);
10333   (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
10334   exception=AcquireExceptionInfo();
10335   (void) SetImageInfo(image_info,0,exception);
10336   if (LocaleCompare(image_info->magick,"X") == 0)
10337     {
10338       char
10339         seconds[MaxTextExtent];
10340
10341       /*
10342         User may want to delay the X server screen grab.
10343       */
10344       (void) CopyMagickString(seconds,"0",MaxTextExtent);
10345       (void) XDialogWidget(display,windows,"Grab","Enter any delay in seconds:",
10346         seconds);
10347       if (*seconds == '\0')
10348         return((Image *) NULL);
10349       XDelay(display,(size_t) (1000*StringToLong(seconds)));
10350     }
10351   magick_info=GetMagickInfo(image_info->magick,exception);
10352   if ((magick_info != (const MagickInfo *) NULL) &&
10353       (magick_info->raw != MagickFalse))
10354     {
10355       char
10356         geometry[MaxTextExtent];
10357
10358       /*
10359         Request image size from the user.
10360       */
10361       (void) CopyMagickString(geometry,"512x512",MaxTextExtent);
10362       if (image_info->size != (char *) NULL)
10363         (void) CopyMagickString(geometry,image_info->size,MaxTextExtent);
10364       (void) XDialogWidget(display,windows,"Load","Enter the image geometry:",
10365         geometry);
10366       (void) CloneString(&image_info->size,geometry);
10367     }
10368   /*
10369     Load the image.
10370   */
10371   XSetCursorState(display,windows,MagickTrue);
10372   XCheckRefreshWindows(display,windows);
10373   (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
10374   nexus=ReadImage(image_info,exception);
10375   CatchException(exception);
10376   XSetCursorState(display,windows,MagickFalse);
10377   if (nexus != (Image *) NULL)
10378     XClientMessage(display,windows->image.id,windows->im_protocols,
10379       windows->im_next_image,CurrentTime);
10380   else
10381     {
10382       char
10383         *text,
10384         **textlist;
10385
10386       /*
10387         Unknown image format.
10388       */
10389       text=FileToString(filename,~0,exception);
10390       if (text == (char *) NULL)
10391         return((Image *) NULL);
10392       textlist=StringToList(text);
10393       if (textlist != (char **) NULL)
10394         {
10395           char
10396             title[MaxTextExtent];
10397
10398           register int
10399             i;
10400
10401           (void) FormatLocaleString(title,MaxTextExtent,
10402             "Unknown format: %s",filename);
10403           XTextViewWidget(display,resource_info,windows,MagickTrue,title,
10404             (const char **) textlist);
10405           for (i=0; textlist[i] != (char *) NULL; i++)
10406             textlist[i]=DestroyString(textlist[i]);
10407           textlist=(char **) RelinquishMagickMemory(textlist);
10408         }
10409       text=DestroyString(text);
10410     }
10411   exception=DestroyExceptionInfo(exception);
10412   image_info=DestroyImageInfo(image_info);
10413   return(nexus);
10414 }
10415 \f
10416 /*
10417 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10418 %                                                                             %
10419 %                                                                             %
10420 %                                                                             %
10421 +   X P a n I m a g e                                                         %
10422 %                                                                             %
10423 %                                                                             %
10424 %                                                                             %
10425 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10426 %
10427 %  XPanImage() pans the image until the mouse button is released.
10428 %
10429 %  The format of the XPanImage method is:
10430 %
10431 %      void XPanImage(Display *display,XWindows *windows,XEvent *event,
10432 %        ExceptionInfo *exception)
10433 %
10434 %  A description of each parameter follows:
10435 %
10436 %    o display: Specifies a connection to an X server;  returned from
10437 %      XOpenDisplay.
10438 %
10439 %    o windows: Specifies a pointer to a XWindows structure.
10440 %
10441 %    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
10442 %      the entire image is refreshed.
10443 %
10444 %    o exception: return any errors or warnings in this structure.
10445 %
10446 */
10447 static void XPanImage(Display *display,XWindows *windows,XEvent *event,
10448   ExceptionInfo *exception)
10449 {
10450   char
10451     text[MaxTextExtent];
10452
10453   Cursor
10454     cursor;
10455
10456   MagickRealType
10457     x_factor,
10458     y_factor;
10459
10460   RectangleInfo
10461     pan_info;
10462
10463   size_t
10464     state;
10465
10466   /*
10467     Define cursor.
10468   */
10469   if ((windows->image.ximage->width > (int) windows->image.width) &&
10470       (windows->image.ximage->height > (int) windows->image.height))
10471     cursor=XCreateFontCursor(display,XC_fleur);
10472   else
10473     if (windows->image.ximage->width > (int) windows->image.width)
10474       cursor=XCreateFontCursor(display,XC_sb_h_double_arrow);
10475     else
10476       if (windows->image.ximage->height > (int) windows->image.height)
10477         cursor=XCreateFontCursor(display,XC_sb_v_double_arrow);
10478       else
10479         cursor=XCreateFontCursor(display,XC_arrow);
10480   (void) XCheckDefineCursor(display,windows->pan.id,cursor);
10481   /*
10482     Pan image as pointer moves until the mouse button is released.
10483   */
10484   x_factor=(MagickRealType) windows->image.ximage->width/windows->pan.width;
10485   y_factor=(MagickRealType) windows->image.ximage->height/windows->pan.height;
10486   pan_info.width=windows->pan.width*windows->image.width/
10487     windows->image.ximage->width;
10488   pan_info.height=windows->pan.height*windows->image.height/
10489     windows->image.ximage->height;
10490   pan_info.x=0;
10491   pan_info.y=0;
10492   state=UpdateConfigurationState;
10493   do
10494   {
10495     switch (event->type)
10496     {
10497       case ButtonPress:
10498       {
10499         /*
10500           User choose an initial pan location.
10501         */
10502         pan_info.x=(ssize_t) event->xbutton.x;
10503         pan_info.y=(ssize_t) event->xbutton.y;
10504         state|=UpdateConfigurationState;
10505         break;
10506       }
10507       case ButtonRelease:
10508       {
10509         /*
10510           User has finished panning the image.
10511         */
10512         pan_info.x=(ssize_t) event->xbutton.x;
10513         pan_info.y=(ssize_t) event->xbutton.y;
10514         state|=UpdateConfigurationState | ExitState;
10515         break;
10516       }
10517       case MotionNotify:
10518       {
10519         pan_info.x=(ssize_t) event->xmotion.x;
10520         pan_info.y=(ssize_t) event->xmotion.y;
10521         state|=UpdateConfigurationState;
10522       }
10523       default:
10524         break;
10525     }
10526     if ((state & UpdateConfigurationState) != 0)
10527       {
10528         /*
10529           Check boundary conditions.
10530         */
10531         if (pan_info.x < (ssize_t) (pan_info.width/2))
10532           pan_info.x=0;
10533         else
10534           pan_info.x=(ssize_t) (x_factor*(pan_info.x-(pan_info.width/2)));
10535         if (pan_info.x < 0)
10536           pan_info.x=0;
10537         else
10538           if ((int) (pan_info.x+windows->image.width) >
10539               windows->image.ximage->width)
10540             pan_info.x=(ssize_t)
10541               (windows->image.ximage->width-windows->image.width);
10542         if (pan_info.y < (ssize_t) (pan_info.height/2))
10543           pan_info.y=0;
10544         else
10545           pan_info.y=(ssize_t) (y_factor*(pan_info.y-(pan_info.height/2)));
10546         if (pan_info.y < 0)
10547           pan_info.y=0;
10548         else
10549           if ((int) (pan_info.y+windows->image.height) >
10550               windows->image.ximage->height)
10551             pan_info.y=(ssize_t)
10552               (windows->image.ximage->height-windows->image.height);
10553         if ((windows->image.x != (int) pan_info.x) ||
10554             (windows->image.y != (int) pan_info.y))
10555           {
10556             /*
10557               Display image pan offset.
10558             */
10559             windows->image.x=(int) pan_info.x;
10560             windows->image.y=(int) pan_info.y;
10561             (void) FormatLocaleString(text,MaxTextExtent," %ux%u%+d%+d ",
10562               windows->image.width,windows->image.height,windows->image.x,
10563               windows->image.y);
10564             XInfoWidget(display,windows,text);
10565             /*
10566               Refresh Image window.
10567             */
10568             XDrawPanRectangle(display,windows);
10569             XRefreshWindow(display,&windows->image,(XEvent *) NULL);
10570           }
10571         state&=(~UpdateConfigurationState);
10572       }
10573     /*
10574       Wait for next event.
10575     */
10576     if ((state & ExitState) == 0)
10577       XScreenEvent(display,windows,event,exception);
10578   } while ((state & ExitState) == 0);
10579   /*
10580     Restore cursor.
10581   */
10582   (void) XCheckDefineCursor(display,windows->pan.id,windows->pan.cursor);
10583   (void) XFreeCursor(display,cursor);
10584   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
10585 }
10586 \f
10587 /*
10588 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10589 %                                                                             %
10590 %                                                                             %
10591 %                                                                             %
10592 +   X P a s t e I m a g e                                                     %
10593 %                                                                             %
10594 %                                                                             %
10595 %                                                                             %
10596 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10597 %
10598 %  XPasteImage() pastes an image previously saved with XCropImage in the X
10599 %  window image at a location the user chooses with the pointer.
10600 %
10601 %  The format of the XPasteImage method is:
10602 %
10603 %      MagickBooleanType XPasteImage(Display *display,
10604 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
10605 %        ExceptionInfo *exception)
10606 %
10607 %  A description of each parameter follows:
10608 %
10609 %    o display: Specifies a connection to an X server;  returned from
10610 %      XOpenDisplay.
10611 %
10612 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10613 %
10614 %    o windows: Specifies a pointer to a XWindows structure.
10615 %
10616 %    o image: the image; returned from ReadImage.
10617 %
10618 %    o exception: return any errors or warnings in this structure.
10619 %
10620 */
10621 static MagickBooleanType XPasteImage(Display *display,
10622   XResourceInfo *resource_info,XWindows *windows,Image *image,
10623   ExceptionInfo *exception)
10624 {
10625   static const char
10626     *PasteMenu[] =
10627     {
10628       "Operator",
10629       "Help",
10630       "Dismiss",
10631       (char *) NULL
10632     };
10633
10634   static const ModeType
10635     PasteCommands[] =
10636     {
10637       PasteOperatorsCommand,
10638       PasteHelpCommand,
10639       PasteDismissCommand
10640     };
10641
10642   static CompositeOperator
10643     compose = CopyCompositeOp;
10644
10645   char
10646     text[MaxTextExtent];
10647
10648   Cursor
10649     cursor;
10650
10651   Image
10652     *paste_image;
10653
10654   int
10655     entry,
10656     id,
10657     x,
10658     y;
10659
10660   MagickRealType
10661     scale_factor;
10662
10663   RectangleInfo
10664     highlight_info,
10665     paste_info;
10666
10667   unsigned int
10668     height,
10669     width;
10670
10671   size_t
10672     state;
10673
10674   XEvent
10675     event;
10676
10677   /*
10678     Copy image.
10679   */
10680   if (resource_info->copy_image == (Image *) NULL)
10681     return(MagickFalse);
10682   paste_image=CloneImage(resource_info->copy_image,0,0,MagickTrue,exception);
10683   /*
10684     Map Command widget.
10685   */
10686   (void) CloneString(&windows->command.name,"Paste");
10687   windows->command.data=1;
10688   (void) XCommandWidget(display,windows,PasteMenu,(XEvent *) NULL);
10689   (void) XMapRaised(display,windows->command.id);
10690   XClientMessage(display,windows->image.id,windows->im_protocols,
10691     windows->im_update_widget,CurrentTime);
10692   /*
10693     Track pointer until button 1 is pressed.
10694   */
10695   XSetCursorState(display,windows,MagickFalse);
10696   XQueryPosition(display,windows->image.id,&x,&y);
10697   (void) XSelectInput(display,windows->image.id,
10698     windows->image.attributes.event_mask | PointerMotionMask);
10699   paste_info.x=(ssize_t) windows->image.x+x;
10700   paste_info.y=(ssize_t) windows->image.y+y;
10701   paste_info.width=0;
10702   paste_info.height=0;
10703   cursor=XCreateFontCursor(display,XC_ul_angle);
10704   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
10705   state=DefaultState;
10706   do
10707   {
10708     if (windows->info.mapped != MagickFalse)
10709       {
10710         /*
10711           Display pointer position.
10712         */
10713         (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
10714           (long) paste_info.x,(long) paste_info.y);
10715         XInfoWidget(display,windows,text);
10716       }
10717     highlight_info=paste_info;
10718     highlight_info.x=paste_info.x-windows->image.x;
10719     highlight_info.y=paste_info.y-windows->image.y;
10720     XHighlightRectangle(display,windows->image.id,
10721       windows->image.highlight_context,&highlight_info);
10722     /*
10723       Wait for next event.
10724     */
10725     XScreenEvent(display,windows,&event,exception);
10726     XHighlightRectangle(display,windows->image.id,
10727       windows->image.highlight_context,&highlight_info);
10728     if (event.xany.window == windows->command.id)
10729       {
10730         /*
10731           Select a command from the Command widget.
10732         */
10733         id=XCommandWidget(display,windows,PasteMenu,&event);
10734         if (id < 0)
10735           continue;
10736         switch (PasteCommands[id])
10737         {
10738           case PasteOperatorsCommand:
10739           {
10740             char
10741               command[MaxTextExtent],
10742               **operators;
10743
10744             /*
10745               Select a command from the pop-up menu.
10746             */
10747             operators=GetCommandOptions(MagickComposeOptions);
10748             if (operators == (char **) NULL)
10749               break;
10750             entry=XMenuWidget(display,windows,PasteMenu[id],
10751               (const char **) operators,command);
10752             if (entry >= 0)
10753               compose=(CompositeOperator) ParseCommandOption(
10754                 MagickComposeOptions,MagickFalse,operators[entry]);
10755             operators=DestroyStringList(operators);
10756             break;
10757           }
10758           case PasteHelpCommand:
10759           {
10760             XTextViewWidget(display,resource_info,windows,MagickFalse,
10761               "Help Viewer - Image Composite",ImagePasteHelp);
10762             break;
10763           }
10764           case PasteDismissCommand:
10765           {
10766             /*
10767               Prematurely exit.
10768             */
10769             state|=EscapeState;
10770             state|=ExitState;
10771             break;
10772           }
10773           default:
10774             break;
10775         }
10776         continue;
10777       }
10778     switch (event.type)
10779     {
10780       case ButtonPress:
10781       {
10782         if (image->debug != MagickFalse)
10783           (void) LogMagickEvent(X11Event,GetMagickModule(),
10784             "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
10785             event.xbutton.button,event.xbutton.x,event.xbutton.y);
10786         if (event.xbutton.button != Button1)
10787           break;
10788         if (event.xbutton.window != windows->image.id)
10789           break;
10790         /*
10791           Paste rectangle is relative to image configuration.
10792         */
10793         width=(unsigned int) image->columns;
10794         height=(unsigned int) image->rows;
10795         x=0;
10796         y=0;
10797         if (windows->image.crop_geometry != (char *) NULL)
10798           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
10799             &width,&height);
10800         scale_factor=(MagickRealType) windows->image.ximage->width/width;
10801         paste_info.width=(unsigned int) (scale_factor*paste_image->columns+0.5);
10802         scale_factor=(MagickRealType) windows->image.ximage->height/height;
10803         paste_info.height=(unsigned int) (scale_factor*paste_image->rows+0.5);
10804         (void) XCheckDefineCursor(display,windows->image.id,cursor);
10805         paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10806         paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10807         break;
10808       }
10809       case ButtonRelease:
10810       {
10811         if (image->debug != MagickFalse)
10812           (void) LogMagickEvent(X11Event,GetMagickModule(),
10813             "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
10814             event.xbutton.button,event.xbutton.x,event.xbutton.y);
10815         if (event.xbutton.button != Button1)
10816           break;
10817         if (event.xbutton.window != windows->image.id)
10818           break;
10819         if ((paste_info.width != 0) && (paste_info.height != 0))
10820           {
10821             /*
10822               User has selected the location of the paste image.
10823             */
10824             paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10825             paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10826             state|=ExitState;
10827           }
10828         break;
10829       }
10830       case Expose:
10831         break;
10832       case KeyPress:
10833       {
10834         char
10835           command[MaxTextExtent];
10836
10837         KeySym
10838           key_symbol;
10839
10840         int
10841           length;
10842
10843         if (event.xkey.window != windows->image.id)
10844           break;
10845         /*
10846           Respond to a user key press.
10847         */
10848         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
10849           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
10850         *(command+length)='\0';
10851         if (image->debug != MagickFalse)
10852           (void) LogMagickEvent(X11Event,GetMagickModule(),
10853             "Key press: 0x%lx (%s)",(long) key_symbol,command);
10854         switch ((int) key_symbol)
10855         {
10856           case XK_Escape:
10857           case XK_F20:
10858           {
10859             /*
10860               Prematurely exit.
10861             */
10862             paste_image=DestroyImage(paste_image);
10863             state|=EscapeState;
10864             state|=ExitState;
10865             break;
10866           }
10867           case XK_F1:
10868           case XK_Help:
10869           {
10870             (void) XSetFunction(display,windows->image.highlight_context,
10871               GXcopy);
10872             XTextViewWidget(display,resource_info,windows,MagickFalse,
10873               "Help Viewer - Image Composite",ImagePasteHelp);
10874             (void) XSetFunction(display,windows->image.highlight_context,
10875               GXinvert);
10876             break;
10877           }
10878           default:
10879           {
10880             (void) XBell(display,0);
10881             break;
10882           }
10883         }
10884         break;
10885       }
10886       case MotionNotify:
10887       {
10888         /*
10889           Map and unmap Info widget as text cursor crosses its boundaries.
10890         */
10891         x=event.xmotion.x;
10892         y=event.xmotion.y;
10893         if (windows->info.mapped != MagickFalse)
10894           {
10895             if ((x < (int) (windows->info.x+windows->info.width)) &&
10896                 (y < (int) (windows->info.y+windows->info.height)))
10897               (void) XWithdrawWindow(display,windows->info.id,
10898                 windows->info.screen);
10899           }
10900         else
10901           if ((x > (int) (windows->info.x+windows->info.width)) ||
10902               (y > (int) (windows->info.y+windows->info.height)))
10903             (void) XMapWindow(display,windows->info.id);
10904         paste_info.x=(ssize_t) windows->image.x+x;
10905         paste_info.y=(ssize_t) windows->image.y+y;
10906         break;
10907       }
10908       default:
10909       {
10910         if (image->debug != MagickFalse)
10911           (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
10912             event.type);
10913         break;
10914       }
10915     }
10916   } while ((state & ExitState) == 0);
10917   (void) XSelectInput(display,windows->image.id,
10918     windows->image.attributes.event_mask);
10919   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
10920   XSetCursorState(display,windows,MagickFalse);
10921   (void) XFreeCursor(display,cursor);
10922   if ((state & EscapeState) != 0)
10923     return(MagickTrue);
10924   /*
10925     Image pasting is relative to image configuration.
10926   */
10927   XSetCursorState(display,windows,MagickTrue);
10928   XCheckRefreshWindows(display,windows);
10929   width=(unsigned int) image->columns;
10930   height=(unsigned int) image->rows;
10931   x=0;
10932   y=0;
10933   if (windows->image.crop_geometry != (char *) NULL)
10934     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
10935   scale_factor=(MagickRealType) width/windows->image.ximage->width;
10936   paste_info.x+=x;
10937   paste_info.x=(ssize_t) (scale_factor*paste_info.x+0.5);
10938   paste_info.width=(unsigned int) (scale_factor*paste_info.width+0.5);
10939   scale_factor=(MagickRealType) height/windows->image.ximage->height;
10940   paste_info.y+=y;
10941   paste_info.y=(ssize_t) (scale_factor*paste_info.y*scale_factor+0.5);
10942   paste_info.height=(unsigned int) (scale_factor*paste_info.height+0.5);
10943   /*
10944     Paste image with X Image window.
10945   */
10946   (void) CompositeImage(image,compose,paste_image,paste_info.x,paste_info.y,
10947     exception);
10948   paste_image=DestroyImage(paste_image);
10949   XSetCursorState(display,windows,MagickFalse);
10950   /*
10951     Update image colormap.
10952   */
10953   XConfigureImageColormap(display,resource_info,windows,image,exception);
10954   (void) XConfigureImage(display,resource_info,windows,image,exception);
10955   return(MagickTrue);
10956 }
10957 \f
10958 /*
10959 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10960 %                                                                             %
10961 %                                                                             %
10962 %                                                                             %
10963 +   X P r i n t I m a g e                                                     %
10964 %                                                                             %
10965 %                                                                             %
10966 %                                                                             %
10967 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10968 %
10969 %  XPrintImage() prints an image to a Postscript printer.
10970 %
10971 %  The format of the XPrintImage method is:
10972 %
10973 %      MagickBooleanType XPrintImage(Display *display,
10974 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
10975 %        ExceptionInfo *exception)
10976 %
10977 %  A description of each parameter follows:
10978 %
10979 %    o display: Specifies a connection to an X server; returned from
10980 %      XOpenDisplay.
10981 %
10982 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10983 %
10984 %    o windows: Specifies a pointer to a XWindows structure.
10985 %
10986 %    o image: the image.
10987 %
10988 %    o exception: return any errors or warnings in this structure.
10989 %
10990 */
10991 static MagickBooleanType XPrintImage(Display *display,
10992   XResourceInfo *resource_info,XWindows *windows,Image *image,
10993   ExceptionInfo *exception)
10994 {
10995   char
10996     filename[MaxTextExtent],
10997     geometry[MaxTextExtent];
10998
10999   Image
11000     *print_image;
11001
11002   ImageInfo
11003     *image_info;
11004
11005   MagickStatusType
11006     status;
11007
11008   /*
11009     Request Postscript page geometry from user.
11010   */
11011   image_info=CloneImageInfo(resource_info->image_info);
11012   (void) FormatLocaleString(geometry,MaxTextExtent,"Letter");
11013   if (image_info->page != (char *) NULL)
11014     (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
11015   XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
11016     "Select Postscript Page Geometry:",geometry);
11017   if (*geometry == '\0')
11018     return(MagickTrue);
11019   image_info->page=GetPageGeometry(geometry);
11020   /*
11021     Apply image transforms.
11022   */
11023   XSetCursorState(display,windows,MagickTrue);
11024   XCheckRefreshWindows(display,windows);
11025   print_image=CloneImage(image,0,0,MagickTrue,exception);
11026   if (print_image == (Image *) NULL)
11027     return(MagickFalse);
11028   (void) FormatLocaleString(geometry,MaxTextExtent,"%dx%d!",
11029     windows->image.ximage->width,windows->image.ximage->height);
11030   (void) TransformImage(&print_image,windows->image.crop_geometry,geometry,
11031     exception);
11032   /*
11033     Print image.
11034   */
11035   (void) AcquireUniqueFilename(filename);
11036   (void) FormatLocaleString(print_image->filename,MaxTextExtent,"print:%s",
11037     filename);
11038   status=WriteImage(image_info,print_image,exception);
11039   (void) RelinquishUniqueFileResource(filename);
11040   print_image=DestroyImage(print_image);
11041   image_info=DestroyImageInfo(image_info);
11042   XSetCursorState(display,windows,MagickFalse);
11043   return(status != 0 ? MagickTrue : MagickFalse);
11044 }
11045 \f
11046 /*
11047 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11048 %                                                                             %
11049 %                                                                             %
11050 %                                                                             %
11051 +   X R O I I m a g e                                                         %
11052 %                                                                             %
11053 %                                                                             %
11054 %                                                                             %
11055 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11056 %
11057 %  XROIImage() applies an image processing technique to a region of interest.
11058 %
11059 %  The format of the XROIImage method is:
11060 %
11061 %      MagickBooleanType XROIImage(Display *display,
11062 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
11063 %        ExceptionInfo *exception)
11064 %
11065 %  A description of each parameter follows:
11066 %
11067 %    o display: Specifies a connection to an X server; returned from
11068 %      XOpenDisplay.
11069 %
11070 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
11071 %
11072 %    o windows: Specifies a pointer to a XWindows structure.
11073 %
11074 %    o image: the image; returned from ReadImage.
11075 %
11076 %    o exception: return any errors or warnings in this structure.
11077 %
11078 */
11079 static MagickBooleanType XROIImage(Display *display,
11080   XResourceInfo *resource_info,XWindows *windows,Image **image,
11081   ExceptionInfo *exception)
11082 {
11083 #define ApplyMenus  7
11084
11085   static const char
11086     *ROIMenu[] =
11087     {
11088       "Help",
11089       "Dismiss",
11090       (char *) NULL
11091     },
11092     *ApplyMenu[] =
11093     {
11094       "File",
11095       "Edit",
11096       "Transform",
11097       "Enhance",
11098       "Effects",
11099       "F/X",
11100       "Miscellany",
11101       "Help",
11102       "Dismiss",
11103       (char *) NULL
11104     },
11105     *FileMenu[] =
11106     {
11107       "Save...",
11108       "Print...",
11109       (char *) NULL
11110     },
11111     *EditMenu[] =
11112     {
11113       "Undo",
11114       "Redo",
11115       (char *) NULL
11116     },
11117     *TransformMenu[] =
11118     {
11119       "Flop",
11120       "Flip",
11121       "Rotate Right",
11122       "Rotate Left",
11123       (char *) NULL
11124     },
11125     *EnhanceMenu[] =
11126     {
11127       "Hue...",
11128       "Saturation...",
11129       "Brightness...",
11130       "Gamma...",
11131       "Spiff",
11132       "Dull",
11133       "Contrast Stretch...",
11134       "Sigmoidal Contrast...",
11135       "Normalize",
11136       "Equalize",
11137       "Negate",
11138       "Grayscale",
11139       "Map...",
11140       "Quantize...",
11141       (char *) NULL
11142     },
11143     *EffectsMenu[] =
11144     {
11145       "Despeckle",
11146       "Emboss",
11147       "Reduce Noise",
11148       "Add Noise",
11149       "Sharpen...",
11150       "Blur...",
11151       "Threshold...",
11152       "Edge Detect...",
11153       "Spread...",
11154       "Shade...",
11155       "Raise...",
11156       "Segment...",
11157       (char *) NULL
11158     },
11159     *FXMenu[] =
11160     {
11161       "Solarize...",
11162       "Sepia Tone...",
11163       "Swirl...",
11164       "Implode...",
11165       "Vignette...",
11166       "Wave...",
11167       "Oil Paint...",
11168       "Charcoal Draw...",
11169       (char *) NULL
11170     },
11171     *MiscellanyMenu[] =
11172     {
11173       "Image Info",
11174       "Zoom Image",
11175       "Show Preview...",
11176       "Show Histogram",
11177       "Show Matte",
11178       (char *) NULL
11179     };
11180
11181   static const char
11182     **Menus[ApplyMenus] =
11183     {
11184       FileMenu,
11185       EditMenu,
11186       TransformMenu,
11187       EnhanceMenu,
11188       EffectsMenu,
11189       FXMenu,
11190       MiscellanyMenu
11191     };
11192
11193   static const CommandType
11194     ApplyCommands[] =
11195     {
11196       NullCommand,
11197       NullCommand,
11198       NullCommand,
11199       NullCommand,
11200       NullCommand,
11201       NullCommand,
11202       NullCommand,
11203       HelpCommand,
11204       QuitCommand
11205     },
11206     FileCommands[] =
11207     {
11208       SaveCommand,
11209       PrintCommand
11210     },
11211     EditCommands[] =
11212     {
11213       UndoCommand,
11214       RedoCommand
11215     },
11216     TransformCommands[] =
11217     {
11218       FlopCommand,
11219       FlipCommand,
11220       RotateRightCommand,
11221       RotateLeftCommand
11222     },
11223     EnhanceCommands[] =
11224     {
11225       HueCommand,
11226       SaturationCommand,
11227       BrightnessCommand,
11228       GammaCommand,
11229       SpiffCommand,
11230       DullCommand,
11231       ContrastStretchCommand,
11232       SigmoidalContrastCommand,
11233       NormalizeCommand,
11234       EqualizeCommand,
11235       NegateCommand,
11236       GrayscaleCommand,
11237       MapCommand,
11238       QuantizeCommand
11239     },
11240     EffectsCommands[] =
11241     {
11242       DespeckleCommand,
11243       EmbossCommand,
11244       ReduceNoiseCommand,
11245       AddNoiseCommand,
11246       SharpenCommand,
11247       BlurCommand,
11248       EdgeDetectCommand,
11249       SpreadCommand,
11250       ShadeCommand,
11251       RaiseCommand,
11252       SegmentCommand
11253     },
11254     FXCommands[] =
11255     {
11256       SolarizeCommand,
11257       SepiaToneCommand,
11258       SwirlCommand,
11259       ImplodeCommand,
11260       VignetteCommand,
11261       WaveCommand,
11262       OilPaintCommand,
11263       CharcoalDrawCommand
11264     },
11265     MiscellanyCommands[] =
11266     {
11267       InfoCommand,
11268       ZoomCommand,
11269       ShowPreviewCommand,
11270       ShowHistogramCommand,
11271       ShowMatteCommand
11272     },
11273     ROICommands[] =
11274     {
11275       ROIHelpCommand,
11276       ROIDismissCommand
11277     };
11278
11279   static const CommandType
11280     *Commands[ApplyMenus] =
11281     {
11282       FileCommands,
11283       EditCommands,
11284       TransformCommands,
11285       EnhanceCommands,
11286       EffectsCommands,
11287       FXCommands,
11288       MiscellanyCommands
11289     };
11290
11291   char
11292     command[MaxTextExtent],
11293     text[MaxTextExtent];
11294
11295   CommandType
11296     command_type;
11297
11298   Cursor
11299     cursor;
11300
11301   Image
11302     *roi_image;
11303
11304   int
11305     entry,
11306     id,
11307     x,
11308     y;
11309
11310   MagickRealType
11311     scale_factor;
11312
11313   MagickProgressMonitor
11314     progress_monitor;
11315
11316   RectangleInfo
11317     crop_info,
11318     highlight_info,
11319     roi_info;
11320
11321   unsigned int
11322     height,
11323     width;
11324
11325   size_t
11326     state;
11327
11328   XEvent
11329     event;
11330
11331   /*
11332     Map Command widget.
11333   */
11334   (void) CloneString(&windows->command.name,"ROI");
11335   windows->command.data=0;
11336   (void) XCommandWidget(display,windows,ROIMenu,(XEvent *) NULL);
11337   (void) XMapRaised(display,windows->command.id);
11338   XClientMessage(display,windows->image.id,windows->im_protocols,
11339     windows->im_update_widget,CurrentTime);
11340   /*
11341     Track pointer until button 1 is pressed.
11342   */
11343   XQueryPosition(display,windows->image.id,&x,&y);
11344   (void) XSelectInput(display,windows->image.id,
11345     windows->image.attributes.event_mask | PointerMotionMask);
11346   roi_info.x=(ssize_t) windows->image.x+x;
11347   roi_info.y=(ssize_t) windows->image.y+y;
11348   roi_info.width=0;
11349   roi_info.height=0;
11350   cursor=XCreateFontCursor(display,XC_fleur);
11351   state=DefaultState;
11352   do
11353   {
11354     if (windows->info.mapped != MagickFalse)
11355       {
11356         /*
11357           Display pointer position.
11358         */
11359         (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
11360           (long) roi_info.x,(long) roi_info.y);
11361         XInfoWidget(display,windows,text);
11362       }
11363     /*
11364       Wait for next event.
11365     */
11366     XScreenEvent(display,windows,&event,exception);
11367     if (event.xany.window == windows->command.id)
11368       {
11369         /*
11370           Select a command from the Command widget.
11371         */
11372         id=XCommandWidget(display,windows,ROIMenu,&event);
11373         if (id < 0)
11374           continue;
11375         switch (ROICommands[id])
11376         {
11377           case ROIHelpCommand:
11378           {
11379             XTextViewWidget(display,resource_info,windows,MagickFalse,
11380               "Help Viewer - Region of Interest",ImageROIHelp);
11381             break;
11382           }
11383           case ROIDismissCommand:
11384           {
11385             /*
11386               Prematurely exit.
11387             */
11388             state|=EscapeState;
11389             state|=ExitState;
11390             break;
11391           }
11392           default:
11393             break;
11394         }
11395         continue;
11396       }
11397     switch (event.type)
11398     {
11399       case ButtonPress:
11400       {
11401         if (event.xbutton.button != Button1)
11402           break;
11403         if (event.xbutton.window != windows->image.id)
11404           break;
11405         /*
11406           Note first corner of region of interest rectangle-- exit loop.
11407         */
11408         (void) XCheckDefineCursor(display,windows->image.id,cursor);
11409         roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11410         roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11411         state|=ExitState;
11412         break;
11413       }
11414       case ButtonRelease:
11415         break;
11416       case Expose:
11417         break;
11418       case KeyPress:
11419       {
11420         KeySym
11421           key_symbol;
11422
11423         if (event.xkey.window != windows->image.id)
11424           break;
11425         /*
11426           Respond to a user key press.
11427         */
11428         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11429           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11430         switch ((int) key_symbol)
11431         {
11432           case XK_Escape:
11433           case XK_F20:
11434           {
11435             /*
11436               Prematurely exit.
11437             */
11438             state|=EscapeState;
11439             state|=ExitState;
11440             break;
11441           }
11442           case XK_F1:
11443           case XK_Help:
11444           {
11445             XTextViewWidget(display,resource_info,windows,MagickFalse,
11446               "Help Viewer - Region of Interest",ImageROIHelp);
11447             break;
11448           }
11449           default:
11450           {
11451             (void) XBell(display,0);
11452             break;
11453           }
11454         }
11455         break;
11456       }
11457       case MotionNotify:
11458       {
11459         /*
11460           Map and unmap Info widget as text cursor crosses its boundaries.
11461         */
11462         x=event.xmotion.x;
11463         y=event.xmotion.y;
11464         if (windows->info.mapped != MagickFalse)
11465           {
11466             if ((x < (int) (windows->info.x+windows->info.width)) &&
11467                 (y < (int) (windows->info.y+windows->info.height)))
11468               (void) XWithdrawWindow(display,windows->info.id,
11469                 windows->info.screen);
11470           }
11471         else
11472           if ((x > (int) (windows->info.x+windows->info.width)) ||
11473               (y > (int) (windows->info.y+windows->info.height)))
11474             (void) XMapWindow(display,windows->info.id);
11475         roi_info.x=(ssize_t) windows->image.x+x;
11476         roi_info.y=(ssize_t) windows->image.y+y;
11477         break;
11478       }
11479       default:
11480         break;
11481     }
11482   } while ((state & ExitState) == 0);
11483   (void) XSelectInput(display,windows->image.id,
11484     windows->image.attributes.event_mask);
11485   if ((state & EscapeState) != 0)
11486     {
11487       /*
11488         User want to exit without region of interest.
11489       */
11490       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11491       (void) XFreeCursor(display,cursor);
11492       return(MagickTrue);
11493     }
11494   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
11495   do
11496   {
11497     /*
11498       Size rectangle as pointer moves until the mouse button is released.
11499     */
11500     x=(int) roi_info.x;
11501     y=(int) roi_info.y;
11502     roi_info.width=0;
11503     roi_info.height=0;
11504     state=DefaultState;
11505     do
11506     {
11507       highlight_info=roi_info;
11508       highlight_info.x=roi_info.x-windows->image.x;
11509       highlight_info.y=roi_info.y-windows->image.y;
11510       if ((highlight_info.width > 3) && (highlight_info.height > 3))
11511         {
11512           /*
11513             Display info and draw region of interest rectangle.
11514           */
11515           if (windows->info.mapped == MagickFalse)
11516             (void) XMapWindow(display,windows->info.id);
11517           (void) FormatLocaleString(text,MaxTextExtent,
11518             " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11519             roi_info.height,(double) roi_info.x,(double) roi_info.y);
11520           XInfoWidget(display,windows,text);
11521           XHighlightRectangle(display,windows->image.id,
11522             windows->image.highlight_context,&highlight_info);
11523         }
11524       else
11525         if (windows->info.mapped != MagickFalse)
11526           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11527       /*
11528         Wait for next event.
11529       */
11530       XScreenEvent(display,windows,&event,exception);
11531       if ((highlight_info.width > 3) && (highlight_info.height > 3))
11532         XHighlightRectangle(display,windows->image.id,
11533           windows->image.highlight_context,&highlight_info);
11534       switch (event.type)
11535       {
11536         case ButtonPress:
11537         {
11538           roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11539           roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11540           break;
11541         }
11542         case ButtonRelease:
11543         {
11544           /*
11545             User has committed to region of interest rectangle.
11546           */
11547           roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11548           roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11549           XSetCursorState(display,windows,MagickFalse);
11550           state|=ExitState;
11551           if (LocaleCompare(windows->command.name,"Apply") == 0)
11552             break;
11553           (void) CloneString(&windows->command.name,"Apply");
11554           windows->command.data=ApplyMenus;
11555           (void) XCommandWidget(display,windows,ApplyMenu,(XEvent *) NULL);
11556           break;
11557         }
11558         case Expose:
11559           break;
11560         case MotionNotify:
11561         {
11562           roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11563           roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11564         }
11565         default:
11566           break;
11567       }
11568       if ((((int) roi_info.x != x) && ((int) roi_info.y != y)) ||
11569           ((state & ExitState) != 0))
11570         {
11571           /*
11572             Check boundary conditions.
11573           */
11574           if (roi_info.x < 0)
11575             roi_info.x=0;
11576           else
11577             if (roi_info.x > (ssize_t) windows->image.ximage->width)
11578               roi_info.x=(ssize_t) windows->image.ximage->width;
11579           if ((int) roi_info.x < x)
11580             roi_info.width=(unsigned int) (x-roi_info.x);
11581           else
11582             {
11583               roi_info.width=(unsigned int) (roi_info.x-x);
11584               roi_info.x=(ssize_t) x;
11585             }
11586           if (roi_info.y < 0)
11587             roi_info.y=0;
11588           else
11589             if (roi_info.y > (ssize_t) windows->image.ximage->height)
11590               roi_info.y=(ssize_t) windows->image.ximage->height;
11591           if ((int) roi_info.y < y)
11592             roi_info.height=(unsigned int) (y-roi_info.y);
11593           else
11594             {
11595               roi_info.height=(unsigned int) (roi_info.y-y);
11596               roi_info.y=(ssize_t) y;
11597             }
11598         }
11599     } while ((state & ExitState) == 0);
11600     /*
11601       Wait for user to grab a corner of the rectangle or press return.
11602     */
11603     state=DefaultState;
11604     command_type=NullCommand;
11605     (void) XMapWindow(display,windows->info.id);
11606     do
11607     {
11608       if (windows->info.mapped != MagickFalse)
11609         {
11610           /*
11611             Display pointer position.
11612           */
11613           (void) FormatLocaleString(text,MaxTextExtent,
11614             " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11615             roi_info.height,(double) roi_info.x,(double) roi_info.y);
11616           XInfoWidget(display,windows,text);
11617         }
11618       highlight_info=roi_info;
11619       highlight_info.x=roi_info.x-windows->image.x;
11620       highlight_info.y=roi_info.y-windows->image.y;
11621       if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
11622         {
11623           state|=EscapeState;
11624           state|=ExitState;
11625           break;
11626         }
11627       if ((state & UpdateRegionState) != 0)
11628         {
11629           (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11630           switch (command_type)
11631           {
11632             case UndoCommand:
11633             case RedoCommand:
11634             {
11635               (void) XMagickCommand(display,resource_info,windows,command_type,
11636                 image,exception);
11637               break;
11638             }
11639             default:
11640             {
11641               /*
11642                 Region of interest is relative to image configuration.
11643               */
11644               progress_monitor=SetImageProgressMonitor(*image,
11645                 (MagickProgressMonitor) NULL,(*image)->client_data);
11646               crop_info=roi_info;
11647               width=(unsigned int) (*image)->columns;
11648               height=(unsigned int) (*image)->rows;
11649               x=0;
11650               y=0;
11651               if (windows->image.crop_geometry != (char *) NULL)
11652                 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
11653                   &width,&height);
11654               scale_factor=(MagickRealType) width/windows->image.ximage->width;
11655               crop_info.x+=x;
11656               crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
11657               crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
11658               scale_factor=(MagickRealType)
11659                 height/windows->image.ximage->height;
11660               crop_info.y+=y;
11661               crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
11662               crop_info.height=(unsigned int)
11663                 (scale_factor*crop_info.height+0.5);
11664               roi_image=CropImage(*image,&crop_info,exception);
11665               (void) SetImageProgressMonitor(*image,progress_monitor,
11666                 (*image)->client_data);
11667               if (roi_image == (Image *) NULL)
11668                 continue;
11669               /*
11670                 Apply image processing technique to the region of interest.
11671               */
11672               windows->image.orphan=MagickTrue;
11673               (void) XMagickCommand(display,resource_info,windows,command_type,
11674                 &roi_image,exception);
11675               progress_monitor=SetImageProgressMonitor(*image,
11676                 (MagickProgressMonitor) NULL,(*image)->client_data);
11677               (void) XMagickCommand(display,resource_info,windows,
11678                 SaveToUndoBufferCommand,image,exception);
11679               windows->image.orphan=MagickFalse;
11680               (void) CompositeImage(*image,CopyCompositeOp,roi_image,
11681                 crop_info.x,crop_info.y,exception);
11682               roi_image=DestroyImage(roi_image);
11683               (void) SetImageProgressMonitor(*image,progress_monitor,
11684                 (*image)->client_data);
11685               break;
11686             }
11687           }
11688           if (command_type != InfoCommand)
11689             {
11690               XConfigureImageColormap(display,resource_info,windows,*image,
11691                 exception);
11692               (void) XConfigureImage(display,resource_info,windows,*image,
11693                 exception);
11694             }
11695           XCheckRefreshWindows(display,windows);
11696           XInfoWidget(display,windows,text);
11697           (void) XSetFunction(display,windows->image.highlight_context,
11698             GXinvert);
11699           state&=(~UpdateRegionState);
11700         }
11701       XHighlightRectangle(display,windows->image.id,
11702         windows->image.highlight_context,&highlight_info);
11703       XScreenEvent(display,windows,&event,exception);
11704       if (event.xany.window == windows->command.id)
11705         {
11706           /*
11707             Select a command from the Command widget.
11708           */
11709           (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11710           command_type=NullCommand;
11711           id=XCommandWidget(display,windows,ApplyMenu,&event);
11712           if (id >= 0)
11713             {
11714               (void) CopyMagickString(command,ApplyMenu[id],MaxTextExtent);
11715               command_type=ApplyCommands[id];
11716               if (id < ApplyMenus)
11717                 {
11718                   /*
11719                     Select a command from a pop-up menu.
11720                   */
11721                   entry=XMenuWidget(display,windows,ApplyMenu[id],
11722                     (const char **) Menus[id],command);
11723                   if (entry >= 0)
11724                     {
11725                       (void) CopyMagickString(command,Menus[id][entry],
11726                         MaxTextExtent);
11727                       command_type=Commands[id][entry];
11728                     }
11729                 }
11730             }
11731           (void) XSetFunction(display,windows->image.highlight_context,
11732             GXinvert);
11733           XHighlightRectangle(display,windows->image.id,
11734             windows->image.highlight_context,&highlight_info);
11735           if (command_type == HelpCommand)
11736             {
11737               (void) XSetFunction(display,windows->image.highlight_context,
11738                 GXcopy);
11739               XTextViewWidget(display,resource_info,windows,MagickFalse,
11740                 "Help Viewer - Region of Interest",ImageROIHelp);
11741               (void) XSetFunction(display,windows->image.highlight_context,
11742                 GXinvert);
11743               continue;
11744             }
11745           if (command_type == QuitCommand)
11746             {
11747               /*
11748                 exit.
11749               */
11750               state|=EscapeState;
11751               state|=ExitState;
11752               continue;
11753             }
11754           if (command_type != NullCommand)
11755             state|=UpdateRegionState;
11756           continue;
11757         }
11758       XHighlightRectangle(display,windows->image.id,
11759         windows->image.highlight_context,&highlight_info);
11760       switch (event.type)
11761       {
11762         case ButtonPress:
11763         {
11764           x=windows->image.x;
11765           y=windows->image.y;
11766           if (event.xbutton.button != Button1)
11767             break;
11768           if (event.xbutton.window != windows->image.id)
11769             break;
11770           x=windows->image.x+event.xbutton.x;
11771           y=windows->image.y+event.xbutton.y;
11772           if ((x < (int) (roi_info.x+RoiDelta)) &&
11773               (x > (int) (roi_info.x-RoiDelta)) &&
11774               (y < (int) (roi_info.y+RoiDelta)) &&
11775               (y > (int) (roi_info.y-RoiDelta)))
11776             {
11777               roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11778               roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11779               state|=UpdateConfigurationState;
11780               break;
11781             }
11782           if ((x < (int) (roi_info.x+RoiDelta)) &&
11783               (x > (int) (roi_info.x-RoiDelta)) &&
11784               (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11785               (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11786             {
11787               roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11788               state|=UpdateConfigurationState;
11789               break;
11790             }
11791           if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11792               (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11793               (y < (int) (roi_info.y+RoiDelta)) &&
11794               (y > (int) (roi_info.y-RoiDelta)))
11795             {
11796               roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11797               state|=UpdateConfigurationState;
11798               break;
11799             }
11800           if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11801               (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11802               (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11803               (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11804             {
11805               state|=UpdateConfigurationState;
11806               break;
11807             }
11808         }
11809         case ButtonRelease:
11810         {
11811           if (event.xbutton.window == windows->pan.id)
11812             if ((highlight_info.x != crop_info.x-windows->image.x) ||
11813                 (highlight_info.y != crop_info.y-windows->image.y))
11814               XHighlightRectangle(display,windows->image.id,
11815                 windows->image.highlight_context,&highlight_info);
11816           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11817             event.xbutton.time);
11818           break;
11819         }
11820         case Expose:
11821         {
11822           if (event.xexpose.window == windows->image.id)
11823             if (event.xexpose.count == 0)
11824               {
11825                 event.xexpose.x=(int) highlight_info.x;
11826                 event.xexpose.y=(int) highlight_info.y;
11827                 event.xexpose.width=(int) highlight_info.width;
11828                 event.xexpose.height=(int) highlight_info.height;
11829                 XRefreshWindow(display,&windows->image,&event);
11830               }
11831           if (event.xexpose.window == windows->info.id)
11832             if (event.xexpose.count == 0)
11833               XInfoWidget(display,windows,text);
11834           break;
11835         }
11836         case KeyPress:
11837         {
11838           KeySym
11839             key_symbol;
11840
11841           if (event.xkey.window != windows->image.id)
11842             break;
11843           /*
11844             Respond to a user key press.
11845           */
11846           (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11847             sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11848           switch ((int) key_symbol)
11849           {
11850             case XK_Shift_L:
11851             case XK_Shift_R:
11852               break;
11853             case XK_Escape:
11854             case XK_F20:
11855               state|=EscapeState;
11856             case XK_Return:
11857             {
11858               state|=ExitState;
11859               break;
11860             }
11861             case XK_Home:
11862             case XK_KP_Home:
11863             {
11864               roi_info.x=(ssize_t) (windows->image.width/2L-roi_info.width/2L);
11865               roi_info.y=(ssize_t) (windows->image.height/2L-
11866                 roi_info.height/2L);
11867               break;
11868             }
11869             case XK_Left:
11870             case XK_KP_Left:
11871             {
11872               roi_info.x--;
11873               break;
11874             }
11875             case XK_Up:
11876             case XK_KP_Up:
11877             case XK_Next:
11878             {
11879               roi_info.y--;
11880               break;
11881             }
11882             case XK_Right:
11883             case XK_KP_Right:
11884             {
11885               roi_info.x++;
11886               break;
11887             }
11888             case XK_Prior:
11889             case XK_Down:
11890             case XK_KP_Down:
11891             {
11892               roi_info.y++;
11893               break;
11894             }
11895             case XK_F1:
11896             case XK_Help:
11897             {
11898               (void) XSetFunction(display,windows->image.highlight_context,
11899                 GXcopy);
11900               XTextViewWidget(display,resource_info,windows,MagickFalse,
11901                 "Help Viewer - Region of Interest",ImageROIHelp);
11902               (void) XSetFunction(display,windows->image.highlight_context,
11903                 GXinvert);
11904               break;
11905             }
11906             default:
11907             {
11908               command_type=XImageWindowCommand(display,resource_info,windows,
11909                 event.xkey.state,key_symbol,image,exception);
11910               if (command_type != NullCommand)
11911                 state|=UpdateRegionState;
11912               break;
11913             }
11914           }
11915           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11916             event.xkey.time);
11917           break;
11918         }
11919         case KeyRelease:
11920           break;
11921         case MotionNotify:
11922         {
11923           if (event.xbutton.window != windows->image.id)
11924             break;
11925           /*
11926             Map and unmap Info widget as text cursor crosses its boundaries.
11927           */
11928           x=event.xmotion.x;
11929           y=event.xmotion.y;
11930           if (windows->info.mapped != MagickFalse)
11931             {
11932               if ((x < (int) (windows->info.x+windows->info.width)) &&
11933                   (y < (int) (windows->info.y+windows->info.height)))
11934                 (void) XWithdrawWindow(display,windows->info.id,
11935                   windows->info.screen);
11936             }
11937           else
11938             if ((x > (int) (windows->info.x+windows->info.width)) ||
11939                 (y > (int) (windows->info.y+windows->info.height)))
11940               (void) XMapWindow(display,windows->info.id);
11941           roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11942           roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11943           break;
11944         }
11945         case SelectionRequest:
11946         {
11947           XSelectionEvent
11948             notify;
11949
11950           XSelectionRequestEvent
11951             *request;
11952
11953           /*
11954             Set primary selection.
11955           */
11956           (void) FormatLocaleString(text,MaxTextExtent,
11957             "%.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11958             roi_info.height,(double) roi_info.x,(double) roi_info.y);
11959           request=(&(event.xselectionrequest));
11960           (void) XChangeProperty(request->display,request->requestor,
11961             request->property,request->target,8,PropModeReplace,
11962             (unsigned char *) text,(int) strlen(text));
11963           notify.type=SelectionNotify;
11964           notify.display=request->display;
11965           notify.requestor=request->requestor;
11966           notify.selection=request->selection;
11967           notify.target=request->target;
11968           notify.time=request->time;
11969           if (request->property == None)
11970             notify.property=request->target;
11971           else
11972             notify.property=request->property;
11973           (void) XSendEvent(request->display,request->requestor,False,0,
11974             (XEvent *) &notify);
11975         }
11976         default:
11977           break;
11978       }
11979       if ((state & UpdateConfigurationState) != 0)
11980         {
11981           (void) XPutBackEvent(display,&event);
11982           (void) XCheckDefineCursor(display,windows->image.id,cursor);
11983           break;
11984         }
11985     } while ((state & ExitState) == 0);
11986   } while ((state & ExitState) == 0);
11987   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11988   XSetCursorState(display,windows,MagickFalse);
11989   if ((state & EscapeState) != 0)
11990     return(MagickTrue);
11991   return(MagickTrue);
11992 }
11993 \f
11994 /*
11995 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11996 %                                                                             %
11997 %                                                                             %
11998 %                                                                             %
11999 +   X R o t a t e I m a g e                                                   %
12000 %                                                                             %
12001 %                                                                             %
12002 %                                                                             %
12003 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12004 %
12005 %  XRotateImage() rotates the X image.  If the degrees parameter if zero, the
12006 %  rotation angle is computed from the slope of a line drawn by the user.
12007 %
12008 %  The format of the XRotateImage method is:
12009 %
12010 %      MagickBooleanType XRotateImage(Display *display,
12011 %        XResourceInfo *resource_info,XWindows *windows,double degrees,
12012 %        Image **image,ExceptionInfo *exception)
12013 %
12014 %  A description of each parameter follows:
12015 %
12016 %    o display: Specifies a connection to an X server; returned from
12017 %      XOpenDisplay.
12018 %
12019 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12020 %
12021 %    o windows: Specifies a pointer to a XWindows structure.
12022 %
12023 %    o degrees: Specifies the number of degrees to rotate the image.
12024 %
12025 %    o image: the image.
12026 %
12027 %    o exception: return any errors or warnings in this structure.
12028 %
12029 */
12030 static MagickBooleanType XRotateImage(Display *display,
12031   XResourceInfo *resource_info,XWindows *windows,double degrees,Image **image,
12032   ExceptionInfo *exception)
12033 {
12034   static const char
12035     *RotateMenu[] =
12036     {
12037       "Pixel Color",
12038       "Direction",
12039       "Help",
12040       "Dismiss",
12041       (char *) NULL
12042     };
12043
12044   static ModeType
12045     direction = HorizontalRotateCommand;
12046
12047   static const ModeType
12048     DirectionCommands[] =
12049     {
12050       HorizontalRotateCommand,
12051       VerticalRotateCommand
12052     },
12053     RotateCommands[] =
12054     {
12055       RotateColorCommand,
12056       RotateDirectionCommand,
12057       RotateHelpCommand,
12058       RotateDismissCommand
12059     };
12060
12061   static unsigned int
12062     pen_id = 0;
12063
12064   char
12065     command[MaxTextExtent],
12066     text[MaxTextExtent];
12067
12068   Image
12069     *rotate_image;
12070
12071   int
12072     id,
12073     x,
12074     y;
12075
12076   MagickRealType
12077     normalized_degrees;
12078
12079   register int
12080     i;
12081
12082   unsigned int
12083     height,
12084     rotations,
12085     width;
12086
12087   if (degrees == 0.0)
12088     {
12089       unsigned int
12090         distance;
12091
12092       size_t
12093         state;
12094
12095       XEvent
12096         event;
12097
12098       XSegment
12099         rotate_info;
12100
12101       /*
12102         Map Command widget.
12103       */
12104       (void) CloneString(&windows->command.name,"Rotate");
12105       windows->command.data=2;
12106       (void) XCommandWidget(display,windows,RotateMenu,(XEvent *) NULL);
12107       (void) XMapRaised(display,windows->command.id);
12108       XClientMessage(display,windows->image.id,windows->im_protocols,
12109         windows->im_update_widget,CurrentTime);
12110       /*
12111         Wait for first button press.
12112       */
12113       (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12114       XQueryPosition(display,windows->image.id,&x,&y);
12115       rotate_info.x1=x;
12116       rotate_info.y1=y;
12117       rotate_info.x2=x;
12118       rotate_info.y2=y;
12119       state=DefaultState;
12120       do
12121       {
12122         XHighlightLine(display,windows->image.id,
12123           windows->image.highlight_context,&rotate_info);
12124         /*
12125           Wait for next event.
12126         */
12127         XScreenEvent(display,windows,&event,exception);
12128         XHighlightLine(display,windows->image.id,
12129           windows->image.highlight_context,&rotate_info);
12130         if (event.xany.window == windows->command.id)
12131           {
12132             /*
12133               Select a command from the Command widget.
12134             */
12135             id=XCommandWidget(display,windows,RotateMenu,&event);
12136             if (id < 0)
12137               continue;
12138             (void) XSetFunction(display,windows->image.highlight_context,
12139               GXcopy);
12140             switch (RotateCommands[id])
12141             {
12142               case RotateColorCommand:
12143               {
12144                 const char
12145                   *ColorMenu[MaxNumberPens];
12146
12147                 int
12148                   pen_number;
12149
12150                 XColor
12151                   color;
12152
12153                 /*
12154                   Initialize menu selections.
12155                 */
12156                 for (i=0; i < (int) (MaxNumberPens-2); i++)
12157                   ColorMenu[i]=resource_info->pen_colors[i];
12158                 ColorMenu[MaxNumberPens-2]="Browser...";
12159                 ColorMenu[MaxNumberPens-1]=(const char *) NULL;
12160                 /*
12161                   Select a pen color from the pop-up menu.
12162                 */
12163                 pen_number=XMenuWidget(display,windows,RotateMenu[id],
12164                   (const char **) ColorMenu,command);
12165                 if (pen_number < 0)
12166                   break;
12167                 if (pen_number == (MaxNumberPens-2))
12168                   {
12169                     static char
12170                       color_name[MaxTextExtent] = "gray";
12171
12172                     /*
12173                       Select a pen color from a dialog.
12174                     */
12175                     resource_info->pen_colors[pen_number]=color_name;
12176                     XColorBrowserWidget(display,windows,"Select",color_name);
12177                     if (*color_name == '\0')
12178                       break;
12179                   }
12180                 /*
12181                   Set pen color.
12182                 */
12183                 (void) XParseColor(display,windows->map_info->colormap,
12184                   resource_info->pen_colors[pen_number],&color);
12185                 XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
12186                   (unsigned int) MaxColors,&color);
12187                 windows->pixel_info->pen_colors[pen_number]=color;
12188                 pen_id=(unsigned int) pen_number;
12189                 break;
12190               }
12191               case RotateDirectionCommand:
12192               {
12193                 static const char
12194                   *Directions[] =
12195                   {
12196                     "horizontal",
12197                     "vertical",
12198                     (char *) NULL,
12199                   };
12200
12201                 /*
12202                   Select a command from the pop-up menu.
12203                 */
12204                 id=XMenuWidget(display,windows,RotateMenu[id],
12205                   Directions,command);
12206                 if (id >= 0)
12207                   direction=DirectionCommands[id];
12208                 break;
12209               }
12210               case RotateHelpCommand:
12211               {
12212                 XTextViewWidget(display,resource_info,windows,MagickFalse,
12213                   "Help Viewer - Image Rotation",ImageRotateHelp);
12214                 break;
12215               }
12216               case RotateDismissCommand:
12217               {
12218                 /*
12219                   Prematurely exit.
12220                 */
12221                 state|=EscapeState;
12222                 state|=ExitState;
12223                 break;
12224               }
12225               default:
12226                 break;
12227             }
12228             (void) XSetFunction(display,windows->image.highlight_context,
12229               GXinvert);
12230             continue;
12231           }
12232         switch (event.type)
12233         {
12234           case ButtonPress:
12235           {
12236             if (event.xbutton.button != Button1)
12237               break;
12238             if (event.xbutton.window != windows->image.id)
12239               break;
12240             /*
12241               exit loop.
12242             */
12243             (void) XSetFunction(display,windows->image.highlight_context,
12244               GXcopy);
12245             rotate_info.x1=event.xbutton.x;
12246             rotate_info.y1=event.xbutton.y;
12247             state|=ExitState;
12248             break;
12249           }
12250           case ButtonRelease:
12251             break;
12252           case Expose:
12253             break;
12254           case KeyPress:
12255           {
12256             char
12257               command[MaxTextExtent];
12258
12259             KeySym
12260               key_symbol;
12261
12262             if (event.xkey.window != windows->image.id)
12263               break;
12264             /*
12265               Respond to a user key press.
12266             */
12267             (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
12268               sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12269             switch ((int) key_symbol)
12270             {
12271               case XK_Escape:
12272               case XK_F20:
12273               {
12274                 /*
12275                   Prematurely exit.
12276                 */
12277                 state|=EscapeState;
12278                 state|=ExitState;
12279                 break;
12280               }
12281               case XK_F1:
12282               case XK_Help:
12283               {
12284                 (void) XSetFunction(display,windows->image.highlight_context,
12285                   GXcopy);
12286                 XTextViewWidget(display,resource_info,windows,MagickFalse,
12287                   "Help Viewer - Image Rotation",ImageRotateHelp);
12288                 (void) XSetFunction(display,windows->image.highlight_context,
12289                   GXinvert);
12290                 break;
12291               }
12292               default:
12293               {
12294                 (void) XBell(display,0);
12295                 break;
12296               }
12297             }
12298             break;
12299           }
12300           case MotionNotify:
12301           {
12302             rotate_info.x1=event.xmotion.x;
12303             rotate_info.y1=event.xmotion.y;
12304           }
12305         }
12306         rotate_info.x2=rotate_info.x1;
12307         rotate_info.y2=rotate_info.y1;
12308         if (direction == HorizontalRotateCommand)
12309           rotate_info.x2+=32;
12310         else
12311           rotate_info.y2-=32;
12312       } while ((state & ExitState) == 0);
12313       (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12314       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12315       if ((state & EscapeState) != 0)
12316         return(MagickTrue);
12317       /*
12318         Draw line as pointer moves until the mouse button is released.
12319       */
12320       distance=0;
12321       (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12322       state=DefaultState;
12323       do
12324       {
12325         if (distance > 9)
12326           {
12327             /*
12328               Display info and draw rotation line.
12329             */
12330             if (windows->info.mapped == MagickFalse)
12331               (void) XMapWindow(display,windows->info.id);
12332             (void) FormatLocaleString(text,MaxTextExtent," %g",
12333               direction == VerticalRotateCommand ? degrees-90.0 : degrees);
12334             XInfoWidget(display,windows,text);
12335             XHighlightLine(display,windows->image.id,
12336               windows->image.highlight_context,&rotate_info);
12337           }
12338         else
12339           if (windows->info.mapped != MagickFalse)
12340             (void) XWithdrawWindow(display,windows->info.id,
12341               windows->info.screen);
12342         /*
12343           Wait for next event.
12344         */
12345         XScreenEvent(display,windows,&event,exception);
12346         if (distance > 9)
12347           XHighlightLine(display,windows->image.id,
12348             windows->image.highlight_context,&rotate_info);
12349         switch (event.type)
12350         {
12351           case ButtonPress:
12352             break;
12353           case ButtonRelease:
12354           {
12355             /*
12356               User has committed to rotation line.
12357             */
12358             rotate_info.x2=event.xbutton.x;
12359             rotate_info.y2=event.xbutton.y;
12360             state|=ExitState;
12361             break;
12362           }
12363           case Expose:
12364             break;
12365           case MotionNotify:
12366           {
12367             rotate_info.x2=event.xmotion.x;
12368             rotate_info.y2=event.xmotion.y;
12369           }
12370           default:
12371             break;
12372         }
12373         /*
12374           Check boundary conditions.
12375         */
12376         if (rotate_info.x2 < 0)
12377           rotate_info.x2=0;
12378         else
12379           if (rotate_info.x2 > (int) windows->image.width)
12380             rotate_info.x2=(short) windows->image.width;
12381         if (rotate_info.y2 < 0)
12382           rotate_info.y2=0;
12383         else
12384           if (rotate_info.y2 > (int) windows->image.height)
12385             rotate_info.y2=(short) windows->image.height;
12386         /*
12387           Compute rotation angle from the slope of the line.
12388         */
12389         degrees=0.0;
12390         distance=(unsigned int)
12391           ((rotate_info.x2-rotate_info.x1+1)*(rotate_info.x2-rotate_info.x1+1))+
12392           ((rotate_info.y2-rotate_info.y1+1)*(rotate_info.y2-rotate_info.y1+1));
12393         if (distance > 9)
12394           degrees=RadiansToDegrees(-atan2((double) (rotate_info.y2-
12395             rotate_info.y1),(double) (rotate_info.x2-rotate_info.x1)));
12396       } while ((state & ExitState) == 0);
12397       (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12398       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12399       if (distance <= 9)
12400         return(MagickTrue);
12401     }
12402   if (direction == VerticalRotateCommand)
12403     degrees-=90.0;
12404   if (degrees == 0.0)
12405     return(MagickTrue);
12406   /*
12407     Rotate image.
12408   */
12409   normalized_degrees=degrees;
12410   while (normalized_degrees < -45.0)
12411     normalized_degrees+=360.0;
12412   for (rotations=0; normalized_degrees > 45.0; rotations++)
12413     normalized_degrees-=90.0;
12414   if (normalized_degrees != 0.0)
12415     (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
12416       exception);
12417   XSetCursorState(display,windows,MagickTrue);
12418   XCheckRefreshWindows(display,windows);
12419   (*image)->background_color.red=ScaleShortToQuantum(
12420     windows->pixel_info->pen_colors[pen_id].red);
12421   (*image)->background_color.green=ScaleShortToQuantum(
12422     windows->pixel_info->pen_colors[pen_id].green);
12423   (*image)->background_color.blue=ScaleShortToQuantum(
12424     windows->pixel_info->pen_colors[pen_id].blue);
12425   rotate_image=RotateImage(*image,degrees,exception);
12426   XSetCursorState(display,windows,MagickFalse);
12427   if (rotate_image == (Image *) NULL)
12428     return(MagickFalse);
12429   *image=DestroyImage(*image);
12430   *image=rotate_image;
12431   if (windows->image.crop_geometry != (char *) NULL)
12432     {
12433       /*
12434         Rotate crop geometry.
12435       */
12436       width=(unsigned int) (*image)->columns;
12437       height=(unsigned int) (*image)->rows;
12438       (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12439       switch (rotations % 4)
12440       {
12441         default:
12442         case 0:
12443           break;
12444         case 1:
12445         {
12446           /*
12447             Rotate 90 degrees.
12448           */
12449           (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12450             "%ux%u%+d%+d",height,width,(int) (*image)->columns-
12451             (int) height-y,x);
12452           break;
12453         }
12454         case 2:
12455         {
12456           /*
12457             Rotate 180 degrees.
12458           */
12459           (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12460             "%ux%u%+d%+d",width,height,(int) width-x,(int) height-y);
12461           break;
12462         }
12463         case 3:
12464         {
12465           /*
12466             Rotate 270 degrees.
12467           */
12468           (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12469             "%ux%u%+d%+d",height,width,y,(int) (*image)->rows-(int) width-x);
12470           break;
12471         }
12472       }
12473     }
12474   if (windows->image.orphan != MagickFalse)
12475     return(MagickTrue);
12476   if (normalized_degrees != 0.0)
12477     {
12478       /*
12479         Update image colormap.
12480       */
12481       windows->image.window_changes.width=(int) (*image)->columns;
12482       windows->image.window_changes.height=(int) (*image)->rows;
12483       if (windows->image.crop_geometry != (char *) NULL)
12484         {
12485           /*
12486             Obtain dimensions of image from crop geometry.
12487           */
12488           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
12489             &width,&height);
12490           windows->image.window_changes.width=(int) width;
12491           windows->image.window_changes.height=(int) height;
12492         }
12493       XConfigureImageColormap(display,resource_info,windows,*image,exception);
12494     }
12495   else
12496     if (((rotations % 4) == 1) || ((rotations % 4) == 3))
12497       {
12498         windows->image.window_changes.width=windows->image.ximage->height;
12499         windows->image.window_changes.height=windows->image.ximage->width;
12500       }
12501   /*
12502     Update image configuration.
12503   */
12504   (void) XConfigureImage(display,resource_info,windows,*image,exception);
12505   return(MagickTrue);
12506 }
12507 \f
12508 /*
12509 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12510 %                                                                             %
12511 %                                                                             %
12512 %                                                                             %
12513 +   X S a v e I m a g e                                                       %
12514 %                                                                             %
12515 %                                                                             %
12516 %                                                                             %
12517 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12518 %
12519 %  XSaveImage() saves an image to a file.
12520 %
12521 %  The format of the XSaveImage method is:
12522 %
12523 %      MagickBooleanType XSaveImage(Display *display,
12524 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
12525 %        ExceptionInfo *exception)
12526 %
12527 %  A description of each parameter follows:
12528 %
12529 %    o display: Specifies a connection to an X server; returned from
12530 %      XOpenDisplay.
12531 %
12532 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12533 %
12534 %    o windows: Specifies a pointer to a XWindows structure.
12535 %
12536 %    o image: the image.
12537 %
12538 %    o exception: return any errors or warnings in this structure.
12539 %
12540 */
12541 static MagickBooleanType XSaveImage(Display *display,
12542   XResourceInfo *resource_info,XWindows *windows,Image *image,
12543   ExceptionInfo *exception)
12544 {
12545   char
12546     filename[MaxTextExtent],
12547     geometry[MaxTextExtent];
12548
12549   Image
12550     *save_image;
12551
12552   ImageInfo
12553     *image_info;
12554
12555   MagickStatusType
12556     status;
12557
12558   /*
12559     Request file name from user.
12560   */
12561   if (resource_info->write_filename != (char *) NULL)
12562     (void) CopyMagickString(filename,resource_info->write_filename,
12563       MaxTextExtent);
12564   else
12565     {
12566       char
12567         path[MaxTextExtent];
12568
12569       int
12570         status;
12571
12572       GetPathComponent(image->filename,HeadPath,path);
12573       GetPathComponent(image->filename,TailPath,filename);
12574       if (*path != '\0')
12575         {
12576           status=chdir(path);
12577           if (status == -1)
12578             (void) ThrowMagickException(exception,GetMagickModule(),
12579               FileOpenError,"UnableToOpenFile","%s",path);
12580         }
12581     }
12582   XFileBrowserWidget(display,windows,"Save",filename);
12583   if (*filename == '\0')
12584     return(MagickTrue);
12585   if (IsPathAccessible(filename) != MagickFalse)
12586     {
12587       int
12588         status;
12589
12590       /*
12591         File exists-- seek user's permission before overwriting.
12592       */
12593       status=XConfirmWidget(display,windows,"Overwrite",filename);
12594       if (status <= 0)
12595         return(MagickTrue);
12596     }
12597   image_info=CloneImageInfo(resource_info->image_info);
12598   (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
12599   (void) SetImageInfo(image_info,1,exception);
12600   if ((LocaleCompare(image_info->magick,"JPEG") == 0) ||
12601       (LocaleCompare(image_info->magick,"JPG") == 0))
12602     {
12603       char
12604         quality[MaxTextExtent];
12605
12606       int
12607         status;
12608
12609       /*
12610         Request JPEG quality from user.
12611       */
12612       (void) FormatLocaleString(quality,MaxTextExtent,"%.20g",(double)
12613         image->quality);
12614       status=XDialogWidget(display,windows,"Save","Enter JPEG quality:",
12615         quality);
12616       if (*quality == '\0')
12617         return(MagickTrue);
12618       image->quality=StringToUnsignedLong(quality);
12619       image_info->interlace=status != 0 ? NoInterlace : PlaneInterlace;
12620     }
12621   if ((LocaleCompare(image_info->magick,"EPS") == 0) ||
12622       (LocaleCompare(image_info->magick,"PDF") == 0) ||
12623       (LocaleCompare(image_info->magick,"PS") == 0) ||
12624       (LocaleCompare(image_info->magick,"PS2") == 0))
12625     {
12626       char
12627         geometry[MaxTextExtent];
12628
12629       /*
12630         Request page geometry from user.
12631       */
12632       (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
12633       if (LocaleCompare(image_info->magick,"PDF") == 0)
12634         (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
12635       if (image_info->page != (char *) NULL)
12636         (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
12637       XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
12638         "Select page geometry:",geometry);
12639       if (*geometry != '\0')
12640         image_info->page=GetPageGeometry(geometry);
12641     }
12642   /*
12643     Apply image transforms.
12644   */
12645   XSetCursorState(display,windows,MagickTrue);
12646   XCheckRefreshWindows(display,windows);
12647   save_image=CloneImage(image,0,0,MagickTrue,exception);
12648   if (save_image == (Image *) NULL)
12649     return(MagickFalse);
12650   (void) FormatLocaleString(geometry,MaxTextExtent,"%dx%d!",
12651     windows->image.ximage->width,windows->image.ximage->height);
12652   (void) TransformImage(&save_image,windows->image.crop_geometry,geometry,
12653     exception);
12654   /*
12655     Write image.
12656   */
12657   (void) CopyMagickString(save_image->filename,filename,MaxTextExtent);
12658   status=WriteImage(image_info,save_image,exception);
12659   if (status != MagickFalse)
12660     image->taint=MagickFalse;
12661   save_image=DestroyImage(save_image);
12662   image_info=DestroyImageInfo(image_info);
12663   XSetCursorState(display,windows,MagickFalse);
12664   return(status != 0 ? MagickTrue : MagickFalse);
12665 }
12666 \f
12667 /*
12668 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12669 %                                                                             %
12670 %                                                                             %
12671 %                                                                             %
12672 +   X S c r e e n E v e n t                                                   %
12673 %                                                                             %
12674 %                                                                             %
12675 %                                                                             %
12676 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12677 %
12678 %  XScreenEvent() handles global events associated with the Pan and Magnify
12679 %  windows.
12680 %
12681 %  The format of the XScreenEvent function is:
12682 %
12683 %      void XScreenEvent(Display *display,XWindows *windows,XEvent *event,
12684 %        ExceptionInfo *exception)
12685 %
12686 %  A description of each parameter follows:
12687 %
12688 %    o display: Specifies a pointer to the Display structure;  returned from
12689 %      XOpenDisplay.
12690 %
12691 %    o windows: Specifies a pointer to a XWindows structure.
12692 %
12693 %    o event: Specifies a pointer to a X11 XEvent structure.
12694 %
12695 %    o exception: return any errors or warnings in this structure.
12696 %
12697 */
12698
12699 #if defined(__cplusplus) || defined(c_plusplus)
12700 extern "C" {
12701 #endif
12702
12703 static int XPredicate(Display *magick_unused(display),XEvent *event,char *data)
12704 {
12705   register XWindows
12706     *windows;
12707
12708   windows=(XWindows *) data;
12709   if ((event->type == ClientMessage) &&
12710       (event->xclient.window == windows->image.id))
12711     return(MagickFalse);
12712   return(MagickTrue);
12713 }
12714
12715 #if defined(__cplusplus) || defined(c_plusplus)
12716 }
12717 #endif
12718
12719 static void XScreenEvent(Display *display,XWindows *windows,XEvent *event,
12720   ExceptionInfo *exception)
12721 {
12722   register int
12723     x,
12724     y;
12725
12726   (void) XIfEvent(display,event,XPredicate,(char *) windows);
12727   if (event->xany.window == windows->command.id)
12728     return;
12729   switch (event->type)
12730   {
12731     case ButtonPress:
12732     case ButtonRelease:
12733     {
12734       if ((event->xbutton.button == Button3) &&
12735           (event->xbutton.state & Mod1Mask))
12736         {
12737           /*
12738             Convert Alt-Button3 to Button2.
12739           */
12740           event->xbutton.button=Button2;
12741           event->xbutton.state&=(~Mod1Mask);
12742         }
12743       if (event->xbutton.window == windows->backdrop.id)
12744         {
12745           (void) XSetInputFocus(display,event->xbutton.window,RevertToParent,
12746             event->xbutton.time);
12747           break;
12748         }
12749       if (event->xbutton.window == windows->pan.id)
12750         {
12751           XPanImage(display,windows,event,exception);
12752           break;
12753         }
12754       if (event->xbutton.window == windows->image.id)
12755         if (event->xbutton.button == Button2)
12756           {
12757             /*
12758               Update magnified image.
12759             */
12760             x=event->xbutton.x;
12761             y=event->xbutton.y;
12762             if (x < 0)
12763               x=0;
12764             else
12765               if (x >= (int) windows->image.width)
12766                 x=(int) (windows->image.width-1);
12767             windows->magnify.x=(int) windows->image.x+x;
12768             if (y < 0)
12769               y=0;
12770             else
12771              if (y >= (int) windows->image.height)
12772                y=(int) (windows->image.height-1);
12773             windows->magnify.y=windows->image.y+y;
12774             if (windows->magnify.mapped == MagickFalse)
12775               (void) XMapRaised(display,windows->magnify.id);
12776             XMakeMagnifyImage(display,windows,exception);
12777             if (event->type == ButtonRelease)
12778               (void) XWithdrawWindow(display,windows->info.id,
12779                 windows->info.screen);
12780             break;
12781           }
12782       break;
12783     }
12784     case ClientMessage:
12785     {
12786       /*
12787         If client window delete message, exit.
12788       */
12789       if (event->xclient.message_type != windows->wm_protocols)
12790         break;
12791       if (*event->xclient.data.l != (long) windows->wm_delete_window)
12792         break;
12793       if (event->xclient.window == windows->magnify.id)
12794         {
12795           (void) XWithdrawWindow(display,windows->magnify.id,
12796             windows->magnify.screen);
12797           break;
12798         }
12799       break;
12800     }
12801     case ConfigureNotify:
12802     {
12803       if (event->xconfigure.window == windows->magnify.id)
12804         {
12805           unsigned int
12806             magnify;
12807
12808           /*
12809             Magnify window has a new configuration.
12810           */
12811           windows->magnify.width=(unsigned int) event->xconfigure.width;
12812           windows->magnify.height=(unsigned int) event->xconfigure.height;
12813           if (windows->magnify.mapped == MagickFalse)
12814             break;
12815           magnify=1;
12816           while ((int) magnify <= event->xconfigure.width)
12817             magnify<<=1;
12818           while ((int) magnify <= event->xconfigure.height)
12819             magnify<<=1;
12820           magnify>>=1;
12821           if (((int) magnify != event->xconfigure.width) ||
12822               ((int) magnify != event->xconfigure.height))
12823             {
12824               XWindowChanges
12825                 window_changes;
12826
12827               window_changes.width=(int) magnify;
12828               window_changes.height=(int) magnify;
12829               (void) XReconfigureWMWindow(display,windows->magnify.id,
12830                 windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
12831                 &window_changes);
12832               break;
12833             }
12834           XMakeMagnifyImage(display,windows,exception);
12835           break;
12836         }
12837       break;
12838     }
12839     case Expose:
12840     {
12841       if (event->xexpose.window == windows->image.id)
12842         {
12843           XRefreshWindow(display,&windows->image,event);
12844           break;
12845         }
12846       if (event->xexpose.window == windows->pan.id)
12847         if (event->xexpose.count == 0)
12848           {
12849             XDrawPanRectangle(display,windows);
12850             break;
12851           }
12852       if (event->xexpose.window == windows->magnify.id)
12853         if (event->xexpose.count == 0)
12854           {
12855             XMakeMagnifyImage(display,windows,exception);
12856             break;
12857           }
12858       break;
12859     }
12860     case KeyPress:
12861     {
12862       char
12863         command[MaxTextExtent];
12864
12865       KeySym
12866         key_symbol;
12867
12868       if (event->xkey.window != windows->magnify.id)
12869         break;
12870       /*
12871         Respond to a user key press.
12872       */
12873       (void) XLookupString((XKeyEvent *) &event->xkey,command,(int)
12874         sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12875       XMagnifyWindowCommand(display,windows,event->xkey.state,key_symbol,
12876         exception);
12877       break;
12878     }
12879     case MapNotify:
12880     {
12881       if (event->xmap.window == windows->magnify.id)
12882         {
12883           windows->magnify.mapped=MagickTrue;
12884           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12885           break;
12886         }
12887       if (event->xmap.window == windows->info.id)
12888         {
12889           windows->info.mapped=MagickTrue;
12890           break;
12891         }
12892       break;
12893     }
12894     case MotionNotify:
12895     {
12896       while (XCheckMaskEvent(display,ButtonMotionMask,event)) ;
12897       if (event->xmotion.window == windows->image.id)
12898         if (windows->magnify.mapped != MagickFalse)
12899           {
12900             /*
12901               Update magnified image.
12902             */
12903             x=event->xmotion.x;
12904             y=event->xmotion.y;
12905             if (x < 0)
12906               x=0;
12907             else
12908               if (x >= (int) windows->image.width)
12909                 x=(int) (windows->image.width-1);
12910             windows->magnify.x=(int) windows->image.x+x;
12911             if (y < 0)
12912               y=0;
12913             else
12914              if (y >= (int) windows->image.height)
12915                y=(int) (windows->image.height-1);
12916             windows->magnify.y=windows->image.y+y;
12917             XMakeMagnifyImage(display,windows,exception);
12918           }
12919       break;
12920     }
12921     case UnmapNotify:
12922     {
12923       if (event->xunmap.window == windows->magnify.id)
12924         {
12925           windows->magnify.mapped=MagickFalse;
12926           break;
12927         }
12928       if (event->xunmap.window == windows->info.id)
12929         {
12930           windows->info.mapped=MagickFalse;
12931           break;
12932         }
12933       break;
12934     }
12935     default:
12936       break;
12937   }
12938 }
12939 \f
12940 /*
12941 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12942 %                                                                             %
12943 %                                                                             %
12944 %                                                                             %
12945 +   X S e t C r o p G e o m e t r y                                           %
12946 %                                                                             %
12947 %                                                                             %
12948 %                                                                             %
12949 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12950 %
12951 %  XSetCropGeometry() accepts a cropping geometry relative to the Image window
12952 %  and translates it to a cropping geometry relative to the image.
12953 %
12954 %  The format of the XSetCropGeometry method is:
12955 %
12956 %      void XSetCropGeometry(Display *display,XWindows *windows,
12957 %        RectangleInfo *crop_info,Image *image)
12958 %
12959 %  A description of each parameter follows:
12960 %
12961 %    o display: Specifies a connection to an X server; returned from
12962 %      XOpenDisplay.
12963 %
12964 %    o windows: Specifies a pointer to a XWindows structure.
12965 %
12966 %    o crop_info:  A pointer to a RectangleInfo that defines a region of the
12967 %      Image window to crop.
12968 %
12969 %    o image: the image.
12970 %
12971 */
12972 static void XSetCropGeometry(Display *display,XWindows *windows,
12973   RectangleInfo *crop_info,Image *image)
12974 {
12975   char
12976     text[MaxTextExtent];
12977
12978   int
12979     x,
12980     y;
12981
12982   MagickRealType
12983     scale_factor;
12984
12985   unsigned int
12986     height,
12987     width;
12988
12989   if (windows->info.mapped != MagickFalse)
12990     {
12991       /*
12992         Display info on cropping rectangle.
12993       */
12994       (void) FormatLocaleString(text,MaxTextExtent," %.20gx%.20g%+.20g%+.20g",
12995         (double) crop_info->width,(double) crop_info->height,(double)
12996         crop_info->x,(double) crop_info->y);
12997       XInfoWidget(display,windows,text);
12998     }
12999   /*
13000     Cropping geometry is relative to any previous crop geometry.
13001   */
13002   x=0;
13003   y=0;
13004   width=(unsigned int) image->columns;
13005   height=(unsigned int) image->rows;
13006   if (windows->image.crop_geometry != (char *) NULL)
13007     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
13008   else
13009     windows->image.crop_geometry=AcquireString((char *) NULL);
13010   /*
13011     Define the crop geometry string from the cropping rectangle.
13012   */
13013   scale_factor=(MagickRealType) width/windows->image.ximage->width;
13014   if (crop_info->x > 0)
13015     x+=(int) (scale_factor*crop_info->x+0.5);
13016   width=(unsigned int) (scale_factor*crop_info->width+0.5);
13017   if (width == 0)
13018     width=1;
13019   scale_factor=(MagickRealType) height/windows->image.ximage->height;
13020   if (crop_info->y > 0)
13021     y+=(int) (scale_factor*crop_info->y+0.5);
13022   height=(unsigned int) (scale_factor*crop_info->height+0.5);
13023   if (height == 0)
13024     height=1;
13025   (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
13026     "%ux%u%+d%+d",width,height,x,y);
13027 }
13028 \f
13029 /*
13030 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13031 %                                                                             %
13032 %                                                                             %
13033 %                                                                             %
13034 +   X T i l e I m a g e                                                       %
13035 %                                                                             %
13036 %                                                                             %
13037 %                                                                             %
13038 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13039 %
13040 %  XTileImage() loads or deletes a selected tile from a visual image directory.
13041 %  The load or delete command is chosen from a menu.
13042 %
13043 %  The format of the XTileImage method is:
13044 %
13045 %      Image *XTileImage(Display *display,XResourceInfo *resource_info,
13046 %        XWindows *windows,Image *image,XEvent *event,ExceptionInfo *exception)
13047 %
13048 %  A description of each parameter follows:
13049 %
13050 %    o tile_image:  XTileImage reads or deletes the tile image
13051 %      and returns it.  A null image is returned if an error occurs.
13052 %
13053 %    o display: Specifies a connection to an X server;  returned from
13054 %      XOpenDisplay.
13055 %
13056 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13057 %
13058 %    o windows: Specifies a pointer to a XWindows structure.
13059 %
13060 %    o image: the image; returned from ReadImage.
13061 %
13062 %    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
13063 %      the entire image is refreshed.
13064 %
13065 %    o exception: return any errors or warnings in this structure.
13066 %
13067 */
13068 static Image *XTileImage(Display *display,XResourceInfo *resource_info,
13069   XWindows *windows,Image *image,XEvent *event,ExceptionInfo *exception)
13070 {
13071   static const char
13072     *VerbMenu[] =
13073     {
13074       "Load",
13075       "Next",
13076       "Former",
13077       "Delete",
13078       "Update",
13079       (char *) NULL,
13080     };
13081
13082   static const ModeType
13083     TileCommands[] =
13084     {
13085       TileLoadCommand,
13086       TileNextCommand,
13087       TileFormerCommand,
13088       TileDeleteCommand,
13089       TileUpdateCommand
13090     };
13091
13092   char
13093     command[MaxTextExtent],
13094     filename[MaxTextExtent];
13095
13096   Image
13097     *tile_image;
13098
13099   int
13100     id,
13101     status,
13102     tile,
13103     x,
13104     y;
13105
13106   MagickRealType
13107     scale_factor;
13108
13109   register char
13110     *p,
13111     *q;
13112
13113   register int
13114     i;
13115
13116   unsigned int
13117     height,
13118     width;
13119
13120   /*
13121     Tile image is relative to montage image configuration.
13122   */
13123   x=0;
13124   y=0;
13125   width=(unsigned int) image->columns;
13126   height=(unsigned int) image->rows;
13127   if (windows->image.crop_geometry != (char *) NULL)
13128     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
13129   scale_factor=(MagickRealType) width/windows->image.ximage->width;
13130   event->xbutton.x+=windows->image.x;
13131   event->xbutton.x=(int) (scale_factor*event->xbutton.x+x+0.5);
13132   scale_factor=(MagickRealType) height/windows->image.ximage->height;
13133   event->xbutton.y+=windows->image.y;
13134   event->xbutton.y=(int) (scale_factor*event->xbutton.y+y+0.5);
13135   /*
13136     Determine size and location of each tile in the visual image directory.
13137   */
13138   width=(unsigned int) image->columns;
13139   height=(unsigned int) image->rows;
13140   x=0;
13141   y=0;
13142   (void) XParseGeometry(image->montage,&x,&y,&width,&height);
13143   tile=((event->xbutton.y-y)/height)*(((int) image->columns-x)/width)+
13144     (event->xbutton.x-x)/width;
13145   if (tile < 0)
13146     {
13147       /*
13148         Button press is outside any tile.
13149       */
13150       (void) XBell(display,0);
13151       return((Image *) NULL);
13152     }
13153   /*
13154     Determine file name from the tile directory.
13155   */
13156   p=image->directory;
13157   for (i=tile; (i != 0) && (*p != '\0'); )
13158   {
13159     if (*p == '\n')
13160       i--;
13161     p++;
13162   }
13163   if (*p == '\0')
13164     {
13165       /*
13166         Button press is outside any tile.
13167       */
13168       (void) XBell(display,0);
13169       return((Image *) NULL);
13170     }
13171   /*
13172     Select a command from the pop-up menu.
13173   */
13174   id=XMenuWidget(display,windows,"Tile Verb",VerbMenu,command);
13175   if (id < 0)
13176     return((Image *) NULL);
13177   q=p;
13178   while ((*q != '\n') && (*q != '\0'))
13179     q++;
13180   (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13181   /*
13182     Perform command for the selected tile.
13183   */
13184   XSetCursorState(display,windows,MagickTrue);
13185   XCheckRefreshWindows(display,windows);
13186   tile_image=NewImageList();
13187   switch (TileCommands[id])
13188   {
13189     case TileLoadCommand:
13190     {
13191       /*
13192         Load tile image.
13193       */
13194       XCheckRefreshWindows(display,windows);
13195       (void) CopyMagickString(resource_info->image_info->magick,"MIFF",
13196         MaxTextExtent);
13197       (void) CopyMagickString(resource_info->image_info->filename,filename,
13198         MaxTextExtent);
13199       tile_image=ReadImage(resource_info->image_info,exception);
13200       CatchException(exception);
13201       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13202       break;
13203     }
13204     case TileNextCommand:
13205     {
13206       /*
13207         Display next image.
13208       */
13209       XClientMessage(display,windows->image.id,windows->im_protocols,
13210         windows->im_next_image,CurrentTime);
13211       break;
13212     }
13213     case TileFormerCommand:
13214     {
13215       /*
13216         Display former image.
13217       */
13218       XClientMessage(display,windows->image.id,windows->im_protocols,
13219         windows->im_former_image,CurrentTime);
13220       break;
13221     }
13222     case TileDeleteCommand:
13223     {
13224       /*
13225         Delete tile image.
13226       */
13227       if (IsPathAccessible(filename) == MagickFalse)
13228         {
13229           XNoticeWidget(display,windows,"Image file does not exist:",filename);
13230           break;
13231         }
13232       status=XConfirmWidget(display,windows,"Really delete tile",filename);
13233       if (status <= 0)
13234         break;
13235       status=remove_utf8(filename) != 0 ? MagickTrue : MagickFalse;
13236       if (status != MagickFalse)
13237         {
13238           XNoticeWidget(display,windows,"Unable to delete image file:",
13239             filename);
13240           break;
13241         }
13242     }
13243     case TileUpdateCommand:
13244     {
13245       int
13246         x_offset,
13247         y_offset;
13248
13249       PixelInfo
13250         pixel;
13251
13252       Quantum
13253         virtual_pixel[CompositePixelChannel];
13254
13255       register int
13256         j;
13257
13258       register Quantum
13259         *s;
13260
13261       /*
13262         Ensure all the images exist.
13263       */
13264       tile=0;
13265       GetPixelInfo(image,&pixel);
13266       for (p=image->directory; *p != '\0'; p++)
13267       {
13268         CacheView
13269           *image_view;
13270
13271         q=p;
13272         while ((*q != '\n') && (*q != '\0'))
13273           q++;
13274         (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13275         p=q;
13276         if (IsPathAccessible(filename) != MagickFalse)
13277           {
13278             tile++;
13279             continue;
13280           }
13281         /*
13282           Overwrite tile with background color.
13283         */
13284         x_offset=(int) (width*(tile % (((int) image->columns-x)/width))+x);
13285         y_offset=(int) (height*(tile/(((int) image->columns-x)/width))+y);
13286         image_view=AcquireCacheView(image);
13287         (void) GetOneCacheViewVirtualPixel(image_view,0,0,virtual_pixel,
13288           exception);
13289         pixel.red=virtual_pixel[RedPixelChannel];
13290         pixel.green=virtual_pixel[GreenPixelChannel];
13291         pixel.blue=virtual_pixel[BluePixelChannel];
13292         pixel.alpha=virtual_pixel[AlphaPixelChannel];
13293         for (i=0; i < (int) height; i++)
13294         {
13295           s=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,(ssize_t)
13296             y_offset+i,width,1,exception);
13297           if (s == (Quantum *) NULL)
13298             break;
13299           for (j=0; j < (int) width; j++)
13300           {
13301             SetPixelInfoPixel(image,&pixel,s);
13302             s+=GetPixelChannels(image);
13303           }
13304           if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
13305             break;
13306         }
13307         image_view=DestroyCacheView(image_view);
13308         tile++;
13309       }
13310       windows->image.window_changes.width=(int) image->columns;
13311       windows->image.window_changes.height=(int) image->rows;
13312       XConfigureImageColormap(display,resource_info,windows,image,exception);
13313       (void) XConfigureImage(display,resource_info,windows,image,exception);
13314       break;
13315     }
13316     default:
13317       break;
13318   }
13319   XSetCursorState(display,windows,MagickFalse);
13320   return(tile_image);
13321 }
13322 \f
13323 /*
13324 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13325 %                                                                             %
13326 %                                                                             %
13327 %                                                                             %
13328 +   X T r a n s l a t e I m a g e                                             %
13329 %                                                                             %
13330 %                                                                             %
13331 %                                                                             %
13332 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13333 %
13334 %  XTranslateImage() translates the image within an Image window by one pixel
13335 %  as specified by the key symbol.  If the image has a `montage string the
13336 %  translation is respect to the width and height contained within the string.
13337 %
13338 %  The format of the XTranslateImage method is:
13339 %
13340 %      void XTranslateImage(Display *display,XWindows *windows,
13341 %        Image *image,const KeySym key_symbol)
13342 %
13343 %  A description of each parameter follows:
13344 %
13345 %    o display: Specifies a connection to an X server; returned from
13346 %      XOpenDisplay.
13347 %
13348 %    o windows: Specifies a pointer to a XWindows structure.
13349 %
13350 %    o image: the image.
13351 %
13352 %    o key_symbol: Specifies a KeySym which indicates which side of the image
13353 %      to trim.
13354 %
13355 */
13356 static void XTranslateImage(Display *display,XWindows *windows,
13357   Image *image,const KeySym key_symbol)
13358 {
13359   char
13360     text[MaxTextExtent];
13361
13362   int
13363     x,
13364     y;
13365
13366   unsigned int
13367     x_offset,
13368     y_offset;
13369
13370   /*
13371     User specified a pan position offset.
13372   */
13373   x_offset=windows->image.width;
13374   y_offset=windows->image.height;
13375   if (image->montage != (char *) NULL)
13376     (void) XParseGeometry(image->montage,&x,&y,&x_offset,&y_offset);
13377   switch ((int) key_symbol)
13378   {
13379     case XK_Home:
13380     case XK_KP_Home:
13381     {
13382       windows->image.x=(int) windows->image.width/2;
13383       windows->image.y=(int) windows->image.height/2;
13384       break;
13385     }
13386     case XK_Left:
13387     case XK_KP_Left:
13388     {
13389       windows->image.x-=x_offset;
13390       break;
13391     }
13392     case XK_Next:
13393     case XK_Up:
13394     case XK_KP_Up:
13395     {
13396       windows->image.y-=y_offset;
13397       break;
13398     }
13399     case XK_Right:
13400     case XK_KP_Right:
13401     {
13402       windows->image.x+=x_offset;
13403       break;
13404     }
13405     case XK_Prior:
13406     case XK_Down:
13407     case XK_KP_Down:
13408     {
13409       windows->image.y+=y_offset;
13410       break;
13411     }
13412     default:
13413       return;
13414   }
13415   /*
13416     Check boundary conditions.
13417   */
13418   if (windows->image.x < 0)
13419     windows->image.x=0;
13420   else
13421     if ((int) (windows->image.x+windows->image.width) >
13422         windows->image.ximage->width)
13423       windows->image.x=(int) windows->image.ximage->width-windows->image.width;
13424   if (windows->image.y < 0)
13425     windows->image.y=0;
13426   else
13427     if ((int) (windows->image.y+windows->image.height) >
13428         windows->image.ximage->height)
13429       windows->image.y=(int) windows->image.ximage->height-windows->image.height;
13430   /*
13431     Refresh Image window.
13432   */
13433   (void) FormatLocaleString(text,MaxTextExtent," %ux%u%+d%+d ",
13434     windows->image.width,windows->image.height,windows->image.x,
13435     windows->image.y);
13436   XInfoWidget(display,windows,text);
13437   XCheckRefreshWindows(display,windows);
13438   XDrawPanRectangle(display,windows);
13439   XRefreshWindow(display,&windows->image,(XEvent *) NULL);
13440   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13441 }
13442 \f
13443 /*
13444 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13445 %                                                                             %
13446 %                                                                             %
13447 %                                                                             %
13448 +   X T r i m I m a g e                                                       %
13449 %                                                                             %
13450 %                                                                             %
13451 %                                                                             %
13452 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13453 %
13454 %  XTrimImage() trims the edges from the Image window.
13455 %
13456 %  The format of the XTrimImage method is:
13457 %
13458 %      MagickBooleanType XTrimImage(Display *display,
13459 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
13460 %        ExceptionInfo *exception)
13461 %
13462 %  A description of each parameter follows:
13463 %
13464 %    o display: Specifies a connection to an X server; returned from
13465 %      XOpenDisplay.
13466 %
13467 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13468 %
13469 %    o windows: Specifies a pointer to a XWindows structure.
13470 %
13471 %    o image: the image.
13472 %
13473 %    o exception: return any errors or warnings in this structure.
13474 %
13475 */
13476 static MagickBooleanType XTrimImage(Display *display,
13477   XResourceInfo *resource_info,XWindows *windows,Image *image,
13478   ExceptionInfo *exception)
13479 {
13480   RectangleInfo
13481     trim_info;
13482
13483   register int
13484     x,
13485     y;
13486
13487   size_t
13488     background,
13489     pixel;
13490
13491   /*
13492     Trim edges from image.
13493   */
13494   XSetCursorState(display,windows,MagickTrue);
13495   XCheckRefreshWindows(display,windows);
13496   /*
13497     Crop the left edge.
13498   */
13499   background=XGetPixel(windows->image.ximage,0,0);
13500   trim_info.width=(size_t) windows->image.ximage->width;
13501   for (x=0; x < windows->image.ximage->width; x++)
13502   {
13503     for (y=0; y < windows->image.ximage->height; y++)
13504     {
13505       pixel=XGetPixel(windows->image.ximage,x,y);
13506       if (pixel != background)
13507         break;
13508     }
13509     if (y < windows->image.ximage->height)
13510       break;
13511   }
13512   trim_info.x=(ssize_t) x;
13513   if (trim_info.x == (ssize_t) windows->image.ximage->width)
13514     {
13515       XSetCursorState(display,windows,MagickFalse);
13516       return(MagickFalse);
13517     }
13518   /*
13519     Crop the right edge.
13520   */
13521   background=XGetPixel(windows->image.ximage,windows->image.ximage->width-1,0);
13522   for (x=windows->image.ximage->width-1; x != 0; x--)
13523   {
13524     for (y=0; y < windows->image.ximage->height; y++)
13525     {
13526       pixel=XGetPixel(windows->image.ximage,x,y);
13527       if (pixel != background)
13528         break;
13529     }
13530     if (y < windows->image.ximage->height)
13531       break;
13532   }
13533   trim_info.width=(size_t) (x-trim_info.x+1);
13534   /*
13535     Crop the top edge.
13536   */
13537   background=XGetPixel(windows->image.ximage,0,0);
13538   trim_info.height=(size_t) windows->image.ximage->height;
13539   for (y=0; y < windows->image.ximage->height; y++)
13540   {
13541     for (x=0; x < windows->image.ximage->width; x++)
13542     {
13543       pixel=XGetPixel(windows->image.ximage,x,y);
13544       if (pixel != background)
13545         break;
13546     }
13547     if (x < windows->image.ximage->width)
13548       break;
13549   }
13550   trim_info.y=(ssize_t) y;
13551   /*
13552     Crop the bottom edge.
13553   */
13554   background=XGetPixel(windows->image.ximage,0,windows->image.ximage->height-1);
13555   for (y=windows->image.ximage->height-1; y != 0; y--)
13556   {
13557     for (x=0; x < windows->image.ximage->width; x++)
13558     {
13559       pixel=XGetPixel(windows->image.ximage,x,y);
13560       if (pixel != background)
13561         break;
13562     }
13563     if (x < windows->image.ximage->width)
13564       break;
13565   }
13566   trim_info.height=(size_t) y-trim_info.y+1;
13567   if (((unsigned int) trim_info.width != windows->image.width) ||
13568       ((unsigned int) trim_info.height != windows->image.height))
13569     {
13570       /*
13571         Reconfigure Image window as defined by the trimming rectangle.
13572       */
13573       XSetCropGeometry(display,windows,&trim_info,image);
13574       windows->image.window_changes.width=(int) trim_info.width;
13575       windows->image.window_changes.height=(int) trim_info.height;
13576       (void) XConfigureImage(display,resource_info,windows,image,exception);
13577     }
13578   XSetCursorState(display,windows,MagickFalse);
13579   return(MagickTrue);
13580 }
13581 \f
13582 /*
13583 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13584 %                                                                             %
13585 %                                                                             %
13586 %                                                                             %
13587 +   X V i s u a l D i r e c t o r y I m a g e                                 %
13588 %                                                                             %
13589 %                                                                             %
13590 %                                                                             %
13591 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13592 %
13593 %  XVisualDirectoryImage() creates a Visual Image Directory.
13594 %
13595 %  The format of the XVisualDirectoryImage method is:
13596 %
13597 %      Image *XVisualDirectoryImage(Display *display,
13598 %        XResourceInfo *resource_info,XWindows *windows,
13599 %        ExceptionInfo *exception)
13600 %
13601 %  A description of each parameter follows:
13602 %
13603 %    o display: Specifies a connection to an X server; returned from
13604 %      XOpenDisplay.
13605 %
13606 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13607 %
13608 %    o windows: Specifies a pointer to a XWindows structure.
13609 %
13610 %    o exception: return any errors or warnings in this structure.
13611 %
13612 */
13613 static Image *XVisualDirectoryImage(Display *display,
13614   XResourceInfo *resource_info,XWindows *windows,ExceptionInfo *exception)
13615 {
13616 #define TileImageTag  "Scale/Image"
13617 #define XClientName  "montage"
13618
13619   char
13620     **filelist;
13621
13622   Image
13623     *images,
13624     *montage_image,
13625     *next_image,
13626     *thumbnail_image;
13627
13628   ImageInfo
13629     *read_info;
13630
13631   int
13632     number_files;
13633
13634   MagickBooleanType
13635     backdrop;
13636
13637   MagickStatusType
13638     status;
13639
13640   MontageInfo
13641     *montage_info;
13642
13643   RectangleInfo
13644     geometry;
13645
13646   register int
13647     i;
13648
13649   static char
13650     filename[MaxTextExtent] = "\0",
13651     filenames[MaxTextExtent] = "*";
13652
13653   XResourceInfo
13654     background_resources;
13655
13656   /*
13657     Request file name from user.
13658   */
13659   XFileBrowserWidget(display,windows,"Directory",filenames);
13660   if (*filenames == '\0')
13661     return((Image *) NULL);
13662   /*
13663     Expand the filenames.
13664   */
13665   filelist=(char **) AcquireMagickMemory(sizeof(*filelist));
13666   if (filelist == (char **) NULL)
13667     {
13668       ThrowXWindowFatalException(ResourceLimitError,"MemoryAllocationFailed",
13669         filenames);
13670       return((Image *) NULL);
13671     }
13672   number_files=1;
13673   filelist[0]=filenames;
13674   status=ExpandFilenames(&number_files,&filelist);
13675   if ((status == MagickFalse) || (number_files == 0))
13676     {
13677       if (number_files == 0)
13678         ThrowXWindowFatalException(ImageError,"NoImagesWereFound",filenames)
13679       else
13680         ThrowXWindowFatalException(ResourceLimitError,"MemoryAllocationFailed",
13681           filenames);
13682       return((Image *) NULL);
13683     }
13684   /*
13685     Set image background resources.
13686   */
13687   background_resources=(*resource_info);
13688   background_resources.window_id=AcquireString("");
13689   (void) FormatLocaleString(background_resources.window_id,MaxTextExtent,
13690     "0x%lx",windows->image.id);
13691   background_resources.backdrop=MagickTrue;
13692   /*
13693     Read each image and convert them to a tile.
13694   */
13695   backdrop=(windows->visual_info->klass == TrueColor) ||
13696     (windows->visual_info->klass == DirectColor) ? MagickTrue : MagickFalse;
13697   read_info=CloneImageInfo(resource_info->image_info);
13698   (void) SetImageOption(read_info,"jpeg:size","120x120");
13699   (void) CloneString(&read_info->size,DefaultTileGeometry);
13700   (void) SetImageInfoProgressMonitor(read_info,(MagickProgressMonitor) NULL,
13701     (void *) NULL);
13702   images=NewImageList();
13703   XSetCursorState(display,windows,MagickTrue);
13704   XCheckRefreshWindows(display,windows);
13705   for (i=0; i < (int) number_files; i++)
13706   {
13707     (void) CopyMagickString(read_info->filename,filelist[i],MaxTextExtent);
13708     filelist[i]=DestroyString(filelist[i]);
13709     *read_info->magick='\0';
13710     next_image=ReadImage(read_info,exception);
13711     CatchException(exception);
13712     if (next_image != (Image *) NULL)
13713       {
13714         (void) DeleteImageProperty(next_image,"label");
13715         (void) SetImageProperty(next_image,"label",InterpretImageProperties(
13716           read_info,next_image,DefaultTileLabel,exception),exception);
13717         (void) ParseRegionGeometry(next_image,read_info->size,&geometry,
13718           exception);
13719         thumbnail_image=ThumbnailImage(next_image,geometry.width,
13720           geometry.height,exception);
13721         if (thumbnail_image != (Image *) NULL)
13722           {
13723             next_image=DestroyImage(next_image);
13724             next_image=thumbnail_image;
13725           }
13726         if (backdrop)
13727           {
13728             (void) XDisplayBackgroundImage(display,&background_resources,
13729               next_image,exception);
13730             XSetCursorState(display,windows,MagickTrue);
13731           }
13732         AppendImageToList(&images,next_image);
13733         if (images->progress_monitor != (MagickProgressMonitor) NULL)
13734           {
13735             MagickBooleanType
13736               proceed;
13737
13738             proceed=SetImageProgress(images,LoadImageTag,(MagickOffsetType) i,
13739               (MagickSizeType) number_files);
13740             if (proceed == MagickFalse)
13741               break;
13742           }
13743       }
13744   }
13745   filelist=(char **) RelinquishMagickMemory(filelist);
13746   if (images == (Image *) NULL)
13747     {
13748       read_info=DestroyImageInfo(read_info);
13749       XSetCursorState(display,windows,MagickFalse);
13750       ThrowXWindowFatalException(ImageError,"NoImagesWereLoaded",filenames);
13751       return((Image *) NULL);
13752     }
13753   /*
13754     Create the Visual Image Directory.
13755   */
13756   montage_info=CloneMontageInfo(read_info,(MontageInfo *) NULL);
13757   montage_info->pointsize=10;
13758   if (resource_info->font != (char *) NULL)
13759     (void) CloneString(&montage_info->font,resource_info->font);
13760   (void) CopyMagickString(montage_info->filename,filename,MaxTextExtent);
13761   montage_image=MontageImageList(read_info,montage_info,GetFirstImageInList(
13762     images),exception);
13763   images=DestroyImageList(images);
13764   montage_info=DestroyMontageInfo(montage_info);
13765   read_info=DestroyImageInfo(read_info);
13766   XSetCursorState(display,windows,MagickFalse);
13767   if (montage_image == (Image *) NULL)
13768     return(montage_image);
13769   XClientMessage(display,windows->image.id,windows->im_protocols,
13770     windows->im_next_image,CurrentTime);
13771   return(montage_image);
13772 }
13773 \f
13774 /*
13775 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13776 %                                                                             %
13777 %                                                                             %
13778 %                                                                             %
13779 %   X D i s p l a y B a c k g r o u n d I m a g e                             %
13780 %                                                                             %
13781 %                                                                             %
13782 %                                                                             %
13783 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13784 %
13785 %  XDisplayBackgroundImage() displays an image in the background of a window.
13786 %
13787 %  The format of the XDisplayBackgroundImage method is:
13788 %
13789 %      MagickBooleanType XDisplayBackgroundImage(Display *display,
13790 %        XResourceInfo *resource_info,Image *image,ExceptionInfo *exception)
13791 %
13792 %  A description of each parameter follows:
13793 %
13794 %    o display: Specifies a connection to an X server;  returned from
13795 %      XOpenDisplay.
13796 %
13797 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13798 %
13799 %    o image: the image.
13800 %
13801 %    o exception: return any errors or warnings in this structure.
13802 %
13803 */
13804 MagickExport MagickBooleanType XDisplayBackgroundImage(Display *display,
13805   XResourceInfo *resource_info,Image *image,ExceptionInfo *exception)
13806 {
13807   char
13808     geometry[MaxTextExtent],
13809     visual_type[MaxTextExtent];
13810
13811   int
13812     height,
13813     status,
13814     width;
13815
13816   RectangleInfo
13817     geometry_info;
13818
13819   static XPixelInfo
13820     pixel;
13821
13822   static XStandardColormap
13823     *map_info;
13824
13825   static XVisualInfo
13826     *visual_info = (XVisualInfo *) NULL;
13827
13828   static XWindowInfo
13829     window_info;
13830
13831   size_t
13832     delay;
13833
13834   Window
13835     root_window;
13836
13837   XGCValues
13838     context_values;
13839
13840   XResourceInfo
13841     resources;
13842
13843   XWindowAttributes
13844     window_attributes;
13845
13846   /*
13847     Determine target window.
13848   */
13849   assert(image != (Image *) NULL);
13850   assert(image->signature == MagickSignature);
13851   if (image->debug != MagickFalse)
13852     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
13853   resources=(*resource_info);
13854   window_info.id=(Window) NULL;
13855   root_window=XRootWindow(display,XDefaultScreen(display));
13856   if (LocaleCompare(resources.window_id,"root") == 0)
13857     window_info.id=root_window;
13858   else
13859     {
13860       if (isdigit((unsigned char) *resources.window_id) != 0)
13861         window_info.id=XWindowByID(display,root_window,
13862           (Window) strtol((char *) resources.window_id,(char **) NULL,0));
13863       if (window_info.id == (Window) NULL)
13864         window_info.id=XWindowByName(display,root_window,resources.window_id);
13865     }
13866   if (window_info.id == (Window) NULL)
13867     {
13868       ThrowXWindowFatalException(XServerError,"NoWindowWithSpecifiedIDExists",
13869         resources.window_id);
13870       return(MagickFalse);
13871     }
13872   /*
13873     Determine window visual id.
13874   */
13875   window_attributes.width=XDisplayWidth(display,XDefaultScreen(display));
13876   window_attributes.height=XDisplayHeight(display,XDefaultScreen(display));
13877   (void) CopyMagickString(visual_type,"default",MaxTextExtent);
13878   status=XGetWindowAttributes(display,window_info.id,&window_attributes);
13879   if (status != 0)
13880     (void) FormatLocaleString(visual_type,MaxTextExtent,"0x%lx",
13881       XVisualIDFromVisual(window_attributes.visual));
13882   if (visual_info == (XVisualInfo *) NULL)
13883     {
13884       /*
13885         Allocate standard colormap.
13886       */
13887       map_info=XAllocStandardColormap();
13888       if (map_info == (XStandardColormap *) NULL)
13889         ThrowXWindowFatalException(XServerFatalError,"MemoryAllocationFailed",
13890           image->filename);
13891       map_info->colormap=(Colormap) NULL;
13892       pixel.pixels=(unsigned long *) NULL;
13893       /*
13894         Initialize visual info.
13895       */
13896       resources.map_type=(char *) NULL;
13897       resources.visual_type=visual_type;
13898       visual_info=XBestVisualInfo(display,map_info,&resources);
13899       if (visual_info == (XVisualInfo *) NULL)
13900         ThrowXWindowFatalException(XServerFatalError,"UnableToGetVisual",
13901           resources.visual_type);
13902       /*
13903         Initialize window info.
13904       */
13905       window_info.ximage=(XImage *) NULL;
13906       window_info.matte_image=(XImage *) NULL;
13907       window_info.pixmap=(Pixmap) NULL;
13908       window_info.matte_pixmap=(Pixmap) NULL;
13909     }
13910   /*
13911     Free previous root colors.
13912   */
13913   if (window_info.id == root_window)
13914     (void) XDestroyWindowColors(display,root_window);
13915   /*
13916     Initialize Standard Colormap.
13917   */
13918   resources.colormap=SharedColormap;
13919   XMakeStandardColormap(display,visual_info,&resources,image,map_info,&pixel,
13920     exception);
13921   /*
13922     Graphic context superclass.
13923   */
13924   context_values.background=pixel.background_color.pixel;
13925   context_values.foreground=pixel.foreground_color.pixel;
13926   pixel.annotate_context=XCreateGC(display,window_info.id,
13927     (size_t) (GCBackground | GCForeground),&context_values);
13928   if (pixel.annotate_context == (GC) NULL)
13929     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
13930       image->filename);
13931   /*
13932     Initialize Image window attributes.
13933   */
13934   window_info.name=AcquireString("\0");
13935   window_info.icon_name=AcquireString("\0");
13936   XGetWindowInfo(display,visual_info,map_info,&pixel,(XFontStruct *) NULL,
13937     &resources,&window_info);
13938   /*
13939     Create the X image.
13940   */
13941   window_info.width=(unsigned int) image->columns;
13942   window_info.height=(unsigned int) image->rows;
13943   if ((image->columns != window_info.width) ||
13944       (image->rows != window_info.height))
13945     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13946       image->filename);
13947   (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>",
13948     window_attributes.width,window_attributes.height);
13949   geometry_info.width=window_info.width;
13950   geometry_info.height=window_info.height;
13951   geometry_info.x=(ssize_t) window_info.x;
13952   geometry_info.y=(ssize_t) window_info.y;
13953   (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
13954     &geometry_info.width,&geometry_info.height);
13955   window_info.width=(unsigned int) geometry_info.width;
13956   window_info.height=(unsigned int) geometry_info.height;
13957   window_info.x=(int) geometry_info.x;
13958   window_info.y=(int) geometry_info.y;
13959   status=XMakeImage(display,&resources,&window_info,image,window_info.width,
13960     window_info.height,exception);
13961   if (status == MagickFalse)
13962     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13963       image->filename);
13964   window_info.x=0;
13965   window_info.y=0;
13966   if (image->debug != MagickFalse)
13967     {
13968       (void) LogMagickEvent(X11Event,GetMagickModule(),
13969         "Image: %s[%.20g] %.20gx%.20g ",image->filename,(double) image->scene,
13970         (double) image->columns,(double) image->rows);
13971       if (image->colors != 0)
13972         (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
13973           image->colors);
13974       (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",image->magick);
13975     }
13976   /*
13977     Adjust image dimensions as specified by backdrop or geometry options.
13978   */
13979   width=(int) window_info.width;
13980   height=(int) window_info.height;
13981   if (resources.backdrop != MagickFalse)
13982     {
13983       /*
13984         Center image on window.
13985       */
13986       window_info.x=(window_attributes.width/2)-
13987         (window_info.ximage->width/2);
13988       window_info.y=(window_attributes.height/2)-
13989         (window_info.ximage->height/2);
13990       width=window_attributes.width;
13991       height=window_attributes.height;
13992     }
13993   if ((resources.image_geometry != (char *) NULL) &&
13994       (*resources.image_geometry != '\0'))
13995     {
13996       char
13997         default_geometry[MaxTextExtent];
13998
13999       int
14000         flags,
14001         gravity;
14002
14003       XSizeHints
14004         *size_hints;
14005
14006       /*
14007         User specified geometry.
14008       */
14009       size_hints=XAllocSizeHints();
14010       if (size_hints == (XSizeHints *) NULL)
14011         ThrowXWindowFatalException(ResourceLimitFatalError,
14012           "MemoryAllocationFailed",image->filename);
14013       size_hints->flags=0L;
14014       (void) FormatLocaleString(default_geometry,MaxTextExtent,"%dx%d",
14015         width,height);
14016       flags=XWMGeometry(display,visual_info->screen,resources.image_geometry,
14017         default_geometry,window_info.border_width,size_hints,&window_info.x,
14018         &window_info.y,&width,&height,&gravity);
14019       if (flags & (XValue | YValue))
14020         {
14021           width=window_attributes.width;
14022           height=window_attributes.height;
14023         }
14024       (void) XFree((void *) size_hints);
14025     }
14026   /*
14027     Create the X pixmap.
14028   */
14029   window_info.pixmap=XCreatePixmap(display,window_info.id,(unsigned int) width,
14030     (unsigned int) height,window_info.depth);
14031   if (window_info.pixmap == (Pixmap) NULL)
14032     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXPixmap",
14033       image->filename);
14034   /*
14035     Display pixmap on the window.
14036   */
14037   if (((unsigned int) width > window_info.width) ||
14038       ((unsigned int) height > window_info.height))
14039     (void) XFillRectangle(display,window_info.pixmap,
14040       window_info.annotate_context,0,0,(unsigned int) width,
14041       (unsigned int) height);
14042   (void) XPutImage(display,window_info.pixmap,window_info.annotate_context,
14043     window_info.ximage,0,0,window_info.x,window_info.y,(unsigned int)
14044     window_info.width,(unsigned int) window_info.height);
14045   (void) XSetWindowBackgroundPixmap(display,window_info.id,window_info.pixmap);
14046   (void) XClearWindow(display,window_info.id);
14047   delay=1000*image->delay/MagickMax(image->ticks_per_second,1L);
14048   XDelay(display,delay == 0UL ? 10UL : delay);
14049   (void) XSync(display,MagickFalse);
14050   return(window_info.id == root_window ? MagickTrue : MagickFalse);
14051 }
14052 \f
14053 /*
14054 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
14055 %                                                                             %
14056 %                                                                             %
14057 %                                                                             %
14058 +   X D i s p l a y I m a g e                                                 %
14059 %                                                                             %
14060 %                                                                             %
14061 %                                                                             %
14062 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
14063 %
14064 %  XDisplayImage() displays an image via X11.  A new image is created and
14065 %  returned if the user interactively transforms the displayed image.
14066 %
14067 %  The format of the XDisplayImage method is:
14068 %
14069 %      Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
14070 %        char **argv,int argc,Image **image,size_t *state,
14071 %        ExceptionInfo *exception)
14072 %
14073 %  A description of each parameter follows:
14074 %
14075 %    o nexus:  Method XDisplayImage returns an image when the
14076 %      user chooses 'Open Image' from the command menu or picks a tile
14077 %      from the image directory.  Otherwise a null image is returned.
14078 %
14079 %    o display: Specifies a connection to an X server;  returned from
14080 %      XOpenDisplay.
14081 %
14082 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
14083 %
14084 %    o argv: Specifies the application's argument list.
14085 %
14086 %    o argc: Specifies the number of arguments.
14087 %
14088 %    o image: Specifies an address to an address of an Image structure;
14089 %
14090 %    o exception: return any errors or warnings in this structure.
14091 %
14092 */
14093 MagickExport Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
14094   char **argv,int argc,Image **image,size_t *state,ExceptionInfo *exception)
14095 {
14096 #define MagnifySize  256  /* must be a power of 2 */
14097 #define MagickMenus  10
14098 #define MagickTitle  "Commands"
14099
14100   static const char
14101     *CommandMenu[] =
14102     {
14103       "File",
14104       "Edit",
14105       "View",
14106       "Transform",
14107       "Enhance",
14108       "Effects",
14109       "F/X",
14110       "Image Edit",
14111       "Miscellany",
14112       "Help",
14113       (char *) NULL
14114     },
14115     *FileMenu[] =
14116     {
14117       "Open...",
14118       "Next",
14119       "Former",
14120       "Select...",
14121       "Save...",
14122       "Print...",
14123       "Delete...",
14124       "New...",
14125       "Visual Directory...",
14126       "Quit",
14127       (char *) NULL
14128     },
14129     *EditMenu[] =
14130     {
14131       "Undo",
14132       "Redo",
14133       "Cut",
14134       "Copy",
14135       "Paste",
14136       (char *) NULL
14137     },
14138     *ViewMenu[] =
14139     {
14140       "Half Size",
14141       "Original Size",
14142       "Double Size",
14143       "Resize...",
14144       "Apply",
14145       "Refresh",
14146       "Restore",
14147       (char *) NULL
14148     },
14149     *TransformMenu[] =
14150     {
14151       "Crop",
14152       "Chop",
14153       "Flop",
14154       "Flip",
14155       "Rotate Right",
14156       "Rotate Left",
14157       "Rotate...",
14158       "Shear...",
14159       "Roll...",
14160       "Trim Edges",
14161       (char *) NULL
14162     },
14163     *EnhanceMenu[] =
14164     {
14165       "Hue...",
14166       "Saturation...",
14167       "Brightness...",
14168       "Gamma...",
14169       "Spiff",
14170       "Dull",
14171       "Contrast Stretch...",
14172       "Sigmoidal Contrast...",
14173       "Normalize",
14174       "Equalize",
14175       "Negate",
14176       "Grayscale",
14177       "Map...",
14178       "Quantize...",
14179       (char *) NULL
14180     },
14181     *EffectsMenu[] =
14182     {
14183       "Despeckle",
14184       "Emboss",
14185       "Reduce Noise",
14186       "Add Noise...",
14187       "Sharpen...",
14188       "Blur...",
14189       "Threshold...",
14190       "Edge Detect...",
14191       "Spread...",
14192       "Shade...",
14193       "Raise...",
14194       "Segment...",
14195       (char *) NULL
14196     },
14197     *FXMenu[] =
14198     {
14199       "Solarize...",
14200       "Sepia Tone...",
14201       "Swirl...",
14202       "Implode...",
14203       "Vignette...",
14204       "Wave...",
14205       "Oil Paint...",
14206       "Charcoal Draw...",
14207       (char *) NULL
14208     },
14209     *ImageEditMenu[] =
14210     {
14211       "Annotate...",
14212       "Draw...",
14213       "Color...",
14214       "Matte...",
14215       "Composite...",
14216       "Add Border...",
14217       "Add Frame...",
14218       "Comment...",
14219       "Launch...",
14220       "Region of Interest...",
14221       (char *) NULL
14222     },
14223     *MiscellanyMenu[] =
14224     {
14225       "Image Info",
14226       "Zoom Image",
14227       "Show Preview...",
14228       "Show Histogram",
14229       "Show Matte",
14230       "Background...",
14231       "Slide Show...",
14232       "Preferences...",
14233       (char *) NULL
14234     },
14235     *HelpMenu[] =
14236     {
14237       "Overview",
14238       "Browse Documentation",
14239       "About Display",
14240       (char *) NULL
14241     },
14242     *ShortCutsMenu[] =
14243     {
14244       "Next",
14245       "Former",
14246       "Open...",
14247       "Save...",
14248       "Print...",
14249       "Undo",
14250       "Restore",
14251       "Image Info",
14252       "Quit",
14253       (char *) NULL
14254     },
14255     *VirtualMenu[] =
14256     {
14257       "Image Info",
14258       "Print",
14259       "Next",
14260       "Quit",
14261       (char *) NULL
14262     };
14263
14264   static const char
14265     **Menus[MagickMenus] =
14266     {
14267       FileMenu,
14268       EditMenu,
14269       ViewMenu,
14270       TransformMenu,
14271       EnhanceMenu,
14272       EffectsMenu,
14273       FXMenu,
14274       ImageEditMenu,
14275       MiscellanyMenu,
14276       HelpMenu
14277     };
14278
14279   static CommandType
14280     CommandMenus[] =
14281     {
14282       NullCommand,
14283       NullCommand,
14284       NullCommand,
14285       NullCommand,
14286       NullCommand,
14287       NullCommand,
14288       NullCommand,
14289       NullCommand,
14290       NullCommand,
14291       NullCommand,
14292     },
14293     FileCommands[] =
14294     {
14295       OpenCommand,
14296       NextCommand,
14297       FormerCommand,
14298       SelectCommand,
14299       SaveCommand,
14300       PrintCommand,
14301       DeleteCommand,
14302       NewCommand,
14303       VisualDirectoryCommand,
14304       QuitCommand
14305     },
14306     EditCommands[] =
14307     {
14308       UndoCommand,
14309       RedoCommand,
14310       CutCommand,
14311       CopyCommand,
14312       PasteCommand
14313     },
14314     ViewCommands[] =
14315     {
14316       HalfSizeCommand,
14317       OriginalSizeCommand,
14318       DoubleSizeCommand,
14319       ResizeCommand,
14320       ApplyCommand,
14321       RefreshCommand,
14322       RestoreCommand
14323     },
14324     TransformCommands[] =
14325     {
14326       CropCommand,
14327       ChopCommand,
14328       FlopCommand,
14329       FlipCommand,
14330       RotateRightCommand,
14331       RotateLeftCommand,
14332       RotateCommand,
14333       ShearCommand,
14334       RollCommand,
14335       TrimCommand
14336     },
14337     EnhanceCommands[] =
14338     {
14339       HueCommand,
14340       SaturationCommand,
14341       BrightnessCommand,
14342       GammaCommand,
14343       SpiffCommand,
14344       DullCommand,
14345       ContrastStretchCommand,
14346       SigmoidalContrastCommand,
14347       NormalizeCommand,
14348       EqualizeCommand,
14349       NegateCommand,
14350       GrayscaleCommand,
14351       MapCommand,
14352       QuantizeCommand
14353     },
14354     EffectsCommands[] =
14355     {
14356       DespeckleCommand,
14357       EmbossCommand,
14358       ReduceNoiseCommand,
14359       AddNoiseCommand,
14360       SharpenCommand,
14361       BlurCommand,
14362       ThresholdCommand,
14363       EdgeDetectCommand,
14364       SpreadCommand,
14365       ShadeCommand,
14366       RaiseCommand,
14367       SegmentCommand
14368     },
14369     FXCommands[] =
14370     {
14371       SolarizeCommand,
14372       SepiaToneCommand,
14373       SwirlCommand,
14374       ImplodeCommand,
14375       VignetteCommand,
14376       WaveCommand,
14377       OilPaintCommand,
14378       CharcoalDrawCommand
14379     },
14380     ImageEditCommands[] =
14381     {
14382       AnnotateCommand,
14383       DrawCommand,
14384       ColorCommand,
14385       MatteCommand,
14386       CompositeCommand,
14387       AddBorderCommand,
14388       AddFrameCommand,
14389       CommentCommand,
14390       LaunchCommand,
14391       RegionofInterestCommand
14392     },
14393     MiscellanyCommands[] =
14394     {
14395       InfoCommand,
14396       ZoomCommand,
14397       ShowPreviewCommand,
14398       ShowHistogramCommand,
14399       ShowMatteCommand,
14400       BackgroundCommand,
14401       SlideShowCommand,
14402       PreferencesCommand
14403     },
14404     HelpCommands[] =
14405     {
14406       HelpCommand,
14407       BrowseDocumentationCommand,
14408       VersionCommand
14409     },
14410     ShortCutsCommands[] =
14411     {
14412       NextCommand,
14413       FormerCommand,
14414       OpenCommand,
14415       SaveCommand,
14416       PrintCommand,
14417       UndoCommand,
14418       RestoreCommand,
14419       InfoCommand,
14420       QuitCommand
14421     },
14422     VirtualCommands[] =
14423     {
14424       InfoCommand,
14425       PrintCommand,
14426       NextCommand,
14427       QuitCommand
14428     };
14429
14430   static CommandType
14431     *Commands[MagickMenus] =
14432     {
14433       FileCommands,
14434       EditCommands,
14435       ViewCommands,
14436       TransformCommands,
14437       EnhanceCommands,
14438       EffectsCommands,
14439       FXCommands,
14440       ImageEditCommands,
14441       MiscellanyCommands,
14442       HelpCommands
14443     };
14444
14445   char
14446     command[MaxTextExtent],
14447     *directory,
14448     geometry[MaxTextExtent],
14449     resource_name[MaxTextExtent];
14450
14451   CommandType
14452     command_type;
14453
14454   Image
14455     *display_image,
14456     *nexus;
14457
14458   int
14459     entry,
14460     id;
14461
14462   KeySym
14463     key_symbol;
14464
14465   MagickStatusType
14466     context_mask,
14467     status;
14468
14469   RectangleInfo
14470     geometry_info;
14471
14472   register int
14473     i;
14474
14475   static char
14476     working_directory[MaxTextExtent];
14477
14478   static XPoint
14479     vid_info;
14480
14481   static XWindowInfo
14482     *magick_windows[MaxXWindows];
14483
14484   static unsigned int
14485     number_windows;
14486
14487   struct stat
14488     attributes;
14489
14490   time_t
14491     timer,
14492     timestamp,
14493     update_time;
14494
14495   unsigned int
14496     height,
14497     width;
14498
14499   size_t
14500     delay;
14501
14502   WarningHandler
14503     warning_handler;
14504
14505   Window
14506     root_window;
14507
14508   XClassHint
14509     *class_hints;
14510
14511   XEvent
14512     event;
14513
14514   XFontStruct
14515     *font_info;
14516
14517   XGCValues
14518     context_values;
14519
14520   XPixelInfo
14521     *icon_pixel,
14522     *pixel;
14523
14524   XResourceInfo
14525     *icon_resources;
14526
14527   XStandardColormap
14528     *icon_map,
14529     *map_info;
14530
14531   XVisualInfo
14532     *icon_visual,
14533     *visual_info;
14534
14535   XWindowChanges
14536     window_changes;
14537
14538   XWindows
14539     *windows;
14540
14541   XWMHints
14542     *manager_hints;
14543
14544   assert(image != (Image **) NULL);
14545   assert((*image)->signature == MagickSignature);
14546   if ((*image)->debug != MagickFalse)
14547     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*image)->filename);
14548   display_image=(*image);
14549   warning_handler=(WarningHandler) NULL;
14550   windows=XSetWindows((XWindows *) ~0);
14551   if (windows != (XWindows *) NULL)
14552     {
14553       int
14554         status;
14555
14556       status=chdir(working_directory);
14557       if (status == -1)
14558         (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
14559           "UnableToOpenFile","%s",working_directory);
14560       warning_handler=resource_info->display_warnings ?
14561         SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
14562       warning_handler=resource_info->display_warnings ?
14563         SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
14564     }
14565   else
14566     {
14567       /*
14568         Allocate windows structure.
14569       */
14570       resource_info->colors=display_image->colors;
14571       windows=XSetWindows(XInitializeWindows(display,resource_info));
14572       if (windows == (XWindows *) NULL)
14573         ThrowXWindowFatalException(XServerFatalError,"UnableToCreateWindow",
14574           (*image)->filename);
14575       /*
14576         Initialize window id's.
14577       */
14578       number_windows=0;
14579       magick_windows[number_windows++]=(&windows->icon);
14580       magick_windows[number_windows++]=(&windows->backdrop);
14581       magick_windows[number_windows++]=(&windows->image);
14582       magick_windows[number_windows++]=(&windows->info);
14583       magick_windows[number_windows++]=(&windows->command);
14584       magick_windows[number_windows++]=(&windows->widget);
14585       magick_windows[number_windows++]=(&windows->popup);
14586       magick_windows[number_windows++]=(&windows->magnify);
14587       magick_windows[number_windows++]=(&windows->pan);
14588       for (i=0; i < (int) number_windows; i++)
14589         magick_windows[i]->id=(Window) NULL;
14590       vid_info.x=0;
14591       vid_info.y=0;
14592     }
14593   /*
14594     Initialize font info.
14595   */
14596   if (windows->font_info != (XFontStruct *) NULL)
14597     (void) XFreeFont(display,windows->font_info);
14598   windows->font_info=XBestFont(display,resource_info,MagickFalse);
14599   if (windows->font_info == (XFontStruct *) NULL)
14600     ThrowXWindowFatalException(XServerFatalError,"UnableToLoadFont",
14601       resource_info->font);
14602   /*
14603     Initialize Standard Colormap.
14604   */
14605   map_info=windows->map_info;
14606   icon_map=windows->icon_map;
14607   visual_info=windows->visual_info;
14608   icon_visual=windows->icon_visual;
14609   pixel=windows->pixel_info;
14610   icon_pixel=windows->icon_pixel;
14611   font_info=windows->font_info;
14612   icon_resources=windows->icon_resources;
14613   class_hints=windows->class_hints;
14614   manager_hints=windows->manager_hints;
14615   root_window=XRootWindow(display,visual_info->screen);
14616   nexus=NewImageList();
14617   if (display_image->debug != MagickFalse)
14618     {
14619       (void) LogMagickEvent(X11Event,GetMagickModule(),
14620         "Image: %s[%.20g] %.20gx%.20g ",display_image->filename,
14621         (double) display_image->scene,(double) display_image->columns,
14622         (double) display_image->rows);
14623       if (display_image->colors != 0)
14624         (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
14625           display_image->colors);
14626       (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",
14627         display_image->magick);
14628     }
14629   XMakeStandardColormap(display,visual_info,resource_info,display_image,
14630     map_info,pixel,exception);
14631   display_image->taint=MagickFalse;
14632   /*
14633     Initialize graphic context.
14634   */
14635   windows->context.id=(Window) NULL;
14636   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14637     resource_info,&windows->context);
14638   (void) CloneString(&class_hints->res_name,resource_info->client_name);
14639   (void) CloneString(&class_hints->res_class,resource_info->client_name);
14640   class_hints->res_class[0]=(char) toupper((int) class_hints->res_class[0]);
14641   manager_hints->flags=InputHint | StateHint;
14642   manager_hints->input=MagickFalse;
14643   manager_hints->initial_state=WithdrawnState;
14644   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14645     &windows->context);
14646   if (display_image->debug != MagickFalse)
14647     (void) LogMagickEvent(X11Event,GetMagickModule(),
14648       "Window id: 0x%lx (context)",windows->context.id);
14649   context_values.background=pixel->background_color.pixel;
14650   context_values.font=font_info->fid;
14651   context_values.foreground=pixel->foreground_color.pixel;
14652   context_values.graphics_exposures=MagickFalse;
14653   context_mask=(MagickStatusType)
14654     (GCBackground | GCFont | GCForeground | GCGraphicsExposures);
14655   if (pixel->annotate_context != (GC) NULL)
14656     (void) XFreeGC(display,pixel->annotate_context);
14657   pixel->annotate_context=XCreateGC(display,windows->context.id,
14658     context_mask,&context_values);
14659   if (pixel->annotate_context == (GC) NULL)
14660     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14661       display_image->filename);
14662   context_values.background=pixel->depth_color.pixel;
14663   if (pixel->widget_context != (GC) NULL)
14664     (void) XFreeGC(display,pixel->widget_context);
14665   pixel->widget_context=XCreateGC(display,windows->context.id,context_mask,
14666     &context_values);
14667   if (pixel->widget_context == (GC) NULL)
14668     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14669       display_image->filename);
14670   context_values.background=pixel->foreground_color.pixel;
14671   context_values.foreground=pixel->background_color.pixel;
14672   context_values.plane_mask=context_values.background ^
14673     context_values.foreground;
14674   if (pixel->highlight_context != (GC) NULL)
14675     (void) XFreeGC(display,pixel->highlight_context);
14676   pixel->highlight_context=XCreateGC(display,windows->context.id,
14677     (size_t) (context_mask | GCPlaneMask),&context_values);
14678   if (pixel->highlight_context == (GC) NULL)
14679     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14680       display_image->filename);
14681   (void) XDestroyWindow(display,windows->context.id);
14682   /*
14683     Initialize icon window.
14684   */
14685   XGetWindowInfo(display,icon_visual,icon_map,icon_pixel,(XFontStruct *) NULL,
14686     icon_resources,&windows->icon);
14687   windows->icon.geometry=resource_info->icon_geometry;
14688   XBestIconSize(display,&windows->icon,display_image);
14689   windows->icon.attributes.colormap=XDefaultColormap(display,
14690     icon_visual->screen);
14691   windows->icon.attributes.event_mask=ExposureMask | StructureNotifyMask;
14692   manager_hints->flags=InputHint | StateHint;
14693   manager_hints->input=MagickFalse;
14694   manager_hints->initial_state=IconicState;
14695   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14696     &windows->icon);
14697   if (display_image->debug != MagickFalse)
14698     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (icon)",
14699       windows->icon.id);
14700   /*
14701     Initialize graphic context for icon window.
14702   */
14703   if (icon_pixel->annotate_context != (GC) NULL)
14704     (void) XFreeGC(display,icon_pixel->annotate_context);
14705   context_values.background=icon_pixel->background_color.pixel;
14706   context_values.foreground=icon_pixel->foreground_color.pixel;
14707   icon_pixel->annotate_context=XCreateGC(display,windows->icon.id,
14708     (size_t) (GCBackground | GCForeground),&context_values);
14709   if (icon_pixel->annotate_context == (GC) NULL)
14710     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14711       display_image->filename);
14712   windows->icon.annotate_context=icon_pixel->annotate_context;
14713   /*
14714     Initialize Image window.
14715   */
14716   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14717     &windows->image);
14718   windows->image.shape=MagickTrue;  /* non-rectangular shape hint */
14719   if (resource_info->use_shared_memory == MagickFalse)
14720     windows->image.shared_memory=MagickFalse;
14721   if ((resource_info->title != (char *) NULL) && !(*state & MontageImageState))
14722     {
14723       char
14724         *title;
14725
14726       title=InterpretImageProperties(resource_info->image_info,display_image,
14727         resource_info->title,exception);
14728       (void) CopyMagickString(windows->image.name,title,MaxTextExtent);
14729       (void) CopyMagickString(windows->image.icon_name,title,MaxTextExtent);
14730       title=DestroyString(title);
14731     }
14732   else
14733     {
14734       char
14735         filename[MaxTextExtent];
14736
14737       /*
14738         Window name is the base of the filename.
14739       */
14740       GetPathComponent(display_image->magick_filename,TailPath,filename);
14741       if (display_image->scene == 0)
14742         (void) FormatLocaleString(windows->image.name,MaxTextExtent,
14743           "%s: %s",MagickPackageName,filename);
14744       else
14745         (void) FormatLocaleString(windows->image.name,MaxTextExtent,
14746           "%s: %s[scene: %.20g frames: %.20g]",MagickPackageName,filename,
14747           (double) display_image->scene,(double) GetImageListLength(
14748           display_image));
14749       (void) CopyMagickString(windows->image.icon_name,filename,MaxTextExtent);
14750     }
14751   if (resource_info->immutable)
14752     windows->image.immutable=MagickTrue;
14753   windows->image.use_pixmap=resource_info->use_pixmap;
14754   windows->image.geometry=resource_info->image_geometry;
14755   (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>!",
14756     XDisplayWidth(display,visual_info->screen),
14757     XDisplayHeight(display,visual_info->screen));
14758   geometry_info.width=display_image->columns;
14759   geometry_info.height=display_image->rows;
14760   geometry_info.x=0;
14761   geometry_info.y=0;
14762   (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
14763     &geometry_info.width,&geometry_info.height);
14764   windows->image.width=(unsigned int) geometry_info.width;
14765   windows->image.height=(unsigned int) geometry_info.height;
14766   windows->image.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14767     ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14768     KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14769     PropertyChangeMask | StructureNotifyMask | SubstructureNotifyMask;
14770   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14771     resource_info,&windows->backdrop);
14772   if ((resource_info->backdrop) || (windows->backdrop.id != (Window) NULL))
14773     {
14774       /*
14775         Initialize backdrop window.
14776       */
14777       windows->backdrop.x=0;
14778       windows->backdrop.y=0;
14779       (void) CloneString(&windows->backdrop.name,"Backdrop");
14780       windows->backdrop.flags=(size_t) (USSize | USPosition);
14781       windows->backdrop.width=(unsigned int)
14782         XDisplayWidth(display,visual_info->screen);
14783       windows->backdrop.height=(unsigned int)
14784         XDisplayHeight(display,visual_info->screen);
14785       windows->backdrop.border_width=0;
14786       windows->backdrop.immutable=MagickTrue;
14787       windows->backdrop.attributes.do_not_propagate_mask=ButtonPressMask |
14788         ButtonReleaseMask;
14789       windows->backdrop.attributes.event_mask=ButtonPressMask | KeyPressMask |
14790         StructureNotifyMask;
14791       manager_hints->flags=IconWindowHint | InputHint | StateHint;
14792       manager_hints->icon_window=windows->icon.id;
14793       manager_hints->input=MagickTrue;
14794       manager_hints->initial_state=resource_info->iconic ? IconicState :
14795         NormalState;
14796       XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14797         &windows->backdrop);
14798       if (display_image->debug != MagickFalse)
14799         (void) LogMagickEvent(X11Event,GetMagickModule(),
14800           "Window id: 0x%lx (backdrop)",windows->backdrop.id);
14801       (void) XMapWindow(display,windows->backdrop.id);
14802       (void) XClearWindow(display,windows->backdrop.id);
14803       if (windows->image.id != (Window) NULL)
14804         {
14805           (void) XDestroyWindow(display,windows->image.id);
14806           windows->image.id=(Window) NULL;
14807         }
14808       /*
14809         Position image in the center the backdrop.
14810       */
14811       windows->image.flags|=USPosition;
14812       windows->image.x=(XDisplayWidth(display,visual_info->screen)/2)-
14813         (windows->image.width/2);
14814       windows->image.y=(XDisplayHeight(display,visual_info->screen)/2)-
14815         (windows->image.height/2);
14816     }
14817   manager_hints->flags=IconWindowHint | InputHint | StateHint;
14818   manager_hints->icon_window=windows->icon.id;
14819   manager_hints->input=MagickTrue;
14820   manager_hints->initial_state=resource_info->iconic ? IconicState :
14821     NormalState;
14822   if (windows->group_leader.id != (Window) NULL)
14823     {
14824       /*
14825         Follow the leader.
14826       */
14827       manager_hints->flags|=WindowGroupHint;
14828       manager_hints->window_group=windows->group_leader.id;
14829       (void) XSelectInput(display,windows->group_leader.id,StructureNotifyMask);
14830       if (display_image->debug != MagickFalse)
14831         (void) LogMagickEvent(X11Event,GetMagickModule(),
14832           "Window id: 0x%lx (group leader)",windows->group_leader.id);
14833     }
14834   XMakeWindow(display,
14835     (Window) (resource_info->backdrop ? windows->backdrop.id : root_window),
14836     argv,argc,class_hints,manager_hints,&windows->image);
14837   (void) XChangeProperty(display,windows->image.id,windows->im_protocols,
14838     XA_STRING,8,PropModeReplace,(unsigned char *) NULL,0);
14839   if (windows->group_leader.id != (Window) NULL)
14840     (void) XSetTransientForHint(display,windows->image.id,
14841       windows->group_leader.id);
14842   if (display_image->debug != MagickFalse)
14843     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (image)",
14844       windows->image.id);
14845   /*
14846     Initialize Info widget.
14847   */
14848   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14849     &windows->info);
14850   (void) CloneString(&windows->info.name,"Info");
14851   (void) CloneString(&windows->info.icon_name,"Info");
14852   windows->info.border_width=1;
14853   windows->info.x=2;
14854   windows->info.y=2;
14855   windows->info.flags|=PPosition;
14856   windows->info.attributes.win_gravity=UnmapGravity;
14857   windows->info.attributes.event_mask=ButtonPressMask | ExposureMask |
14858     StructureNotifyMask;
14859   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14860   manager_hints->input=MagickFalse;
14861   manager_hints->initial_state=NormalState;
14862   manager_hints->window_group=windows->image.id;
14863   XMakeWindow(display,windows->image.id,argv,argc,class_hints,manager_hints,
14864     &windows->info);
14865   windows->info.highlight_stipple=XCreateBitmapFromData(display,
14866     windows->info.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14867   windows->info.shadow_stipple=XCreateBitmapFromData(display,
14868     windows->info.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14869   (void) XSetTransientForHint(display,windows->info.id,windows->image.id);
14870   if (windows->image.mapped != MagickFalse)
14871     (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14872   if (display_image->debug != MagickFalse)
14873     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (info)",
14874       windows->info.id);
14875   /*
14876     Initialize Command widget.
14877   */
14878   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14879     resource_info,&windows->command);
14880   windows->command.data=MagickMenus;
14881   (void) XCommandWidget(display,windows,CommandMenu,(XEvent *) NULL);
14882   (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.command",
14883     resource_info->client_name);
14884   windows->command.geometry=XGetResourceClass(resource_info->resource_database,
14885     resource_name,"geometry",(char *) NULL);
14886   (void) CloneString(&windows->command.name,MagickTitle);
14887   windows->command.border_width=0;
14888   windows->command.flags|=PPosition;
14889   windows->command.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14890     ButtonReleaseMask | EnterWindowMask | ExposureMask | LeaveWindowMask |
14891     OwnerGrabButtonMask | StructureNotifyMask;
14892   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14893   manager_hints->input=MagickTrue;
14894   manager_hints->initial_state=NormalState;
14895   manager_hints->window_group=windows->image.id;
14896   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14897     &windows->command);
14898   windows->command.highlight_stipple=XCreateBitmapFromData(display,
14899     windows->command.id,(char *) HighlightBitmap,HighlightWidth,
14900     HighlightHeight);
14901   windows->command.shadow_stipple=XCreateBitmapFromData(display,
14902     windows->command.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14903   (void) XSetTransientForHint(display,windows->command.id,windows->image.id);
14904   if (windows->command.mapped != MagickFalse)
14905     (void) XMapRaised(display,windows->command.id);
14906   if (display_image->debug != MagickFalse)
14907     (void) LogMagickEvent(X11Event,GetMagickModule(),
14908       "Window id: 0x%lx (command)",windows->command.id);
14909   /*
14910     Initialize Widget window.
14911   */
14912   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14913     resource_info,&windows->widget);
14914   (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.widget",
14915     resource_info->client_name);
14916   windows->widget.geometry=XGetResourceClass(resource_info->resource_database,
14917     resource_name,"geometry",(char *) NULL);
14918   windows->widget.border_width=0;
14919   windows->widget.flags|=PPosition;
14920   windows->widget.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14921     ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14922     KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14923     StructureNotifyMask;
14924   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14925   manager_hints->input=MagickTrue;
14926   manager_hints->initial_state=NormalState;
14927   manager_hints->window_group=windows->image.id;
14928   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14929     &windows->widget);
14930   windows->widget.highlight_stipple=XCreateBitmapFromData(display,
14931     windows->widget.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14932   windows->widget.shadow_stipple=XCreateBitmapFromData(display,
14933     windows->widget.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14934   (void) XSetTransientForHint(display,windows->widget.id,windows->image.id);
14935   if (display_image->debug != MagickFalse)
14936     (void) LogMagickEvent(X11Event,GetMagickModule(),
14937       "Window id: 0x%lx (widget)",windows->widget.id);
14938   /*
14939     Initialize popup window.
14940   */
14941   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14942     resource_info,&windows->popup);
14943   windows->popup.border_width=0;
14944   windows->popup.flags|=PPosition;
14945   windows->popup.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14946     ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14947     KeyReleaseMask | LeaveWindowMask | StructureNotifyMask;
14948   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14949   manager_hints->input=MagickTrue;
14950   manager_hints->initial_state=NormalState;
14951   manager_hints->window_group=windows->image.id;
14952   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14953     &windows->popup);
14954   windows->popup.highlight_stipple=XCreateBitmapFromData(display,
14955     windows->popup.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14956   windows->popup.shadow_stipple=XCreateBitmapFromData(display,
14957     windows->popup.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14958   (void) XSetTransientForHint(display,windows->popup.id,windows->image.id);
14959   if (display_image->debug != MagickFalse)
14960     (void) LogMagickEvent(X11Event,GetMagickModule(),
14961       "Window id: 0x%lx (pop up)",windows->popup.id);
14962   /*
14963     Initialize Magnify window and cursor.
14964   */
14965   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14966     resource_info,&windows->magnify);
14967   if (resource_info->use_shared_memory == MagickFalse)
14968     windows->magnify.shared_memory=MagickFalse;
14969   (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.magnify",
14970     resource_info->client_name);
14971   windows->magnify.geometry=XGetResourceClass(resource_info->resource_database,
14972     resource_name,"geometry",(char *) NULL);
14973   (void) FormatLocaleString(windows->magnify.name,MaxTextExtent,"Magnify %uX",
14974     resource_info->magnify);
14975   if (windows->magnify.cursor != (Cursor) NULL)
14976     (void) XFreeCursor(display,windows->magnify.cursor);
14977   windows->magnify.cursor=XMakeCursor(display,windows->image.id,
14978     map_info->colormap,resource_info->background_color,
14979     resource_info->foreground_color);
14980   if (windows->magnify.cursor == (Cursor) NULL)
14981     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateCursor",
14982       display_image->filename);
14983   windows->magnify.width=MagnifySize;
14984   windows->magnify.height=MagnifySize;
14985   windows->magnify.flags|=PPosition;
14986   windows->magnify.min_width=MagnifySize;
14987   windows->magnify.min_height=MagnifySize;
14988   windows->magnify.width_inc=MagnifySize;
14989   windows->magnify.height_inc=MagnifySize;
14990   windows->magnify.data=resource_info->magnify;
14991   windows->magnify.attributes.cursor=windows->magnify.cursor;
14992   windows->magnify.attributes.event_mask=ButtonPressMask | ButtonReleaseMask |
14993     ExposureMask | KeyPressMask | KeyReleaseMask | OwnerGrabButtonMask |
14994     StructureNotifyMask;
14995   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14996   manager_hints->input=MagickTrue;
14997   manager_hints->initial_state=NormalState;
14998   manager_hints->window_group=windows->image.id;
14999   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
15000     &windows->magnify);
15001   if (display_image->debug != MagickFalse)
15002     (void) LogMagickEvent(X11Event,GetMagickModule(),
15003       "Window id: 0x%lx (magnify)",windows->magnify.id);
15004   (void) XSetTransientForHint(display,windows->magnify.id,windows->image.id);
15005   /*
15006     Initialize panning window.
15007   */
15008   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
15009     resource_info,&windows->pan);
15010   (void) CloneString(&windows->pan.name,"Pan Icon");
15011   windows->pan.width=windows->icon.width;
15012   windows->pan.height=windows->icon.height;
15013   (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.pan",
15014     resource_info->client_name);
15015   windows->pan.geometry=XGetResourceClass(resource_info->resource_database,
15016     resource_name,"geometry",(char *) NULL);
15017   (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
15018     &windows->pan.width,&windows->pan.height);
15019   windows->pan.flags|=PPosition;
15020   windows->pan.immutable=MagickTrue;
15021   windows->pan.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
15022     ButtonReleaseMask | ExposureMask | KeyPressMask | KeyReleaseMask |
15023     StructureNotifyMask;
15024   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
15025   manager_hints->input=MagickFalse;
15026   manager_hints->initial_state=NormalState;
15027   manager_hints->window_group=windows->image.id;
15028   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
15029     &windows->pan);
15030   if (display_image->debug != MagickFalse)
15031     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (pan)",
15032       windows->pan.id);
15033   (void) XSetTransientForHint(display,windows->pan.id,windows->image.id);
15034   if (windows->info.mapped != MagickFalse)
15035     (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15036   if ((windows->image.mapped == MagickFalse) ||
15037       (windows->backdrop.id != (Window) NULL))
15038     (void) XMapWindow(display,windows->image.id);
15039   /*
15040     Set our progress monitor and warning handlers.
15041   */
15042   if (warning_handler == (WarningHandler) NULL)
15043     {
15044       warning_handler=resource_info->display_warnings ?
15045         SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
15046       warning_handler=resource_info->display_warnings ?
15047         SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
15048     }
15049   /*
15050     Initialize Image and Magnify X images.
15051   */
15052   windows->image.x=0;
15053   windows->image.y=0;
15054   windows->magnify.shape=MagickFalse;
15055   width=(unsigned int) display_image->columns;
15056   height=(unsigned int) display_image->rows;
15057   if ((display_image->columns != width) || (display_image->rows != height))
15058     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15059       display_image->filename);
15060   status=XMakeImage(display,resource_info,&windows->image,display_image,
15061     width,height,exception);
15062   if (status == MagickFalse)
15063     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15064       display_image->filename);
15065   status=XMakeImage(display,resource_info,&windows->magnify,(Image *) NULL,
15066     windows->magnify.width,windows->magnify.height,exception);
15067   if (status == MagickFalse)
15068     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15069       display_image->filename);
15070   if (windows->magnify.mapped != MagickFalse)
15071     (void) XMapRaised(display,windows->magnify.id);
15072   if (windows->pan.mapped != MagickFalse)
15073     (void) XMapRaised(display,windows->pan.id);
15074   windows->image.window_changes.width=(int) display_image->columns;
15075   windows->image.window_changes.height=(int) display_image->rows;
15076   (void) XConfigureImage(display,resource_info,windows,display_image,exception);
15077   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15078   (void) XSync(display,MagickFalse);
15079   /*
15080     Respond to events.
15081   */
15082   delay=display_image->delay/MagickMax(display_image->ticks_per_second,1L);
15083   timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15084   update_time=0;
15085   if (resource_info->update != MagickFalse)
15086     {
15087       MagickBooleanType
15088         status;
15089
15090       /*
15091         Determine when file data was last modified.
15092       */
15093       status=GetPathAttributes(display_image->filename,&attributes);
15094       if (status != MagickFalse)
15095         update_time=attributes.st_mtime;
15096     }
15097   *state&=(~FormerImageState);
15098   *state&=(~MontageImageState);
15099   *state&=(~NextImageState);
15100   do
15101   {
15102     /*
15103       Handle a window event.
15104     */
15105     if (windows->image.mapped != MagickFalse)
15106       if ((display_image->delay != 0) || (resource_info->update != 0))
15107         {
15108           if (timer < time((time_t *) NULL))
15109             {
15110               if (resource_info->update == MagickFalse)
15111                 *state|=NextImageState | ExitState;
15112               else
15113                 {
15114                   MagickBooleanType
15115                     status;
15116
15117                   /*
15118                     Determine if image file was modified.
15119                   */
15120                   status=GetPathAttributes(display_image->filename,&attributes);
15121                   if (status != MagickFalse)
15122                     if (update_time != attributes.st_mtime)
15123                       {
15124                         /*
15125                           Redisplay image.
15126                         */
15127                         (void) FormatLocaleString(
15128                           resource_info->image_info->filename,MaxTextExtent,
15129                           "%s:%s",display_image->magick,
15130                           display_image->filename);
15131                         nexus=ReadImage(resource_info->image_info,exception);
15132                         if (nexus != (Image *) NULL)
15133                           {
15134                             nexus=DestroyImage(nexus);
15135                             *state|=NextImageState | ExitState;
15136                           }
15137                       }
15138                   delay=display_image->delay/MagickMax(
15139                     display_image->ticks_per_second,1L);
15140                   timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15141                 }
15142             }
15143           if (XEventsQueued(display,QueuedAfterFlush) == 0)
15144             {
15145               /*
15146                 Do not block if delay > 0.
15147               */
15148               XDelay(display,SuspendTime << 2);
15149               continue;
15150             }
15151         }
15152     timestamp=time((time_t *) NULL);
15153     (void) XNextEvent(display,&event);
15154     if (windows->image.stasis == MagickFalse)
15155       windows->image.stasis=(time((time_t *) NULL)-timestamp) > 0 ?
15156         MagickTrue : MagickFalse;
15157     if (windows->magnify.stasis == MagickFalse)
15158       windows->magnify.stasis=(time((time_t *) NULL)-timestamp) > 0 ?
15159         MagickTrue : MagickFalse;
15160     if (event.xany.window == windows->command.id)
15161       {
15162         /*
15163           Select a command from the Command widget.
15164         */
15165         id=XCommandWidget(display,windows,CommandMenu,&event);
15166         if (id < 0)
15167           continue;
15168         (void) CopyMagickString(command,CommandMenu[id],MaxTextExtent);
15169         command_type=CommandMenus[id];
15170         if (id < MagickMenus)
15171           {
15172             /*
15173               Select a command from a pop-up menu.
15174             */
15175             entry=XMenuWidget(display,windows,CommandMenu[id],Menus[id],
15176               command);
15177             if (entry < 0)
15178               continue;
15179             (void) CopyMagickString(command,Menus[id][entry],MaxTextExtent);
15180             command_type=Commands[id][entry];
15181           }
15182         if (command_type != NullCommand)
15183           nexus=XMagickCommand(display,resource_info,windows,command_type,
15184             &display_image,exception);
15185         continue;
15186       }
15187     switch (event.type)
15188     {
15189       case ButtonPress:
15190       {
15191         if (display_image->debug != MagickFalse)
15192           (void) LogMagickEvent(X11Event,GetMagickModule(),
15193             "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
15194             event.xbutton.button,event.xbutton.x,event.xbutton.y);
15195         if ((event.xbutton.button == Button3) &&
15196             (event.xbutton.state & Mod1Mask))
15197           {
15198             /*
15199               Convert Alt-Button3 to Button2.
15200             */
15201             event.xbutton.button=Button2;
15202             event.xbutton.state&=(~Mod1Mask);
15203           }
15204         if (event.xbutton.window == windows->backdrop.id)
15205           {
15206             (void) XSetInputFocus(display,event.xbutton.window,RevertToParent,
15207               event.xbutton.time);
15208             break;
15209           }
15210         if (event.xbutton.window == windows->image.id)
15211           {
15212             switch (event.xbutton.button)
15213             {
15214               case Button1:
15215               {
15216                 if (resource_info->immutable)
15217                   {
15218                     /*
15219                       Select a command from the Virtual menu.
15220                     */
15221                     entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15222                       command);
15223                     if (entry >= 0)
15224                       nexus=XMagickCommand(display,resource_info,windows,
15225                         VirtualCommands[entry],&display_image,exception);
15226                     break;
15227                   }
15228                 /*
15229                   Map/unmap Command widget.
15230                 */
15231                 if (windows->command.mapped != MagickFalse)
15232                   (void) XWithdrawWindow(display,windows->command.id,
15233                     windows->command.screen);
15234                 else
15235                   {
15236                     (void) XCommandWidget(display,windows,CommandMenu,
15237                       (XEvent *) NULL);
15238                     (void) XMapRaised(display,windows->command.id);
15239                   }
15240                 break;
15241               }
15242               case Button2:
15243               {
15244                 /*
15245                   User pressed the image magnify button.
15246                 */
15247                 (void) XMagickCommand(display,resource_info,windows,ZoomCommand,
15248                   &display_image,exception);
15249                 XMagnifyImage(display,windows,&event,exception);
15250                 break;
15251               }
15252               case Button3:
15253               {
15254                 if (resource_info->immutable)
15255                   {
15256                     /*
15257                       Select a command from the Virtual menu.
15258                     */
15259                     entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15260                       command);
15261                     if (entry >= 0)
15262                       nexus=XMagickCommand(display,resource_info,windows,
15263                         VirtualCommands[entry],&display_image,exception);
15264                     break;
15265                   }
15266                 if (display_image->montage != (char *) NULL)
15267                   {
15268                     /*
15269                       Open or delete a tile from a visual image directory.
15270                     */
15271                     nexus=XTileImage(display,resource_info,windows,
15272                       display_image,&event,exception);
15273                     if (nexus != (Image *) NULL)
15274                       *state|=MontageImageState | NextImageState | ExitState;
15275                     vid_info.x=(short int) windows->image.x;
15276                     vid_info.y=(short int) windows->image.y;
15277                     break;
15278                   }
15279                 /*
15280                   Select a command from the Short Cuts menu.
15281                 */
15282                 entry=XMenuWidget(display,windows,"Short Cuts",ShortCutsMenu,
15283                   command);
15284                 if (entry >= 0)
15285                   nexus=XMagickCommand(display,resource_info,windows,
15286                     ShortCutsCommands[entry],&display_image,exception);
15287                 break;
15288               }
15289               case Button4:
15290               {
15291                 /*
15292                   Wheel up.
15293                 */
15294                 XTranslateImage(display,windows,*image,XK_Up);
15295                 break;
15296               }
15297               case Button5:
15298               {
15299                 /*
15300                   Wheel down.
15301                 */
15302                 XTranslateImage(display,windows,*image,XK_Down);
15303                 break;
15304               }
15305               default:
15306                 break;
15307             }
15308             break;
15309           }
15310         if (event.xbutton.window == windows->magnify.id)
15311           {
15312             int
15313               factor;
15314
15315             static const char
15316               *MagnifyMenu[] =
15317               {
15318                 "2",
15319                 "4",
15320                 "5",
15321                 "6",
15322                 "7",
15323                 "8",
15324                 "9",
15325                 "3",
15326                 (char *) NULL,
15327               };
15328
15329             static KeySym
15330               MagnifyCommands[] =
15331               {
15332                 XK_2,
15333                 XK_4,
15334                 XK_5,
15335                 XK_6,
15336                 XK_7,
15337                 XK_8,
15338                 XK_9,
15339                 XK_3
15340               };
15341
15342             /*
15343               Select a magnify factor from the pop-up menu.
15344             */
15345             factor=XMenuWidget(display,windows,"Magnify",MagnifyMenu,command);
15346             if (factor >= 0)
15347               XMagnifyWindowCommand(display,windows,0,MagnifyCommands[factor],
15348                 exception);
15349             break;
15350           }
15351         if (event.xbutton.window == windows->pan.id)
15352           {
15353             switch (event.xbutton.button)
15354             {
15355               case Button4:
15356               {
15357                 /*
15358                   Wheel up.
15359                 */
15360                 XTranslateImage(display,windows,*image,XK_Up);
15361                 break;
15362               }
15363               case Button5:
15364               {
15365                 /*
15366                   Wheel down.
15367                 */
15368                 XTranslateImage(display,windows,*image,XK_Down);
15369                 break;
15370               }
15371               default:
15372               {
15373                 XPanImage(display,windows,&event,exception);
15374                 break;
15375               }
15376             }
15377             break;
15378           }
15379         delay=display_image->delay/MagickMax(display_image->ticks_per_second,
15380           1L);
15381         timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15382         break;
15383       }
15384       case ButtonRelease:
15385       {
15386         if (display_image->debug != MagickFalse)
15387           (void) LogMagickEvent(X11Event,GetMagickModule(),
15388             "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
15389             event.xbutton.button,event.xbutton.x,event.xbutton.y);
15390         break;
15391       }
15392       case ClientMessage:
15393       {
15394         if (display_image->debug != MagickFalse)
15395           (void) LogMagickEvent(X11Event,GetMagickModule(),
15396             "Client Message: 0x%lx 0x%lx %d 0x%lx",event.xclient.window,
15397             event.xclient.message_type,event.xclient.format,(unsigned long)
15398             event.xclient.data.l[0]);
15399         if (event.xclient.message_type == windows->im_protocols)
15400           {
15401             if (*event.xclient.data.l == (long) windows->im_update_widget)
15402               {
15403                 (void) CloneString(&windows->command.name,MagickTitle);
15404                 windows->command.data=MagickMenus;
15405                 (void) XCommandWidget(display,windows,CommandMenu,
15406                   (XEvent *) NULL);
15407                 break;
15408               }
15409             if (*event.xclient.data.l == (long) windows->im_update_colormap)
15410               {
15411                 /*
15412                   Update graphic context and window colormap.
15413                 */
15414                 for (i=0; i < (int) number_windows; i++)
15415                 {
15416                   if (magick_windows[i]->id == windows->icon.id)
15417                     continue;
15418                   context_values.background=pixel->background_color.pixel;
15419                   context_values.foreground=pixel->foreground_color.pixel;
15420                   (void) XChangeGC(display,magick_windows[i]->annotate_context,
15421                     context_mask,&context_values);
15422                   (void) XChangeGC(display,magick_windows[i]->widget_context,
15423                     context_mask,&context_values);
15424                   context_values.background=pixel->foreground_color.pixel;
15425                   context_values.foreground=pixel->background_color.pixel;
15426                   context_values.plane_mask=context_values.background ^
15427                     context_values.foreground;
15428                   (void) XChangeGC(display,magick_windows[i]->highlight_context,
15429                     (size_t) (context_mask | GCPlaneMask),
15430                     &context_values);
15431                   magick_windows[i]->attributes.background_pixel=
15432                     pixel->background_color.pixel;
15433                   magick_windows[i]->attributes.border_pixel=
15434                     pixel->border_color.pixel;
15435                   magick_windows[i]->attributes.colormap=map_info->colormap;
15436                   (void) XChangeWindowAttributes(display,magick_windows[i]->id,
15437                     (unsigned long) magick_windows[i]->mask,
15438                     &magick_windows[i]->attributes);
15439                 }
15440                 if (windows->pan.mapped != MagickFalse)
15441                   {
15442                     (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
15443                       windows->pan.pixmap);
15444                     (void) XClearWindow(display,windows->pan.id);
15445                     XDrawPanRectangle(display,windows);
15446                   }
15447                 if (windows->backdrop.id != (Window) NULL)
15448                   (void) XInstallColormap(display,map_info->colormap);
15449                 break;
15450               }
15451             if (*event.xclient.data.l == (long) windows->im_former_image)
15452               {
15453                 *state|=FormerImageState | ExitState;
15454                 break;
15455               }
15456             if (*event.xclient.data.l == (long) windows->im_next_image)
15457               {
15458                 *state|=NextImageState | ExitState;
15459                 break;
15460               }
15461             if (*event.xclient.data.l == (long) windows->im_retain_colors)
15462               {
15463                 *state|=RetainColorsState;
15464                 break;
15465               }
15466             if (*event.xclient.data.l == (long) windows->im_exit)
15467               {
15468                 *state|=ExitState;
15469                 break;
15470               }
15471             break;
15472           }
15473         if (event.xclient.message_type == windows->dnd_protocols)
15474           {
15475             Atom
15476               selection,
15477               type;
15478
15479             int
15480               format,
15481               status;
15482
15483             unsigned char
15484               *data;
15485
15486             unsigned long
15487               after,
15488               length;
15489
15490             /*
15491               Display image named by the Drag-and-Drop selection.
15492             */
15493             if ((*event.xclient.data.l != 2) && (*event.xclient.data.l != 128))
15494               break;
15495             selection=XInternAtom(display,"DndSelection",MagickFalse);
15496             status=XGetWindowProperty(display,root_window,selection,0L,(long)
15497               MaxTextExtent,MagickFalse,(Atom) AnyPropertyType,&type,&format,
15498               &length,&after,&data);
15499             if ((status != Success) || (length == 0))
15500               break;
15501             if (*event.xclient.data.l == 2)
15502               {
15503                 /*
15504                   Offix DND.
15505                 */
15506                 (void) CopyMagickString(resource_info->image_info->filename,
15507                   (char *) data,MaxTextExtent);
15508               }
15509             else
15510               {
15511                 /*
15512                   XDND.
15513                 */
15514                 if (strncmp((char *) data, "file:", 5) != 0)
15515                   {
15516                     (void) XFree((void *) data);
15517                     break;
15518                   }
15519                 (void) CopyMagickString(resource_info->image_info->filename,
15520                   ((char *) data)+5,MaxTextExtent);
15521               }
15522             nexus=ReadImage(resource_info->image_info,exception);
15523             CatchException(exception);
15524             if (nexus != (Image *) NULL)
15525               *state|=NextImageState | ExitState;
15526             (void) XFree((void *) data);
15527             break;
15528           }
15529         /*
15530           If client window delete message, exit.
15531         */
15532         if (event.xclient.message_type != windows->wm_protocols)
15533           break;
15534         if (*event.xclient.data.l != (long) windows->wm_delete_window)
15535           break;
15536         (void) XWithdrawWindow(display,event.xclient.window,
15537           visual_info->screen);
15538         if (event.xclient.window == windows->image.id)
15539           {
15540             *state|=ExitState;
15541             break;
15542           }
15543         if (event.xclient.window == windows->pan.id)
15544           {
15545             /*
15546               Restore original image size when pan window is deleted.
15547             */
15548             windows->image.window_changes.width=windows->image.ximage->width;
15549             windows->image.window_changes.height=windows->image.ximage->height;
15550             (void) XConfigureImage(display,resource_info,windows,
15551               display_image,exception);
15552           }
15553         break;
15554       }
15555       case ConfigureNotify:
15556       {
15557         if (display_image->debug != MagickFalse)
15558           (void) LogMagickEvent(X11Event,GetMagickModule(),
15559             "Configure Notify: 0x%lx %dx%d+%d+%d %d",event.xconfigure.window,
15560             event.xconfigure.width,event.xconfigure.height,event.xconfigure.x,
15561             event.xconfigure.y,event.xconfigure.send_event);
15562         if (event.xconfigure.window == windows->image.id)
15563           {
15564             /*
15565               Image window has a new configuration.
15566             */
15567             if (event.xconfigure.send_event != 0)
15568               {
15569                 XWindowChanges
15570                   window_changes;
15571
15572                 /*
15573                   Position the transient windows relative of the Image window.
15574                 */
15575                 if (windows->command.geometry == (char *) NULL)
15576                   if (windows->command.mapped == MagickFalse)
15577                     {
15578                       windows->command.x=event.xconfigure.x-
15579                         windows->command.width-25;
15580                       windows->command.y=event.xconfigure.y;
15581                       XConstrainWindowPosition(display,&windows->command);
15582                       window_changes.x=windows->command.x;
15583                       window_changes.y=windows->command.y;
15584                       (void) XReconfigureWMWindow(display,windows->command.id,
15585                         windows->command.screen,(unsigned int) (CWX | CWY),
15586                         &window_changes);
15587                     }
15588                 if (windows->widget.geometry == (char *) NULL)
15589                   if (windows->widget.mapped == MagickFalse)
15590                     {
15591                       windows->widget.x=event.xconfigure.x+
15592                         event.xconfigure.width/10;
15593                       windows->widget.y=event.xconfigure.y+
15594                         event.xconfigure.height/10;
15595                       XConstrainWindowPosition(display,&windows->widget);
15596                       window_changes.x=windows->widget.x;
15597                       window_changes.y=windows->widget.y;
15598                       (void) XReconfigureWMWindow(display,windows->widget.id,
15599                         windows->widget.screen,(unsigned int) (CWX | CWY),
15600                         &window_changes);
15601                     }
15602                 if (windows->magnify.geometry == (char *) NULL)
15603                   if (windows->magnify.mapped == MagickFalse)
15604                     {
15605                       windows->magnify.x=event.xconfigure.x+
15606                         event.xconfigure.width+25;
15607                       windows->magnify.y=event.xconfigure.y;
15608                       XConstrainWindowPosition(display,&windows->magnify);
15609                       window_changes.x=windows->magnify.x;
15610                       window_changes.y=windows->magnify.y;
15611                       (void) XReconfigureWMWindow(display,windows->magnify.id,
15612                         windows->magnify.screen,(unsigned int) (CWX | CWY),
15613                         &window_changes);
15614                     }
15615                 if (windows->pan.geometry == (char *) NULL)
15616                   if (windows->pan.mapped == MagickFalse)
15617                     {
15618                       windows->pan.x=event.xconfigure.x+
15619                         event.xconfigure.width+25;
15620                       windows->pan.y=event.xconfigure.y+
15621                         windows->magnify.height+50;
15622                       XConstrainWindowPosition(display,&windows->pan);
15623                       window_changes.x=windows->pan.x;
15624                       window_changes.y=windows->pan.y;
15625                       (void) XReconfigureWMWindow(display,windows->pan.id,
15626                         windows->pan.screen,(unsigned int) (CWX | CWY),
15627                         &window_changes);
15628                     }
15629               }
15630             if ((event.xconfigure.width == (int) windows->image.width) &&
15631                 (event.xconfigure.height == (int) windows->image.height))
15632               break;
15633             windows->image.width=(unsigned int) event.xconfigure.width;
15634             windows->image.height=(unsigned int) event.xconfigure.height;
15635             windows->image.x=0;
15636             windows->image.y=0;
15637             if (display_image->montage != (char *) NULL)
15638               {
15639                 windows->image.x=vid_info.x;
15640                 windows->image.y=vid_info.y;
15641               }
15642             if ((windows->image.mapped != MagickFalse) &&
15643                 (windows->image.stasis != MagickFalse))
15644               {
15645                 /*
15646                   Update image window configuration.
15647                 */
15648                 windows->image.window_changes.width=event.xconfigure.width;
15649                 windows->image.window_changes.height=event.xconfigure.height;
15650                 (void) XConfigureImage(display,resource_info,windows,
15651                   display_image,exception);
15652               }
15653             /*
15654               Update pan window configuration.
15655             */
15656             if ((event.xconfigure.width < windows->image.ximage->width) ||
15657                 (event.xconfigure.height < windows->image.ximage->height))
15658               {
15659                 (void) XMapRaised(display,windows->pan.id);
15660                 XDrawPanRectangle(display,windows);
15661               }
15662             else
15663               if (windows->pan.mapped != MagickFalse)
15664                 (void) XWithdrawWindow(display,windows->pan.id,
15665                   windows->pan.screen);
15666             break;
15667           }
15668         if (event.xconfigure.window == windows->magnify.id)
15669           {
15670             unsigned int
15671               magnify;
15672
15673             /*
15674               Magnify window has a new configuration.
15675             */
15676             windows->magnify.width=(unsigned int) event.xconfigure.width;
15677             windows->magnify.height=(unsigned int) event.xconfigure.height;
15678             if (windows->magnify.mapped == MagickFalse)
15679               break;
15680             magnify=1;
15681             while ((int) magnify <= event.xconfigure.width)
15682               magnify<<=1;
15683             while ((int) magnify <= event.xconfigure.height)
15684               magnify<<=1;
15685             magnify>>=1;
15686             if (((int) magnify != event.xconfigure.width) ||
15687                 ((int) magnify != event.xconfigure.height))
15688               {
15689                 window_changes.width=(int) magnify;
15690                 window_changes.height=(int) magnify;
15691                 (void) XReconfigureWMWindow(display,windows->magnify.id,
15692                   windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
15693                   &window_changes);
15694                 break;
15695               }
15696             if ((windows->magnify.mapped != MagickFalse) &&
15697                 (windows->magnify.stasis != MagickFalse))
15698               {
15699                 status=XMakeImage(display,resource_info,&windows->magnify,
15700                   display_image,windows->magnify.width,windows->magnify.height,
15701                   exception);
15702                 XMakeMagnifyImage(display,windows,exception);
15703               }
15704             break;
15705           }
15706         if ((windows->magnify.mapped != MagickFalse) &&
15707             (event.xconfigure.window == windows->pan.id))
15708           {
15709             /*
15710               Pan icon window has a new configuration.
15711             */
15712             if (event.xconfigure.send_event != 0)
15713               {
15714                 windows->pan.x=event.xconfigure.x;
15715                 windows->pan.y=event.xconfigure.y;
15716               }
15717             windows->pan.width=(unsigned int) event.xconfigure.width;
15718             windows->pan.height=(unsigned int) event.xconfigure.height;
15719             break;
15720           }
15721         if (event.xconfigure.window == windows->icon.id)
15722           {
15723             /*
15724               Icon window has a new configuration.
15725             */
15726             windows->icon.width=(unsigned int) event.xconfigure.width;
15727             windows->icon.height=(unsigned int) event.xconfigure.height;
15728             break;
15729           }
15730         break;
15731       }
15732       case DestroyNotify:
15733       {
15734         /*
15735           Group leader has exited.
15736         */
15737         if (display_image->debug != MagickFalse)
15738           (void) LogMagickEvent(X11Event,GetMagickModule(),
15739             "Destroy Notify: 0x%lx",event.xdestroywindow.window);
15740         if (event.xdestroywindow.window == windows->group_leader.id)
15741           {
15742             *state|=ExitState;
15743             break;
15744           }
15745         break;
15746       }
15747       case EnterNotify:
15748       {
15749         /*
15750           Selectively install colormap.
15751         */
15752         if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15753           if (event.xcrossing.mode != NotifyUngrab)
15754             XInstallColormap(display,map_info->colormap);
15755         break;
15756       }
15757       case Expose:
15758       {
15759         if (display_image->debug != MagickFalse)
15760           (void) LogMagickEvent(X11Event,GetMagickModule(),
15761             "Expose: 0x%lx %dx%d+%d+%d",event.xexpose.window,
15762             event.xexpose.width,event.xexpose.height,event.xexpose.x,
15763             event.xexpose.y);
15764         /*
15765           Refresh windows that are now exposed.
15766         */
15767         if ((event.xexpose.window == windows->image.id) &&
15768             (windows->image.mapped != MagickFalse))
15769           {
15770             XRefreshWindow(display,&windows->image,&event);
15771             delay=display_image->delay/MagickMax(
15772               display_image->ticks_per_second,1L);
15773             timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15774             break;
15775           }
15776         if ((event.xexpose.window == windows->magnify.id) &&
15777             (windows->magnify.mapped != MagickFalse))
15778           {
15779             XMakeMagnifyImage(display,windows,exception);
15780             break;
15781           }
15782         if (event.xexpose.window == windows->pan.id)
15783           {
15784             XDrawPanRectangle(display,windows);
15785             break;
15786           }
15787         if (event.xexpose.window == windows->icon.id)
15788           {
15789             XRefreshWindow(display,&windows->icon,&event);
15790             break;
15791           }
15792         break;
15793       }
15794       case KeyPress:
15795       {
15796         int
15797           length;
15798
15799         /*
15800           Respond to a user key press.
15801         */
15802         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
15803           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15804         *(command+length)='\0';
15805         if (display_image->debug != MagickFalse)
15806           (void) LogMagickEvent(X11Event,GetMagickModule(),
15807             "Key press: %d 0x%lx (%s)",event.xkey.state,(unsigned long)
15808             key_symbol,command);
15809         if (event.xkey.window == windows->image.id)
15810           {
15811             command_type=XImageWindowCommand(display,resource_info,windows,
15812               event.xkey.state,key_symbol,&display_image,exception);
15813             if (command_type != NullCommand)
15814               nexus=XMagickCommand(display,resource_info,windows,command_type,
15815                 &display_image,exception);
15816           }
15817         if (event.xkey.window == windows->magnify.id)
15818           XMagnifyWindowCommand(display,windows,event.xkey.state,key_symbol,
15819             exception);
15820         if (event.xkey.window == windows->pan.id)
15821           {
15822             if ((key_symbol == XK_q) || (key_symbol == XK_Escape))
15823               (void) XWithdrawWindow(display,windows->pan.id,
15824                 windows->pan.screen);
15825             else
15826               if ((key_symbol == XK_F1) || (key_symbol == XK_Help))
15827                 XTextViewWidget(display,resource_info,windows,MagickFalse,
15828                   "Help Viewer - Image Pan",ImagePanHelp);
15829               else
15830                 XTranslateImage(display,windows,*image,key_symbol);
15831           }
15832         delay=display_image->delay/MagickMax(
15833           display_image->ticks_per_second,1L);
15834         timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15835         break;
15836       }
15837       case KeyRelease:
15838       {
15839         /*
15840           Respond to a user key release.
15841         */
15842         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
15843           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15844         if (display_image->debug != MagickFalse)
15845           (void) LogMagickEvent(X11Event,GetMagickModule(),
15846             "Key release: 0x%lx (%c)",(unsigned long) key_symbol,*command);
15847         break;
15848       }
15849       case LeaveNotify:
15850       {
15851         /*
15852           Selectively uninstall colormap.
15853         */
15854         if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15855           if (event.xcrossing.mode != NotifyUngrab)
15856             XUninstallColormap(display,map_info->colormap);
15857         break;
15858       }
15859       case MapNotify:
15860       {
15861         if (display_image->debug != MagickFalse)
15862           (void) LogMagickEvent(X11Event,GetMagickModule(),"Map Notify: 0x%lx",
15863             event.xmap.window);
15864         if (event.xmap.window == windows->backdrop.id)
15865           {
15866             (void) XSetInputFocus(display,event.xmap.window,RevertToParent,
15867               CurrentTime);
15868             windows->backdrop.mapped=MagickTrue;
15869             break;
15870           }
15871         if (event.xmap.window == windows->image.id)
15872           {
15873             if (windows->backdrop.id != (Window) NULL)
15874               (void) XInstallColormap(display,map_info->colormap);
15875             if (LocaleCompare(display_image->magick,"LOGO") == 0)
15876               {
15877                 if (LocaleCompare(display_image->filename,"LOGO") == 0)
15878                   nexus=XOpenImage(display,resource_info,windows,MagickFalse);
15879               }
15880             if (((int) windows->image.width < windows->image.ximage->width) ||
15881                 ((int) windows->image.height < windows->image.ximage->height))
15882               (void) XMapRaised(display,windows->pan.id);
15883             windows->image.mapped=MagickTrue;
15884             break;
15885           }
15886         if (event.xmap.window == windows->magnify.id)
15887           {
15888             XMakeMagnifyImage(display,windows,exception);
15889             windows->magnify.mapped=MagickTrue;
15890             (void) XWithdrawWindow(display,windows->info.id,
15891               windows->info.screen);
15892             break;
15893           }
15894         if (event.xmap.window == windows->pan.id)
15895           {
15896             XMakePanImage(display,resource_info,windows,display_image,
15897               exception);
15898             windows->pan.mapped=MagickTrue;
15899             break;
15900           }
15901         if (event.xmap.window == windows->info.id)
15902           {
15903             windows->info.mapped=MagickTrue;
15904             break;
15905           }
15906         if (event.xmap.window == windows->icon.id)
15907           {
15908             MagickBooleanType
15909               taint;
15910
15911             /*
15912               Create an icon image.
15913             */
15914             taint=display_image->taint;
15915             XMakeStandardColormap(display,icon_visual,icon_resources,
15916               display_image,icon_map,icon_pixel,exception);
15917             (void) XMakeImage(display,icon_resources,&windows->icon,
15918               display_image,windows->icon.width,windows->icon.height,
15919               exception);
15920             display_image->taint=taint;
15921             (void) XSetWindowBackgroundPixmap(display,windows->icon.id,
15922               windows->icon.pixmap);
15923             (void) XClearWindow(display,windows->icon.id);
15924             (void) XWithdrawWindow(display,windows->info.id,
15925               windows->info.screen);
15926             windows->icon.mapped=MagickTrue;
15927             break;
15928           }
15929         if (event.xmap.window == windows->command.id)
15930           {
15931             windows->command.mapped=MagickTrue;
15932             break;
15933           }
15934         if (event.xmap.window == windows->popup.id)
15935           {
15936             windows->popup.mapped=MagickTrue;
15937             break;
15938           }
15939         if (event.xmap.window == windows->widget.id)
15940           {
15941             windows->widget.mapped=MagickTrue;
15942             break;
15943           }
15944         break;
15945       }
15946       case MappingNotify:
15947       {
15948         (void) XRefreshKeyboardMapping(&event.xmapping);
15949         break;
15950       }
15951       case NoExpose:
15952         break;
15953       case PropertyNotify:
15954       {
15955         Atom
15956           type;
15957
15958         int
15959           format,
15960           status;
15961
15962         unsigned char
15963           *data;
15964
15965         unsigned long
15966           after,
15967           length;
15968
15969         if (display_image->debug != MagickFalse)
15970           (void) LogMagickEvent(X11Event,GetMagickModule(),
15971             "Property Notify: 0x%lx 0x%lx %d",event.xproperty.window,
15972             event.xproperty.atom,event.xproperty.state);
15973         if (event.xproperty.atom != windows->im_remote_command)
15974           break;
15975         /*
15976           Display image named by the remote command protocol.
15977         */
15978         status=XGetWindowProperty(display,event.xproperty.window,
15979           event.xproperty.atom,0L,(long) MaxTextExtent,MagickFalse,(Atom)
15980           AnyPropertyType,&type,&format,&length,&after,&data);
15981         if ((status != Success) || (length == 0))
15982           break;
15983         if (LocaleCompare((char *) data,"-quit") == 0)
15984           {
15985             XClientMessage(display,windows->image.id,windows->im_protocols,
15986               windows->im_exit,CurrentTime);
15987             (void) XFree((void *) data);
15988             break;
15989           }
15990         (void) CopyMagickString(resource_info->image_info->filename,
15991           (char *) data,MaxTextExtent);
15992         (void) XFree((void *) data);
15993         nexus=ReadImage(resource_info->image_info,exception);
15994         CatchException(exception);
15995         if (nexus != (Image *) NULL)
15996           *state|=NextImageState | ExitState;
15997         break;
15998       }
15999       case ReparentNotify:
16000       {
16001         if (display_image->debug != MagickFalse)
16002           (void) LogMagickEvent(X11Event,GetMagickModule(),
16003             "Reparent Notify: 0x%lx=>0x%lx",event.xreparent.parent,
16004             event.xreparent.window);
16005         break;
16006       }
16007       case UnmapNotify:
16008       {
16009         if (display_image->debug != MagickFalse)
16010           (void) LogMagickEvent(X11Event,GetMagickModule(),
16011             "Unmap Notify: 0x%lx",event.xunmap.window);
16012         if (event.xunmap.window == windows->backdrop.id)
16013           {
16014             windows->backdrop.mapped=MagickFalse;
16015             break;
16016           }
16017         if (event.xunmap.window == windows->image.id)
16018           {
16019             windows->image.mapped=MagickFalse;
16020             break;
16021           }
16022         if (event.xunmap.window == windows->magnify.id)
16023           {
16024             windows->magnify.mapped=MagickFalse;
16025             break;
16026           }
16027         if (event.xunmap.window == windows->pan.id)
16028           {
16029             windows->pan.mapped=MagickFalse;
16030             break;
16031           }
16032         if (event.xunmap.window == windows->info.id)
16033           {
16034             windows->info.mapped=MagickFalse;
16035             break;
16036           }
16037         if (event.xunmap.window == windows->icon.id)
16038           {
16039             if (map_info->colormap == icon_map->colormap)
16040               XConfigureImageColormap(display,resource_info,windows,
16041                 display_image,exception);
16042             (void) XFreeStandardColormap(display,icon_visual,icon_map,
16043               icon_pixel);
16044             windows->icon.mapped=MagickFalse;
16045             break;
16046           }
16047         if (event.xunmap.window == windows->command.id)
16048           {
16049             windows->command.mapped=MagickFalse;
16050             break;
16051           }
16052         if (event.xunmap.window == windows->popup.id)
16053           {
16054             if (windows->backdrop.id != (Window) NULL)
16055               (void) XSetInputFocus(display,windows->image.id,RevertToParent,
16056                 CurrentTime);
16057             windows->popup.mapped=MagickFalse;
16058             break;
16059           }
16060         if (event.xunmap.window == windows->widget.id)
16061           {
16062             if (windows->backdrop.id != (Window) NULL)
16063               (void) XSetInputFocus(display,windows->image.id,RevertToParent,
16064                 CurrentTime);
16065             windows->widget.mapped=MagickFalse;
16066             break;
16067           }
16068         break;
16069       }
16070       default:
16071       {
16072         if (display_image->debug != MagickFalse)
16073           (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
16074             event.type);
16075         break;
16076       }
16077     }
16078   } while (!(*state & ExitState));
16079   if ((*state & ExitState) == 0)
16080     (void) XMagickCommand(display,resource_info,windows,FreeBuffersCommand,
16081       &display_image,exception);
16082   else
16083     if (resource_info->confirm_edit != MagickFalse)
16084       {
16085         /*
16086           Query user if image has changed.
16087         */
16088         if ((resource_info->immutable == MagickFalse) &&
16089             (display_image->taint != MagickFalse))
16090           {
16091             int
16092               status;
16093
16094             status=XConfirmWidget(display,windows,"Your image changed.",
16095               "Do you want to save it");
16096             if (status == 0)
16097               *state&=(~ExitState);
16098             else
16099               if (status > 0)
16100                 (void) XMagickCommand(display,resource_info,windows,SaveCommand,
16101                   &display_image,exception);
16102           }
16103       }
16104   if ((windows->visual_info->klass == GrayScale) ||
16105       (windows->visual_info->klass == PseudoColor) ||
16106       (windows->visual_info->klass == DirectColor))
16107     {
16108       /*
16109         Withdraw pan and Magnify window.
16110       */
16111       if (windows->info.mapped != MagickFalse)
16112         (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
16113       if (windows->magnify.mapped != MagickFalse)
16114         (void) XWithdrawWindow(display,windows->magnify.id,
16115           windows->magnify.screen);
16116       if (windows->command.mapped != MagickFalse)
16117         (void) XWithdrawWindow(display,windows->command.id,
16118           windows->command.screen);
16119     }
16120   if (windows->pan.mapped != MagickFalse)
16121     (void) XWithdrawWindow(display,windows->pan.id,windows->pan.screen);
16122   if (resource_info->backdrop == MagickFalse)
16123     if (windows->backdrop.mapped)
16124       {
16125         (void) XWithdrawWindow(display,windows->backdrop.id,
16126           windows->backdrop.screen);
16127         (void) XDestroyWindow(display,windows->backdrop.id);
16128         windows->backdrop.id=(Window) NULL;
16129         (void) XWithdrawWindow(display,windows->image.id,
16130           windows->image.screen);
16131         (void) XDestroyWindow(display,windows->image.id);
16132         windows->image.id=(Window) NULL;
16133       }
16134   XSetCursorState(display,windows,MagickTrue);
16135   XCheckRefreshWindows(display,windows);
16136   if (((*state & FormerImageState) != 0) || ((*state & NextImageState) != 0))
16137     *state&=(~ExitState);
16138   if (*state & ExitState)
16139     {
16140       /*
16141         Free Standard Colormap.
16142       */
16143       (void) XFreeStandardColormap(display,icon_visual,icon_map,icon_pixel);
16144       if (resource_info->map_type == (char *) NULL)
16145         (void) XFreeStandardColormap(display,visual_info,map_info,pixel);
16146       /*
16147         Free X resources.
16148       */
16149       if (resource_info->copy_image != (Image *) NULL)
16150         {
16151           resource_info->copy_image=DestroyImage(resource_info->copy_image);
16152           resource_info->copy_image=NewImageList();
16153         }
16154       DestroyXResources();
16155     }
16156   (void) XSync(display,MagickFalse);
16157   /*
16158     Restore our progress monitor and warning handlers.
16159   */
16160   (void) SetErrorHandler(warning_handler);
16161   (void) SetWarningHandler(warning_handler);
16162   /*
16163     Change to home directory.
16164   */
16165   directory=getcwd(working_directory,MaxTextExtent);
16166   (void) directory;
16167   {
16168     int
16169       status;
16170
16171     status=chdir(resource_info->home_directory);
16172     if (status == -1)
16173       (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
16174         "UnableToOpenFile","%s",resource_info->home_directory);
16175   }
16176   *image=display_image;
16177   return(nexus);
16178 }
16179 #else
16180 \f
16181 /*
16182 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16183 %                                                                             %
16184 %                                                                             %
16185 %                                                                             %
16186 +   D i s p l a y I m a g e s                                                 %
16187 %                                                                             %
16188 %                                                                             %
16189 %                                                                             %
16190 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16191 %
16192 %  DisplayImages() displays an image sequence to any X window screen.  It
16193 %  returns a value other than 0 if successful.  Check the exception member
16194 %  of image to determine the reason for any failure.
16195 %
16196 %  The format of the DisplayImages method is:
16197 %
16198 %      MagickBooleanType DisplayImages(const ImageInfo *image_info,
16199 %        Image *images,ExceptionInfo *exception)
16200 %
16201 %  A description of each parameter follows:
16202 %
16203 %    o image_info: the image info.
16204 %
16205 %    o image: the image.
16206 %
16207 %    o exception: return any errors or warnings in this structure.
16208 %
16209 */
16210 MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
16211   Image *image,ExceptionInfo *exception)
16212 {
16213   assert(image_info != (const ImageInfo *) NULL);
16214   assert(image_info->signature == MagickSignature);
16215   assert(image != (Image *) NULL);
16216   assert(image->signature == MagickSignature);
16217   if (image->debug != MagickFalse)
16218     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
16219   (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16220     "DelegateLibrarySupportNotBuiltIn","`%s' (X11)",image->filename);
16221   return(MagickFalse);
16222 }
16223 \f
16224 /*
16225 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16226 %                                                                             %
16227 %                                                                             %
16228 %                                                                             %
16229 +   R e m o t e D i s p l a y C o m m a n d                                   %
16230 %                                                                             %
16231 %                                                                             %
16232 %                                                                             %
16233 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16234 %
16235 %  RemoteDisplayCommand() encourages a remote display program to display the
16236 %  specified image filename.
16237 %
16238 %  The format of the RemoteDisplayCommand method is:
16239 %
16240 %      MagickBooleanType RemoteDisplayCommand(const ImageInfo *image,
16241 %        const char *window,const char *filename,ExceptionInfo *exception)
16242 %
16243 %  A description of each parameter follows:
16244 %
16245 %    o image_info: the image info.
16246 %
16247 %    o window: Specifies the name or id of an X window.
16248 %
16249 %    o filename: the name of the image filename to display.
16250 %
16251 %    o exception: return any errors or warnings in this structure.
16252 %
16253 */
16254 MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
16255   const char *window,const char *filename,ExceptionInfo *exception)
16256 {
16257   assert(image_info != (const ImageInfo *) NULL);
16258   assert(image_info->signature == MagickSignature);
16259   assert(filename != (char *) NULL);
16260   (void) window;
16261   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
16262   (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16263     "DelegateLibrarySupportNotBuiltIn","`%s' (X11)",image_info->filename);
16264   return(MagickFalse);
16265 }
16266 #endif