]> granicus.if.org Git - imagemagick/blob - MagickCore/display.c
(no commit message)
[imagemagick] / MagickCore / display.c
1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 %                                                                             %
4 %                                                                             %
5 %                                                                             %
6 %               DDDD   IIIII  SSSSS  PPPP   L       AAA   Y   Y               %
7 %               D   D    I    SS     P   P  L      A   A   Y Y                %
8 %               D   D    I     SSS   PPPP   L      AAAAA    Y                 %
9 %               D   D    I       SS  P      L      A   A    Y                 %
10 %               DDDD   IIIII  SSSSS  P      LLLLL  A   A    Y                 %
11 %                                                                             %
12 %                                                                             %
13 %        MagickCore Methods to Interactively Display and Edit an Image        %
14 %                                                                             %
15 %                             Software Design                                 %
16 %                               John Cristy                                   %
17 %                                July 1992                                    %
18 %                                                                             %
19 %                                                                             %
20 %  Copyright 1999-2011 ImageMagick Studio LLC, a non-profit organization      %
21 %  dedicated to making software imaging solutions freely available.           %
22 %                                                                             %
23 %  You may not use this file except in compliance with the License.  You may  %
24 %  obtain a copy of the License at                                            %
25 %                                                                             %
26 %    http://www.imagemagick.org/script/license.php                            %
27 %                                                                             %
28 %  Unless required by applicable law or agreed to in writing, software        %
29 %  distributed under the License is distributed on an "AS IS" BASIS,          %
30 %  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
31 %  See the License for the specific language governing permissions and        %
32 %  limitations under the License.                                             %
33 %                                                                             %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35 %
36 %
37 */
38 \f
39 /*
40   Include declarations.
41 */
42 #include "MagickCore/studio.h"
43 #include "MagickCore/artifact.h"
44 #include "MagickCore/blob.h"
45 #include "MagickCore/cache.h"
46 #include "MagickCore/client.h"
47 #include "MagickCore/color.h"
48 #include "MagickCore/colorspace.h"
49 #include "MagickCore/composite.h"
50 #include "MagickCore/constitute.h"
51 #include "MagickCore/decorate.h"
52 #include "MagickCore/delegate.h"
53 #include "MagickCore/display.h"
54 #include "MagickCore/display-private.h"
55 #include "MagickCore/draw.h"
56 #include "MagickCore/effect.h"
57 #include "MagickCore/enhance.h"
58 #include "MagickCore/exception.h"
59 #include "MagickCore/exception-private.h"
60 #include "MagickCore/fx.h"
61 #include "MagickCore/geometry.h"
62 #include "MagickCore/image.h"
63 #include "MagickCore/image-private.h"
64 #include "MagickCore/list.h"
65 #include "MagickCore/log.h"
66 #include "MagickCore/magick.h"
67 #include "MagickCore/memory_.h"
68 #include "MagickCore/monitor.h"
69 #include "MagickCore/monitor-private.h"
70 #include "MagickCore/montage.h"
71 #include "MagickCore/option.h"
72 #include "MagickCore/paint.h"
73 #include "MagickCore/pixel.h"
74 #include "MagickCore/pixel-accessor.h"
75 #include "MagickCore/PreRvIcccm.h"
76 #include "MagickCore/property.h"
77 #include "MagickCore/quantum.h"
78 #include "MagickCore/quantum-private.h"
79 #include "MagickCore/resize.h"
80 #include "MagickCore/resource_.h"
81 #include "MagickCore/shear.h"
82 #include "MagickCore/segment.h"
83 #include "MagickCore/string_.h"
84 #include "MagickCore/string-private.h"
85 #include "MagickCore/transform.h"
86 #include "MagickCore/threshold.h"
87 #include "MagickCore/utility.h"
88 #include "MagickCore/utility-private.h"
89 #include "MagickCore/version.h"
90 #include "MagickCore/widget.h"
91 #include "MagickCore/widget-private.h"
92 #include "MagickCore/xwindow.h"
93 #include "MagickCore/xwindow-private.h"
94 \f
95 #if defined(MAGICKCORE_X11_DELEGATE)
96 /*
97   Define declarations.
98 */
99 #define MaxColors  MagickMin((ssize_t) windows->visual_info->colormap_size,256L)
100 \f
101 /*
102   Constant declarations.
103 */
104 static const unsigned char
105   HighlightBitmap[8] =
106   {
107     0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55
108   },
109   OpaqueBitmap[8] =
110   {
111     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
112   },
113   ShadowBitmap[8] =
114   {
115     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
116   };
117
118 static const char
119   *PageSizes[] =
120   {
121     "Letter",
122     "Tabloid",
123     "Ledger",
124     "Legal",
125     "Statement",
126     "Executive",
127     "A3",
128     "A4",
129     "A5",
130     "B4",
131     "B5",
132     "Folio",
133     "Quarto",
134     "10x14",
135     (char *) NULL
136   };
137 \f
138 /*
139   Help widget declarations.
140 */
141 static const char
142   *ImageAnnotateHelp[] =
143   {
144     "In annotate mode, the Command widget has these options:",
145     "",
146     "    Font Name",
147     "      fixed",
148     "      variable",
149     "      5x8",
150     "      6x10",
151     "      7x13bold",
152     "      8x13bold",
153     "      9x15bold",
154     "      10x20",
155     "      12x24",
156     "      Browser...",
157     "    Font Color",
158     "      black",
159     "      blue",
160     "      cyan",
161     "      green",
162     "      gray",
163     "      red",
164     "      magenta",
165     "      yellow",
166     "      white",
167     "      transparent",
168     "      Browser...",
169     "    Font Color",
170     "      black",
171     "      blue",
172     "      cyan",
173     "      green",
174     "      gray",
175     "      red",
176     "      magenta",
177     "      yellow",
178     "      white",
179     "      transparent",
180     "      Browser...",
181     "    Rotate Text",
182     "      -90",
183     "      -45",
184     "      -30",
185     "      0",
186     "      30",
187     "      45",
188     "      90",
189     "      180",
190     "      Dialog...",
191     "    Help",
192     "    Dismiss",
193     "",
194     "Choose a font name from the Font Name sub-menu.  Additional",
195     "font names can be specified with the font browser.  You can",
196     "change the menu names by setting the X resources font1",
197     "through font9.",
198     "",
199     "Choose a font color from the Font Color sub-menu.",
200     "Additional font colors can be specified with the color",
201     "browser.  You can change the menu colors by setting the X",
202     "resources pen1 through pen9.",
203     "",
204     "If you select the color browser and press Grab, you can",
205     "choose the font color by moving the pointer to the desired",
206     "color on the screen and press any button.",
207     "",
208     "If you choose to rotate the text, choose Rotate Text from the",
209     "menu and select an angle.  Typically you will only want to",
210     "rotate one line of text at a time.  Depending on the angle you",
211     "choose, subsequent lines may end up overwriting each other.",
212     "",
213     "Choosing a font and its color is optional.  The default font",
214     "is fixed and the default color is black.  However, you must",
215     "choose a location to begin entering text and press button 1.",
216     "An underscore character will appear at the location of the",
217     "pointer.  The cursor changes to a pencil to indicate you are",
218     "in text mode.  To exit immediately, press Dismiss.",
219     "",
220     "In text mode, any key presses will display the character at",
221     "the location of the underscore and advance the underscore",
222     "cursor.  Enter your text and once completed press Apply to",
223     "finish your image annotation.  To correct errors press BACK",
224     "SPACE.  To delete an entire line of text, press DELETE.  Any",
225     "text that exceeds the boundaries of the image window is",
226     "automagically continued onto the next line.",
227     "",
228     "The actual color you request for the font is saved in the",
229     "image.  However, the color that appears in your image window",
230     "may be different.  For example, on a monochrome screen the",
231     "text will appear black or white even if you choose the color",
232     "red as the font color.  However, the image saved to a file",
233     "with -write is written with red lettering.  To assure the",
234     "correct color text in the final image, any PseudoClass image",
235     "is promoted to DirectClass (see miff(5)).  To force a",
236     "PseudoClass image to remain PseudoClass, use -colors.",
237     (char *) NULL,
238   },
239   *ImageChopHelp[] =
240   {
241     "In chop mode, the Command widget has these options:",
242     "",
243     "    Direction",
244     "      horizontal",
245     "      vertical",
246     "    Help",
247     "    Dismiss",
248     "",
249     "If the you choose the horizontal direction (this the",
250     "default), the area of the image between the two horizontal",
251     "endpoints of the chop line is removed.  Otherwise, the area",
252     "of the image between the two vertical endpoints of the chop",
253     "line is removed.",
254     "",
255     "Select a location within the image window to begin your chop,",
256     "press and hold any button.  Next, move the pointer to",
257     "another location in the image.  As you move a line will",
258     "connect the initial location and the pointer.  When you",
259     "release the button, the area within the image to chop is",
260     "determined by which direction you choose from the Command",
261     "widget.",
262     "",
263     "To cancel the image chopping, move the pointer back to the",
264     "starting point of the line and release the button.",
265     (char *) NULL,
266   },
267   *ImageColorEditHelp[] =
268   {
269     "In color edit mode, the Command widget has these options:",
270     "",
271     "    Method",
272     "      point",
273     "      replace",
274     "      floodfill",
275     "      filltoborder",
276     "      reset",
277     "    Pixel Color",
278     "      black",
279     "      blue",
280     "      cyan",
281     "      green",
282     "      gray",
283     "      red",
284     "      magenta",
285     "      yellow",
286     "      white",
287     "      Browser...",
288     "    Border Color",
289     "      black",
290     "      blue",
291     "      cyan",
292     "      green",
293     "      gray",
294     "      red",
295     "      magenta",
296     "      yellow",
297     "      white",
298     "      Browser...",
299     "    Fuzz",
300     "      0%",
301     "      2%",
302     "      5%",
303     "      10%",
304     "      15%",
305     "      Dialog...",
306     "    Undo",
307     "    Help",
308     "    Dismiss",
309     "",
310     "Choose a color editing method from the Method sub-menu",
311     "of the Command widget.  The point method recolors any pixel",
312     "selected with the pointer until the button is released.  The",
313     "replace method recolors any pixel that matches the color of",
314     "the pixel you select with a button press.  Floodfill recolors",
315     "any pixel that matches the color of the pixel you select with",
316     "a button press and is a neighbor.  Whereas filltoborder recolors",
317     "any neighbor pixel that is not the border color.  Finally reset",
318     "changes the entire image to the designated color.",
319     "",
320     "Next, choose a pixel color from the Pixel Color sub-menu.",
321     "Additional pixel colors can be specified with the color",
322     "browser.  You can change the menu colors by setting the X",
323     "resources pen1 through pen9.",
324     "",
325     "Now press button 1 to select a pixel within the image window",
326     "to change its color.  Additional pixels may be recolored as",
327     "prescribed by the method you choose.",
328     "",
329     "If the Magnify widget is mapped, it can be helpful in positioning",
330     "your pointer within the image (refer to button 2).",
331     "",
332     "The actual color you request for the pixels is saved in the",
333     "image.  However, the color that appears in your image window",
334     "may be different.  For example, on a monochrome screen the",
335     "pixel will appear black or white even if you choose the",
336     "color red as the pixel color.  However, the image saved to a",
337     "file with -write is written with red pixels.  To assure the",
338     "correct color text in the final image, any PseudoClass image",
339     "is promoted to DirectClass (see miff(5)).  To force a",
340     "PseudoClass image to remain PseudoClass, use -colors.",
341     (char *) NULL,
342   },
343   *ImageCompositeHelp[] =
344   {
345     "First a widget window is displayed requesting you to enter an",
346     "image name. Press Composite, Grab or type a file name.",
347     "Press Cancel if you choose not to create a composite image.",
348     "When you choose Grab, move the pointer to the desired window",
349     "and press any button.",
350     "",
351     "If the Composite image does not have any matte information,",
352     "you are informed and the file browser is displayed again.",
353     "Enter the name of a mask image.  The image is typically",
354     "grayscale and the same size as the composite image.  If the",
355     "image is not grayscale, it is converted to grayscale and the",
356     "resulting intensities are used as matte information.",
357     "",
358     "A small window appears showing the location of the cursor in",
359     "the image window. You are now in composite mode.  To exit",
360     "immediately, press Dismiss.  In composite mode, the Command",
361     "widget has these options:",
362     "",
363     "    Operators",
364     "      Over",
365     "      In",
366     "      Out",
367     "      Atop",
368     "      Xor",
369     "      Plus",
370     "      Minus",
371     "      Add",
372     "      Subtract",
373     "      Difference",
374     "      Multiply",
375     "      Bumpmap",
376     "      Copy",
377     "      CopyRed",
378     "      CopyGreen",
379     "      CopyBlue",
380     "      CopyOpacity",
381     "      Clear",
382     "    Dissolve",
383     "    Displace",
384     "    Help",
385     "    Dismiss",
386     "",
387     "Choose a composite operation from the Operators sub-menu of",
388     "the Command widget.  How each operator behaves is described",
389     "below.  Image window is the image currently displayed on",
390     "your X server and image is the image obtained with the File",
391     "Browser widget.",
392     "",
393     "Over     The result is the union of the two image shapes,",
394     "         with image obscuring image window in the region of",
395     "         overlap.",
396     "",
397     "In       The result is simply image cut by the shape of",
398     "         image window.  None of the image data of image",
399     "         window is in the result.",
400     "",
401     "Out      The resulting image is image with the shape of",
402     "         image window cut out.",
403     "",
404     "Atop     The result is the same shape as image image window,",
405     "         with image obscuring image window where the image",
406     "         shapes overlap.  Note this differs from over",
407     "         because the portion of image outside image window's",
408     "         shape does not appear in the result.",
409     "",
410     "Xor      The result is the image data from both image and",
411     "         image window that is outside the overlap region.",
412     "         The overlap region is blank.",
413     "",
414     "Plus     The result is just the sum of the image data.",
415     "         Output values are cropped to QuantumRange (no overflow).",
416     "",
417     "Minus    The result of image - image window, with underflow",
418     "         cropped to zero.",
419     "",
420     "Add      The result of image + image window, with overflow",
421     "         wrapping around (mod 256).",
422     "",
423     "Subtract The result of image - image window, with underflow",
424     "         wrapping around (mod 256).  The add and subtract",
425     "         operators can be used to perform reversible",
426     "         transformations.",
427     "",
428     "Difference",
429     "         The result of abs(image - image window).  This",
430     "         useful for comparing two very similar images.",
431     "",
432     "Multiply",
433     "         The result of image * image window.  This",
434     "         useful for the creation of drop-shadows.",
435     "",
436     "Bumpmap  The result of surface normals from image * image",
437     "         window.",
438     "",
439     "Copy     The resulting image is image window replaced with",
440     "         image.  Here the matte information is ignored.",
441     "",
442     "CopyRed  The red layer of the image window is replace with",
443     "         the red layer of the image.  The other layers are",
444     "         untouched.",
445     "",
446     "CopyGreen",
447     "         The green layer of the image window is replace with",
448     "         the green layer of the image.  The other layers are",
449     "         untouched.",
450     "",
451     "CopyBlue The blue layer of the image window is replace with",
452     "         the blue layer of the image.  The other layers are",
453     "         untouched.",
454     "",
455     "CopyOpacity",
456     "         The matte layer of the image window is replace with",
457     "         the matte layer of the image.  The other layers are",
458     "         untouched.",
459     "",
460     "The image compositor requires a matte, or alpha channel in",
461     "the image for some operations.  This extra channel usually",
462     "defines a mask which represents a sort of a cookie-cutter",
463     "for the image.  This the case when matte is opaque (full",
464     "coverage) for pixels inside the shape, zero outside, and",
465     "between 0 and QuantumRange on the boundary.  If image does not",
466     "have a matte channel, it is initialized with 0 for any pixel",
467     "matching in color to pixel location (0,0), otherwise QuantumRange.",
468     "",
469     "If you choose Dissolve, the composite operator becomes Over.  The",
470     "image matte channel percent transparency is initialized to factor.",
471     "The image window is initialized to (100-factor). Where factor is the",
472     "value you specify in the Dialog widget.",
473     "",
474     "Displace shifts the image pixels as defined by a displacement",
475     "map.  With this option, image is used as a displacement map.",
476     "Black, within the displacement map, is a maximum positive",
477     "displacement.  White is a maximum negative displacement and",
478     "middle gray is neutral.  The displacement is scaled to determine",
479     "the pixel shift.  By default, the displacement applies in both the",
480     "horizontal and vertical directions.  However, if you specify a mask,",
481     "image is the horizontal X displacement and mask the vertical Y",
482     "displacement.",
483     "",
484     "Note that matte information for image window is not retained",
485     "for colormapped X server visuals (e.g. StaticColor,",
486     "StaticColor, GrayScale, PseudoColor).  Correct compositing",
487     "behavior may require a TrueColor or DirectColor visual or a",
488     "Standard Colormap.",
489     "",
490     "Choosing a composite operator is optional.  The default",
491     "operator is replace.  However, you must choose a location to",
492     "composite your image and press button 1.  Press and hold the",
493     "button before releasing and an outline of the image will",
494     "appear to help you identify your location.",
495     "",
496     "The actual colors of the composite image is saved.  However,",
497     "the color that appears in image window may be different.",
498     "For example, on a monochrome screen image window will appear",
499     "black or white even though your composited image may have",
500     "many colors.  If the image is saved to a file it is written",
501     "with the correct colors.  To assure the correct colors are",
502     "saved in the final image, any PseudoClass image is promoted",
503     "to DirectClass (see miff(5)).  To force a PseudoClass image",
504     "to remain PseudoClass, use -colors.",
505     (char *) NULL,
506   },
507   *ImageCutHelp[] =
508   {
509     "In cut mode, the Command widget has these options:",
510     "",
511     "    Help",
512     "    Dismiss",
513     "",
514     "To define a cut region, press button 1 and drag.  The",
515     "cut region is defined by a highlighted rectangle that",
516     "expands or contracts as it follows the pointer.  Once you",
517     "are satisfied with the cut region, release the button.",
518     "You are now in rectify mode.  In rectify mode, the Command",
519     "widget has these options:",
520     "",
521     "    Cut",
522     "    Help",
523     "    Dismiss",
524     "",
525     "You can make adjustments by moving the pointer to one of the",
526     "cut rectangle corners, pressing a button, and dragging.",
527     "Finally, press Cut to commit your copy region.  To",
528     "exit without cutting the image, press Dismiss.",
529     (char *) NULL,
530   },
531   *ImageCopyHelp[] =
532   {
533     "In copy mode, the Command widget has these options:",
534     "",
535     "    Help",
536     "    Dismiss",
537     "",
538     "To define a copy region, press button 1 and drag.  The",
539     "copy region is defined by a highlighted rectangle that",
540     "expands or contracts as it follows the pointer.  Once you",
541     "are satisfied with the copy region, release the button.",
542     "You are now in rectify mode.  In rectify mode, the Command",
543     "widget has these options:",
544     "",
545     "    Copy",
546     "    Help",
547     "    Dismiss",
548     "",
549     "You can make adjustments by moving the pointer to one of the",
550     "copy rectangle corners, pressing a button, and dragging.",
551     "Finally, press Copy to commit your copy region.  To",
552     "exit without copying the image, press Dismiss.",
553     (char *) NULL,
554   },
555   *ImageCropHelp[] =
556   {
557     "In crop mode, the Command widget has these options:",
558     "",
559     "    Help",
560     "    Dismiss",
561     "",
562     "To define a cropping region, press button 1 and drag.  The",
563     "cropping region is defined by a highlighted rectangle that",
564     "expands or contracts as it follows the pointer.  Once you",
565     "are satisfied with the cropping region, release the button.",
566     "You are now in rectify mode.  In rectify mode, the Command",
567     "widget has these options:",
568     "",
569     "    Crop",
570     "    Help",
571     "    Dismiss",
572     "",
573     "You can make adjustments by moving the pointer to one of the",
574     "cropping rectangle corners, pressing a button, and dragging.",
575     "Finally, press Crop to commit your cropping region.  To",
576     "exit without cropping the image, press Dismiss.",
577     (char *) NULL,
578   },
579   *ImageDrawHelp[] =
580   {
581     "The cursor changes to a crosshair to indicate you are in",
582     "draw mode.  To exit immediately, press Dismiss.  In draw mode,",
583     "the Command widget has these options:",
584     "",
585     "    Element",
586     "      point",
587     "      line",
588     "      rectangle",
589     "      fill rectangle",
590     "      circle",
591     "      fill circle",
592     "      ellipse",
593     "      fill ellipse",
594     "      polygon",
595     "      fill polygon",
596     "    Color",
597     "      black",
598     "      blue",
599     "      cyan",
600     "      green",
601     "      gray",
602     "      red",
603     "      magenta",
604     "      yellow",
605     "      white",
606     "      transparent",
607     "      Browser...",
608     "    Stipple",
609     "      Brick",
610     "      Diagonal",
611     "      Scales",
612     "      Vertical",
613     "      Wavy",
614     "      Translucent",
615     "      Opaque",
616     "      Open...",
617     "    Width",
618     "      1",
619     "      2",
620     "      4",
621     "      8",
622     "      16",
623     "      Dialog...",
624     "    Undo",
625     "    Help",
626     "    Dismiss",
627     "",
628     "Choose a drawing primitive from the Element sub-menu.",
629     "",
630     "Choose a color from the Color sub-menu.  Additional",
631     "colors can be specified with the color browser.",
632     "",
633     "If you choose the color browser and press Grab, you can",
634     "select the color by moving the pointer to the desired",
635     "color on the screen and press any button.  The transparent",
636     "color updates the image matte channel and is useful for",
637     "image compositing.",
638     "",
639     "Choose a stipple, if appropriate, from the Stipple sub-menu.",
640     "Additional stipples can be specified with the file browser.",
641     "Stipples obtained from the file browser must be on disk in the",
642     "X11 bitmap format.",
643     "",
644     "Choose a width, if appropriate, from the Width sub-menu.  To",
645     "choose a specific width select the Dialog widget.",
646     "",
647     "Choose a point in the Image window and press button 1 and",
648     "hold.  Next, move the pointer to another location in the",
649     "image.  As you move, a line connects the initial location and",
650     "the pointer.  When you release the button, the image is",
651     "updated with the primitive you just drew.  For polygons, the",
652     "image is updated when you press and release the button without",
653     "moving the pointer.",
654     "",
655     "To cancel image drawing, move the pointer back to the",
656     "starting point of the line and release the button.",
657     (char *) NULL,
658   },
659   *DisplayHelp[] =
660   {
661     "BUTTONS",
662     "  The effects of each button press is described below.  Three",
663     "  buttons are required.  If you have a two button mouse,",
664     "  button 1 and 3 are returned.  Press ALT and button 3 to",
665     "  simulate button 2.",
666     "",
667     "  1    Press this button to map or unmap the Command widget.",
668     "",
669     "  2    Press and drag to define a region of the image to",
670     "       magnify.",
671     "",
672     "  3    Press and drag to choose from a select set of commands.",
673     "       This button behaves differently if the image being",
674     "       displayed is a visual image directory.  Here, choose a",
675     "       particular tile of the directory and press this button and",
676     "       drag to select a command from a pop-up menu.  Choose from",
677     "       these menu items:",
678     "",
679     "           Open",
680     "           Next",
681     "           Former",
682     "           Delete",
683     "           Update",
684     "",
685     "       If you choose Open, the image represented by the tile is",
686     "       displayed.  To return to the visual image directory, choose",
687     "       Next from the Command widget.  Next and Former moves to the",
688     "       next or former image respectively.  Choose Delete to delete",
689     "       a particular image tile.  Finally, choose Update to",
690     "       synchronize all the image tiles with their respective",
691     "       images.",
692     "",
693     "COMMAND WIDGET",
694     "  The Command widget lists a number of sub-menus and commands.",
695     "  They are",
696     "",
697     "      File",
698     "        Open...",
699     "        Next",
700     "        Former",
701     "        Select...",
702     "        Save...",
703     "        Print...",
704     "        Delete...",
705     "        New...",
706     "        Visual Directory...",
707     "        Quit",
708     "      Edit",
709     "        Undo",
710     "        Redo",
711     "        Cut",
712     "        Copy",
713     "        Paste",
714     "      View",
715     "        Half Size",
716     "        Original Size",
717     "        Double Size",
718     "        Resize...",
719     "        Apply",
720     "        Refresh",
721     "        Restore",
722     "      Transform",
723     "        Crop",
724     "        Chop",
725     "        Flop",
726     "        Flip",
727     "        Rotate Right",
728     "        Rotate Left",
729     "        Rotate...",
730     "        Shear...",
731     "        Roll...",
732     "        Trim Edges",
733     "      Enhance",
734     "        Brightness...",
735     "        Saturation...",
736     "        Hue...",
737     "        Gamma...",
738     "        Sharpen...",
739     "        Dull",
740     "        Contrast Stretch...",
741     "        Sigmoidal Contrast...",
742     "        Normalize",
743     "        Equalize",
744     "        Negate",
745     "        Grayscale",
746     "        Map...",
747     "        Quantize...",
748     "      Effects",
749     "        Despeckle",
750     "        Emboss",
751     "        Reduce Noise",
752     "        Add Noise",
753     "        Sharpen...",
754     "        Blur...",
755     "        Threshold...",
756     "        Edge Detect...",
757     "        Spread...",
758     "        Shade...",
759     "        Painting...",
760     "        Segment...",
761     "      F/X",
762     "        Solarize...",
763     "        Sepia Tone...",
764     "        Swirl...",
765     "        Implode...",
766     "        Vignette...",
767     "        Wave...",
768     "        Oil Painting...",
769     "        Charcoal Drawing...",
770     "      Image Edit",
771     "        Annotate...",
772     "        Draw...",
773     "        Color...",
774     "        Matte...",
775     "        Composite...",
776     "        Add Border...",
777     "        Add Frame...",
778     "        Comment...",
779     "        Launch...",
780     "        Region of Interest...",
781     "      Miscellany",
782     "        Image Info",
783     "        Zoom Image",
784     "        Show Preview...",
785     "        Show Histogram",
786     "        Show Matte",
787     "        Background...",
788     "        Slide Show",
789     "        Preferences...",
790     "      Help",
791     "        Overview",
792     "        Browse Documentation",
793     "        About Display",
794     "",
795     "  Menu items with a indented triangle have a sub-menu.  They",
796     "  are represented above as the indented items.  To access a",
797     "  sub-menu item, move the pointer to the appropriate menu and",
798     "  press a button and drag.  When you find the desired sub-menu",
799     "  item, release the button and the command is executed.  Move",
800     "  the pointer away from the sub-menu if you decide not to",
801     "  execute a particular command.",
802     "",
803     "KEYBOARD ACCELERATORS",
804     "  Accelerators are one or two key presses that effect a",
805     "  particular command.  The keyboard accelerators that",
806     "  display(1) understands is:",
807     "",
808     "  Ctl+O     Press to open an image from a file.",
809     "",
810     "  space     Press to display the next image.",
811     "",
812     "            If the image is a multi-paged document such as a Postscript",
813     "            document, you can skip ahead several pages by preceding",
814     "            this command with a number.  For example to display the",
815     "            third page beyond the current page, press 3<space>.",
816     "",
817     "  backspace Press to display the former image.",
818     "",
819     "            If the image is a multi-paged document such as a Postscript",
820     "            document, you can skip behind several pages by preceding",
821     "            this command with a number.  For example to display the",
822     "            third page preceding the current page, press 3<backspace>.",
823     "",
824     "  Ctl+S     Press to write the image to a file.",
825     "",
826     "  Ctl+P     Press to print the image to a Postscript printer.",
827     "",
828     "  Ctl+D     Press to delete an image file.",
829     "",
830     "  Ctl+N     Press to create a blank canvas.",
831     "",
832     "  Ctl+Q     Press to discard all images and exit program.",
833     "",
834     "  Ctl+Z     Press to undo last image transformation.",
835     "",
836     "  Ctl+R     Press to redo last image transformation.",
837     "",
838     "  Ctl+X     Press to cut a region of the image.",
839     "",
840     "  Ctl+C     Press to copy a region of the image.",
841     "",
842     "  Ctl+V     Press to paste a region to the image.",
843     "",
844     "  <         Press to half the image size.",
845     "",
846     "  -         Press to return to the original image size.",
847     "",
848     "  >         Press to double the image size.",
849     "",
850     "  %         Press to resize the image to a width and height you",
851     "            specify.",
852     "",
853     "Cmd-A       Press to make any image transformations permanent."
854     "",
855     "            By default, any image size transformations are applied",
856     "            to the original image to create the image displayed on",
857     "            the X server.  However, the transformations are not",
858     "            permanent (i.e. the original image does not change",
859     "            size only the X image does).  For example, if you",
860     "            press > the X image will appear to double in size,",
861     "            but the original image will in fact remain the same size.",
862     "            To force the original image to double in size, press >",
863     "            followed by Cmd-A.",
864     "",
865     "  @         Press to refresh the image window.",
866     "",
867     "  C         Press to cut out a rectangular region of the image.",
868     "",
869     "  [         Press to chop the image.",
870     "",
871     "  H         Press to flop image in the horizontal direction.",
872     "",
873     "  V         Press to flip image in the vertical direction.",
874     "",
875     "  /         Press to rotate the image 90 degrees clockwise.",
876     "",
877     " \\         Press to rotate the image 90 degrees counter-clockwise.",
878     "",
879     "  *         Press to rotate the image the number of degrees you",
880     "            specify.",
881     "",
882     "  S         Press to shear the image the number of degrees you",
883     "            specify.",
884     "",
885     "  R         Press to roll the image.",
886     "",
887     "  T         Press to trim the image edges.",
888     "",
889     "  Shft-H    Press to vary the image hue.",
890     "",
891     "  Shft-S    Press to vary the color saturation.",
892     "",
893     "  Shft-L    Press to vary the color brightness.",
894     "",
895     "  Shft-G    Press to gamma correct the image.",
896     "",
897     "  Shft-C    Press to sharpen the image contrast.",
898     "",
899     "  Shft-Z    Press to dull the image contrast.",
900     "",
901     "  =         Press to perform histogram equalization on the image.",
902     "",
903     "  Shft-N    Press to perform histogram normalization on the image.",
904     "",
905     "  Shft-~    Press to negate the colors of the image.",
906     "",
907     "  .         Press to convert the image colors to gray.",
908     "",
909     "  Shft-#    Press to set the maximum number of unique colors in the",
910     "            image.",
911     "",
912     "  F2        Press to reduce the speckles in an image.",
913     "",
914     "  F3        Press to eliminate peak noise from an image.",
915     "",
916     "  F4        Press to add noise to an image.",
917     "",
918     "  F5        Press to sharpen an image.",
919     "",
920     "  F6        Press to delete an image file.",
921     "",
922     "  F7        Press to threshold the image.",
923     "",
924     "  F8        Press to detect edges within an image.",
925     "",
926     "  F9        Press to emboss an image.",
927     "",
928     "  F10       Press to displace pixels by a random amount.",
929     "",
930     "  F11       Press to negate all pixels above the threshold level.",
931     "",
932     "  F12       Press to shade the image using a distant light source.",
933     "",
934     "  F13       Press to lighten or darken image edges to create a 3-D effect.",
935     "",
936     "  F14       Press to segment the image by color.",
937     "",
938     "  Meta-S    Press to swirl image pixels about the center.",
939     "",
940     "  Meta-I    Press to implode image pixels about the center.",
941     "",
942     "  Meta-W    Press to alter an image along a sine wave.",
943     "",
944     "  Meta-P    Press to simulate an oil painting.",
945     "",
946     "  Meta-C    Press to simulate a charcoal drawing.",
947     "",
948     "  Alt-A     Press to annotate the image with text.",
949     "",
950     "  Alt-D     Press to draw on an image.",
951     "",
952     "  Alt-P     Press to edit an image pixel color.",
953     "",
954     "  Alt-M     Press to edit the image matte information.",
955     "",
956     "  Alt-V     Press to composite the image with another.",
957     "",
958     "  Alt-B     Press to add a border to the image.",
959     "",
960     "  Alt-F     Press to add an ornamental border to the image.",
961     "",
962     "  Alt-Shft-!",
963     "            Press to add an image comment.",
964     "",
965     "  Ctl-A     Press to apply image processing techniques to a region",
966     "            of interest.",
967     "",
968     "  Shft-?    Press to display information about the image.",
969     "",
970     "  Shft-+    Press to map the zoom image window.",
971     "",
972     "  Shft-P    Press to preview an image enhancement, effect, or f/x.",
973     "",
974     "  F1        Press to display helpful information about display(1).",
975     "",
976     "  Find      Press to browse documentation about ImageMagick.",
977     "",
978     "  1-9       Press to change the level of magnification.",
979     "",
980     "  Use the arrow keys to move the image one pixel up, down,",
981     "  left, or right within the magnify window.  Be sure to first",
982     "  map the magnify window by pressing button 2.",
983     "",
984     "  Press ALT and one of the arrow keys to trim off one pixel",
985     "  from any side of the image.",
986     (char *) NULL,
987   },
988   *ImageMatteEditHelp[] =
989   {
990     "Matte information within an image is useful for some",
991     "operations such as image compositing (See IMAGE",
992     "COMPOSITING).  This extra channel usually defines a mask",
993     "which represents a sort of a cookie-cutter for the image.",
994     "This the case when matte is opaque (full coverage) for",
995     "pixels inside the shape, zero outside, and between 0 and",
996     "QuantumRange on the boundary.",
997     "",
998     "A small window appears showing the location of the cursor in",
999     "the image window. You are now in matte edit mode.  To exit",
1000     "immediately, press Dismiss.  In matte edit mode, the Command",
1001     "widget has these options:",
1002     "",
1003     "    Method",
1004     "      point",
1005     "      replace",
1006     "      floodfill",
1007     "      filltoborder",
1008     "      reset",
1009     "    Border Color",
1010     "      black",
1011     "      blue",
1012     "      cyan",
1013     "      green",
1014     "      gray",
1015     "      red",
1016     "      magenta",
1017     "      yellow",
1018     "      white",
1019     "      Browser...",
1020     "    Fuzz",
1021     "      0%",
1022     "      2%",
1023     "      5%",
1024     "      10%",
1025     "      15%",
1026     "      Dialog...",
1027     "    Matte",
1028     "      Opaque",
1029     "      Transparent",
1030     "      Dialog...",
1031     "    Undo",
1032     "    Help",
1033     "    Dismiss",
1034     "",
1035     "Choose a matte editing method from the Method sub-menu of",
1036     "the Command widget.  The point method changes the matte value",
1037     "of any pixel selected with the pointer until the button is",
1038     "is released.  The replace method changes the matte value of",
1039     "any pixel that matches the color of the pixel you select with",
1040     "a button press.  Floodfill changes the matte value of any pixel",
1041     "that matches the color of the pixel you select with a button",
1042     "press and is a neighbor.  Whereas filltoborder changes the matte",
1043     "value any neighbor pixel that is not the border color.  Finally",
1044     "reset changes the entire image to the designated matte value.",
1045     "",
1046     "Choose Matte Value and pick Opaque or Transarent.  For other values",
1047     "select the Dialog entry.  Here a dialog appears requesting a matte",
1048     "value.  The value you select is assigned as the opacity value of the",
1049     "selected pixel or pixels.",
1050     "",
1051     "Now, press any button to select a pixel within the image",
1052     "window to change its matte value.",
1053     "",
1054     "If the Magnify widget is mapped, it can be helpful in positioning",
1055     "your pointer within the image (refer to button 2).",
1056     "",
1057     "Matte information is only valid in a DirectClass image.",
1058     "Therefore, any PseudoClass image is promoted to DirectClass",
1059     "(see miff(5)).  Note that matte information for PseudoClass",
1060     "is not retained for colormapped X server visuals (e.g.",
1061     "StaticColor, StaticColor, GrayScale, PseudoColor) unless you",
1062     "immediately save your image to a file (refer to Write).",
1063     "Correct matte editing behavior may require a TrueColor or",
1064     "DirectColor visual or a Standard Colormap.",
1065     (char *) NULL,
1066   },
1067   *ImagePanHelp[] =
1068   {
1069     "When an image exceeds the width or height of the X server",
1070     "screen, display maps a small panning icon.  The rectangle",
1071     "within the panning icon shows the area that is currently",
1072     "displayed in the image window.  To pan about the image,",
1073     "press any button and drag the pointer within the panning",
1074     "icon.  The pan rectangle moves with the pointer and the",
1075     "image window is updated to reflect the location of the",
1076     "rectangle within the panning icon.  When you have selected",
1077     "the area of the image you wish to view, release the button.",
1078     "",
1079     "Use the arrow keys to pan the image one pixel up, down,",
1080     "left, or right within the image window.",
1081     "",
1082     "The panning icon is withdrawn if the image becomes smaller",
1083     "than the dimensions of the X server screen.",
1084     (char *) NULL,
1085   },
1086   *ImagePasteHelp[] =
1087   {
1088     "A small window appears showing the location of the cursor in",
1089     "the image window. You are now in paste mode.  To exit",
1090     "immediately, press Dismiss.  In paste mode, the Command",
1091     "widget has these options:",
1092     "",
1093     "    Operators",
1094     "      over",
1095     "      in",
1096     "      out",
1097     "      atop",
1098     "      xor",
1099     "      plus",
1100     "      minus",
1101     "      add",
1102     "      subtract",
1103     "      difference",
1104     "      replace",
1105     "    Help",
1106     "    Dismiss",
1107     "",
1108     "Choose a composite operation from the Operators sub-menu of",
1109     "the Command widget.  How each operator behaves is described",
1110     "below.  Image window is the image currently displayed on",
1111     "your X server and image is the image obtained with the File",
1112     "Browser widget.",
1113     "",
1114     "Over     The result is the union of the two image shapes,",
1115     "         with image obscuring image window in the region of",
1116     "         overlap.",
1117     "",
1118     "In       The result is simply image cut by the shape of",
1119     "         image window.  None of the image data of image",
1120     "         window is in the result.",
1121     "",
1122     "Out      The resulting image is image with the shape of",
1123     "         image window cut out.",
1124     "",
1125     "Atop     The result is the same shape as image image window,",
1126     "         with image obscuring image window where the image",
1127     "         shapes overlap.  Note this differs from over",
1128     "         because the portion of image outside image window's",
1129     "         shape does not appear in the result.",
1130     "",
1131     "Xor      The result is the image data from both image and",
1132     "         image window that is outside the overlap region.",
1133     "         The overlap region is blank.",
1134     "",
1135     "Plus     The result is just the sum of the image data.",
1136     "         Output values are cropped to QuantumRange (no overflow).",
1137     "         This operation is independent of the matte",
1138     "         channels.",
1139     "",
1140     "Minus    The result of image - image window, with underflow",
1141     "         cropped to zero.",
1142     "",
1143     "Add      The result of image + image window, with overflow",
1144     "         wrapping around (mod 256).",
1145     "",
1146     "Subtract The result of image - image window, with underflow",
1147     "         wrapping around (mod 256).  The add and subtract",
1148     "         operators can be used to perform reversible",
1149     "         transformations.",
1150     "",
1151     "Difference",
1152     "         The result of abs(image - image window).  This",
1153     "         useful for comparing two very similar images.",
1154     "",
1155     "Copy     The resulting image is image window replaced with",
1156     "         image.  Here the matte information is ignored.",
1157     "",
1158     "CopyRed  The red layer of the image window is replace with",
1159     "         the red layer of the image.  The other layers are",
1160     "         untouched.",
1161     "",
1162     "CopyGreen",
1163     "         The green layer of the image window is replace with",
1164     "         the green layer of the image.  The other layers are",
1165     "         untouched.",
1166     "",
1167     "CopyBlue The blue layer of the image window is replace with",
1168     "         the blue layer of the image.  The other layers are",
1169     "         untouched.",
1170     "",
1171     "CopyOpacity",
1172     "         The matte layer of the image window is replace with",
1173     "         the matte layer of the image.  The other layers are",
1174     "         untouched.",
1175     "",
1176     "The image compositor requires a matte, or alpha channel in",
1177     "the image for some operations.  This extra channel usually",
1178     "defines a mask which represents a sort of a cookie-cutter",
1179     "for the image.  This the case when matte is opaque (full",
1180     "coverage) for pixels inside the shape, zero outside, and",
1181     "between 0 and QuantumRange on the boundary.  If image does not",
1182     "have a matte channel, it is initialized with 0 for any pixel",
1183     "matching in color to pixel location (0,0), otherwise QuantumRange.",
1184     "",
1185     "Note that matte information for image window is not retained",
1186     "for colormapped X server visuals (e.g. StaticColor,",
1187     "StaticColor, GrayScale, PseudoColor).  Correct compositing",
1188     "behavior may require a TrueColor or DirectColor visual or a",
1189     "Standard Colormap.",
1190     "",
1191     "Choosing a composite operator is optional.  The default",
1192     "operator is replace.  However, you must choose a location to",
1193     "paste your image and press button 1.  Press and hold the",
1194     "button before releasing and an outline of the image will",
1195     "appear to help you identify your location.",
1196     "",
1197     "The actual colors of the pasted image is saved.  However,",
1198     "the color that appears in image window may be different.",
1199     "For example, on a monochrome screen image window will appear",
1200     "black or white even though your pasted image may have",
1201     "many colors.  If the image is saved to a file it is written",
1202     "with the correct colors.  To assure the correct colors are",
1203     "saved in the final image, any PseudoClass image is promoted",
1204     "to DirectClass (see miff(5)).  To force a PseudoClass image",
1205     "to remain PseudoClass, use -colors.",
1206     (char *) NULL,
1207   },
1208   *ImageROIHelp[] =
1209   {
1210     "In region of interest mode, the Command widget has these",
1211     "options:",
1212     "",
1213     "    Help",
1214     "    Dismiss",
1215     "",
1216     "To define a region of interest, press button 1 and drag.",
1217     "The region of interest is defined by a highlighted rectangle",
1218     "that expands or contracts as it follows the pointer.  Once",
1219     "you are satisfied with the region of interest, release the",
1220     "button.  You are now in apply mode.  In apply mode the",
1221     "Command widget has these options:",
1222     "",
1223     "      File",
1224     "        Save...",
1225     "        Print...",
1226     "      Edit",
1227     "        Undo",
1228     "        Redo",
1229     "      Transform",
1230     "        Flop",
1231     "        Flip",
1232     "        Rotate Right",
1233     "        Rotate Left",
1234     "      Enhance",
1235     "        Hue...",
1236     "        Saturation...",
1237     "        Brightness...",
1238     "        Gamma...",
1239     "        Spiff",
1240     "        Dull",
1241     "        Contrast Stretch",
1242     "        Sigmoidal Contrast...",
1243     "        Normalize",
1244     "        Equalize",
1245     "        Negate",
1246     "        Grayscale",
1247     "        Map...",
1248     "        Quantize...",
1249     "      Effects",
1250     "        Despeckle",
1251     "        Emboss",
1252     "        Reduce Noise",
1253     "        Sharpen...",
1254     "        Blur...",
1255     "        Threshold...",
1256     "        Edge Detect...",
1257     "        Spread...",
1258     "        Shade...",
1259     "        Raise...",
1260     "        Segment...",
1261     "      F/X",
1262     "        Solarize...",
1263     "        Sepia Tone...",
1264     "        Swirl...",
1265     "        Implode...",
1266     "        Vignette...",
1267     "        Wave...",
1268     "        Oil Painting...",
1269     "        Charcoal Drawing...",
1270     "      Miscellany",
1271     "        Image Info",
1272     "        Zoom Image",
1273     "        Show Preview...",
1274     "        Show Histogram",
1275     "        Show Matte",
1276     "      Help",
1277     "      Dismiss",
1278     "",
1279     "You can make adjustments to the region of interest by moving",
1280     "the pointer to one of the rectangle corners, pressing a",
1281     "button, and dragging.  Finally, choose an image processing",
1282     "technique from the Command widget.  You can choose more than",
1283     "one image processing technique to apply to an area.",
1284     "Alternatively, you can move the region of interest before",
1285     "applying another image processing technique.  To exit, press",
1286     "Dismiss.",
1287     (char *) NULL,
1288   },
1289   *ImageRotateHelp[] =
1290   {
1291     "In rotate mode, the Command widget has these options:",
1292     "",
1293     "    Pixel Color",
1294     "      black",
1295     "      blue",
1296     "      cyan",
1297     "      green",
1298     "      gray",
1299     "      red",
1300     "      magenta",
1301     "      yellow",
1302     "      white",
1303     "      Browser...",
1304     "    Direction",
1305     "      horizontal",
1306     "      vertical",
1307     "    Help",
1308     "    Dismiss",
1309     "",
1310     "Choose a background color from the Pixel Color sub-menu.",
1311     "Additional background colors can be specified with the color",
1312     "browser.  You can change the menu colors by setting the X",
1313     "resources pen1 through pen9.",
1314     "",
1315     "If you choose the color browser and press Grab, you can",
1316     "select the background color by moving the pointer to the",
1317     "desired color on the screen and press any button.",
1318     "",
1319     "Choose a point in the image window and press this button and",
1320     "hold.  Next, move the pointer to another location in the",
1321     "image.  As you move a line connects the initial location and",
1322     "the pointer.  When you release the button, the degree of",
1323     "image rotation is determined by the slope of the line you",
1324     "just drew.  The slope is relative to the direction you",
1325     "choose from the Direction sub-menu of the Command widget.",
1326     "",
1327     "To cancel the image rotation, move the pointer back to the",
1328     "starting point of the line and release the button.",
1329     (char *) NULL,
1330   };
1331 \f
1332 /*
1333   Enumeration declarations.
1334 */
1335 typedef enum
1336 {
1337   CopyMode,
1338   CropMode,
1339   CutMode
1340 } ClipboardMode;
1341
1342 typedef enum
1343 {
1344   OpenCommand,
1345   NextCommand,
1346   FormerCommand,
1347   SelectCommand,
1348   SaveCommand,
1349   PrintCommand,
1350   DeleteCommand,
1351   NewCommand,
1352   VisualDirectoryCommand,
1353   QuitCommand,
1354   UndoCommand,
1355   RedoCommand,
1356   CutCommand,
1357   CopyCommand,
1358   PasteCommand,
1359   HalfSizeCommand,
1360   OriginalSizeCommand,
1361   DoubleSizeCommand,
1362   ResizeCommand,
1363   ApplyCommand,
1364   RefreshCommand,
1365   RestoreCommand,
1366   CropCommand,
1367   ChopCommand,
1368   FlopCommand,
1369   FlipCommand,
1370   RotateRightCommand,
1371   RotateLeftCommand,
1372   RotateCommand,
1373   ShearCommand,
1374   RollCommand,
1375   TrimCommand,
1376   HueCommand,
1377   SaturationCommand,
1378   BrightnessCommand,
1379   GammaCommand,
1380   SpiffCommand,
1381   DullCommand,
1382   ContrastStretchCommand,
1383   SigmoidalContrastCommand,
1384   NormalizeCommand,
1385   EqualizeCommand,
1386   NegateCommand,
1387   GrayscaleCommand,
1388   MapCommand,
1389   QuantizeCommand,
1390   DespeckleCommand,
1391   EmbossCommand,
1392   ReduceNoiseCommand,
1393   AddNoiseCommand,
1394   SharpenCommand,
1395   BlurCommand,
1396   ThresholdCommand,
1397   EdgeDetectCommand,
1398   SpreadCommand,
1399   ShadeCommand,
1400   RaiseCommand,
1401   SegmentCommand,
1402   SolarizeCommand,
1403   SepiaToneCommand,
1404   SwirlCommand,
1405   ImplodeCommand,
1406   VignetteCommand,
1407   WaveCommand,
1408   OilPaintCommand,
1409   CharcoalDrawCommand,
1410   AnnotateCommand,
1411   DrawCommand,
1412   ColorCommand,
1413   MatteCommand,
1414   CompositeCommand,
1415   AddBorderCommand,
1416   AddFrameCommand,
1417   CommentCommand,
1418   LaunchCommand,
1419   RegionofInterestCommand,
1420   ROIHelpCommand,
1421   ROIDismissCommand,
1422   InfoCommand,
1423   ZoomCommand,
1424   ShowPreviewCommand,
1425   ShowHistogramCommand,
1426   ShowMatteCommand,
1427   BackgroundCommand,
1428   SlideShowCommand,
1429   PreferencesCommand,
1430   HelpCommand,
1431   BrowseDocumentationCommand,
1432   VersionCommand,
1433   SaveToUndoBufferCommand,
1434   FreeBuffersCommand,
1435   NullCommand
1436 } CommandType;
1437
1438 typedef enum
1439 {
1440   AnnotateNameCommand,
1441   AnnotateFontColorCommand,
1442   AnnotateBackgroundColorCommand,
1443   AnnotateRotateCommand,
1444   AnnotateHelpCommand,
1445   AnnotateDismissCommand,
1446   TextHelpCommand,
1447   TextApplyCommand,
1448   ChopDirectionCommand,
1449   ChopHelpCommand,
1450   ChopDismissCommand,
1451   HorizontalChopCommand,
1452   VerticalChopCommand,
1453   ColorEditMethodCommand,
1454   ColorEditColorCommand,
1455   ColorEditBorderCommand,
1456   ColorEditFuzzCommand,
1457   ColorEditUndoCommand,
1458   ColorEditHelpCommand,
1459   ColorEditDismissCommand,
1460   CompositeOperatorsCommand,
1461   CompositeDissolveCommand,
1462   CompositeDisplaceCommand,
1463   CompositeHelpCommand,
1464   CompositeDismissCommand,
1465   CropHelpCommand,
1466   CropDismissCommand,
1467   RectifyCopyCommand,
1468   RectifyHelpCommand,
1469   RectifyDismissCommand,
1470   DrawElementCommand,
1471   DrawColorCommand,
1472   DrawStippleCommand,
1473   DrawWidthCommand,
1474   DrawUndoCommand,
1475   DrawHelpCommand,
1476   DrawDismissCommand,
1477   MatteEditMethod,
1478   MatteEditBorderCommand,
1479   MatteEditFuzzCommand,
1480   MatteEditValueCommand,
1481   MatteEditUndoCommand,
1482   MatteEditHelpCommand,
1483   MatteEditDismissCommand,
1484   PasteOperatorsCommand,
1485   PasteHelpCommand,
1486   PasteDismissCommand,
1487   RotateColorCommand,
1488   RotateDirectionCommand,
1489   RotateCropCommand,
1490   RotateSharpenCommand,
1491   RotateHelpCommand,
1492   RotateDismissCommand,
1493   HorizontalRotateCommand,
1494   VerticalRotateCommand,
1495   TileLoadCommand,
1496   TileNextCommand,
1497   TileFormerCommand,
1498   TileDeleteCommand,
1499   TileUpdateCommand
1500 } ModeType;
1501 \f
1502 /*
1503   Stipples.
1504 */
1505 #define BricksWidth  20
1506 #define BricksHeight  20
1507 #define DiagonalWidth  16
1508 #define DiagonalHeight  16
1509 #define HighlightWidth  8
1510 #define HighlightHeight  8
1511 #define OpaqueWidth  8
1512 #define OpaqueHeight  8
1513 #define ScalesWidth  16
1514 #define ScalesHeight  16
1515 #define ShadowWidth  8
1516 #define ShadowHeight  8
1517 #define VerticalWidth  16
1518 #define VerticalHeight  16
1519 #define WavyWidth  16
1520 #define WavyHeight  16
1521 \f
1522 /*
1523   Constant declaration.
1524 */
1525 static const int
1526   RoiDelta = 8;
1527
1528 static const unsigned char
1529   BricksBitmap[] =
1530   {
1531     0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00,
1532     0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01,
1533     0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00,
1534     0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f,
1535     0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01
1536   },
1537   DiagonalBitmap[] =
1538   {
1539     0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88,
1540     0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22,
1541     0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22
1542   },
1543   ScalesBitmap[] =
1544   {
1545     0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3, 0x80, 0x80, 0x80, 0x80,
1546     0x41, 0x41, 0x3e, 0x3e, 0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3,
1547     0x80, 0x80, 0x80, 0x80, 0x41, 0x41, 0x3e, 0x3e
1548   },
1549   VerticalBitmap[] =
1550   {
1551     0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1552     0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1553     0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11
1554   },
1555   WavyBitmap[] =
1556   {
1557     0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfb, 0xff,
1558     0xe7, 0xff, 0x1f, 0xff, 0xff, 0xf8, 0xff, 0xe7, 0xff, 0xdf, 0xff, 0xbf,
1559     0xff, 0xbf, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f
1560   };
1561 \f
1562 /*
1563   Function prototypes.
1564 */
1565 static CommandType
1566   XImageWindowCommand(Display *,XResourceInfo *,XWindows *,
1567     const MagickStatusType,KeySym,Image **,ExceptionInfo *);
1568
1569 static Image
1570   *XMagickCommand(Display *,XResourceInfo *,XWindows *,const CommandType,
1571     Image **,ExceptionInfo *),
1572   *XOpenImage(Display *,XResourceInfo *,XWindows *,const MagickBooleanType),
1573   *XTileImage(Display *,XResourceInfo *,XWindows *,Image *,XEvent *,
1574     ExceptionInfo *),
1575   *XVisualDirectoryImage(Display *,XResourceInfo *,XWindows *,
1576     ExceptionInfo *);
1577
1578 static MagickBooleanType
1579   XAnnotateEditImage(Display *,XResourceInfo *,XWindows *,Image *,
1580     ExceptionInfo *),
1581   XBackgroundImage(Display *,XResourceInfo *,XWindows *,Image **,
1582     ExceptionInfo *),
1583   XChopImage(Display *,XResourceInfo *,XWindows *,Image **,
1584     ExceptionInfo *),
1585   XCropImage(Display *,XResourceInfo *,XWindows *,Image *,const ClipboardMode,
1586     ExceptionInfo *),
1587   XColorEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1588     ExceptionInfo *),
1589   XCompositeImage(Display *,XResourceInfo *,XWindows *,Image *,
1590     ExceptionInfo *),
1591   XConfigureImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1592   XDrawEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1593     ExceptionInfo *),
1594   XMatteEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1595     ExceptionInfo *),
1596   XPasteImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1597   XPrintImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1598   XRotateImage(Display *,XResourceInfo *,XWindows *,double,Image **,
1599     ExceptionInfo *),
1600   XROIImage(Display *,XResourceInfo *,XWindows *,Image **,ExceptionInfo *),
1601   XSaveImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1602   XTrimImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *);
1603
1604 static void
1605   XDrawPanRectangle(Display *,XWindows *),
1606   XImageCache(Display *,XResourceInfo *,XWindows *,const CommandType,Image **,
1607     ExceptionInfo *),
1608   XMagnifyImage(Display *,XWindows *,XEvent *),
1609   XMakePanImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1610   XPanImage(Display *,XWindows *,XEvent *),
1611   XMagnifyWindowCommand(Display *,XWindows *,const MagickStatusType,
1612     const KeySym),
1613   XSetCropGeometry(Display *,XWindows *,RectangleInfo *,Image *),
1614   XScreenEvent(Display *,XWindows *,XEvent *),
1615   XTranslateImage(Display *,XWindows *,Image *,const KeySym);
1616 \f
1617 /*
1618 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1619 %                                                                             %
1620 %                                                                             %
1621 %                                                                             %
1622 %   D i s p l a y I m a g e s                                                 %
1623 %                                                                             %
1624 %                                                                             %
1625 %                                                                             %
1626 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1627 %
1628 %  DisplayImages() displays an image sequence to any X window screen.  It
1629 %  returns a value other than 0 if successful.  Check the exception member
1630 %  of image to determine the reason for any failure.
1631 %
1632 %  The format of the DisplayImages method is:
1633 %
1634 %      MagickBooleanType DisplayImages(const ImageInfo *image_info,
1635 %        Image *images,ExceptionInfo *exception)
1636 %
1637 %  A description of each parameter follows:
1638 %
1639 %    o image_info: the image info.
1640 %
1641 %    o image: the image.
1642 %
1643 %    o exception: return any errors or warnings in this structure.
1644 %
1645 */
1646 MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
1647   Image *images,ExceptionInfo *exception)
1648 {
1649   char
1650     *argv[1];
1651
1652   Display
1653     *display;
1654
1655   Image
1656     *image;
1657
1658   register ssize_t
1659     i;
1660
1661   size_t
1662     state;
1663
1664   XrmDatabase
1665     resource_database;
1666
1667   XResourceInfo
1668     resource_info;
1669
1670   assert(image_info != (const ImageInfo *) NULL);
1671   assert(image_info->signature == MagickSignature);
1672   assert(images != (Image *) NULL);
1673   assert(images->signature == MagickSignature);
1674   if (images->debug != MagickFalse)
1675     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
1676   display=XOpenDisplay(image_info->server_name);
1677   if (display == (Display *) NULL)
1678     {
1679       (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
1680         "UnableToOpenXServer","`%s'",XDisplayName(image_info->server_name));
1681       return(MagickFalse);
1682     }
1683   if (exception->severity != UndefinedException)
1684     CatchException(exception);
1685   (void) XSetErrorHandler(XError);
1686   resource_database=XGetResourceDatabase(display,GetClientName());
1687   (void) ResetMagickMemory(&resource_info,0,sizeof(resource_info));
1688   XGetResourceInfo(image_info,resource_database,GetClientName(),&resource_info);
1689   if (image_info->page != (char *) NULL)
1690     resource_info.image_geometry=AcquireString(image_info->page);
1691   resource_info.immutable=MagickTrue;
1692   argv[0]=AcquireString(GetClientName());
1693   state=DefaultState;
1694   for (i=0; (state & ExitState) == 0; i++)
1695   {
1696     if ((images->iterations != 0) && (i >= (ssize_t) images->iterations))
1697       break;
1698     image=GetImageFromList(images,i % GetImageListLength(images));
1699     (void) XDisplayImage(display,&resource_info,argv,1,&image,&state,exception);
1700   }
1701   SetErrorHandler((ErrorHandler) NULL);
1702   SetWarningHandler((WarningHandler) NULL);
1703   argv[0]=DestroyString(argv[0]);
1704   (void) XCloseDisplay(display);
1705   XDestroyResourceInfo(&resource_info);
1706   if (exception->severity != UndefinedException)
1707     return(MagickFalse);
1708   return(MagickTrue);
1709 }
1710 \f
1711 /*
1712 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1713 %                                                                             %
1714 %                                                                             %
1715 %                                                                             %
1716 %   R e m o t e D i s p l a y C o m m a n d                                   %
1717 %                                                                             %
1718 %                                                                             %
1719 %                                                                             %
1720 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1721 %
1722 %  RemoteDisplayCommand() encourages a remote display program to display the
1723 %  specified image filename.
1724 %
1725 %  The format of the RemoteDisplayCommand method is:
1726 %
1727 %      MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1728 %        const char *window,const char *filename,ExceptionInfo *exception)
1729 %
1730 %  A description of each parameter follows:
1731 %
1732 %    o image_info: the image info.
1733 %
1734 %    o window: Specifies the name or id of an X window.
1735 %
1736 %    o filename: the name of the image filename to display.
1737 %
1738 %    o exception: return any errors or warnings in this structure.
1739 %
1740 */
1741 MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1742   const char *window,const char *filename,ExceptionInfo *exception)
1743 {
1744   Display
1745     *display;
1746
1747   MagickStatusType
1748     status;
1749
1750   assert(image_info != (const ImageInfo *) NULL);
1751   assert(image_info->signature == MagickSignature);
1752   assert(filename != (char *) NULL);
1753   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
1754   display=XOpenDisplay(image_info->server_name);
1755   if (display == (Display *) NULL)
1756     {
1757       (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
1758         "UnableToOpenXServer","`%s'",XDisplayName(image_info->server_name));
1759       return(MagickFalse);
1760     }
1761   (void) XSetErrorHandler(XError);
1762   status=XRemoteCommand(display,window,filename);
1763   (void) XCloseDisplay(display);
1764   return(status != 0 ? MagickTrue : MagickFalse);
1765 }
1766 \f
1767 /*
1768 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1769 %                                                                             %
1770 %                                                                             %
1771 %                                                                             %
1772 +   X A n n o t a t e E d i t I m a g e                                       %
1773 %                                                                             %
1774 %                                                                             %
1775 %                                                                             %
1776 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1777 %
1778 %  XAnnotateEditImage() annotates the image with text.
1779 %
1780 %  The format of the XAnnotateEditImage method is:
1781 %
1782 %      MagickBooleanType XAnnotateEditImage(Display *display,
1783 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
1784 %        ExceptionInfo *exception)
1785 %
1786 %  A description of each parameter follows:
1787 %
1788 %    o display: Specifies a connection to an X server;  returned from
1789 %      XOpenDisplay.
1790 %
1791 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
1792 %
1793 %    o windows: Specifies a pointer to a XWindows structure.
1794 %
1795 %    o image: the image; returned from ReadImage.
1796 %
1797 */
1798
1799 static inline ssize_t MagickMax(const ssize_t x,const ssize_t y)
1800 {
1801   if (x > y)
1802     return(x);
1803   return(y);
1804 }
1805
1806 static inline ssize_t MagickMin(const ssize_t x,const ssize_t y)
1807 {
1808   if (x < y)
1809     return(x);
1810   return(y);
1811 }
1812
1813 static MagickBooleanType XAnnotateEditImage(Display *display,
1814   XResourceInfo *resource_info,XWindows *windows,Image *image,
1815   ExceptionInfo *exception)
1816 {
1817   static const char
1818     *AnnotateMenu[] =
1819     {
1820       "Font Name",
1821       "Font Color",
1822       "Box Color",
1823       "Rotate Text",
1824       "Help",
1825       "Dismiss",
1826       (char *) NULL
1827     },
1828     *TextMenu[] =
1829     {
1830       "Help",
1831       "Apply",
1832       (char *) NULL
1833     };
1834
1835   static const ModeType
1836     AnnotateCommands[] =
1837     {
1838       AnnotateNameCommand,
1839       AnnotateFontColorCommand,
1840       AnnotateBackgroundColorCommand,
1841       AnnotateRotateCommand,
1842       AnnotateHelpCommand,
1843       AnnotateDismissCommand
1844     },
1845     TextCommands[] =
1846     {
1847       TextHelpCommand,
1848       TextApplyCommand
1849     };
1850
1851   static MagickBooleanType
1852     transparent_box = MagickTrue,
1853     transparent_pen = MagickFalse;
1854
1855   static MagickRealType
1856     degrees = 0.0;
1857
1858   static unsigned int
1859     box_id = MaxNumberPens-2,
1860     font_id = 0,
1861     pen_id = 0;
1862
1863   char
1864     command[MaxTextExtent],
1865     text[MaxTextExtent];
1866
1867   const char
1868     *ColorMenu[MaxNumberPens+1];
1869
1870   Cursor
1871     cursor;
1872
1873   GC
1874     annotate_context;
1875
1876   int
1877     id,
1878     pen_number,
1879     status,
1880     x,
1881     y;
1882
1883   KeySym
1884     key_symbol;
1885
1886   register char
1887     *p;
1888
1889   register ssize_t
1890     i;
1891
1892   unsigned int
1893     height,
1894     width;
1895
1896   size_t
1897     state;
1898
1899   XAnnotateInfo
1900     *annotate_info,
1901     *previous_info;
1902
1903   XColor
1904     color;
1905
1906   XFontStruct
1907     *font_info;
1908
1909   XEvent
1910     event,
1911     text_event;
1912
1913   /*
1914     Map Command widget.
1915   */
1916   (void) CloneString(&windows->command.name,"Annotate");
1917   windows->command.data=4;
1918   (void) XCommandWidget(display,windows,AnnotateMenu,(XEvent *) NULL);
1919   (void) XMapRaised(display,windows->command.id);
1920   XClientMessage(display,windows->image.id,windows->im_protocols,
1921     windows->im_update_widget,CurrentTime);
1922   /*
1923     Track pointer until button 1 is pressed.
1924   */
1925   XQueryPosition(display,windows->image.id,&x,&y);
1926   (void) XSelectInput(display,windows->image.id,
1927     windows->image.attributes.event_mask | PointerMotionMask);
1928   cursor=XCreateFontCursor(display,XC_left_side);
1929   (void) XCheckDefineCursor(display,windows->image.id,cursor);
1930   state=DefaultState;
1931   do
1932   {
1933     if (windows->info.mapped != MagickFalse)
1934       {
1935         /*
1936           Display pointer position.
1937         */
1938         (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
1939           x+windows->image.x,y+windows->image.y);
1940         XInfoWidget(display,windows,text);
1941       }
1942     /*
1943       Wait for next event.
1944     */
1945     XScreenEvent(display,windows,&event);
1946     if (event.xany.window == windows->command.id)
1947       {
1948         /*
1949           Select a command from the Command widget.
1950         */
1951         id=XCommandWidget(display,windows,AnnotateMenu,&event);
1952         (void) XCheckDefineCursor(display,windows->image.id,cursor);
1953         if (id < 0)
1954           continue;
1955         switch (AnnotateCommands[id])
1956         {
1957           case AnnotateNameCommand:
1958           {
1959             const char
1960               *FontMenu[MaxNumberFonts];
1961
1962             int
1963               font_number;
1964
1965             /*
1966               Initialize menu selections.
1967             */
1968             for (i=0; i < MaxNumberFonts; i++)
1969               FontMenu[i]=resource_info->font_name[i];
1970             FontMenu[MaxNumberFonts-2]="Browser...";
1971             FontMenu[MaxNumberFonts-1]=(const char *) NULL;
1972             /*
1973               Select a font name from the pop-up menu.
1974             */
1975             font_number=XMenuWidget(display,windows,AnnotateMenu[id],
1976               (const char **) FontMenu,command);
1977             if (font_number < 0)
1978               break;
1979             if (font_number == (MaxNumberFonts-2))
1980               {
1981                 static char
1982                   font_name[MaxTextExtent] = "fixed";
1983
1984                 /*
1985                   Select a font name from a browser.
1986                 */
1987                 resource_info->font_name[font_number]=font_name;
1988                 XFontBrowserWidget(display,windows,"Select",font_name);
1989                 if (*font_name == '\0')
1990                   break;
1991               }
1992             /*
1993               Initialize font info.
1994             */
1995             font_info=XLoadQueryFont(display,resource_info->font_name[
1996               font_number]);
1997             if (font_info == (XFontStruct *) NULL)
1998               {
1999                 XNoticeWidget(display,windows,"Unable to load font:",
2000                   resource_info->font_name[font_number]);
2001                 break;
2002               }
2003             font_id=(unsigned int) font_number;
2004             (void) XFreeFont(display,font_info);
2005             break;
2006           }
2007           case AnnotateFontColorCommand:
2008           {
2009             /*
2010               Initialize menu selections.
2011             */
2012             for (i=0; i < (int) (MaxNumberPens-2); i++)
2013               ColorMenu[i]=resource_info->pen_colors[i];
2014             ColorMenu[MaxNumberPens-2]="transparent";
2015             ColorMenu[MaxNumberPens-1]="Browser...";
2016             ColorMenu[MaxNumberPens]=(const char *) NULL;
2017             /*
2018               Select a pen color from the pop-up menu.
2019             */
2020             pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
2021               (const char **) ColorMenu,command);
2022             if (pen_number < 0)
2023               break;
2024             transparent_pen=pen_number == (MaxNumberPens-2) ? MagickTrue :
2025               MagickFalse;
2026             if (transparent_pen != MagickFalse)
2027               break;
2028             if (pen_number == (MaxNumberPens-1))
2029               {
2030                 static char
2031                   color_name[MaxTextExtent] = "gray";
2032
2033                 /*
2034                   Select a pen color from a dialog.
2035                 */
2036                 resource_info->pen_colors[pen_number]=color_name;
2037                 XColorBrowserWidget(display,windows,"Select",color_name);
2038                 if (*color_name == '\0')
2039                   break;
2040               }
2041             /*
2042               Set pen color.
2043             */
2044             (void) XParseColor(display,windows->map_info->colormap,
2045               resource_info->pen_colors[pen_number],&color);
2046             XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
2047               (unsigned int) MaxColors,&color);
2048             windows->pixel_info->pen_colors[pen_number]=color;
2049             pen_id=(unsigned int) pen_number;
2050             break;
2051           }
2052           case AnnotateBackgroundColorCommand:
2053           {
2054             /*
2055               Initialize menu selections.
2056             */
2057             for (i=0; i < (int) (MaxNumberPens-2); i++)
2058               ColorMenu[i]=resource_info->pen_colors[i];
2059             ColorMenu[MaxNumberPens-2]="transparent";
2060             ColorMenu[MaxNumberPens-1]="Browser...";
2061             ColorMenu[MaxNumberPens]=(const char *) NULL;
2062             /*
2063               Select a pen color from the pop-up menu.
2064             */
2065             pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
2066               (const char **) ColorMenu,command);
2067             if (pen_number < 0)
2068               break;
2069             transparent_box=pen_number == (MaxNumberPens-2) ? MagickTrue :
2070               MagickFalse;
2071             if (transparent_box != MagickFalse)
2072               break;
2073             if (pen_number == (MaxNumberPens-1))
2074               {
2075                 static char
2076                   color_name[MaxTextExtent] = "gray";
2077
2078                 /*
2079                   Select a pen color from a dialog.
2080                 */
2081                 resource_info->pen_colors[pen_number]=color_name;
2082                 XColorBrowserWidget(display,windows,"Select",color_name);
2083                 if (*color_name == '\0')
2084                   break;
2085               }
2086             /*
2087               Set pen color.
2088             */
2089             (void) XParseColor(display,windows->map_info->colormap,
2090               resource_info->pen_colors[pen_number],&color);
2091             XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
2092               (unsigned int) MaxColors,&color);
2093             windows->pixel_info->pen_colors[pen_number]=color;
2094             box_id=(unsigned int) pen_number;
2095             break;
2096           }
2097           case AnnotateRotateCommand:
2098           {
2099             int
2100               entry;
2101
2102             static char
2103               angle[MaxTextExtent] = "30.0";
2104
2105             static const char
2106               *RotateMenu[] =
2107               {
2108                 "-90",
2109                 "-45",
2110                 "-30",
2111                 "0",
2112                 "30",
2113                 "45",
2114                 "90",
2115                 "180",
2116                 "Dialog...",
2117                 (char *) NULL,
2118               };
2119
2120             /*
2121               Select a command from the pop-up menu.
2122             */
2123             entry=XMenuWidget(display,windows,AnnotateMenu[id],RotateMenu,
2124               command);
2125             if (entry < 0)
2126               break;
2127             if (entry != 8)
2128               {
2129                 degrees=InterpretLocaleValue(RotateMenu[entry],(char **) NULL);
2130                 break;
2131               }
2132             (void) XDialogWidget(display,windows,"OK","Enter rotation angle:",
2133               angle);
2134             if (*angle == '\0')
2135               break;
2136             degrees=InterpretLocaleValue(angle,(char **) NULL);
2137             break;
2138           }
2139           case AnnotateHelpCommand:
2140           {
2141             XTextViewWidget(display,resource_info,windows,MagickFalse,
2142               "Help Viewer - Image Annotation",ImageAnnotateHelp);
2143             break;
2144           }
2145           case AnnotateDismissCommand:
2146           {
2147             /*
2148               Prematurely exit.
2149             */
2150             state|=EscapeState;
2151             state|=ExitState;
2152             break;
2153           }
2154           default:
2155             break;
2156         }
2157         continue;
2158       }
2159     switch (event.type)
2160     {
2161       case ButtonPress:
2162       {
2163         if (event.xbutton.button != Button1)
2164           break;
2165         if (event.xbutton.window != windows->image.id)
2166           break;
2167         /*
2168           Change to text entering mode.
2169         */
2170         x=event.xbutton.x;
2171         y=event.xbutton.y;
2172         state|=ExitState;
2173         break;
2174       }
2175       case ButtonRelease:
2176         break;
2177       case Expose:
2178         break;
2179       case KeyPress:
2180       {
2181         if (event.xkey.window != windows->image.id)
2182           break;
2183         /*
2184           Respond to a user key press.
2185         */
2186         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2187           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2188         switch ((int) key_symbol)
2189         {
2190           case XK_Escape:
2191           case XK_F20:
2192           {
2193             /*
2194               Prematurely exit.
2195             */
2196             state|=EscapeState;
2197             state|=ExitState;
2198             break;
2199           }
2200           case XK_F1:
2201           case XK_Help:
2202           {
2203             XTextViewWidget(display,resource_info,windows,MagickFalse,
2204               "Help Viewer - Image Annotation",ImageAnnotateHelp);
2205             break;
2206           }
2207           default:
2208           {
2209             (void) XBell(display,0);
2210             break;
2211           }
2212         }
2213         break;
2214       }
2215       case MotionNotify:
2216       {
2217         /*
2218           Map and unmap Info widget as cursor crosses its boundaries.
2219         */
2220         x=event.xmotion.x;
2221         y=event.xmotion.y;
2222         if (windows->info.mapped != MagickFalse)
2223           {
2224             if ((x < (int) (windows->info.x+windows->info.width)) &&
2225                 (y < (int) (windows->info.y+windows->info.height)))
2226               (void) XWithdrawWindow(display,windows->info.id,
2227                 windows->info.screen);
2228           }
2229         else
2230           if ((x > (int) (windows->info.x+windows->info.width)) ||
2231               (y > (int) (windows->info.y+windows->info.height)))
2232             (void) XMapWindow(display,windows->info.id);
2233         break;
2234       }
2235       default:
2236         break;
2237     }
2238   } while ((state & ExitState) == 0);
2239   (void) XSelectInput(display,windows->image.id,
2240     windows->image.attributes.event_mask);
2241   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
2242   if ((state & EscapeState) != 0)
2243     return(MagickTrue);
2244   /*
2245     Set font info and check boundary conditions.
2246   */
2247   font_info=XLoadQueryFont(display,resource_info->font_name[font_id]);
2248   if (font_info == (XFontStruct *) NULL)
2249     {
2250       XNoticeWidget(display,windows,"Unable to load font:",
2251         resource_info->font_name[font_id]);
2252       font_info=windows->font_info;
2253     }
2254   if ((x+font_info->max_bounds.width) >= (int) windows->image.width)
2255     x=(int) windows->image.width-font_info->max_bounds.width;
2256   if (y < (int) (font_info->ascent+font_info->descent))
2257     y=(int) font_info->ascent+font_info->descent;
2258   if (((int) font_info->max_bounds.width > (int) windows->image.width) ||
2259       ((font_info->ascent+font_info->descent) >= (int) windows->image.height))
2260     return(MagickFalse);
2261   /*
2262     Initialize annotate structure.
2263   */
2264   annotate_info=(XAnnotateInfo *) AcquireMagickMemory(sizeof(*annotate_info));
2265   if (annotate_info == (XAnnotateInfo *) NULL)
2266     return(MagickFalse);
2267   XGetAnnotateInfo(annotate_info);
2268   annotate_info->x=x;
2269   annotate_info->y=y;
2270   if ((transparent_box == MagickFalse) && (transparent_pen == MagickFalse))
2271     annotate_info->stencil=OpaqueStencil;
2272   else
2273     if (transparent_box == MagickFalse)
2274       annotate_info->stencil=BackgroundStencil;
2275     else
2276       annotate_info->stencil=ForegroundStencil;
2277   annotate_info->height=(unsigned int) font_info->ascent+font_info->descent;
2278   annotate_info->degrees=degrees;
2279   annotate_info->font_info=font_info;
2280   annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2281     windows->image.width/MagickMax((ssize_t) font_info->min_bounds.width,1)+2UL,
2282     sizeof(*annotate_info->text));
2283   if (annotate_info->text == (char *) NULL)
2284     return(MagickFalse);
2285   /*
2286     Create cursor and set graphic context.
2287   */
2288   cursor=XCreateFontCursor(display,XC_pencil);
2289   (void) XCheckDefineCursor(display,windows->image.id,cursor);
2290   annotate_context=windows->image.annotate_context;
2291   (void) XSetFont(display,annotate_context,font_info->fid);
2292   (void) XSetBackground(display,annotate_context,
2293     windows->pixel_info->pen_colors[box_id].pixel);
2294   (void) XSetForeground(display,annotate_context,
2295     windows->pixel_info->pen_colors[pen_id].pixel);
2296   /*
2297     Begin annotating the image with text.
2298   */
2299   (void) CloneString(&windows->command.name,"Text");
2300   windows->command.data=0;
2301   (void) XCommandWidget(display,windows,TextMenu,(XEvent *) NULL);
2302   state=DefaultState;
2303   (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2304   text_event.xexpose.width=(int) font_info->max_bounds.width;
2305   text_event.xexpose.height=font_info->max_bounds.ascent+
2306     font_info->max_bounds.descent;
2307   p=annotate_info->text;
2308   do
2309   {
2310     /*
2311       Display text cursor.
2312     */
2313     *p='\0';
2314     (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2315     /*
2316       Wait for next event.
2317     */
2318     XScreenEvent(display,windows,&event);
2319     if (event.xany.window == windows->command.id)
2320       {
2321         /*
2322           Select a command from the Command widget.
2323         */
2324         (void) XSetBackground(display,annotate_context,
2325           windows->pixel_info->background_color.pixel);
2326         (void) XSetForeground(display,annotate_context,
2327           windows->pixel_info->foreground_color.pixel);
2328         id=XCommandWidget(display,windows,AnnotateMenu,&event);
2329         (void) XSetBackground(display,annotate_context,
2330           windows->pixel_info->pen_colors[box_id].pixel);
2331         (void) XSetForeground(display,annotate_context,
2332           windows->pixel_info->pen_colors[pen_id].pixel);
2333         if (id < 0)
2334           continue;
2335         switch (TextCommands[id])
2336         {
2337           case TextHelpCommand:
2338           {
2339             XTextViewWidget(display,resource_info,windows,MagickFalse,
2340               "Help Viewer - Image Annotation",ImageAnnotateHelp);
2341             (void) XCheckDefineCursor(display,windows->image.id,cursor);
2342             break;
2343           }
2344           case TextApplyCommand:
2345           {
2346             /*
2347               Finished annotating.
2348             */
2349             annotate_info->width=(unsigned int) XTextWidth(font_info,
2350               annotate_info->text,(int) strlen(annotate_info->text));
2351             XRefreshWindow(display,&windows->image,&text_event);
2352             state|=ExitState;
2353             break;
2354           }
2355           default:
2356             break;
2357         }
2358         continue;
2359       }
2360     /*
2361       Erase text cursor.
2362     */
2363     text_event.xexpose.x=x;
2364     text_event.xexpose.y=y-font_info->max_bounds.ascent;
2365     (void) XClearArea(display,windows->image.id,x,text_event.xexpose.y,
2366       (unsigned int) text_event.xexpose.width,(unsigned int)
2367       text_event.xexpose.height,MagickFalse);
2368     XRefreshWindow(display,&windows->image,&text_event);
2369     switch (event.type)
2370     {
2371       case ButtonPress:
2372       {
2373         if (event.xbutton.window != windows->image.id)
2374           break;
2375         if (event.xbutton.button == Button2)
2376           {
2377             /*
2378               Request primary selection.
2379             */
2380             (void) XConvertSelection(display,XA_PRIMARY,XA_STRING,XA_STRING,
2381               windows->image.id,CurrentTime);
2382             break;
2383           }
2384         break;
2385       }
2386       case Expose:
2387       {
2388         if (event.xexpose.count == 0)
2389           {
2390             XAnnotateInfo
2391               *text_info;
2392
2393             /*
2394               Refresh Image window.
2395             */
2396             XRefreshWindow(display,&windows->image,(XEvent *) NULL);
2397             text_info=annotate_info;
2398             while (text_info != (XAnnotateInfo *) NULL)
2399             {
2400               if (annotate_info->stencil == ForegroundStencil)
2401                 (void) XDrawString(display,windows->image.id,annotate_context,
2402                   text_info->x,text_info->y,text_info->text,
2403                   (int) strlen(text_info->text));
2404               else
2405                 (void) XDrawImageString(display,windows->image.id,
2406                   annotate_context,text_info->x,text_info->y,text_info->text,
2407                   (int) strlen(text_info->text));
2408               text_info=text_info->previous;
2409             }
2410             (void) XDrawString(display,windows->image.id,annotate_context,
2411               x,y,"_",1);
2412           }
2413         break;
2414       }
2415       case KeyPress:
2416       {
2417         int
2418           length;
2419
2420         if (event.xkey.window != windows->image.id)
2421           break;
2422         /*
2423           Respond to a user key press.
2424         */
2425         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
2426           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2427         *(command+length)='\0';
2428         if (((event.xkey.state & ControlMask) != 0) ||
2429             ((event.xkey.state & Mod1Mask) != 0))
2430           state|=ModifierState;
2431         if ((state & ModifierState) != 0)
2432           switch ((int) key_symbol)
2433           {
2434             case XK_u:
2435             case XK_U:
2436             {
2437               key_symbol=DeleteCommand;
2438               break;
2439             }
2440             default:
2441               break;
2442           }
2443         switch ((int) key_symbol)
2444         {
2445           case XK_BackSpace:
2446           {
2447             /*
2448               Erase one character.
2449             */
2450             if (p == annotate_info->text)
2451               {
2452                 if (annotate_info->previous == (XAnnotateInfo *) NULL)
2453                   break;
2454                 else
2455                   {
2456                     /*
2457                       Go to end of the previous line of text.
2458                     */
2459                     annotate_info=annotate_info->previous;
2460                     p=annotate_info->text;
2461                     x=annotate_info->x+annotate_info->width;
2462                     y=annotate_info->y;
2463                     if (annotate_info->width != 0)
2464                       p+=strlen(annotate_info->text);
2465                     break;
2466                   }
2467               }
2468             p--;
2469             x-=XTextWidth(font_info,p,1);
2470             text_event.xexpose.x=x;
2471             text_event.xexpose.y=y-font_info->max_bounds.ascent;
2472             XRefreshWindow(display,&windows->image,&text_event);
2473             break;
2474           }
2475           case XK_bracketleft:
2476           {
2477             key_symbol=XK_Escape;
2478             break;
2479           }
2480           case DeleteCommand:
2481           {
2482             /*
2483               Erase the entire line of text.
2484             */
2485             while (p != annotate_info->text)
2486             {
2487               p--;
2488               x-=XTextWidth(font_info,p,1);
2489               text_event.xexpose.x=x;
2490               XRefreshWindow(display,&windows->image,&text_event);
2491             }
2492             break;
2493           }
2494           case XK_Escape:
2495           case XK_F20:
2496           {
2497             /*
2498               Finished annotating.
2499             */
2500             annotate_info->width=(unsigned int) XTextWidth(font_info,
2501               annotate_info->text,(int) strlen(annotate_info->text));
2502             XRefreshWindow(display,&windows->image,&text_event);
2503             state|=ExitState;
2504             break;
2505           }
2506           default:
2507           {
2508             /*
2509               Draw a single character on the Image window.
2510             */
2511             if ((state & ModifierState) != 0)
2512               break;
2513             if (*command == '\0')
2514               break;
2515             *p=(*command);
2516             if (annotate_info->stencil == ForegroundStencil)
2517               (void) XDrawString(display,windows->image.id,annotate_context,
2518                 x,y,p,1);
2519             else
2520               (void) XDrawImageString(display,windows->image.id,
2521                 annotate_context,x,y,p,1);
2522             x+=XTextWidth(font_info,p,1);
2523             p++;
2524             if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2525               break;
2526           }
2527           case XK_Return:
2528           case XK_KP_Enter:
2529           {
2530             /*
2531               Advance to the next line of text.
2532             */
2533             *p='\0';
2534             annotate_info->width=(unsigned int) XTextWidth(font_info,
2535               annotate_info->text,(int) strlen(annotate_info->text));
2536             if (annotate_info->next != (XAnnotateInfo *) NULL)
2537               {
2538                 /*
2539                   Line of text already exists.
2540                 */
2541                 annotate_info=annotate_info->next;
2542                 x=annotate_info->x;
2543                 y=annotate_info->y;
2544                 p=annotate_info->text;
2545                 break;
2546               }
2547             annotate_info->next=(XAnnotateInfo *) AcquireMagickMemory(
2548               sizeof(*annotate_info->next));
2549             if (annotate_info->next == (XAnnotateInfo *) NULL)
2550               return(MagickFalse);
2551             *annotate_info->next=(*annotate_info);
2552             annotate_info->next->previous=annotate_info;
2553             annotate_info=annotate_info->next;
2554             annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2555               windows->image.width/MagickMax((ssize_t)
2556               font_info->min_bounds.width,1)+2UL,sizeof(*annotate_info->text));
2557             if (annotate_info->text == (char *) NULL)
2558               return(MagickFalse);
2559             annotate_info->y+=annotate_info->height;
2560             if (annotate_info->y > (int) windows->image.height)
2561               annotate_info->y=(int) annotate_info->height;
2562             annotate_info->next=(XAnnotateInfo *) NULL;
2563             x=annotate_info->x;
2564             y=annotate_info->y;
2565             p=annotate_info->text;
2566             break;
2567           }
2568         }
2569         break;
2570       }
2571       case KeyRelease:
2572       {
2573         /*
2574           Respond to a user key release.
2575         */
2576         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2577           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2578         state&=(~ModifierState);
2579         break;
2580       }
2581       case SelectionNotify:
2582       {
2583         Atom
2584           type;
2585
2586         int
2587           format;
2588
2589         unsigned char
2590           *data;
2591
2592         unsigned long
2593           after,
2594           length;
2595
2596         /*
2597           Obtain response from primary selection.
2598         */
2599         if (event.xselection.property == (Atom) None)
2600           break;
2601         status=XGetWindowProperty(display,event.xselection.requestor,
2602           event.xselection.property,0L,(long) MaxTextExtent,True,XA_STRING,
2603           &type,&format,&length,&after,&data);
2604         if ((status != Success) || (type != XA_STRING) || (format == 32) ||
2605             (length == 0))
2606           break;
2607         /*
2608           Annotate Image window with primary selection.
2609         */
2610         for (i=0; i < (ssize_t) length; i++)
2611         {
2612           if ((char) data[i] != '\n')
2613             {
2614               /*
2615                 Draw a single character on the Image window.
2616               */
2617               *p=(char) data[i];
2618               (void) XDrawString(display,windows->image.id,annotate_context,
2619                 x,y,p,1);
2620               x+=XTextWidth(font_info,p,1);
2621               p++;
2622               if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2623                 continue;
2624             }
2625           /*
2626             Advance to the next line of text.
2627           */
2628           *p='\0';
2629           annotate_info->width=(unsigned int) XTextWidth(font_info,
2630             annotate_info->text,(int) strlen(annotate_info->text));
2631           if (annotate_info->next != (XAnnotateInfo *) NULL)
2632             {
2633               /*
2634                 Line of text already exists.
2635               */
2636               annotate_info=annotate_info->next;
2637               x=annotate_info->x;
2638               y=annotate_info->y;
2639               p=annotate_info->text;
2640               continue;
2641             }
2642           annotate_info->next=(XAnnotateInfo *) AcquireMagickMemory(
2643             sizeof(*annotate_info->next));
2644           if (annotate_info->next == (XAnnotateInfo *) NULL)
2645             return(MagickFalse);
2646           *annotate_info->next=(*annotate_info);
2647           annotate_info->next->previous=annotate_info;
2648           annotate_info=annotate_info->next;
2649           annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2650             windows->image.width/MagickMax((ssize_t)
2651             font_info->min_bounds.width,1)+2UL,sizeof(*annotate_info->text));
2652           if (annotate_info->text == (char *) NULL)
2653             return(MagickFalse);
2654           annotate_info->y+=annotate_info->height;
2655           if (annotate_info->y > (int) windows->image.height)
2656             annotate_info->y=(int) annotate_info->height;
2657           annotate_info->next=(XAnnotateInfo *) NULL;
2658           x=annotate_info->x;
2659           y=annotate_info->y;
2660           p=annotate_info->text;
2661         }
2662         (void) XFree((void *) data);
2663         break;
2664       }
2665       default:
2666         break;
2667     }
2668   } while ((state & ExitState) == 0);
2669   (void) XFreeCursor(display,cursor);
2670   /*
2671     Annotation is relative to image configuration.
2672   */
2673   width=(unsigned int) image->columns;
2674   height=(unsigned int) image->rows;
2675   x=0;
2676   y=0;
2677   if (windows->image.crop_geometry != (char *) NULL)
2678     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
2679   /*
2680     Initialize annotated image.
2681   */
2682   XSetCursorState(display,windows,MagickTrue);
2683   XCheckRefreshWindows(display,windows);
2684   while (annotate_info != (XAnnotateInfo *) NULL)
2685   {
2686     if (annotate_info->width == 0)
2687       {
2688         /*
2689           No text on this line--  go to the next line of text.
2690         */
2691         previous_info=annotate_info->previous;
2692         annotate_info->text=(char *)
2693           RelinquishMagickMemory(annotate_info->text);
2694         annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2695         annotate_info=previous_info;
2696         continue;
2697       }
2698     /*
2699       Determine pixel index for box and pen color.
2700     */
2701     windows->pixel_info->box_color=windows->pixel_info->pen_colors[box_id];
2702     if (windows->pixel_info->colors != 0)
2703       for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
2704         if (windows->pixel_info->pixels[i] ==
2705             windows->pixel_info->pen_colors[box_id].pixel)
2706           {
2707             windows->pixel_info->box_index=(unsigned short) i;
2708             break;
2709           }
2710     windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
2711     if (windows->pixel_info->colors != 0)
2712       for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
2713         if (windows->pixel_info->pixels[i] ==
2714             windows->pixel_info->pen_colors[pen_id].pixel)
2715           {
2716             windows->pixel_info->pen_index=(unsigned short) i;
2717             break;
2718           }
2719     /*
2720       Define the annotate geometry string.
2721     */
2722     annotate_info->x=(int)
2723       width*(annotate_info->x+windows->image.x)/windows->image.ximage->width;
2724     annotate_info->y=(int) height*(annotate_info->y-font_info->ascent+
2725       windows->image.y)/windows->image.ximage->height;
2726     (void) FormatLocaleString(annotate_info->geometry,MaxTextExtent,
2727       "%ux%u%+d%+d",width*annotate_info->width/windows->image.ximage->width,
2728       height*annotate_info->height/windows->image.ximage->height,
2729       annotate_info->x+x,annotate_info->y+y);
2730     /*
2731       Annotate image with text.
2732     */
2733     status=XAnnotateImage(display,windows->pixel_info,annotate_info,image,
2734       exception);
2735     if (status == 0)
2736       return(MagickFalse);
2737     /*
2738       Free up memory.
2739     */
2740     previous_info=annotate_info->previous;
2741     annotate_info->text=DestroyString(annotate_info->text);
2742     annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2743     annotate_info=previous_info;
2744   }
2745   (void) XSetForeground(display,annotate_context,
2746     windows->pixel_info->foreground_color.pixel);
2747   (void) XSetBackground(display,annotate_context,
2748     windows->pixel_info->background_color.pixel);
2749   (void) XSetFont(display,annotate_context,windows->font_info->fid);
2750   XSetCursorState(display,windows,MagickFalse);
2751   (void) XFreeFont(display,font_info);
2752   /*
2753     Update image configuration.
2754   */
2755   XConfigureImageColormap(display,resource_info,windows,image);
2756   (void) XConfigureImage(display,resource_info,windows,image,exception);
2757   return(MagickTrue);
2758 }
2759 \f
2760 /*
2761 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2762 %                                                                             %
2763 %                                                                             %
2764 %                                                                             %
2765 +   X B a c k g r o u n d I m a g e                                           %
2766 %                                                                             %
2767 %                                                                             %
2768 %                                                                             %
2769 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2770 %
2771 %  XBackgroundImage() displays the image in the background of a window.
2772 %
2773 %  The format of the XBackgroundImage method is:
2774 %
2775 %      MagickBooleanType XBackgroundImage(Display *display,
2776 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
2777 %        ExceptionInfo *exception)
2778 %
2779 %  A description of each parameter follows:
2780 %
2781 %    o display: Specifies a connection to an X server; returned from
2782 %      XOpenDisplay.
2783 %
2784 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2785 %
2786 %    o windows: Specifies a pointer to a XWindows structure.
2787 %
2788 %    o image: the image.
2789 %
2790 %    o exception: return any errors or warnings in this structure.
2791 %
2792 */
2793 static MagickBooleanType XBackgroundImage(Display *display,
2794   XResourceInfo *resource_info,XWindows *windows,Image **image,
2795   ExceptionInfo *exception)
2796 {
2797 #define BackgroundImageTag  "Background/Image"
2798
2799   int
2800     status;
2801
2802   static char
2803     window_id[MaxTextExtent] = "root";
2804
2805   XResourceInfo
2806     background_resources;
2807
2808   /*
2809     Put image in background.
2810   */
2811   status=XDialogWidget(display,windows,"Background",
2812     "Enter window id (id 0x00 selects window with pointer):",window_id);
2813   if (*window_id == '\0')
2814     return(MagickFalse);
2815   (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
2816     exception);
2817   XInfoWidget(display,windows,BackgroundImageTag);
2818   XSetCursorState(display,windows,MagickTrue);
2819   XCheckRefreshWindows(display,windows);
2820   background_resources=(*resource_info);
2821   background_resources.window_id=window_id;
2822   background_resources.backdrop=status != 0 ? MagickTrue : MagickFalse;
2823   status=XDisplayBackgroundImage(display,&background_resources,*image,
2824     exception);
2825   if (status != MagickFalse)
2826     XClientMessage(display,windows->image.id,windows->im_protocols,
2827       windows->im_retain_colors,CurrentTime);
2828   XSetCursorState(display,windows,MagickFalse);
2829   (void) XMagickCommand(display,resource_info,windows,UndoCommand,image,
2830     exception);
2831   return(MagickTrue);
2832 }
2833 \f
2834 /*
2835 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2836 %                                                                             %
2837 %                                                                             %
2838 %                                                                             %
2839 +   X C h o p I m a g e                                                       %
2840 %                                                                             %
2841 %                                                                             %
2842 %                                                                             %
2843 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2844 %
2845 %  XChopImage() chops the X image.
2846 %
2847 %  The format of the XChopImage method is:
2848 %
2849 %    MagickBooleanType XChopImage(Display *display,XResourceInfo *resource_info,
2850 %      XWindows *windows,Image **image,ExceptionInfo *exception)
2851 %
2852 %  A description of each parameter follows:
2853 %
2854 %    o display: Specifies a connection to an X server; returned from
2855 %      XOpenDisplay.
2856 %
2857 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2858 %
2859 %    o windows: Specifies a pointer to a XWindows structure.
2860 %
2861 %    o image: the image.
2862 %
2863 %    o exception: return any errors or warnings in this structure.
2864 %
2865 */
2866 static MagickBooleanType XChopImage(Display *display,
2867   XResourceInfo *resource_info,XWindows *windows,Image **image,
2868   ExceptionInfo *exception)
2869 {
2870   static const char
2871     *ChopMenu[] =
2872     {
2873       "Direction",
2874       "Help",
2875       "Dismiss",
2876       (char *) NULL
2877     };
2878
2879   static ModeType
2880     direction = HorizontalChopCommand;
2881
2882   static const ModeType
2883     ChopCommands[] =
2884     {
2885       ChopDirectionCommand,
2886       ChopHelpCommand,
2887       ChopDismissCommand
2888     },
2889     DirectionCommands[] =
2890     {
2891       HorizontalChopCommand,
2892       VerticalChopCommand
2893     };
2894
2895   char
2896     text[MaxTextExtent];
2897
2898   Image
2899     *chop_image;
2900
2901   int
2902     id,
2903     x,
2904     y;
2905
2906   MagickRealType
2907     scale_factor;
2908
2909   RectangleInfo
2910     chop_info;
2911
2912   unsigned int
2913     distance,
2914     height,
2915     width;
2916
2917   size_t
2918     state;
2919
2920   XEvent
2921     event;
2922
2923   XSegment
2924     segment_info;
2925
2926   /*
2927     Map Command widget.
2928   */
2929   (void) CloneString(&windows->command.name,"Chop");
2930   windows->command.data=1;
2931   (void) XCommandWidget(display,windows,ChopMenu,(XEvent *) NULL);
2932   (void) XMapRaised(display,windows->command.id);
2933   XClientMessage(display,windows->image.id,windows->im_protocols,
2934     windows->im_update_widget,CurrentTime);
2935   /*
2936     Track pointer until button 1 is pressed.
2937   */
2938   XQueryPosition(display,windows->image.id,&x,&y);
2939   (void) XSelectInput(display,windows->image.id,
2940     windows->image.attributes.event_mask | PointerMotionMask);
2941   state=DefaultState;
2942   do
2943   {
2944     if (windows->info.mapped != MagickFalse)
2945       {
2946         /*
2947           Display pointer position.
2948         */
2949         (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
2950           x+windows->image.x,y+windows->image.y);
2951         XInfoWidget(display,windows,text);
2952       }
2953     /*
2954       Wait for next event.
2955     */
2956     XScreenEvent(display,windows,&event);
2957     if (event.xany.window == windows->command.id)
2958       {
2959         /*
2960           Select a command from the Command widget.
2961         */
2962         id=XCommandWidget(display,windows,ChopMenu,&event);
2963         if (id < 0)
2964           continue;
2965         switch (ChopCommands[id])
2966         {
2967           case ChopDirectionCommand:
2968           {
2969             char
2970               command[MaxTextExtent];
2971
2972             static const char
2973               *Directions[] =
2974               {
2975                 "horizontal",
2976                 "vertical",
2977                 (char *) NULL,
2978               };
2979
2980             /*
2981               Select a command from the pop-up menu.
2982             */
2983             id=XMenuWidget(display,windows,ChopMenu[id],Directions,command);
2984             if (id >= 0)
2985               direction=DirectionCommands[id];
2986             break;
2987           }
2988           case ChopHelpCommand:
2989           {
2990             XTextViewWidget(display,resource_info,windows,MagickFalse,
2991               "Help Viewer - Image Chop",ImageChopHelp);
2992             break;
2993           }
2994           case ChopDismissCommand:
2995           {
2996             /*
2997               Prematurely exit.
2998             */
2999             state|=EscapeState;
3000             state|=ExitState;
3001             break;
3002           }
3003           default:
3004             break;
3005         }
3006         continue;
3007       }
3008     switch (event.type)
3009     {
3010       case ButtonPress:
3011       {
3012         if (event.xbutton.button != Button1)
3013           break;
3014         if (event.xbutton.window != windows->image.id)
3015           break;
3016         /*
3017           User has committed to start point of chopping line.
3018         */
3019         segment_info.x1=(short int) event.xbutton.x;
3020         segment_info.x2=(short int) event.xbutton.x;
3021         segment_info.y1=(short int) event.xbutton.y;
3022         segment_info.y2=(short int) event.xbutton.y;
3023         state|=ExitState;
3024         break;
3025       }
3026       case ButtonRelease:
3027         break;
3028       case Expose:
3029         break;
3030       case KeyPress:
3031       {
3032         char
3033           command[MaxTextExtent];
3034
3035         KeySym
3036           key_symbol;
3037
3038         if (event.xkey.window != windows->image.id)
3039           break;
3040         /*
3041           Respond to a user key press.
3042         */
3043         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3044           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3045         switch ((int) key_symbol)
3046         {
3047           case XK_Escape:
3048           case XK_F20:
3049           {
3050             /*
3051               Prematurely exit.
3052             */
3053             state|=EscapeState;
3054             state|=ExitState;
3055             break;
3056           }
3057           case XK_F1:
3058           case XK_Help:
3059           {
3060             (void) XSetFunction(display,windows->image.highlight_context,
3061               GXcopy);
3062             XTextViewWidget(display,resource_info,windows,MagickFalse,
3063               "Help Viewer - Image Chop",ImageChopHelp);
3064             (void) XSetFunction(display,windows->image.highlight_context,
3065               GXinvert);
3066             break;
3067           }
3068           default:
3069           {
3070             (void) XBell(display,0);
3071             break;
3072           }
3073         }
3074         break;
3075       }
3076       case MotionNotify:
3077       {
3078         /*
3079           Map and unmap Info widget as text cursor crosses its boundaries.
3080         */
3081         x=event.xmotion.x;
3082         y=event.xmotion.y;
3083         if (windows->info.mapped != MagickFalse)
3084           {
3085             if ((x < (int) (windows->info.x+windows->info.width)) &&
3086                 (y < (int) (windows->info.y+windows->info.height)))
3087               (void) XWithdrawWindow(display,windows->info.id,
3088                 windows->info.screen);
3089           }
3090         else
3091           if ((x > (int) (windows->info.x+windows->info.width)) ||
3092               (y > (int) (windows->info.y+windows->info.height)))
3093             (void) XMapWindow(display,windows->info.id);
3094       }
3095     }
3096   } while ((state & ExitState) == 0);
3097   (void) XSelectInput(display,windows->image.id,
3098     windows->image.attributes.event_mask);
3099   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3100   if ((state & EscapeState) != 0)
3101     return(MagickTrue);
3102   /*
3103     Draw line as pointer moves until the mouse button is released.
3104   */
3105   chop_info.width=0;
3106   chop_info.height=0;
3107   chop_info.x=0;
3108   chop_info.y=0;
3109   distance=0;
3110   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
3111   state=DefaultState;
3112   do
3113   {
3114     if (distance > 9)
3115       {
3116         /*
3117           Display info and draw chopping line.
3118         */
3119         if (windows->info.mapped == MagickFalse)
3120           (void) XMapWindow(display,windows->info.id);
3121         (void) FormatLocaleString(text,MaxTextExtent,
3122           " %.20gx%.20g%+.20g%+.20g",(double) chop_info.width,(double)
3123           chop_info.height,(double) chop_info.x,(double) chop_info.y);
3124         XInfoWidget(display,windows,text);
3125         XHighlightLine(display,windows->image.id,
3126           windows->image.highlight_context,&segment_info);
3127       }
3128     else
3129       if (windows->info.mapped != MagickFalse)
3130         (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3131     /*
3132       Wait for next event.
3133     */
3134     XScreenEvent(display,windows,&event);
3135     if (distance > 9)
3136       XHighlightLine(display,windows->image.id,
3137         windows->image.highlight_context,&segment_info);
3138     switch (event.type)
3139     {
3140       case ButtonPress:
3141       {
3142         segment_info.x2=(short int) event.xmotion.x;
3143         segment_info.y2=(short int) event.xmotion.y;
3144         break;
3145       }
3146       case ButtonRelease:
3147       {
3148         /*
3149           User has committed to chopping line.
3150         */
3151         segment_info.x2=(short int) event.xbutton.x;
3152         segment_info.y2=(short int) event.xbutton.y;
3153         state|=ExitState;
3154         break;
3155       }
3156       case Expose:
3157         break;
3158       case MotionNotify:
3159       {
3160         segment_info.x2=(short int) event.xmotion.x;
3161         segment_info.y2=(short int) event.xmotion.y;
3162       }
3163       default:
3164         break;
3165     }
3166     /*
3167       Check boundary conditions.
3168     */
3169     if (segment_info.x2 < 0)
3170       segment_info.x2=0;
3171     else
3172       if (segment_info.x2 > windows->image.ximage->width)
3173         segment_info.x2=windows->image.ximage->width;
3174     if (segment_info.y2 < 0)
3175       segment_info.y2=0;
3176     else
3177       if (segment_info.y2 > windows->image.ximage->height)
3178         segment_info.y2=windows->image.ximage->height;
3179     distance=(unsigned int)
3180       (((segment_info.x2-segment_info.x1)*(segment_info.x2-segment_info.x1))+
3181        ((segment_info.y2-segment_info.y1)*(segment_info.y2-segment_info.y1)));
3182     /*
3183       Compute chopping geometry.
3184     */
3185     if (direction == HorizontalChopCommand)
3186       {
3187         chop_info.width=(size_t) (segment_info.x2-segment_info.x1+1);
3188         chop_info.x=(ssize_t) windows->image.x+segment_info.x1;
3189         chop_info.height=0;
3190         chop_info.y=0;
3191         if (segment_info.x1 > (int) segment_info.x2)
3192           {
3193             chop_info.width=(size_t) (segment_info.x1-segment_info.x2+1);
3194             chop_info.x=(ssize_t) windows->image.x+segment_info.x2;
3195           }
3196       }
3197     else
3198       {
3199         chop_info.width=0;
3200         chop_info.height=(size_t) (segment_info.y2-segment_info.y1+1);
3201         chop_info.x=0;
3202         chop_info.y=(ssize_t) windows->image.y+segment_info.y1;
3203         if (segment_info.y1 > segment_info.y2)
3204           {
3205             chop_info.height=(size_t) (segment_info.y1-segment_info.y2+1);
3206             chop_info.y=(ssize_t) windows->image.y+segment_info.y2;
3207           }
3208       }
3209   } while ((state & ExitState) == 0);
3210   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
3211   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3212   if (distance <= 9)
3213     return(MagickTrue);
3214   /*
3215     Image chopping is relative to image configuration.
3216   */
3217   (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
3218     exception);
3219   XSetCursorState(display,windows,MagickTrue);
3220   XCheckRefreshWindows(display,windows);
3221   windows->image.window_changes.width=windows->image.ximage->width-
3222     (unsigned int) chop_info.width;
3223   windows->image.window_changes.height=windows->image.ximage->height-
3224     (unsigned int) chop_info.height;
3225   width=(unsigned int) (*image)->columns;
3226   height=(unsigned int) (*image)->rows;
3227   x=0;
3228   y=0;
3229   if (windows->image.crop_geometry != (char *) NULL)
3230     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
3231   scale_factor=(MagickRealType) width/windows->image.ximage->width;
3232   chop_info.x+=x;
3233   chop_info.x=(ssize_t) (scale_factor*chop_info.x+0.5);
3234   chop_info.width=(unsigned int) (scale_factor*chop_info.width+0.5);
3235   scale_factor=(MagickRealType) height/windows->image.ximage->height;
3236   chop_info.y+=y;
3237   chop_info.y=(ssize_t) (scale_factor*chop_info.y+0.5);
3238   chop_info.height=(unsigned int) (scale_factor*chop_info.height+0.5);
3239   /*
3240     Chop image.
3241   */
3242   chop_image=ChopImage(*image,&chop_info,exception);
3243   XSetCursorState(display,windows,MagickFalse);
3244   if (chop_image == (Image *) NULL)
3245     return(MagickFalse);
3246   *image=DestroyImage(*image);
3247   *image=chop_image;
3248   /*
3249     Update image configuration.
3250   */
3251   XConfigureImageColormap(display,resource_info,windows,*image);
3252   (void) XConfigureImage(display,resource_info,windows,*image,exception);
3253   return(MagickTrue);
3254 }
3255 \f
3256 /*
3257 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3258 %                                                                             %
3259 %                                                                             %
3260 %                                                                             %
3261 +   X C o l o r E d i t I m a g e                                             %
3262 %                                                                             %
3263 %                                                                             %
3264 %                                                                             %
3265 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3266 %
3267 %  XColorEditImage() allows the user to interactively change the color of one
3268 %  pixel for a DirectColor image or one colormap entry for a PseudoClass image.
3269 %
3270 %  The format of the XColorEditImage method is:
3271 %
3272 %      MagickBooleanType XColorEditImage(Display *display,
3273 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
3274 %          ExceptionInfo *exception)
3275 %
3276 %  A description of each parameter follows:
3277 %
3278 %    o display: Specifies a connection to an X server;  returned from
3279 %      XOpenDisplay.
3280 %
3281 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3282 %
3283 %    o windows: Specifies a pointer to a XWindows structure.
3284 %
3285 %    o image: the image; returned from ReadImage.
3286 %
3287 %    o exception: return any errors or warnings in this structure.
3288 %
3289 */
3290 static MagickBooleanType XColorEditImage(Display *display,
3291   XResourceInfo *resource_info,XWindows *windows,Image **image,
3292   ExceptionInfo *exception)
3293 {
3294   static const char
3295     *ColorEditMenu[] =
3296     {
3297       "Method",
3298       "Pixel Color",
3299       "Border Color",
3300       "Fuzz",
3301       "Undo",
3302       "Help",
3303       "Dismiss",
3304       (char *) NULL
3305     };
3306
3307   static const ModeType
3308     ColorEditCommands[] =
3309     {
3310       ColorEditMethodCommand,
3311       ColorEditColorCommand,
3312       ColorEditBorderCommand,
3313       ColorEditFuzzCommand,
3314       ColorEditUndoCommand,
3315       ColorEditHelpCommand,
3316       ColorEditDismissCommand
3317     };
3318
3319   static PaintMethod
3320     method = PointMethod;
3321
3322   static unsigned int
3323     pen_id = 0;
3324
3325   static XColor
3326     border_color = { 0, 0, 0, 0, 0, 0 };
3327
3328   char
3329     command[MaxTextExtent],
3330     text[MaxTextExtent];
3331
3332   Cursor
3333     cursor;
3334
3335   int
3336     entry,
3337     id,
3338     x,
3339     x_offset,
3340     y,
3341     y_offset;
3342
3343   register Quantum
3344     *q;
3345
3346   register ssize_t
3347     i;
3348
3349   unsigned int
3350     height,
3351     width;
3352
3353   size_t
3354     state;
3355
3356   XColor
3357     color;
3358
3359   XEvent
3360     event;
3361
3362   /*
3363     Map Command widget.
3364   */
3365   (void) CloneString(&windows->command.name,"Color Edit");
3366   windows->command.data=4;
3367   (void) XCommandWidget(display,windows,ColorEditMenu,(XEvent *) NULL);
3368   (void) XMapRaised(display,windows->command.id);
3369   XClientMessage(display,windows->image.id,windows->im_protocols,
3370     windows->im_update_widget,CurrentTime);
3371   /*
3372     Make cursor.
3373   */
3374   cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
3375     resource_info->background_color,resource_info->foreground_color);
3376   (void) XCheckDefineCursor(display,windows->image.id,cursor);
3377   /*
3378     Track pointer until button 1 is pressed.
3379   */
3380   XQueryPosition(display,windows->image.id,&x,&y);
3381   (void) XSelectInput(display,windows->image.id,
3382     windows->image.attributes.event_mask | PointerMotionMask);
3383   state=DefaultState;
3384   do
3385   {
3386     if (windows->info.mapped != MagickFalse)
3387       {
3388         /*
3389           Display pointer position.
3390         */
3391         (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
3392           x+windows->image.x,y+windows->image.y);
3393         XInfoWidget(display,windows,text);
3394       }
3395     /*
3396       Wait for next event.
3397     */
3398     XScreenEvent(display,windows,&event);
3399     if (event.xany.window == windows->command.id)
3400       {
3401         /*
3402           Select a command from the Command widget.
3403         */
3404         id=XCommandWidget(display,windows,ColorEditMenu,&event);
3405         if (id < 0)
3406           {
3407             (void) XCheckDefineCursor(display,windows->image.id,cursor);
3408             continue;
3409           }
3410         switch (ColorEditCommands[id])
3411         {
3412           case ColorEditMethodCommand:
3413           {
3414             char
3415               **methods;
3416
3417             /*
3418               Select a method from the pop-up menu.
3419             */
3420             methods=(char **) GetCommandOptions(MagickMethodOptions);
3421             if (methods == (char **) NULL)
3422               break;
3423             entry=XMenuWidget(display,windows,ColorEditMenu[id],
3424               (const char **) methods,command);
3425             if (entry >= 0)
3426               method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
3427                 MagickFalse,methods[entry]);
3428             methods=DestroyStringList(methods);
3429             break;
3430           }
3431           case ColorEditColorCommand:
3432           {
3433             const char
3434               *ColorMenu[MaxNumberPens];
3435
3436             int
3437               pen_number;
3438
3439             /*
3440               Initialize menu selections.
3441             */
3442             for (i=0; i < (int) (MaxNumberPens-2); i++)
3443               ColorMenu[i]=resource_info->pen_colors[i];
3444             ColorMenu[MaxNumberPens-2]="Browser...";
3445             ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3446             /*
3447               Select a pen color from the pop-up menu.
3448             */
3449             pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3450               (const char **) ColorMenu,command);
3451             if (pen_number < 0)
3452               break;
3453             if (pen_number == (MaxNumberPens-2))
3454               {
3455                 static char
3456                   color_name[MaxTextExtent] = "gray";
3457
3458                 /*
3459                   Select a pen color from a dialog.
3460                 */
3461                 resource_info->pen_colors[pen_number]=color_name;
3462                 XColorBrowserWidget(display,windows,"Select",color_name);
3463                 if (*color_name == '\0')
3464                   break;
3465               }
3466             /*
3467               Set pen color.
3468             */
3469             (void) XParseColor(display,windows->map_info->colormap,
3470               resource_info->pen_colors[pen_number],&color);
3471             XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
3472               (unsigned int) MaxColors,&color);
3473             windows->pixel_info->pen_colors[pen_number]=color;
3474             pen_id=(unsigned int) pen_number;
3475             break;
3476           }
3477           case ColorEditBorderCommand:
3478           {
3479             const char
3480               *ColorMenu[MaxNumberPens];
3481
3482             int
3483               pen_number;
3484
3485             /*
3486               Initialize menu selections.
3487             */
3488             for (i=0; i < (int) (MaxNumberPens-2); i++)
3489               ColorMenu[i]=resource_info->pen_colors[i];
3490             ColorMenu[MaxNumberPens-2]="Browser...";
3491             ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3492             /*
3493               Select a pen color from the pop-up menu.
3494             */
3495             pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3496               (const char **) ColorMenu,command);
3497             if (pen_number < 0)
3498               break;
3499             if (pen_number == (MaxNumberPens-2))
3500               {
3501                 static char
3502                   color_name[MaxTextExtent] = "gray";
3503
3504                 /*
3505                   Select a pen color from a dialog.
3506                 */
3507                 resource_info->pen_colors[pen_number]=color_name;
3508                 XColorBrowserWidget(display,windows,"Select",color_name);
3509                 if (*color_name == '\0')
3510                   break;
3511               }
3512             /*
3513               Set border color.
3514             */
3515             (void) XParseColor(display,windows->map_info->colormap,
3516               resource_info->pen_colors[pen_number],&border_color);
3517             break;
3518           }
3519           case ColorEditFuzzCommand:
3520           {
3521             static char
3522               fuzz[MaxTextExtent];
3523
3524             static const char
3525               *FuzzMenu[] =
3526               {
3527                 "0%",
3528                 "2%",
3529                 "5%",
3530                 "10%",
3531                 "15%",
3532                 "Dialog...",
3533                 (char *) NULL,
3534               };
3535
3536             /*
3537               Select a command from the pop-up menu.
3538             */
3539             entry=XMenuWidget(display,windows,ColorEditMenu[id],FuzzMenu,
3540               command);
3541             if (entry < 0)
3542               break;
3543             if (entry != 5)
3544               {
3545                 (*image)->fuzz=SiPrefixToDouble(FuzzMenu[entry],(double)
3546                   QuantumRange+1.0);
3547                 break;
3548               }
3549             (void) (void) CopyMagickString(fuzz,"20%",MaxTextExtent);
3550             (void) XDialogWidget(display,windows,"Ok",
3551               "Enter fuzz factor (0.0 - 99.9%):",fuzz);
3552             if (*fuzz == '\0')
3553               break;
3554             (void) ConcatenateMagickString(fuzz,"%",MaxTextExtent);
3555             (*image)->fuzz=SiPrefixToDouble(fuzz,(double) QuantumRange+1.0);
3556             break;
3557           }
3558           case ColorEditUndoCommand:
3559           {
3560             (void) XMagickCommand(display,resource_info,windows,UndoCommand,
3561               image,exception);
3562             break;
3563           }
3564           case ColorEditHelpCommand:
3565           default:
3566           {
3567             XTextViewWidget(display,resource_info,windows,MagickFalse,
3568               "Help Viewer - Image Annotation",ImageColorEditHelp);
3569             break;
3570           }
3571           case ColorEditDismissCommand:
3572           {
3573             /*
3574               Prematurely exit.
3575             */
3576             state|=EscapeState;
3577             state|=ExitState;
3578             break;
3579           }
3580         }
3581         (void) XCheckDefineCursor(display,windows->image.id,cursor);
3582         continue;
3583       }
3584     switch (event.type)
3585     {
3586       case ButtonPress:
3587       {
3588         if (event.xbutton.button != Button1)
3589           break;
3590         if ((event.xbutton.window != windows->image.id) &&
3591             (event.xbutton.window != windows->magnify.id))
3592           break;
3593         /*
3594           exit loop.
3595         */
3596         x=event.xbutton.x;
3597         y=event.xbutton.y;
3598         (void) XMagickCommand(display,resource_info,windows,
3599           SaveToUndoBufferCommand,image,exception);
3600         state|=UpdateConfigurationState;
3601         break;
3602       }
3603       case ButtonRelease:
3604       {
3605         if (event.xbutton.button != Button1)
3606           break;
3607         if ((event.xbutton.window != windows->image.id) &&
3608             (event.xbutton.window != windows->magnify.id))
3609           break;
3610         /*
3611           Update colormap information.
3612         */
3613         x=event.xbutton.x;
3614         y=event.xbutton.y;
3615         XConfigureImageColormap(display,resource_info,windows,*image);
3616         (void) XConfigureImage(display,resource_info,windows,*image,exception);
3617         XInfoWidget(display,windows,text);
3618         (void) XCheckDefineCursor(display,windows->image.id,cursor);
3619         state&=(~UpdateConfigurationState);
3620         break;
3621       }
3622       case Expose:
3623         break;
3624       case KeyPress:
3625       {
3626         KeySym
3627           key_symbol;
3628
3629         if (event.xkey.window == windows->magnify.id)
3630           {
3631             Window
3632               window;
3633
3634             window=windows->magnify.id;
3635             while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
3636           }
3637         if (event.xkey.window != windows->image.id)
3638           break;
3639         /*
3640           Respond to a user key press.
3641         */
3642         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3643           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3644         switch ((int) key_symbol)
3645         {
3646           case XK_Escape:
3647           case XK_F20:
3648           {
3649             /*
3650               Prematurely exit.
3651             */
3652             state|=ExitState;
3653             break;
3654           }
3655           case XK_F1:
3656           case XK_Help:
3657           {
3658             XTextViewWidget(display,resource_info,windows,MagickFalse,
3659               "Help Viewer - Image Annotation",ImageColorEditHelp);
3660             break;
3661           }
3662           default:
3663           {
3664             (void) XBell(display,0);
3665             break;
3666           }
3667         }
3668         break;
3669       }
3670       case MotionNotify:
3671       {
3672         /*
3673           Map and unmap Info widget as cursor crosses its boundaries.
3674         */
3675         x=event.xmotion.x;
3676         y=event.xmotion.y;
3677         if (windows->info.mapped != MagickFalse)
3678           {
3679             if ((x < (int) (windows->info.x+windows->info.width)) &&
3680                 (y < (int) (windows->info.y+windows->info.height)))
3681               (void) XWithdrawWindow(display,windows->info.id,
3682                 windows->info.screen);
3683           }
3684         else
3685           if ((x > (int) (windows->info.x+windows->info.width)) ||
3686               (y > (int) (windows->info.y+windows->info.height)))
3687             (void) XMapWindow(display,windows->info.id);
3688         break;
3689       }
3690       default:
3691         break;
3692     }
3693     if (event.xany.window == windows->magnify.id)
3694       {
3695         x=windows->magnify.x-windows->image.x;
3696         y=windows->magnify.y-windows->image.y;
3697       }
3698     x_offset=x;
3699     y_offset=y;
3700     if ((state & UpdateConfigurationState) != 0)
3701       {
3702         CacheView
3703           *image_view;
3704
3705         int
3706           x,
3707           y;
3708
3709         /*
3710           Pixel edit is relative to image configuration.
3711         */
3712         (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
3713           MagickTrue);
3714         color=windows->pixel_info->pen_colors[pen_id];
3715         XPutPixel(windows->image.ximage,x_offset,y_offset,color.pixel);
3716         width=(unsigned int) (*image)->columns;
3717         height=(unsigned int) (*image)->rows;
3718         x=0;
3719         y=0;
3720         if (windows->image.crop_geometry != (char *) NULL)
3721           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
3722             &width,&height);
3723         x_offset=(int)
3724           (width*(windows->image.x+x_offset)/windows->image.ximage->width+x);
3725         y_offset=(int)
3726           (height*(windows->image.y+y_offset)/windows->image.ximage->height+y);
3727         if ((x_offset < 0) || (y_offset < 0))
3728           continue;
3729         if ((x_offset >= (int) (*image)->columns) ||
3730             (y_offset >= (int) (*image)->rows))
3731           continue;
3732         image_view=AcquireCacheView(*image);
3733         switch (method)
3734         {
3735           case PointMethod:
3736           default:
3737           {
3738             /*
3739               Update color information using point algorithm.
3740             */
3741             if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
3742               return(MagickFalse);
3743             q=GetCacheViewAuthenticPixels(image_view,(ssize_t)x_offset,
3744               (ssize_t) y_offset,1,1,exception);
3745             if (q == (Quantum *) NULL)
3746               break;
3747             SetPixelRed(*image,ScaleShortToQuantum(color.red),q);
3748             SetPixelGreen(*image,ScaleShortToQuantum(color.green),q);
3749             SetPixelBlue(*image,ScaleShortToQuantum(color.blue),q);
3750             (void) SyncCacheViewAuthenticPixels(image_view,exception);
3751             break;
3752           }
3753           case ReplaceMethod:
3754           {
3755             PixelInfo
3756               pixel,
3757               target;
3758
3759             Quantum
3760               virtual_pixel[MaxPixelChannels];
3761
3762             /*
3763               Update color information using replace algorithm.
3764             */
3765             (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t) x_offset,
3766               (ssize_t) y_offset,virtual_pixel,exception);
3767             target.red=virtual_pixel[RedPixelChannel];
3768             target.green=virtual_pixel[GreenPixelChannel];
3769             target.blue=virtual_pixel[BluePixelChannel];
3770             target.alpha=virtual_pixel[AlphaPixelChannel];
3771             if ((*image)->storage_class == DirectClass)
3772               {
3773                 for (y=0; y < (int) (*image)->rows; y++)
3774                 {
3775                   q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3776                     (*image)->columns,1,exception);
3777                   if (q == (Quantum *) NULL)
3778                     break;
3779                   for (x=0; x < (int) (*image)->columns; x++)
3780                   {
3781                     GetPixelInfoPixel(*image,q,&pixel);
3782                     if (IsFuzzyEquivalencePixelInfo(&pixel,&target))
3783                       {
3784                         SetPixelRed(*image,ScaleShortToQuantum(
3785                           color.red),q);
3786                         SetPixelGreen(*image,ScaleShortToQuantum(
3787                           color.green),q);
3788                         SetPixelBlue(*image,ScaleShortToQuantum(
3789                           color.blue),q);
3790                       }
3791                     q+=GetPixelChannels(*image);
3792                   }
3793                   if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3794                     break;
3795                 }
3796               }
3797             else
3798               {
3799                 for (i=0; i < (ssize_t) (*image)->colors; i++)
3800                   if (IsFuzzyEquivalencePixelInfo((*image)->colormap+i,&target))
3801                     {
3802                       (*image)->colormap[i].red=ScaleShortToQuantum(
3803                         color.red);
3804                       (*image)->colormap[i].green=ScaleShortToQuantum(
3805                         color.green);
3806                       (*image)->colormap[i].blue=ScaleShortToQuantum(
3807                         color.blue);
3808                     }
3809                 (void) SyncImage(*image,exception);
3810               }
3811             break;
3812           }
3813           case FloodfillMethod:
3814           case FillToBorderMethod:
3815           {
3816             DrawInfo
3817               *draw_info;
3818
3819             PixelInfo
3820               target;
3821
3822             /*
3823               Update color information using floodfill algorithm.
3824             */
3825             (void) GetOneVirtualMagickPixel(*image,(ssize_t) x_offset,
3826               (ssize_t) y_offset,&target,exception);
3827             if (method == FillToBorderMethod)
3828               {
3829                 target.red=(MagickRealType)
3830                   ScaleShortToQuantum(border_color.red);
3831                 target.green=(MagickRealType)
3832                   ScaleShortToQuantum(border_color.green);
3833                 target.blue=(MagickRealType)
3834                   ScaleShortToQuantum(border_color.blue);
3835               }
3836             draw_info=CloneDrawInfo(resource_info->image_info,
3837               (DrawInfo *) NULL);
3838             (void) QueryColorCompliance(resource_info->pen_colors[pen_id],
3839               AllCompliance,&draw_info->fill,exception);
3840             (void) FloodfillPaintImage(*image,draw_info,&target,(ssize_t)
3841               x_offset,(ssize_t) y_offset,method == FloodfillMethod ?
3842               MagickFalse : MagickTrue,exception);
3843             draw_info=DestroyDrawInfo(draw_info);
3844             break;
3845           }
3846           case ResetMethod:
3847           {
3848             /*
3849               Update color information using reset algorithm.
3850             */
3851             if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
3852               return(MagickFalse);
3853             for (y=0; y < (int) (*image)->rows; y++)
3854             {
3855               q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3856                 (*image)->columns,1,exception);
3857               if (q == (Quantum *) NULL)
3858                 break;
3859               for (x=0; x < (int) (*image)->columns; x++)
3860               {
3861                 SetPixelRed(*image,ScaleShortToQuantum(color.red),q);
3862                 SetPixelGreen(*image,ScaleShortToQuantum(color.green),q);
3863                 SetPixelBlue(*image,ScaleShortToQuantum(color.blue),q);
3864                 q+=GetPixelChannels(*image);
3865               }
3866               if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3867                 break;
3868             }
3869             break;
3870           }
3871         }
3872         image_view=DestroyCacheView(image_view);
3873         state&=(~UpdateConfigurationState);
3874       }
3875   } while ((state & ExitState) == 0);
3876   (void) XSelectInput(display,windows->image.id,
3877     windows->image.attributes.event_mask);
3878   XSetCursorState(display,windows,MagickFalse);
3879   (void) XFreeCursor(display,cursor);
3880   return(MagickTrue);
3881 }
3882 \f
3883 /*
3884 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3885 %                                                                             %
3886 %                                                                             %
3887 %                                                                             %
3888 +   X C o m p o s i t e I m a g e                                             %
3889 %                                                                             %
3890 %                                                                             %
3891 %                                                                             %
3892 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3893 %
3894 %  XCompositeImage() requests an image name from the user, reads the image and
3895 %  composites it with the X window image at a location the user chooses with
3896 %  the pointer.
3897 %
3898 %  The format of the XCompositeImage method is:
3899 %
3900 %      MagickBooleanType XCompositeImage(Display *display,
3901 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
3902 %        ExceptionInfo *exception)
3903 %
3904 %  A description of each parameter follows:
3905 %
3906 %    o display: Specifies a connection to an X server;  returned from
3907 %      XOpenDisplay.
3908 %
3909 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3910 %
3911 %    o windows: Specifies a pointer to a XWindows structure.
3912 %
3913 %    o image: the image; returned from ReadImage.
3914 %
3915 %    o exception: return any errors or warnings in this structure.
3916 %
3917 */
3918 static MagickBooleanType XCompositeImage(Display *display,
3919   XResourceInfo *resource_info,XWindows *windows,Image *image,
3920   ExceptionInfo *exception)
3921 {
3922   static char
3923     displacement_geometry[MaxTextExtent] = "30x30",
3924     filename[MaxTextExtent] = "\0";
3925
3926   static const char
3927     *CompositeMenu[] =
3928     {
3929       "Operators",
3930       "Dissolve",
3931       "Displace",
3932       "Help",
3933       "Dismiss",
3934       (char *) NULL
3935     };
3936
3937   static CompositeOperator
3938     compose = CopyCompositeOp;
3939
3940   static const ModeType
3941     CompositeCommands[] =
3942     {
3943       CompositeOperatorsCommand,
3944       CompositeDissolveCommand,
3945       CompositeDisplaceCommand,
3946       CompositeHelpCommand,
3947       CompositeDismissCommand
3948     };
3949
3950   char
3951     text[MaxTextExtent];
3952
3953   Cursor
3954     cursor;
3955
3956   Image
3957     *composite_image;
3958
3959   int
3960     entry,
3961     id,
3962     x,
3963     y;
3964
3965   MagickRealType
3966     blend,
3967     scale_factor;
3968
3969   RectangleInfo
3970     highlight_info,
3971     composite_info;
3972
3973   unsigned int
3974     height,
3975     width;
3976
3977   size_t
3978     state;
3979
3980   XEvent
3981     event;
3982
3983   /*
3984     Request image file name from user.
3985   */
3986   XFileBrowserWidget(display,windows,"Composite",filename);
3987   if (*filename == '\0')
3988     return(MagickTrue);
3989   /*
3990     Read image.
3991   */
3992   XSetCursorState(display,windows,MagickTrue);
3993   XCheckRefreshWindows(display,windows);
3994   (void) CopyMagickString(resource_info->image_info->filename,filename,
3995     MaxTextExtent);
3996   composite_image=ReadImage(resource_info->image_info,exception);
3997   CatchException(exception);
3998   XSetCursorState(display,windows,MagickFalse);
3999   if (composite_image == (Image *) NULL)
4000     return(MagickFalse);
4001   /*
4002     Map Command widget.
4003   */
4004   (void) CloneString(&windows->command.name,"Composite");
4005   windows->command.data=1;
4006   (void) XCommandWidget(display,windows,CompositeMenu,(XEvent *) NULL);
4007   (void) XMapRaised(display,windows->command.id);
4008   XClientMessage(display,windows->image.id,windows->im_protocols,
4009     windows->im_update_widget,CurrentTime);
4010   /*
4011     Track pointer until button 1 is pressed.
4012   */
4013   XQueryPosition(display,windows->image.id,&x,&y);
4014   (void) XSelectInput(display,windows->image.id,
4015     windows->image.attributes.event_mask | PointerMotionMask);
4016   composite_info.x=(ssize_t) windows->image.x+x;
4017   composite_info.y=(ssize_t) windows->image.y+y;
4018   composite_info.width=0;
4019   composite_info.height=0;
4020   cursor=XCreateFontCursor(display,XC_ul_angle);
4021   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
4022   blend=0.0;
4023   state=DefaultState;
4024   do
4025   {
4026     if (windows->info.mapped != MagickFalse)
4027       {
4028         /*
4029           Display pointer position.
4030         */
4031         (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
4032           (long) composite_info.x,(long) composite_info.y);
4033         XInfoWidget(display,windows,text);
4034       }
4035     highlight_info=composite_info;
4036     highlight_info.x=composite_info.x-windows->image.x;
4037     highlight_info.y=composite_info.y-windows->image.y;
4038     XHighlightRectangle(display,windows->image.id,
4039       windows->image.highlight_context,&highlight_info);
4040     /*
4041       Wait for next event.
4042     */
4043     XScreenEvent(display,windows,&event);
4044     XHighlightRectangle(display,windows->image.id,
4045       windows->image.highlight_context,&highlight_info);
4046     if (event.xany.window == windows->command.id)
4047       {
4048         /*
4049           Select a command from the Command widget.
4050         */
4051         id=XCommandWidget(display,windows,CompositeMenu,&event);
4052         if (id < 0)
4053           continue;
4054         switch (CompositeCommands[id])
4055         {
4056           case CompositeOperatorsCommand:
4057           {
4058             char
4059               command[MaxTextExtent],
4060               **operators;
4061
4062             /*
4063               Select a command from the pop-up menu.
4064             */
4065             operators=GetCommandOptions(MagickComposeOptions);
4066             if (operators == (char **) NULL)
4067               break;
4068             entry=XMenuWidget(display,windows,CompositeMenu[id],
4069               (const char **) operators,command);
4070             if (entry >= 0)
4071               compose=(CompositeOperator) ParseCommandOption(
4072                 MagickComposeOptions,MagickFalse,operators[entry]);
4073             operators=DestroyStringList(operators);
4074             break;
4075           }
4076           case CompositeDissolveCommand:
4077           {
4078             static char
4079               factor[MaxTextExtent] = "20.0";
4080
4081             /*
4082               Dissolve the two images a given percent.
4083             */
4084             (void) XSetFunction(display,windows->image.highlight_context,
4085               GXcopy);
4086             (void) XDialogWidget(display,windows,"Dissolve",
4087               "Enter the blend factor (0.0 - 99.9%):",factor);
4088             (void) XSetFunction(display,windows->image.highlight_context,
4089               GXinvert);
4090             if (*factor == '\0')
4091               break;
4092             blend=InterpretLocaleValue(factor,(char **) NULL);
4093             compose=DissolveCompositeOp;
4094             break;
4095           }
4096           case CompositeDisplaceCommand:
4097           {
4098             /*
4099               Get horizontal and vertical scale displacement geometry.
4100             */
4101             (void) XSetFunction(display,windows->image.highlight_context,
4102               GXcopy);
4103             (void) XDialogWidget(display,windows,"Displace",
4104               "Enter the horizontal and vertical scale:",displacement_geometry);
4105             (void) XSetFunction(display,windows->image.highlight_context,
4106               GXinvert);
4107             if (*displacement_geometry == '\0')
4108               break;
4109             compose=DisplaceCompositeOp;
4110             break;
4111           }
4112           case CompositeHelpCommand:
4113           {
4114             (void) XSetFunction(display,windows->image.highlight_context,
4115               GXcopy);
4116             XTextViewWidget(display,resource_info,windows,MagickFalse,
4117               "Help Viewer - Image Composite",ImageCompositeHelp);
4118             (void) XSetFunction(display,windows->image.highlight_context,
4119               GXinvert);
4120             break;
4121           }
4122           case CompositeDismissCommand:
4123           {
4124             /*
4125               Prematurely exit.
4126             */
4127             state|=EscapeState;
4128             state|=ExitState;
4129             break;
4130           }
4131           default:
4132             break;
4133         }
4134         continue;
4135       }
4136     switch (event.type)
4137     {
4138       case ButtonPress:
4139       {
4140         if (image->debug != MagickFalse)
4141           (void) LogMagickEvent(X11Event,GetMagickModule(),
4142             "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
4143             event.xbutton.button,event.xbutton.x,event.xbutton.y);
4144         if (event.xbutton.button != Button1)
4145           break;
4146         if (event.xbutton.window != windows->image.id)
4147           break;
4148         /*
4149           Change cursor.
4150         */
4151         composite_info.width=composite_image->columns;
4152         composite_info.height=composite_image->rows;
4153         (void) XCheckDefineCursor(display,windows->image.id,cursor);
4154         composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4155         composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4156         break;
4157       }
4158       case ButtonRelease:
4159       {
4160         if (image->debug != MagickFalse)
4161           (void) LogMagickEvent(X11Event,GetMagickModule(),
4162             "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
4163             event.xbutton.button,event.xbutton.x,event.xbutton.y);
4164         if (event.xbutton.button != Button1)
4165           break;
4166         if (event.xbutton.window != windows->image.id)
4167           break;
4168         if ((composite_info.width != 0) && (composite_info.height != 0))
4169           {
4170             /*
4171               User has selected the location of the composite image.
4172             */
4173             composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4174             composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4175             state|=ExitState;
4176           }
4177         break;
4178       }
4179       case Expose:
4180         break;
4181       case KeyPress:
4182       {
4183         char
4184           command[MaxTextExtent];
4185
4186         KeySym
4187           key_symbol;
4188
4189         int
4190           length;
4191
4192         if (event.xkey.window != windows->image.id)
4193           break;
4194         /*
4195           Respond to a user key press.
4196         */
4197         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
4198           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4199         *(command+length)='\0';
4200         if (image->debug != MagickFalse)
4201           (void) LogMagickEvent(X11Event,GetMagickModule(),
4202             "Key press: 0x%lx (%s)",(unsigned long) key_symbol,command);
4203         switch ((int) key_symbol)
4204         {
4205           case XK_Escape:
4206           case XK_F20:
4207           {
4208             /*
4209               Prematurely exit.
4210             */
4211             composite_image=DestroyImage(composite_image);
4212             state|=EscapeState;
4213             state|=ExitState;
4214             break;
4215           }
4216           case XK_F1:
4217           case XK_Help:
4218           {
4219             (void) XSetFunction(display,windows->image.highlight_context,
4220               GXcopy);
4221             XTextViewWidget(display,resource_info,windows,MagickFalse,
4222               "Help Viewer - Image Composite",ImageCompositeHelp);
4223             (void) XSetFunction(display,windows->image.highlight_context,
4224               GXinvert);
4225             break;
4226           }
4227           default:
4228           {
4229             (void) XBell(display,0);
4230             break;
4231           }
4232         }
4233         break;
4234       }
4235       case MotionNotify:
4236       {
4237         /*
4238           Map and unmap Info widget as text cursor crosses its boundaries.
4239         */
4240         x=event.xmotion.x;
4241         y=event.xmotion.y;
4242         if (windows->info.mapped != MagickFalse)
4243           {
4244             if ((x < (int) (windows->info.x+windows->info.width)) &&
4245                 (y < (int) (windows->info.y+windows->info.height)))
4246               (void) XWithdrawWindow(display,windows->info.id,
4247                 windows->info.screen);
4248           }
4249         else
4250           if ((x > (int) (windows->info.x+windows->info.width)) ||
4251               (y > (int) (windows->info.y+windows->info.height)))
4252             (void) XMapWindow(display,windows->info.id);
4253         composite_info.x=(ssize_t) windows->image.x+x;
4254         composite_info.y=(ssize_t) windows->image.y+y;
4255         break;
4256       }
4257       default:
4258       {
4259         if (image->debug != MagickFalse)
4260           (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
4261             event.type);
4262         break;
4263       }
4264     }
4265   } while ((state & ExitState) == 0);
4266   (void) XSelectInput(display,windows->image.id,
4267     windows->image.attributes.event_mask);
4268   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
4269   XSetCursorState(display,windows,MagickFalse);
4270   (void) XFreeCursor(display,cursor);
4271   if ((state & EscapeState) != 0)
4272     return(MagickTrue);
4273   /*
4274     Image compositing is relative to image configuration.
4275   */
4276   XSetCursorState(display,windows,MagickTrue);
4277   XCheckRefreshWindows(display,windows);
4278   width=(unsigned int) image->columns;
4279   height=(unsigned int) image->rows;
4280   x=0;
4281   y=0;
4282   if (windows->image.crop_geometry != (char *) NULL)
4283     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
4284   scale_factor=(MagickRealType) width/windows->image.ximage->width;
4285   composite_info.x+=x;
4286   composite_info.x=(ssize_t) (scale_factor*composite_info.x+0.5);
4287   composite_info.width=(unsigned int) (scale_factor*composite_info.width+0.5);
4288   scale_factor=(MagickRealType) height/windows->image.ximage->height;
4289   composite_info.y+=y;
4290   composite_info.y=(ssize_t) (scale_factor*composite_info.y+0.5);
4291   composite_info.height=(unsigned int) (scale_factor*composite_info.height+0.5);
4292   if ((composite_info.width != composite_image->columns) ||
4293       (composite_info.height != composite_image->rows))
4294     {
4295       Image
4296         *resize_image;
4297
4298       /*
4299         Scale composite image.
4300       */
4301       resize_image=ResizeImage(composite_image,composite_info.width,
4302         composite_info.height,composite_image->filter,composite_image->blur,
4303         exception);
4304       composite_image=DestroyImage(composite_image);
4305       if (resize_image == (Image *) NULL)
4306         {
4307           XSetCursorState(display,windows,MagickFalse);
4308           return(MagickFalse);
4309         }
4310       composite_image=resize_image;
4311     }
4312   if (compose == DisplaceCompositeOp)
4313     (void) SetImageArtifact(composite_image,"compose:args",
4314       displacement_geometry);
4315   if (blend != 0.0)
4316     {
4317       CacheView
4318         *image_view;
4319
4320       int
4321         y;
4322
4323       Quantum
4324         opacity;
4325
4326       register int
4327         x;
4328
4329       register Quantum
4330         *q;
4331
4332       /*
4333         Create mattes for blending.
4334       */
4335       (void) SetImageAlphaChannel(composite_image,OpaqueAlphaChannel,exception);
4336       opacity=(Quantum) (ScaleQuantumToChar((Quantum) QuantumRange)-
4337         ((ssize_t) ScaleQuantumToChar((Quantum) QuantumRange)*blend)/100);
4338       if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
4339         return(MagickFalse);
4340       image->matte=MagickTrue;
4341       image_view=AcquireCacheView(image);
4342       for (y=0; y < (int) image->rows; y++)
4343       {
4344         q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,image->columns,1,
4345           exception);
4346         if (q == (Quantum *) NULL)
4347           break;
4348         for (x=0; x < (int) image->columns; x++)
4349         {
4350           SetPixelAlpha(image,opacity,q);
4351           q+=GetPixelChannels(image);
4352         }
4353         if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4354           break;
4355       }
4356       image_view=DestroyCacheView(image_view);
4357     }
4358   /*
4359     Composite image with X Image window.
4360   */
4361   (void) CompositeImage(image,compose,composite_image,composite_info.x,
4362     composite_info.y,exception);
4363   composite_image=DestroyImage(composite_image);
4364   XSetCursorState(display,windows,MagickFalse);
4365   /*
4366     Update image configuration.
4367   */
4368   XConfigureImageColormap(display,resource_info,windows,image);
4369   (void) XConfigureImage(display,resource_info,windows,image,exception);
4370   return(MagickTrue);
4371 }
4372 \f
4373 /*
4374 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4375 %                                                                             %
4376 %                                                                             %
4377 %                                                                             %
4378 +   X C o n f i g u r e I m a g e                                             %
4379 %                                                                             %
4380 %                                                                             %
4381 %                                                                             %
4382 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4383 %
4384 %  XConfigureImage() creates a new X image.  It also notifies the window
4385 %  manager of the new image size and configures the transient widows.
4386 %
4387 %  The format of the XConfigureImage method is:
4388 %
4389 %      MagickBooleanType XConfigureImage(Display *display,
4390 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
4391 %        ExceptionInfo *exception)
4392 %
4393 %  A description of each parameter follows:
4394 %
4395 %    o display: Specifies a connection to an X server; returned from
4396 %      XOpenDisplay.
4397 %
4398 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4399 %
4400 %    o windows: Specifies a pointer to a XWindows structure.
4401 %
4402 %    o image: the image.
4403 %
4404 %    o exception: return any errors or warnings in this structure.
4405 %
4406 %    o exception: return any errors or warnings in this structure.
4407 %
4408 */
4409 static MagickBooleanType XConfigureImage(Display *display,
4410   XResourceInfo *resource_info,XWindows *windows,Image *image,
4411   ExceptionInfo *exception)
4412 {
4413   char
4414     geometry[MaxTextExtent];
4415
4416   MagickStatusType
4417     status;
4418
4419   size_t
4420     mask,
4421     height,
4422     width;
4423
4424   ssize_t
4425     x,
4426     y;
4427
4428   XSizeHints
4429     *size_hints;
4430
4431   XWindowChanges
4432     window_changes;
4433
4434   /*
4435     Dismiss if window dimensions are zero.
4436   */
4437   width=(unsigned int) windows->image.window_changes.width;
4438   height=(unsigned int) windows->image.window_changes.height;
4439   if (image->debug != MagickFalse)
4440     (void) LogMagickEvent(X11Event,GetMagickModule(),
4441       "Configure Image: %dx%d=>%.20gx%.20g",windows->image.ximage->width,
4442       windows->image.ximage->height,(double) width,(double) height);
4443   if ((width*height) == 0)
4444     return(MagickTrue);
4445   x=0;
4446   y=0;
4447   /*
4448     Resize image to fit Image window dimensions.
4449   */
4450   XSetCursorState(display,windows,MagickTrue);
4451   (void) XFlush(display);
4452   if (((int) width != windows->image.ximage->width) ||
4453       ((int) height != windows->image.ximage->height))
4454     image->taint=MagickTrue;
4455   windows->magnify.x=(int)
4456     width*windows->magnify.x/windows->image.ximage->width;
4457   windows->magnify.y=(int)
4458     height*windows->magnify.y/windows->image.ximage->height;
4459   windows->image.x=(int) (width*windows->image.x/windows->image.ximage->width);
4460   windows->image.y=(int)
4461     (height*windows->image.y/windows->image.ximage->height);
4462   status=XMakeImage(display,resource_info,&windows->image,image,
4463     (unsigned int) width,(unsigned int) height,exception);
4464   if (status == MagickFalse)
4465     XNoticeWidget(display,windows,"Unable to configure X image:",
4466       windows->image.name);
4467   /*
4468     Notify window manager of the new configuration.
4469   */
4470   if (resource_info->image_geometry != (char *) NULL)
4471     (void) FormatLocaleString(geometry,MaxTextExtent,"%s>!",
4472       resource_info->image_geometry);
4473   else
4474     (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>!",
4475       XDisplayWidth(display,windows->image.screen),
4476       XDisplayHeight(display,windows->image.screen));
4477   (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
4478   window_changes.width=(int) width;
4479   if (window_changes.width > XDisplayWidth(display,windows->image.screen))
4480     window_changes.width=XDisplayWidth(display,windows->image.screen);
4481   window_changes.height=(int) height;
4482   if (window_changes.height > XDisplayHeight(display,windows->image.screen))
4483     window_changes.height=XDisplayHeight(display,windows->image.screen);
4484   mask=(size_t) (CWWidth | CWHeight);
4485   if (resource_info->backdrop)
4486     {
4487       mask|=CWX | CWY;
4488       window_changes.x=(int)
4489         ((XDisplayWidth(display,windows->image.screen)/2)-(width/2));
4490       window_changes.y=(int)
4491         ((XDisplayHeight(display,windows->image.screen)/2)-(height/2));
4492     }
4493   (void) XReconfigureWMWindow(display,windows->image.id,windows->image.screen,
4494     (unsigned int) mask,&window_changes);
4495   (void) XClearWindow(display,windows->image.id);
4496   XRefreshWindow(display,&windows->image,(XEvent *) NULL);
4497   /*
4498     Update Magnify window configuration.
4499   */
4500   if (windows->magnify.mapped != MagickFalse)
4501     XMakeMagnifyImage(display,windows);
4502   windows->pan.crop_geometry=windows->image.crop_geometry;
4503   XBestIconSize(display,&windows->pan,image);
4504   while (((windows->pan.width << 1) < MaxIconSize) &&
4505          ((windows->pan.height << 1) < MaxIconSize))
4506   {
4507     windows->pan.width<<=1;
4508     windows->pan.height<<=1;
4509   }
4510   if (windows->pan.geometry != (char *) NULL)
4511     (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
4512       &windows->pan.width,&windows->pan.height);
4513   window_changes.width=(int) windows->pan.width;
4514   window_changes.height=(int) windows->pan.height;
4515   size_hints=XAllocSizeHints();
4516   if (size_hints != (XSizeHints *) NULL)
4517     {
4518       /*
4519         Set new size hints.
4520       */
4521       size_hints->flags=PSize | PMinSize | PMaxSize;
4522       size_hints->width=window_changes.width;
4523       size_hints->height=window_changes.height;
4524       size_hints->min_width=size_hints->width;
4525       size_hints->min_height=size_hints->height;
4526       size_hints->max_width=size_hints->width;
4527       size_hints->max_height=size_hints->height;
4528       (void) XSetNormalHints(display,windows->pan.id,size_hints);
4529       (void) XFree((void *) size_hints);
4530     }
4531   (void) XReconfigureWMWindow(display,windows->pan.id,windows->pan.screen,
4532     (unsigned int) (CWWidth | CWHeight),&window_changes);
4533   /*
4534     Update icon window configuration.
4535   */
4536   windows->icon.crop_geometry=windows->image.crop_geometry;
4537   XBestIconSize(display,&windows->icon,image);
4538   window_changes.width=(int) windows->icon.width;
4539   window_changes.height=(int) windows->icon.height;
4540   (void) XReconfigureWMWindow(display,windows->icon.id,windows->icon.screen,
4541     (unsigned int) (CWWidth | CWHeight),&window_changes);
4542   XSetCursorState(display,windows,MagickFalse);
4543   return(status != 0 ? MagickTrue : MagickFalse);
4544 }
4545 \f
4546 /*
4547 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4548 %                                                                             %
4549 %                                                                             %
4550 %                                                                             %
4551 +   X C r o p I m a g e                                                       %
4552 %                                                                             %
4553 %                                                                             %
4554 %                                                                             %
4555 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4556 %
4557 %  XCropImage() allows the user to select a region of the image and crop, copy,
4558 %  or cut it.  For copy or cut, the image can subsequently be composited onto
4559 %  the image with XPasteImage.
4560 %
4561 %  The format of the XCropImage method is:
4562 %
4563 %      MagickBooleanType XCropImage(Display *display,
4564 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
4565 %        const ClipboardMode mode,ExceptionInfo *exception)
4566 %
4567 %  A description of each parameter follows:
4568 %
4569 %    o display: Specifies a connection to an X server; returned from
4570 %      XOpenDisplay.
4571 %
4572 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4573 %
4574 %    o windows: Specifies a pointer to a XWindows structure.
4575 %
4576 %    o image: the image; returned from ReadImage.
4577 %
4578 %    o mode: This unsigned value specified whether the image should be
4579 %      cropped, copied, or cut.
4580 %
4581 %    o exception: return any errors or warnings in this structure.
4582 %
4583 */
4584 static MagickBooleanType XCropImage(Display *display,
4585   XResourceInfo *resource_info,XWindows *windows,Image *image,
4586   const ClipboardMode mode,ExceptionInfo *exception)
4587 {
4588   static const char
4589     *CropModeMenu[] =
4590     {
4591       "Help",
4592       "Dismiss",
4593       (char *) NULL
4594     },
4595     *RectifyModeMenu[] =
4596     {
4597       "Crop",
4598       "Help",
4599       "Dismiss",
4600       (char *) NULL
4601     };
4602
4603   static const ModeType
4604     CropCommands[] =
4605     {
4606       CropHelpCommand,
4607       CropDismissCommand
4608     },
4609     RectifyCommands[] =
4610     {
4611       RectifyCopyCommand,
4612       RectifyHelpCommand,
4613       RectifyDismissCommand
4614     };
4615
4616   CacheView
4617     *image_view;
4618
4619   char
4620     command[MaxTextExtent],
4621     text[MaxTextExtent];
4622
4623   Cursor
4624     cursor;
4625
4626   int
4627     id,
4628     x,
4629     y;
4630
4631   KeySym
4632     key_symbol;
4633
4634   Image
4635     *crop_image;
4636
4637   MagickRealType
4638     scale_factor;
4639
4640   RectangleInfo
4641     crop_info,
4642     highlight_info;
4643
4644   register Quantum
4645     *q;
4646
4647   unsigned int
4648     height,
4649     width;
4650
4651   size_t
4652     state;
4653
4654   XEvent
4655     event;
4656
4657   /*
4658     Map Command widget.
4659   */
4660   switch (mode)
4661   {
4662     case CopyMode:
4663     {
4664       (void) CloneString(&windows->command.name,"Copy");
4665       break;
4666     }
4667     case CropMode:
4668     {
4669       (void) CloneString(&windows->command.name,"Crop");
4670       break;
4671     }
4672     case CutMode:
4673     {
4674       (void) CloneString(&windows->command.name,"Cut");
4675       break;
4676     }
4677   }
4678   RectifyModeMenu[0]=windows->command.name;
4679   windows->command.data=0;
4680   (void) XCommandWidget(display,windows,CropModeMenu,(XEvent *) NULL);
4681   (void) XMapRaised(display,windows->command.id);
4682   XClientMessage(display,windows->image.id,windows->im_protocols,
4683     windows->im_update_widget,CurrentTime);
4684   /*
4685     Track pointer until button 1 is pressed.
4686   */
4687   XQueryPosition(display,windows->image.id,&x,&y);
4688   (void) XSelectInput(display,windows->image.id,
4689     windows->image.attributes.event_mask | PointerMotionMask);
4690   crop_info.x=(ssize_t) windows->image.x+x;
4691   crop_info.y=(ssize_t) windows->image.y+y;
4692   crop_info.width=0;
4693   crop_info.height=0;
4694   cursor=XCreateFontCursor(display,XC_fleur);
4695   state=DefaultState;
4696   do
4697   {
4698     if (windows->info.mapped != MagickFalse)
4699       {
4700         /*
4701           Display pointer position.
4702         */
4703         (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
4704           (long) crop_info.x,(long) crop_info.y);
4705         XInfoWidget(display,windows,text);
4706       }
4707     /*
4708       Wait for next event.
4709     */
4710     XScreenEvent(display,windows,&event);
4711     if (event.xany.window == windows->command.id)
4712       {
4713         /*
4714           Select a command from the Command widget.
4715         */
4716         id=XCommandWidget(display,windows,CropModeMenu,&event);
4717         if (id < 0)
4718           continue;
4719         switch (CropCommands[id])
4720         {
4721           case CropHelpCommand:
4722           {
4723             switch (mode)
4724             {
4725               case CopyMode:
4726               {
4727                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4728                   "Help Viewer - Image Copy",ImageCopyHelp);
4729                 break;
4730               }
4731               case CropMode:
4732               {
4733                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4734                   "Help Viewer - Image Crop",ImageCropHelp);
4735                 break;
4736               }
4737               case CutMode:
4738               {
4739                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4740                   "Help Viewer - Image Cut",ImageCutHelp);
4741                 break;
4742               }
4743             }
4744             break;
4745           }
4746           case CropDismissCommand:
4747           {
4748             /*
4749               Prematurely exit.
4750             */
4751             state|=EscapeState;
4752             state|=ExitState;
4753             break;
4754           }
4755           default:
4756             break;
4757         }
4758         continue;
4759       }
4760     switch (event.type)
4761     {
4762       case ButtonPress:
4763       {
4764         if (event.xbutton.button != Button1)
4765           break;
4766         if (event.xbutton.window != windows->image.id)
4767           break;
4768         /*
4769           Note first corner of cropping rectangle-- exit loop.
4770         */
4771         (void) XCheckDefineCursor(display,windows->image.id,cursor);
4772         crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4773         crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4774         state|=ExitState;
4775         break;
4776       }
4777       case ButtonRelease:
4778         break;
4779       case Expose:
4780         break;
4781       case KeyPress:
4782       {
4783         if (event.xkey.window != windows->image.id)
4784           break;
4785         /*
4786           Respond to a user key press.
4787         */
4788         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
4789           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4790         switch ((int) key_symbol)
4791         {
4792           case XK_Escape:
4793           case XK_F20:
4794           {
4795             /*
4796               Prematurely exit.
4797             */
4798             state|=EscapeState;
4799             state|=ExitState;
4800             break;
4801           }
4802           case XK_F1:
4803           case XK_Help:
4804           {
4805             switch (mode)
4806             {
4807               case CopyMode:
4808               {
4809                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4810                   "Help Viewer - Image Copy",ImageCopyHelp);
4811                 break;
4812               }
4813               case CropMode:
4814               {
4815                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4816                   "Help Viewer - Image Crop",ImageCropHelp);
4817                 break;
4818               }
4819               case CutMode:
4820               {
4821                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4822                   "Help Viewer - Image Cut",ImageCutHelp);
4823                 break;
4824               }
4825             }
4826             break;
4827           }
4828           default:
4829           {
4830             (void) XBell(display,0);
4831             break;
4832           }
4833         }
4834         break;
4835       }
4836       case MotionNotify:
4837       {
4838         if (event.xmotion.window != windows->image.id)
4839           break;
4840         /*
4841           Map and unmap Info widget as text cursor crosses its boundaries.
4842         */
4843         x=event.xmotion.x;
4844         y=event.xmotion.y;
4845         if (windows->info.mapped != MagickFalse)
4846           {
4847             if ((x < (int) (windows->info.x+windows->info.width)) &&
4848                 (y < (int) (windows->info.y+windows->info.height)))
4849               (void) XWithdrawWindow(display,windows->info.id,
4850                 windows->info.screen);
4851           }
4852         else
4853           if ((x > (int) (windows->info.x+windows->info.width)) ||
4854               (y > (int) (windows->info.y+windows->info.height)))
4855             (void) XMapWindow(display,windows->info.id);
4856         crop_info.x=(ssize_t) windows->image.x+x;
4857         crop_info.y=(ssize_t) windows->image.y+y;
4858         break;
4859       }
4860       default:
4861         break;
4862     }
4863   } while ((state & ExitState) == 0);
4864   (void) XSelectInput(display,windows->image.id,
4865     windows->image.attributes.event_mask);
4866   if ((state & EscapeState) != 0)
4867     {
4868       /*
4869         User want to exit without cropping.
4870       */
4871       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4872       (void) XFreeCursor(display,cursor);
4873       return(MagickTrue);
4874     }
4875   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
4876   do
4877   {
4878     /*
4879       Size rectangle as pointer moves until the mouse button is released.
4880     */
4881     x=(int) crop_info.x;
4882     y=(int) crop_info.y;
4883     crop_info.width=0;
4884     crop_info.height=0;
4885     state=DefaultState;
4886     do
4887     {
4888       highlight_info=crop_info;
4889       highlight_info.x=crop_info.x-windows->image.x;
4890       highlight_info.y=crop_info.y-windows->image.y;
4891       if ((highlight_info.width > 3) && (highlight_info.height > 3))
4892         {
4893           /*
4894             Display info and draw cropping rectangle.
4895           */
4896           if (windows->info.mapped == MagickFalse)
4897             (void) XMapWindow(display,windows->info.id);
4898           (void) FormatLocaleString(text,MaxTextExtent,
4899             " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
4900             crop_info.height,(double) crop_info.x,(double) crop_info.y);
4901           XInfoWidget(display,windows,text);
4902           XHighlightRectangle(display,windows->image.id,
4903             windows->image.highlight_context,&highlight_info);
4904         }
4905       else
4906         if (windows->info.mapped != MagickFalse)
4907           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4908       /*
4909         Wait for next event.
4910       */
4911       XScreenEvent(display,windows,&event);
4912       if ((highlight_info.width > 3) && (highlight_info.height > 3))
4913         XHighlightRectangle(display,windows->image.id,
4914           windows->image.highlight_context,&highlight_info);
4915       switch (event.type)
4916       {
4917         case ButtonPress:
4918         {
4919           crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4920           crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4921           break;
4922         }
4923         case ButtonRelease:
4924         {
4925           /*
4926             User has committed to cropping rectangle.
4927           */
4928           crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4929           crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4930           XSetCursorState(display,windows,MagickFalse);
4931           state|=ExitState;
4932           windows->command.data=0;
4933           (void) XCommandWidget(display,windows,RectifyModeMenu,
4934             (XEvent *) NULL);
4935           break;
4936         }
4937         case Expose:
4938           break;
4939         case MotionNotify:
4940         {
4941           crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
4942           crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
4943         }
4944         default:
4945           break;
4946       }
4947       if ((((int) crop_info.x != x) && ((int) crop_info.y != y)) ||
4948           ((state & ExitState) != 0))
4949         {
4950           /*
4951             Check boundary conditions.
4952           */
4953           if (crop_info.x < 0)
4954             crop_info.x=0;
4955           else
4956             if (crop_info.x > (ssize_t) windows->image.ximage->width)
4957               crop_info.x=(ssize_t) windows->image.ximage->width;
4958           if ((int) crop_info.x < x)
4959             crop_info.width=(unsigned int) (x-crop_info.x);
4960           else
4961             {
4962               crop_info.width=(unsigned int) (crop_info.x-x);
4963               crop_info.x=(ssize_t) x;
4964             }
4965           if (crop_info.y < 0)
4966             crop_info.y=0;
4967           else
4968             if (crop_info.y > (ssize_t) windows->image.ximage->height)
4969               crop_info.y=(ssize_t) windows->image.ximage->height;
4970           if ((int) crop_info.y < y)
4971             crop_info.height=(unsigned int) (y-crop_info.y);
4972           else
4973             {
4974               crop_info.height=(unsigned int) (crop_info.y-y);
4975               crop_info.y=(ssize_t) y;
4976             }
4977         }
4978     } while ((state & ExitState) == 0);
4979     /*
4980       Wait for user to grab a corner of the rectangle or press return.
4981     */
4982     state=DefaultState;
4983     (void) XMapWindow(display,windows->info.id);
4984     do
4985     {
4986       if (windows->info.mapped != MagickFalse)
4987         {
4988           /*
4989             Display pointer position.
4990           */
4991           (void) FormatLocaleString(text,MaxTextExtent,
4992             " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
4993             crop_info.height,(double) crop_info.x,(double) crop_info.y);
4994           XInfoWidget(display,windows,text);
4995         }
4996       highlight_info=crop_info;
4997       highlight_info.x=crop_info.x-windows->image.x;
4998       highlight_info.y=crop_info.y-windows->image.y;
4999       if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
5000         {
5001           state|=EscapeState;
5002           state|=ExitState;
5003           break;
5004         }
5005       XHighlightRectangle(display,windows->image.id,
5006         windows->image.highlight_context,&highlight_info);
5007       XScreenEvent(display,windows,&event);
5008       if (event.xany.window == windows->command.id)
5009         {
5010           /*
5011             Select a command from the Command widget.
5012           */
5013           (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
5014           id=XCommandWidget(display,windows,RectifyModeMenu,&event);
5015           (void) XSetFunction(display,windows->image.highlight_context,
5016             GXinvert);
5017           XHighlightRectangle(display,windows->image.id,
5018             windows->image.highlight_context,&highlight_info);
5019           if (id >= 0)
5020             switch (RectifyCommands[id])
5021             {
5022               case RectifyCopyCommand:
5023               {
5024                 state|=ExitState;
5025                 break;
5026               }
5027               case RectifyHelpCommand:
5028               {
5029                 (void) XSetFunction(display,windows->image.highlight_context,
5030                   GXcopy);
5031                 switch (mode)
5032                 {
5033                   case CopyMode:
5034                   {
5035                     XTextViewWidget(display,resource_info,windows,MagickFalse,
5036                       "Help Viewer - Image Copy",ImageCopyHelp);
5037                     break;
5038                   }
5039                   case CropMode:
5040                   {
5041                     XTextViewWidget(display,resource_info,windows,MagickFalse,
5042                       "Help Viewer - Image Crop",ImageCropHelp);
5043                     break;
5044                   }
5045                   case CutMode:
5046                   {
5047                     XTextViewWidget(display,resource_info,windows,MagickFalse,
5048                       "Help Viewer - Image Cut",ImageCutHelp);
5049                     break;
5050                   }
5051                 }
5052                 (void) XSetFunction(display,windows->image.highlight_context,
5053                   GXinvert);
5054                 break;
5055               }
5056               case RectifyDismissCommand:
5057               {
5058                 /*
5059                   Prematurely exit.
5060                 */
5061                 state|=EscapeState;
5062                 state|=ExitState;
5063                 break;
5064               }
5065               default:
5066                 break;
5067             }
5068           continue;
5069         }
5070       XHighlightRectangle(display,windows->image.id,
5071         windows->image.highlight_context,&highlight_info);
5072       switch (event.type)
5073       {
5074         case ButtonPress:
5075         {
5076           if (event.xbutton.button != Button1)
5077             break;
5078           if (event.xbutton.window != windows->image.id)
5079             break;
5080           x=windows->image.x+event.xbutton.x;
5081           y=windows->image.y+event.xbutton.y;
5082           if ((x < (int) (crop_info.x+RoiDelta)) &&
5083               (x > (int) (crop_info.x-RoiDelta)) &&
5084               (y < (int) (crop_info.y+RoiDelta)) &&
5085               (y > (int) (crop_info.y-RoiDelta)))
5086             {
5087               crop_info.x=(ssize_t) (crop_info.x+crop_info.width);
5088               crop_info.y=(ssize_t) (crop_info.y+crop_info.height);
5089               state|=UpdateConfigurationState;
5090               break;
5091             }
5092           if ((x < (int) (crop_info.x+RoiDelta)) &&
5093               (x > (int) (crop_info.x-RoiDelta)) &&
5094               (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5095               (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5096             {
5097               crop_info.x=(ssize_t) (crop_info.x+crop_info.width);
5098               state|=UpdateConfigurationState;
5099               break;
5100             }
5101           if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5102               (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5103               (y < (int) (crop_info.y+RoiDelta)) &&
5104               (y > (int) (crop_info.y-RoiDelta)))
5105             {
5106               crop_info.y=(ssize_t) (crop_info.y+crop_info.height);
5107               state|=UpdateConfigurationState;
5108               break;
5109             }
5110           if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5111               (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5112               (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5113               (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5114             {
5115               state|=UpdateConfigurationState;
5116               break;
5117             }
5118         }
5119         case ButtonRelease:
5120         {
5121           if (event.xbutton.window == windows->pan.id)
5122             if ((highlight_info.x != crop_info.x-windows->image.x) ||
5123                 (highlight_info.y != crop_info.y-windows->image.y))
5124               XHighlightRectangle(display,windows->image.id,
5125                 windows->image.highlight_context,&highlight_info);
5126           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5127             event.xbutton.time);
5128           break;
5129         }
5130         case Expose:
5131         {
5132           if (event.xexpose.window == windows->image.id)
5133             if (event.xexpose.count == 0)
5134               {
5135                 event.xexpose.x=(int) highlight_info.x;
5136                 event.xexpose.y=(int) highlight_info.y;
5137                 event.xexpose.width=(int) highlight_info.width;
5138                 event.xexpose.height=(int) highlight_info.height;
5139                 XRefreshWindow(display,&windows->image,&event);
5140               }
5141           if (event.xexpose.window == windows->info.id)
5142             if (event.xexpose.count == 0)
5143               XInfoWidget(display,windows,text);
5144           break;
5145         }
5146         case KeyPress:
5147         {
5148           if (event.xkey.window != windows->image.id)
5149             break;
5150           /*
5151             Respond to a user key press.
5152           */
5153           (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5154             sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5155           switch ((int) key_symbol)
5156           {
5157             case XK_Escape:
5158             case XK_F20:
5159               state|=EscapeState;
5160             case XK_Return:
5161             {
5162               state|=ExitState;
5163               break;
5164             }
5165             case XK_Home:
5166             case XK_KP_Home:
5167             {
5168               crop_info.x=(ssize_t) (windows->image.width/2L-
5169                 crop_info.width/2L);
5170               crop_info.y=(ssize_t) (windows->image.height/2L-
5171                 crop_info.height/2L);
5172               break;
5173             }
5174             case XK_Left:
5175             case XK_KP_Left:
5176             {
5177               crop_info.x--;
5178               break;
5179             }
5180             case XK_Up:
5181             case XK_KP_Up:
5182             case XK_Next:
5183             {
5184               crop_info.y--;
5185               break;
5186             }
5187             case XK_Right:
5188             case XK_KP_Right:
5189             {
5190               crop_info.x++;
5191               break;
5192             }
5193             case XK_Prior:
5194             case XK_Down:
5195             case XK_KP_Down:
5196             {
5197               crop_info.y++;
5198               break;
5199             }
5200             case XK_F1:
5201             case XK_Help:
5202             {
5203               (void) XSetFunction(display,windows->image.highlight_context,
5204                 GXcopy);
5205               switch (mode)
5206               {
5207                 case CopyMode:
5208                 {
5209                   XTextViewWidget(display,resource_info,windows,MagickFalse,
5210                     "Help Viewer - Image Copy",ImageCopyHelp);
5211                   break;
5212                 }
5213                 case CropMode:
5214                 {
5215                   XTextViewWidget(display,resource_info,windows,MagickFalse,
5216                     "Help Viewer - Image Cropg",ImageCropHelp);
5217                   break;
5218                 }
5219                 case CutMode:
5220                 {
5221                   XTextViewWidget(display,resource_info,windows,MagickFalse,
5222                     "Help Viewer - Image Cutg",ImageCutHelp);
5223                   break;
5224                 }
5225               }
5226               (void) XSetFunction(display,windows->image.highlight_context,
5227                 GXinvert);
5228               break;
5229             }
5230             default:
5231             {
5232               (void) XBell(display,0);
5233               break;
5234             }
5235           }
5236           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5237             event.xkey.time);
5238           break;
5239         }
5240         case KeyRelease:
5241           break;
5242         case MotionNotify:
5243         {
5244           if (event.xmotion.window != windows->image.id)
5245             break;
5246           /*
5247             Map and unmap Info widget as text cursor crosses its boundaries.
5248           */
5249           x=event.xmotion.x;
5250           y=event.xmotion.y;
5251           if (windows->info.mapped != MagickFalse)
5252             {
5253               if ((x < (int) (windows->info.x+windows->info.width)) &&
5254                   (y < (int) (windows->info.y+windows->info.height)))
5255                 (void) XWithdrawWindow(display,windows->info.id,
5256                   windows->info.screen);
5257             }
5258           else
5259             if ((x > (int) (windows->info.x+windows->info.width)) ||
5260                 (y > (int) (windows->info.y+windows->info.height)))
5261               (void) XMapWindow(display,windows->info.id);
5262           crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
5263           crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
5264           break;
5265         }
5266         case SelectionRequest:
5267         {
5268           XSelectionEvent
5269             notify;
5270
5271           XSelectionRequestEvent
5272             *request;
5273
5274           /*
5275             Set primary selection.
5276           */
5277           (void) FormatLocaleString(text,MaxTextExtent,
5278             "%.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
5279             crop_info.height,(double) crop_info.x,(double) crop_info.y);
5280           request=(&(event.xselectionrequest));
5281           (void) XChangeProperty(request->display,request->requestor,
5282             request->property,request->target,8,PropModeReplace,
5283             (unsigned char *) text,(int) strlen(text));
5284           notify.type=SelectionNotify;
5285           notify.display=request->display;
5286           notify.requestor=request->requestor;
5287           notify.selection=request->selection;
5288           notify.target=request->target;
5289           notify.time=request->time;
5290           if (request->property == None)
5291             notify.property=request->target;
5292           else
5293             notify.property=request->property;
5294           (void) XSendEvent(request->display,request->requestor,False,0,
5295             (XEvent *) &notify);
5296         }
5297         default:
5298           break;
5299       }
5300       if ((state & UpdateConfigurationState) != 0)
5301         {
5302           (void) XPutBackEvent(display,&event);
5303           (void) XCheckDefineCursor(display,windows->image.id,cursor);
5304           break;
5305         }
5306     } while ((state & ExitState) == 0);
5307   } while ((state & ExitState) == 0);
5308   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
5309   XSetCursorState(display,windows,MagickFalse);
5310   if ((state & EscapeState) != 0)
5311     return(MagickTrue);
5312   if (mode == CropMode)
5313     if (((int) crop_info.width != windows->image.ximage->width) ||
5314         ((int) crop_info.height != windows->image.ximage->height))
5315       {
5316         /*
5317           Reconfigure Image window as defined by cropping rectangle.
5318         */
5319         XSetCropGeometry(display,windows,&crop_info,image);
5320         windows->image.window_changes.width=(int) crop_info.width;
5321         windows->image.window_changes.height=(int) crop_info.height;
5322         (void) XConfigureImage(display,resource_info,windows,image,exception);
5323         return(MagickTrue);
5324       }
5325   /*
5326     Copy image before applying image transforms.
5327   */
5328   XSetCursorState(display,windows,MagickTrue);
5329   XCheckRefreshWindows(display,windows);
5330   width=(unsigned int) image->columns;
5331   height=(unsigned int) image->rows;
5332   x=0;
5333   y=0;
5334   if (windows->image.crop_geometry != (char *) NULL)
5335     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
5336   scale_factor=(MagickRealType) width/windows->image.ximage->width;
5337   crop_info.x+=x;
5338   crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
5339   crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
5340   scale_factor=(MagickRealType) height/windows->image.ximage->height;
5341   crop_info.y+=y;
5342   crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
5343   crop_info.height=(unsigned int) (scale_factor*crop_info.height+0.5);
5344   crop_image=CropImage(image,&crop_info,exception);
5345   XSetCursorState(display,windows,MagickFalse);
5346   if (crop_image == (Image *) NULL)
5347     return(MagickFalse);
5348   if (resource_info->copy_image != (Image *) NULL)
5349     resource_info->copy_image=DestroyImage(resource_info->copy_image);
5350   resource_info->copy_image=crop_image;
5351   if (mode == CopyMode)
5352     {
5353       (void) XConfigureImage(display,resource_info,windows,image,exception);
5354       return(MagickTrue);
5355     }
5356   /*
5357     Cut image.
5358   */
5359   if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
5360     return(MagickFalse);
5361   image->matte=MagickTrue;
5362   image_view=AcquireCacheView(image);
5363   for (y=0; y < (int) crop_info.height; y++)
5364   {
5365     q=GetCacheViewAuthenticPixels(image_view,crop_info.x,y+crop_info.y,
5366       crop_info.width,1,exception);
5367     if (q == (Quantum *) NULL)
5368       break;
5369     for (x=0; x < (int) crop_info.width; x++)
5370     {
5371       SetPixelAlpha(image,TransparentAlpha,q);
5372       q+=GetPixelChannels(image);
5373     }
5374     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
5375       break;
5376   }
5377   image_view=DestroyCacheView(image_view);
5378   /*
5379     Update image configuration.
5380   */
5381   XConfigureImageColormap(display,resource_info,windows,image);
5382   (void) XConfigureImage(display,resource_info,windows,image,exception);
5383   return(MagickTrue);
5384 }
5385 \f
5386 /*
5387 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5388 %                                                                             %
5389 %                                                                             %
5390 %                                                                             %
5391 +   X D r a w I m a g e                                                       %
5392 %                                                                             %
5393 %                                                                             %
5394 %                                                                             %
5395 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5396 %
5397 %  XDrawEditImage() draws a graphic element (point, line, rectangle, etc.) on
5398 %  the image.
5399 %
5400 %  The format of the XDrawEditImage method is:
5401 %
5402 %      MagickBooleanType XDrawEditImage(Display *display,
5403 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
5404 %        ExceptionInfo *exception)
5405 %
5406 %  A description of each parameter follows:
5407 %
5408 %    o display: Specifies a connection to an X server; returned from
5409 %      XOpenDisplay.
5410 %
5411 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
5412 %
5413 %    o windows: Specifies a pointer to a XWindows structure.
5414 %
5415 %    o image: the image.
5416 %
5417 %    o exception: return any errors or warnings in this structure.
5418 %
5419 */
5420 static MagickBooleanType XDrawEditImage(Display *display,
5421   XResourceInfo *resource_info,XWindows *windows,Image **image,
5422   ExceptionInfo *exception)
5423 {
5424   static const char
5425     *DrawMenu[] =
5426     {
5427       "Element",
5428       "Color",
5429       "Stipple",
5430       "Width",
5431       "Undo",
5432       "Help",
5433       "Dismiss",
5434       (char *) NULL
5435     };
5436
5437   static ElementType
5438     element = PointElement;
5439
5440   static const ModeType
5441     DrawCommands[] =
5442     {
5443       DrawElementCommand,
5444       DrawColorCommand,
5445       DrawStippleCommand,
5446       DrawWidthCommand,
5447       DrawUndoCommand,
5448       DrawHelpCommand,
5449       DrawDismissCommand
5450     };
5451
5452   static Pixmap
5453     stipple = (Pixmap) NULL;
5454
5455   static unsigned int
5456     pen_id = 0,
5457     line_width = 1;
5458
5459   char
5460     command[MaxTextExtent],
5461     text[MaxTextExtent];
5462
5463   Cursor
5464     cursor;
5465
5466   int
5467     entry,
5468     id,
5469     number_coordinates,
5470     x,
5471     y;
5472
5473   MagickRealType
5474     degrees;
5475
5476   MagickStatusType
5477     status;
5478
5479   RectangleInfo
5480     rectangle_info;
5481
5482   register int
5483     i;
5484
5485   unsigned int
5486     distance,
5487     height,
5488     max_coordinates,
5489     width;
5490
5491   size_t
5492     state;
5493
5494   Window
5495     root_window;
5496
5497   XDrawInfo
5498     draw_info;
5499
5500   XEvent
5501     event;
5502
5503   XPoint
5504     *coordinate_info;
5505
5506   XSegment
5507     line_info;
5508
5509   /*
5510     Allocate polygon info.
5511   */
5512   max_coordinates=2048;
5513   coordinate_info=(XPoint *) AcquireQuantumMemory((size_t) max_coordinates,
5514     sizeof(*coordinate_info));
5515   if (coordinate_info == (XPoint *) NULL)
5516     {
5517       (void) ThrowMagickException(exception,GetMagickModule(),
5518         ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
5519       return(MagickFalse);
5520     }
5521   /*
5522     Map Command widget.
5523   */
5524   (void) CloneString(&windows->command.name,"Draw");
5525   windows->command.data=4;
5526   (void) XCommandWidget(display,windows,DrawMenu,(XEvent *) NULL);
5527   (void) XMapRaised(display,windows->command.id);
5528   XClientMessage(display,windows->image.id,windows->im_protocols,
5529     windows->im_update_widget,CurrentTime);
5530   /*
5531     Wait for first button press.
5532   */
5533   root_window=XRootWindow(display,XDefaultScreen(display));
5534   draw_info.stencil=OpaqueStencil;
5535   status=MagickTrue;
5536   cursor=XCreateFontCursor(display,XC_tcross);
5537   for ( ; ; )
5538   {
5539     XQueryPosition(display,windows->image.id,&x,&y);
5540     (void) XSelectInput(display,windows->image.id,
5541       windows->image.attributes.event_mask | PointerMotionMask);
5542     (void) XCheckDefineCursor(display,windows->image.id,cursor);
5543     state=DefaultState;
5544     do
5545     {
5546       if (windows->info.mapped != MagickFalse)
5547         {
5548           /*
5549             Display pointer position.
5550           */
5551           (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
5552             x+windows->image.x,y+windows->image.y);
5553           XInfoWidget(display,windows,text);
5554         }
5555       /*
5556         Wait for next event.
5557       */
5558       XScreenEvent(display,windows,&event);
5559       if (event.xany.window == windows->command.id)
5560         {
5561           /*
5562             Select a command from the Command widget.
5563           */
5564           id=XCommandWidget(display,windows,DrawMenu,&event);
5565           if (id < 0)
5566             continue;
5567           switch (DrawCommands[id])
5568           {
5569             case DrawElementCommand:
5570             {
5571               static const char
5572                 *Elements[] =
5573                 {
5574                   "point",
5575                   "line",
5576                   "rectangle",
5577                   "fill rectangle",
5578                   "circle",
5579                   "fill circle",
5580                   "ellipse",
5581                   "fill ellipse",
5582                   "polygon",
5583                   "fill polygon",
5584                   (char *) NULL,
5585                 };
5586
5587               /*
5588                 Select a command from the pop-up menu.
5589               */
5590               element=(ElementType) (XMenuWidget(display,windows,
5591                 DrawMenu[id],Elements,command)+1);
5592               break;
5593             }
5594             case DrawColorCommand:
5595             {
5596               const char
5597                 *ColorMenu[MaxNumberPens+1];
5598
5599               int
5600                 pen_number;
5601
5602               MagickBooleanType
5603                 transparent;
5604
5605               XColor
5606                 color;
5607
5608               /*
5609                 Initialize menu selections.
5610               */
5611               for (i=0; i < (int) (MaxNumberPens-2); i++)
5612                 ColorMenu[i]=resource_info->pen_colors[i];
5613               ColorMenu[MaxNumberPens-2]="transparent";
5614               ColorMenu[MaxNumberPens-1]="Browser...";
5615               ColorMenu[MaxNumberPens]=(char *) NULL;
5616               /*
5617                 Select a pen color from the pop-up menu.
5618               */
5619               pen_number=XMenuWidget(display,windows,DrawMenu[id],
5620                 (const char **) ColorMenu,command);
5621               if (pen_number < 0)
5622                 break;
5623               transparent=pen_number == (MaxNumberPens-2) ? MagickTrue :
5624                 MagickFalse;
5625               if (transparent != MagickFalse)
5626                 {
5627                   draw_info.stencil=TransparentStencil;
5628                   break;
5629                 }
5630               if (pen_number == (MaxNumberPens-1))
5631                 {
5632                   static char
5633                     color_name[MaxTextExtent] = "gray";
5634
5635                   /*
5636                     Select a pen color from a dialog.
5637                   */
5638                   resource_info->pen_colors[pen_number]=color_name;
5639                   XColorBrowserWidget(display,windows,"Select",color_name);
5640                   if (*color_name == '\0')
5641                     break;
5642                 }
5643               /*
5644                 Set pen color.
5645               */
5646               (void) XParseColor(display,windows->map_info->colormap,
5647                 resource_info->pen_colors[pen_number],&color);
5648               XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
5649                 (unsigned int) MaxColors,&color);
5650               windows->pixel_info->pen_colors[pen_number]=color;
5651               pen_id=(unsigned int) pen_number;
5652               draw_info.stencil=OpaqueStencil;
5653               break;
5654             }
5655             case DrawStippleCommand:
5656             {
5657               Image
5658                 *stipple_image;
5659
5660               ImageInfo
5661                 *image_info;
5662
5663               int
5664                 status;
5665
5666               static char
5667                 filename[MaxTextExtent] = "\0";
5668
5669               static const char
5670                 *StipplesMenu[] =
5671                 {
5672                   "Brick",
5673                   "Diagonal",
5674                   "Scales",
5675                   "Vertical",
5676                   "Wavy",
5677                   "Translucent",
5678                   "Opaque",
5679                   (char *) NULL,
5680                   (char *) NULL,
5681                 };
5682
5683               /*
5684                 Select a command from the pop-up menu.
5685               */
5686               StipplesMenu[7]="Open...";
5687               entry=XMenuWidget(display,windows,DrawMenu[id],StipplesMenu,
5688                 command);
5689               if (entry < 0)
5690                 break;
5691               if (stipple != (Pixmap) NULL)
5692                 (void) XFreePixmap(display,stipple);
5693               stipple=(Pixmap) NULL;
5694               if (entry != 7)
5695                 {
5696                   switch (entry)
5697                   {
5698                     case 0:
5699                     {
5700                       stipple=XCreateBitmapFromData(display,root_window,
5701                         (char *) BricksBitmap,BricksWidth,BricksHeight);
5702                       break;
5703                     }
5704                     case 1:
5705                     {
5706                       stipple=XCreateBitmapFromData(display,root_window,
5707                         (char *) DiagonalBitmap,DiagonalWidth,DiagonalHeight);
5708                       break;
5709                     }
5710                     case 2:
5711                     {
5712                       stipple=XCreateBitmapFromData(display,root_window,
5713                         (char *) ScalesBitmap,ScalesWidth,ScalesHeight);
5714                       break;
5715                     }
5716                     case 3:
5717                     {
5718                       stipple=XCreateBitmapFromData(display,root_window,
5719                         (char *) VerticalBitmap,VerticalWidth,VerticalHeight);
5720                       break;
5721                     }
5722                     case 4:
5723                     {
5724                       stipple=XCreateBitmapFromData(display,root_window,
5725                         (char *) WavyBitmap,WavyWidth,WavyHeight);
5726                       break;
5727                     }
5728                     case 5:
5729                     {
5730                       stipple=XCreateBitmapFromData(display,root_window,
5731                         (char *) HighlightBitmap,HighlightWidth,
5732                         HighlightHeight);
5733                       break;
5734                     }
5735                     case 6:
5736                     default:
5737                     {
5738                       stipple=XCreateBitmapFromData(display,root_window,
5739                         (char *) OpaqueBitmap,OpaqueWidth,OpaqueHeight);
5740                       break;
5741                     }
5742                   }
5743                   break;
5744                 }
5745               XFileBrowserWidget(display,windows,"Stipple",filename);
5746               if (*filename == '\0')
5747                 break;
5748               /*
5749                 Read image.
5750               */
5751               XSetCursorState(display,windows,MagickTrue);
5752               XCheckRefreshWindows(display,windows);
5753               image_info=AcquireImageInfo();
5754               (void) CopyMagickString(image_info->filename,filename,
5755                 MaxTextExtent);
5756               stipple_image=ReadImage(image_info,exception);
5757               CatchException(exception);
5758               XSetCursorState(display,windows,MagickFalse);
5759               if (stipple_image == (Image *) NULL)
5760                 break;
5761               (void) AcquireUniqueFileResource(filename);
5762               (void) FormatLocaleString(stipple_image->filename,MaxTextExtent,
5763                 "xbm:%s",filename);
5764               (void) WriteImage(image_info,stipple_image,exception);
5765               stipple_image=DestroyImage(stipple_image);
5766               image_info=DestroyImageInfo(image_info);
5767               status=XReadBitmapFile(display,root_window,filename,&width,
5768                 &height,&stipple,&x,&y);
5769               (void) RelinquishUniqueFileResource(filename);
5770               if ((status != BitmapSuccess) != 0)
5771                 XNoticeWidget(display,windows,"Unable to read X bitmap image:",
5772                   filename);
5773               break;
5774             }
5775             case DrawWidthCommand:
5776             {
5777               static char
5778                 width[MaxTextExtent] = "0";
5779
5780               static const char
5781                 *WidthsMenu[] =
5782                 {
5783                   "1",
5784                   "2",
5785                   "4",
5786                   "8",
5787                   "16",
5788                   "Dialog...",
5789                   (char *) NULL,
5790                 };
5791
5792               /*
5793                 Select a command from the pop-up menu.
5794               */
5795               entry=XMenuWidget(display,windows,DrawMenu[id],WidthsMenu,
5796                 command);
5797               if (entry < 0)
5798                 break;
5799               if (entry != 5)
5800                 {
5801                   line_width=(unsigned int) StringToUnsignedLong(
5802                     WidthsMenu[entry]);
5803                   break;
5804                 }
5805               (void) XDialogWidget(display,windows,"Ok","Enter line width:",
5806                 width);
5807               if (*width == '\0')
5808                 break;
5809               line_width=(unsigned int) StringToUnsignedLong(width);
5810               break;
5811             }
5812             case DrawUndoCommand:
5813             {
5814               (void) XMagickCommand(display,resource_info,windows,UndoCommand,
5815                 image,exception);
5816               break;
5817             }
5818             case DrawHelpCommand:
5819             {
5820               XTextViewWidget(display,resource_info,windows,MagickFalse,
5821                 "Help Viewer - Image Rotation",ImageDrawHelp);
5822               (void) XCheckDefineCursor(display,windows->image.id,cursor);
5823               break;
5824             }
5825             case DrawDismissCommand:
5826             {
5827               /*
5828                 Prematurely exit.
5829               */
5830               state|=EscapeState;
5831               state|=ExitState;
5832               break;
5833             }
5834             default:
5835               break;
5836           }
5837           (void) XCheckDefineCursor(display,windows->image.id,cursor);
5838           continue;
5839         }
5840       switch (event.type)
5841       {
5842         case ButtonPress:
5843         {
5844           if (event.xbutton.button != Button1)
5845             break;
5846           if (event.xbutton.window != windows->image.id)
5847             break;
5848           /*
5849             exit loop.
5850           */
5851           x=event.xbutton.x;
5852           y=event.xbutton.y;
5853           state|=ExitState;
5854           break;
5855         }
5856         case ButtonRelease:
5857           break;
5858         case Expose:
5859           break;
5860         case KeyPress:
5861         {
5862           KeySym
5863             key_symbol;
5864
5865           if (event.xkey.window != windows->image.id)
5866             break;
5867           /*
5868             Respond to a user key press.
5869           */
5870           (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5871             sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5872           switch ((int) key_symbol)
5873           {
5874             case XK_Escape:
5875             case XK_F20:
5876             {
5877               /*
5878                 Prematurely exit.
5879               */
5880               state|=EscapeState;
5881               state|=ExitState;
5882               break;
5883             }
5884             case XK_F1:
5885             case XK_Help:
5886             {
5887               XTextViewWidget(display,resource_info,windows,MagickFalse,
5888                 "Help Viewer - Image Rotation",ImageDrawHelp);
5889               break;
5890             }
5891             default:
5892             {
5893               (void) XBell(display,0);
5894               break;
5895             }
5896           }
5897           break;
5898         }
5899         case MotionNotify:
5900         {
5901           /*
5902             Map and unmap Info widget as text cursor crosses its boundaries.
5903           */
5904           x=event.xmotion.x;
5905           y=event.xmotion.y;
5906           if (windows->info.mapped != MagickFalse)
5907             {
5908               if ((x < (int) (windows->info.x+windows->info.width)) &&
5909                   (y < (int) (windows->info.y+windows->info.height)))
5910                 (void) XWithdrawWindow(display,windows->info.id,
5911                   windows->info.screen);
5912             }
5913           else
5914             if ((x > (int) (windows->info.x+windows->info.width)) ||
5915                 (y > (int) (windows->info.y+windows->info.height)))
5916               (void) XMapWindow(display,windows->info.id);
5917           break;
5918         }
5919       }
5920     } while ((state & ExitState) == 0);
5921     (void) XSelectInput(display,windows->image.id,
5922       windows->image.attributes.event_mask);
5923     (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
5924     if ((state & EscapeState) != 0)
5925       break;
5926     /*
5927       Draw element as pointer moves until the button is released.
5928     */
5929     distance=0;
5930     degrees=0.0;
5931     line_info.x1=x;
5932     line_info.y1=y;
5933     line_info.x2=x;
5934     line_info.y2=y;
5935     rectangle_info.x=(ssize_t) x;
5936     rectangle_info.y=(ssize_t) y;
5937     rectangle_info.width=0;
5938     rectangle_info.height=0;
5939     number_coordinates=1;
5940     coordinate_info->x=x;
5941     coordinate_info->y=y;
5942     (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
5943     state=DefaultState;
5944     do
5945     {
5946       switch (element)
5947       {
5948         case PointElement:
5949         default:
5950         {
5951           if (number_coordinates > 1)
5952             {
5953               (void) XDrawLines(display,windows->image.id,
5954                 windows->image.highlight_context,coordinate_info,
5955                 number_coordinates,CoordModeOrigin);
5956               (void) FormatLocaleString(text,MaxTextExtent," %+d%+d",
5957                 coordinate_info[number_coordinates-1].x,
5958                 coordinate_info[number_coordinates-1].y);
5959               XInfoWidget(display,windows,text);
5960             }
5961           break;
5962         }
5963         case LineElement:
5964         {
5965           if (distance > 9)
5966             {
5967               /*
5968                 Display angle of the line.
5969               */
5970               degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
5971                 line_info.y1),(double) (line_info.x2-line_info.x1)));
5972               (void) FormatLocaleString(text,MaxTextExtent," %g",
5973                 (double) degrees);
5974               XInfoWidget(display,windows,text);
5975               XHighlightLine(display,windows->image.id,
5976                 windows->image.highlight_context,&line_info);
5977             }
5978           else
5979             if (windows->info.mapped != MagickFalse)
5980               (void) XWithdrawWindow(display,windows->info.id,
5981                 windows->info.screen);
5982           break;
5983         }
5984         case RectangleElement:
5985         case FillRectangleElement:
5986         {
5987           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
5988             {
5989               /*
5990                 Display info and draw drawing rectangle.
5991               */
5992               (void) FormatLocaleString(text,MaxTextExtent,
5993                 " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
5994                 (double) rectangle_info.height,(double) rectangle_info.x,
5995                 (double) rectangle_info.y);
5996               XInfoWidget(display,windows,text);
5997               XHighlightRectangle(display,windows->image.id,
5998                 windows->image.highlight_context,&rectangle_info);
5999             }
6000           else
6001             if (windows->info.mapped != MagickFalse)
6002               (void) XWithdrawWindow(display,windows->info.id,
6003                 windows->info.screen);
6004           break;
6005         }
6006         case CircleElement:
6007         case FillCircleElement:
6008         case EllipseElement:
6009         case FillEllipseElement:
6010         {
6011           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6012             {
6013               /*
6014                 Display info and draw drawing rectangle.
6015               */
6016               (void) FormatLocaleString(text,MaxTextExtent,
6017                 " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
6018                 (double) rectangle_info.height,(double) rectangle_info.x,
6019                 (double) rectangle_info.y);
6020               XInfoWidget(display,windows,text);
6021               XHighlightEllipse(display,windows->image.id,
6022                 windows->image.highlight_context,&rectangle_info);
6023             }
6024           else
6025             if (windows->info.mapped != MagickFalse)
6026               (void) XWithdrawWindow(display,windows->info.id,
6027                 windows->info.screen);
6028           break;
6029         }
6030         case PolygonElement:
6031         case FillPolygonElement:
6032         {
6033           if (number_coordinates > 1)
6034             (void) XDrawLines(display,windows->image.id,
6035               windows->image.highlight_context,coordinate_info,
6036               number_coordinates,CoordModeOrigin);
6037           if (distance > 9)
6038             {
6039               /*
6040                 Display angle of the line.
6041               */
6042               degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
6043                 line_info.y1),(double) (line_info.x2-line_info.x1)));
6044               (void) FormatLocaleString(text,MaxTextExtent," %g",
6045                 (double) degrees);
6046               XInfoWidget(display,windows,text);
6047               XHighlightLine(display,windows->image.id,
6048                 windows->image.highlight_context,&line_info);
6049             }
6050           else
6051             if (windows->info.mapped != MagickFalse)
6052               (void) XWithdrawWindow(display,windows->info.id,
6053                 windows->info.screen);
6054           break;
6055         }
6056       }
6057       /*
6058         Wait for next event.
6059       */
6060       XScreenEvent(display,windows,&event);
6061       switch (element)
6062       {
6063         case PointElement:
6064         default:
6065         {
6066           if (number_coordinates > 1)
6067             (void) XDrawLines(display,windows->image.id,
6068               windows->image.highlight_context,coordinate_info,
6069               number_coordinates,CoordModeOrigin);
6070           break;
6071         }
6072         case LineElement:
6073         {
6074           if (distance > 9)
6075             XHighlightLine(display,windows->image.id,
6076               windows->image.highlight_context,&line_info);
6077           break;
6078         }
6079         case RectangleElement:
6080         case FillRectangleElement:
6081         {
6082           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6083             XHighlightRectangle(display,windows->image.id,
6084               windows->image.highlight_context,&rectangle_info);
6085           break;
6086         }
6087         case CircleElement:
6088         case FillCircleElement:
6089         case EllipseElement:
6090         case FillEllipseElement:
6091         {
6092           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6093             XHighlightEllipse(display,windows->image.id,
6094               windows->image.highlight_context,&rectangle_info);
6095           break;
6096         }
6097         case PolygonElement:
6098         case FillPolygonElement:
6099         {
6100           if (number_coordinates > 1)
6101             (void) XDrawLines(display,windows->image.id,
6102               windows->image.highlight_context,coordinate_info,
6103               number_coordinates,CoordModeOrigin);
6104           if (distance > 9)
6105             XHighlightLine(display,windows->image.id,
6106               windows->image.highlight_context,&line_info);
6107           break;
6108         }
6109       }
6110       switch (event.type)
6111       {
6112         case ButtonPress:
6113           break;
6114         case ButtonRelease:
6115         {
6116           /*
6117             User has committed to element.
6118           */
6119           line_info.x2=event.xbutton.x;
6120           line_info.y2=event.xbutton.y;
6121           rectangle_info.x=(ssize_t) event.xbutton.x;
6122           rectangle_info.y=(ssize_t) event.xbutton.y;
6123           coordinate_info[number_coordinates].x=event.xbutton.x;
6124           coordinate_info[number_coordinates].y=event.xbutton.y;
6125           if (((element != PolygonElement) &&
6126                (element != FillPolygonElement)) || (distance <= 9))
6127             {
6128               state|=ExitState;
6129               break;
6130             }
6131           number_coordinates++;
6132           if (number_coordinates < (int) max_coordinates)
6133             {
6134               line_info.x1=event.xbutton.x;
6135               line_info.y1=event.xbutton.y;
6136               break;
6137             }
6138           max_coordinates<<=1;
6139           coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6140             max_coordinates,sizeof(*coordinate_info));
6141           if (coordinate_info == (XPoint *) NULL)
6142             (void) ThrowMagickException(exception,GetMagickModule(),
6143               ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6144           break;
6145         }
6146         case Expose:
6147           break;
6148         case MotionNotify:
6149         {
6150           if (event.xmotion.window != windows->image.id)
6151             break;
6152           if (element != PointElement)
6153             {
6154               line_info.x2=event.xmotion.x;
6155               line_info.y2=event.xmotion.y;
6156               rectangle_info.x=(ssize_t) event.xmotion.x;
6157               rectangle_info.y=(ssize_t) event.xmotion.y;
6158               break;
6159             }
6160           coordinate_info[number_coordinates].x=event.xbutton.x;
6161           coordinate_info[number_coordinates].y=event.xbutton.y;
6162           number_coordinates++;
6163           if (number_coordinates < (int) max_coordinates)
6164             break;
6165           max_coordinates<<=1;
6166           coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6167             max_coordinates,sizeof(*coordinate_info));
6168           if (coordinate_info == (XPoint *) NULL)
6169             (void) ThrowMagickException(exception,GetMagickModule(),
6170               ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6171           break;
6172         }
6173         default:
6174           break;
6175       }
6176       /*
6177         Check boundary conditions.
6178       */
6179       if (line_info.x2 < 0)
6180         line_info.x2=0;
6181       else
6182         if (line_info.x2 > (int) windows->image.width)
6183           line_info.x2=(short) windows->image.width;
6184       if (line_info.y2 < 0)
6185         line_info.y2=0;
6186       else
6187         if (line_info.y2 > (int) windows->image.height)
6188           line_info.y2=(short) windows->image.height;
6189       distance=(unsigned int)
6190         (((line_info.x2-line_info.x1+1)*(line_info.x2-line_info.x1+1))+
6191          ((line_info.y2-line_info.y1+1)*(line_info.y2-line_info.y1+1)));
6192       if ((((int) rectangle_info.x != x) && ((int) rectangle_info.y != y)) ||
6193           ((state & ExitState) != 0))
6194         {
6195           if (rectangle_info.x < 0)
6196             rectangle_info.x=0;
6197           else
6198             if (rectangle_info.x > (ssize_t) windows->image.width)
6199               rectangle_info.x=(ssize_t) windows->image.width;
6200           if ((int) rectangle_info.x < x)
6201             rectangle_info.width=(unsigned int) (x-rectangle_info.x);
6202           else
6203             {
6204               rectangle_info.width=(unsigned int) (rectangle_info.x-x);
6205               rectangle_info.x=(ssize_t) x;
6206             }
6207           if (rectangle_info.y < 0)
6208             rectangle_info.y=0;
6209           else
6210             if (rectangle_info.y > (ssize_t) windows->image.height)
6211               rectangle_info.y=(ssize_t) windows->image.height;
6212           if ((int) rectangle_info.y < y)
6213             rectangle_info.height=(unsigned int) (y-rectangle_info.y);
6214           else
6215             {
6216               rectangle_info.height=(unsigned int) (rectangle_info.y-y);
6217               rectangle_info.y=(ssize_t) y;
6218             }
6219         }
6220     } while ((state & ExitState) == 0);
6221     (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
6222     if ((element == PointElement) || (element == PolygonElement) ||
6223         (element == FillPolygonElement))
6224       {
6225         /*
6226           Determine polygon bounding box.
6227         */
6228         rectangle_info.x=(ssize_t) coordinate_info->x;
6229         rectangle_info.y=(ssize_t) coordinate_info->y;
6230         x=coordinate_info->x;
6231         y=coordinate_info->y;
6232         for (i=1; i < number_coordinates; i++)
6233         {
6234           if (coordinate_info[i].x > x)
6235             x=coordinate_info[i].x;
6236           if (coordinate_info[i].y > y)
6237             y=coordinate_info[i].y;
6238           if ((ssize_t) coordinate_info[i].x < rectangle_info.x)
6239             rectangle_info.x=MagickMax((ssize_t) coordinate_info[i].x,0);
6240           if ((ssize_t) coordinate_info[i].y < rectangle_info.y)
6241             rectangle_info.y=MagickMax((ssize_t) coordinate_info[i].y,0);
6242         }
6243         rectangle_info.width=(size_t) (x-rectangle_info.x);
6244         rectangle_info.height=(size_t) (y-rectangle_info.y);
6245         for (i=0; i < number_coordinates; i++)
6246         {
6247           coordinate_info[i].x-=rectangle_info.x;
6248           coordinate_info[i].y-=rectangle_info.y;
6249         }
6250       }
6251     else
6252       if (distance <= 9)
6253         continue;
6254       else
6255         if ((element == RectangleElement) ||
6256             (element == CircleElement) || (element == EllipseElement))
6257           {
6258             rectangle_info.width--;
6259             rectangle_info.height--;
6260           }
6261     /*
6262       Drawing is relative to image configuration.
6263     */
6264     draw_info.x=(int) rectangle_info.x;
6265     draw_info.y=(int) rectangle_info.y;
6266     (void) XMagickCommand(display,resource_info,windows,SaveToUndoBufferCommand,
6267       image,exception);
6268     width=(unsigned int) (*image)->columns;
6269     height=(unsigned int) (*image)->rows;
6270     x=0;
6271     y=0;
6272     if (windows->image.crop_geometry != (char *) NULL)
6273       (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
6274     draw_info.x+=windows->image.x-(line_width/2);
6275     if (draw_info.x < 0)
6276       draw_info.x=0;
6277     draw_info.x=(int) (width*draw_info.x/windows->image.ximage->width);
6278     draw_info.y+=windows->image.y-(line_width/2);
6279     if (draw_info.y < 0)
6280       draw_info.y=0;
6281     draw_info.y=(int) height*draw_info.y/windows->image.ximage->height;
6282     draw_info.width=(unsigned int) rectangle_info.width+(line_width << 1);
6283     if (draw_info.width > (unsigned int) (*image)->columns)
6284       draw_info.width=(unsigned int) (*image)->columns;
6285     draw_info.height=(unsigned int) rectangle_info.height+(line_width << 1);
6286     if (draw_info.height > (unsigned int) (*image)->rows)
6287       draw_info.height=(unsigned int) (*image)->rows;
6288     (void) FormatLocaleString(draw_info.geometry,MaxTextExtent,"%ux%u%+d%+d",
6289       width*draw_info.width/windows->image.ximage->width,
6290       height*draw_info.height/windows->image.ximage->height,
6291       draw_info.x+x,draw_info.y+y);
6292     /*
6293       Initialize drawing attributes.
6294     */
6295     draw_info.degrees=0.0;
6296     draw_info.element=element;
6297     draw_info.stipple=stipple;
6298     draw_info.line_width=line_width;
6299     draw_info.line_info=line_info;
6300     if (line_info.x1 > (int) (line_width/2))
6301       draw_info.line_info.x1=(short) line_width/2;
6302     if (line_info.y1 > (int) (line_width/2))
6303       draw_info.line_info.y1=(short) line_width/2;
6304     draw_info.line_info.x2=(short) (line_info.x2-line_info.x1+(line_width/2));
6305     draw_info.line_info.y2=(short) (line_info.y2-line_info.y1+(line_width/2));
6306     if ((draw_info.line_info.x2 < 0) && (draw_info.line_info.y2 < 0))
6307       {
6308         draw_info.line_info.x2=(-draw_info.line_info.x2);
6309         draw_info.line_info.y2=(-draw_info.line_info.y2);
6310       }
6311     if (draw_info.line_info.x2 < 0)
6312       {
6313         draw_info.line_info.x2=(-draw_info.line_info.x2);
6314         Swap(draw_info.line_info.x1,draw_info.line_info.x2);
6315       }
6316     if (draw_info.line_info.y2 < 0)
6317       {
6318         draw_info.line_info.y2=(-draw_info.line_info.y2);
6319         Swap(draw_info.line_info.y1,draw_info.line_info.y2);
6320       }
6321     draw_info.rectangle_info=rectangle_info;
6322     if (draw_info.rectangle_info.x > (ssize_t) (line_width/2))
6323       draw_info.rectangle_info.x=(ssize_t) line_width/2;
6324     if (draw_info.rectangle_info.y > (ssize_t) (line_width/2))
6325       draw_info.rectangle_info.y=(ssize_t) line_width/2;
6326     draw_info.number_coordinates=(unsigned int) number_coordinates;
6327     draw_info.coordinate_info=coordinate_info;
6328     windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
6329     /*
6330       Draw element on image.
6331     */
6332     XSetCursorState(display,windows,MagickTrue);
6333     XCheckRefreshWindows(display,windows);
6334     status=XDrawImage(display,windows->pixel_info,&draw_info,*image);
6335     XSetCursorState(display,windows,MagickFalse);
6336     /*
6337       Update image colormap and return to image drawing.
6338     */
6339     XConfigureImageColormap(display,resource_info,windows,*image);
6340     (void) XConfigureImage(display,resource_info,windows,*image,exception);
6341   }
6342   XSetCursorState(display,windows,MagickFalse);
6343   coordinate_info=(XPoint *) RelinquishMagickMemory(coordinate_info);
6344   return(status != 0 ? MagickTrue : MagickFalse);
6345 }
6346 \f
6347 /*
6348 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6349 %                                                                             %
6350 %                                                                             %
6351 %                                                                             %
6352 +   X D r a w P a n R e c t a n g l e                                         %
6353 %                                                                             %
6354 %                                                                             %
6355 %                                                                             %
6356 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6357 %
6358 %  XDrawPanRectangle() draws a rectangle in the pan window.  The pan window
6359 %  displays a zoom image and the rectangle shows which portion of the image is
6360 %  displayed in the Image window.
6361 %
6362 %  The format of the XDrawPanRectangle method is:
6363 %
6364 %      XDrawPanRectangle(Display *display,XWindows *windows)
6365 %
6366 %  A description of each parameter follows:
6367 %
6368 %    o display: Specifies a connection to an X server;  returned from
6369 %      XOpenDisplay.
6370 %
6371 %    o windows: Specifies a pointer to a XWindows structure.
6372 %
6373 */
6374 static void XDrawPanRectangle(Display *display,XWindows *windows)
6375 {
6376   MagickRealType
6377     scale_factor;
6378
6379   RectangleInfo
6380     highlight_info;
6381
6382   /*
6383     Determine dimensions of the panning rectangle.
6384   */
6385   scale_factor=(MagickRealType) windows->pan.width/windows->image.ximage->width;
6386   highlight_info.x=(ssize_t) (scale_factor*windows->image.x+0.5);
6387   highlight_info.width=(unsigned int) (scale_factor*windows->image.width+0.5);
6388   scale_factor=(MagickRealType)
6389     windows->pan.height/windows->image.ximage->height;
6390   highlight_info.y=(ssize_t) (scale_factor*windows->image.y+0.5);
6391   highlight_info.height=(unsigned int) (scale_factor*windows->image.height+0.5);
6392   /*
6393     Display the panning rectangle.
6394   */
6395   (void) XClearWindow(display,windows->pan.id);
6396   XHighlightRectangle(display,windows->pan.id,windows->pan.annotate_context,
6397     &highlight_info);
6398 }
6399 \f
6400 /*
6401 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6402 %                                                                             %
6403 %                                                                             %
6404 %                                                                             %
6405 +   X I m a g e C a c h e                                                     %
6406 %                                                                             %
6407 %                                                                             %
6408 %                                                                             %
6409 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6410 %
6411 %  XImageCache() handles the creation, manipulation, and destruction of the
6412 %  image cache (undo and redo buffers).
6413 %
6414 %  The format of the XImageCache method is:
6415 %
6416 %      void XImageCache(Display *display,XResourceInfo *resource_info,
6417 %        XWindows *windows,const CommandType command,Image **image,
6418 %        ExceptionInfo *exception)
6419 %
6420 %  A description of each parameter follows:
6421 %
6422 %    o display: Specifies a connection to an X server; returned from
6423 %      XOpenDisplay.
6424 %
6425 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6426 %
6427 %    o windows: Specifies a pointer to a XWindows structure.
6428 %
6429 %    o command: Specifies a command to perform.
6430 %
6431 %    o image: the image;  XImageCache may transform the image and return a new
6432 %      image pointer.
6433 %
6434 %    o exception: return any errors or warnings in this structure.
6435 %
6436 */
6437 static void XImageCache(Display *display,XResourceInfo *resource_info,
6438   XWindows *windows,const CommandType command,Image **image,
6439   ExceptionInfo *exception)
6440 {
6441   Image
6442     *cache_image;
6443
6444   static Image
6445     *redo_image = (Image *) NULL,
6446     *undo_image = (Image *) NULL;
6447
6448   switch (command)
6449   {
6450     case FreeBuffersCommand:
6451     {
6452       /*
6453         Free memory from the undo and redo cache.
6454       */
6455       while (undo_image != (Image *) NULL)
6456       {
6457         cache_image=undo_image;
6458         undo_image=GetPreviousImageInList(undo_image);
6459         cache_image->list=DestroyImage(cache_image->list);
6460         cache_image=DestroyImage(cache_image);
6461       }
6462       undo_image=NewImageList();
6463       if (redo_image != (Image *) NULL)
6464         redo_image=DestroyImage(redo_image);
6465       redo_image=NewImageList();
6466       return;
6467     }
6468     case UndoCommand:
6469     {
6470       char
6471         image_geometry[MaxTextExtent];
6472
6473       /*
6474         Undo the last image transformation.
6475       */
6476       if (undo_image == (Image *) NULL)
6477         {
6478           (void) XBell(display,0);
6479           return;
6480         }
6481       cache_image=undo_image;
6482       undo_image=GetPreviousImageInList(undo_image);
6483       windows->image.window_changes.width=(int) cache_image->columns;
6484       windows->image.window_changes.height=(int) cache_image->rows;
6485       (void) FormatLocaleString(image_geometry,MaxTextExtent,"%dx%d!",
6486         windows->image.ximage->width,windows->image.ximage->height);
6487       (void) TransformImage(image,windows->image.crop_geometry,image_geometry,
6488         exception);
6489       if (windows->image.crop_geometry != (char *) NULL)
6490         windows->image.crop_geometry=(char *) RelinquishMagickMemory(
6491           windows->image.crop_geometry);
6492       windows->image.crop_geometry=cache_image->geometry;
6493       if (redo_image != (Image *) NULL)
6494         redo_image=DestroyImage(redo_image);
6495       redo_image=(*image);
6496       *image=cache_image->list;
6497       cache_image=DestroyImage(cache_image);
6498       if (windows->image.orphan != MagickFalse)
6499         return;
6500       XConfigureImageColormap(display,resource_info,windows,*image);
6501       (void) XConfigureImage(display,resource_info,windows,*image,exception);
6502       return;
6503     }
6504     case CutCommand:
6505     case PasteCommand:
6506     case ApplyCommand:
6507     case HalfSizeCommand:
6508     case OriginalSizeCommand:
6509     case DoubleSizeCommand:
6510     case ResizeCommand:
6511     case TrimCommand:
6512     case CropCommand:
6513     case ChopCommand:
6514     case FlipCommand:
6515     case FlopCommand:
6516     case RotateRightCommand:
6517     case RotateLeftCommand:
6518     case RotateCommand:
6519     case ShearCommand:
6520     case RollCommand:
6521     case NegateCommand:
6522     case ContrastStretchCommand:
6523     case SigmoidalContrastCommand:
6524     case NormalizeCommand:
6525     case EqualizeCommand:
6526     case HueCommand:
6527     case SaturationCommand:
6528     case BrightnessCommand:
6529     case GammaCommand:
6530     case SpiffCommand:
6531     case DullCommand:
6532     case GrayscaleCommand:
6533     case MapCommand:
6534     case QuantizeCommand:
6535     case DespeckleCommand:
6536     case EmbossCommand:
6537     case ReduceNoiseCommand:
6538     case AddNoiseCommand:
6539     case SharpenCommand:
6540     case BlurCommand:
6541     case ThresholdCommand:
6542     case EdgeDetectCommand:
6543     case SpreadCommand:
6544     case ShadeCommand:
6545     case RaiseCommand:
6546     case SegmentCommand:
6547     case SolarizeCommand:
6548     case SepiaToneCommand:
6549     case SwirlCommand:
6550     case ImplodeCommand:
6551     case VignetteCommand:
6552     case WaveCommand:
6553     case OilPaintCommand:
6554     case CharcoalDrawCommand:
6555     case AnnotateCommand:
6556     case AddBorderCommand:
6557     case AddFrameCommand:
6558     case CompositeCommand:
6559     case CommentCommand:
6560     case LaunchCommand:
6561     case RegionofInterestCommand:
6562     case SaveToUndoBufferCommand:
6563     case RedoCommand:
6564     {
6565       Image
6566         *previous_image;
6567
6568       ssize_t
6569         bytes;
6570
6571       bytes=(ssize_t) ((*image)->columns*(*image)->rows*sizeof(PixelInfo));
6572       if (undo_image != (Image *) NULL)
6573         {
6574           /*
6575             Ensure the undo cache has enough memory available.
6576           */
6577           previous_image=undo_image;
6578           while (previous_image != (Image *) NULL)
6579           {
6580             bytes+=previous_image->list->columns*previous_image->list->rows*
6581               sizeof(PixelInfo);
6582             if (bytes <= (ssize_t) (resource_info->undo_cache << 20))
6583               {
6584                 previous_image=GetPreviousImageInList(previous_image);
6585                 continue;
6586               }
6587             bytes-=previous_image->list->columns*previous_image->list->rows*
6588               sizeof(PixelInfo);
6589             if (previous_image == undo_image)
6590               undo_image=NewImageList();
6591             else
6592               previous_image->next->previous=NewImageList();
6593             break;
6594           }
6595           while (previous_image != (Image *) NULL)
6596           {
6597             /*
6598               Delete any excess memory from undo cache.
6599             */
6600             cache_image=previous_image;
6601             previous_image=GetPreviousImageInList(previous_image);
6602             cache_image->list=DestroyImage(cache_image->list);
6603             cache_image=DestroyImage(cache_image);
6604           }
6605         }
6606       if (bytes > (ssize_t) (resource_info->undo_cache << 20))
6607         break;
6608       /*
6609         Save image before transformations are applied.
6610       */
6611       cache_image=AcquireImage((ImageInfo *) NULL,exception);
6612       if (cache_image == (Image *) NULL)
6613         break;
6614       XSetCursorState(display,windows,MagickTrue);
6615       XCheckRefreshWindows(display,windows);
6616       cache_image->list=CloneImage(*image,0,0,MagickTrue,exception);
6617       XSetCursorState(display,windows,MagickFalse);
6618       if (cache_image->list == (Image *) NULL)
6619         {
6620           cache_image=DestroyImage(cache_image);
6621           break;
6622         }
6623       cache_image->columns=(size_t) windows->image.ximage->width;
6624       cache_image->rows=(size_t) windows->image.ximage->height;
6625       cache_image->geometry=windows->image.crop_geometry;
6626       if (windows->image.crop_geometry != (char *) NULL)
6627         {
6628           cache_image->geometry=AcquireString((char *) NULL);
6629           (void) CopyMagickString(cache_image->geometry,
6630             windows->image.crop_geometry,MaxTextExtent);
6631         }
6632       if (undo_image == (Image *) NULL)
6633         {
6634           undo_image=cache_image;
6635           break;
6636         }
6637       undo_image->next=cache_image;
6638       undo_image->next->previous=undo_image;
6639       undo_image=undo_image->next;
6640       break;
6641     }
6642     default:
6643       break;
6644   }
6645   if (command == RedoCommand)
6646     {
6647       /*
6648         Redo the last image transformation.
6649       */
6650       if (redo_image == (Image *) NULL)
6651         {
6652           (void) XBell(display,0);
6653           return;
6654         }
6655       windows->image.window_changes.width=(int) redo_image->columns;
6656       windows->image.window_changes.height=(int) redo_image->rows;
6657       if (windows->image.crop_geometry != (char *) NULL)
6658         windows->image.crop_geometry=(char *)
6659           RelinquishMagickMemory(windows->image.crop_geometry);
6660       windows->image.crop_geometry=redo_image->geometry;
6661       *image=DestroyImage(*image);
6662       *image=redo_image;
6663       redo_image=NewImageList();
6664       if (windows->image.orphan != MagickFalse)
6665         return;
6666       XConfigureImageColormap(display,resource_info,windows,*image);
6667       (void) XConfigureImage(display,resource_info,windows,*image,exception);
6668       return;
6669     }
6670   if (command != InfoCommand)
6671     return;
6672   /*
6673     Display image info.
6674   */
6675   XSetCursorState(display,windows,MagickTrue);
6676   XCheckRefreshWindows(display,windows);
6677   XDisplayImageInfo(display,resource_info,windows,undo_image,*image);
6678   XSetCursorState(display,windows,MagickFalse);
6679 }
6680 \f
6681 /*
6682 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6683 %                                                                             %
6684 %                                                                             %
6685 %                                                                             %
6686 +   X I m a g e W i n d o w C o m m a n d                                     %
6687 %                                                                             %
6688 %                                                                             %
6689 %                                                                             %
6690 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6691 %
6692 %  XImageWindowCommand() makes a transform to the image or Image window as
6693 %  specified by a user menu button or keyboard command.
6694 %
6695 %  The format of the XImageWindowCommand method is:
6696 %
6697 %      CommandType XImageWindowCommand(Display *display,
6698 %        XResourceInfo *resource_info,XWindows *windows,
6699 %        const MagickStatusType state,KeySym key_symbol,Image **image,
6700 %        ExceptionInfo *exception)
6701 %
6702 %  A description of each parameter follows:
6703 %
6704 %    o nexus:  Method XImageWindowCommand returns an image when the
6705 %      user chooses 'Open Image' from the command menu.  Otherwise a null
6706 %      image is returned.
6707 %
6708 %    o display: Specifies a connection to an X server; returned from
6709 %      XOpenDisplay.
6710 %
6711 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6712 %
6713 %    o windows: Specifies a pointer to a XWindows structure.
6714 %
6715 %    o state: key mask.
6716 %
6717 %    o key_symbol: Specifies a command to perform.
6718 %
6719 %    o image: the image;  XImageWIndowCommand may transform the image and
6720 %      return a new image pointer.
6721 %
6722 %    o exception: return any errors or warnings in this structure.
6723 %
6724 */
6725 static CommandType XImageWindowCommand(Display *display,
6726   XResourceInfo *resource_info,XWindows *windows,const MagickStatusType state,
6727   KeySym key_symbol,Image **image,ExceptionInfo *exception)
6728 {
6729   static char
6730     delta[MaxTextExtent] = "";
6731
6732   static const char
6733     Digits[] = "01234567890";
6734
6735   static KeySym
6736     last_symbol = XK_0;
6737
6738   if ((key_symbol >= XK_0) && (key_symbol <= XK_9))
6739     {
6740       if (((last_symbol < XK_0) || (last_symbol > XK_9)))
6741         {
6742           *delta='\0';
6743           resource_info->quantum=1;
6744         }
6745       last_symbol=key_symbol;
6746       delta[strlen(delta)+1]='\0';
6747       delta[strlen(delta)]=Digits[key_symbol-XK_0];
6748       resource_info->quantum=StringToLong(delta);
6749       return(NullCommand);
6750     }
6751   last_symbol=key_symbol;
6752   if (resource_info->immutable)
6753     {
6754       /*
6755         Virtual image window has a restricted command set.
6756       */
6757       switch (key_symbol)
6758       {
6759         case XK_question:
6760           return(InfoCommand);
6761         case XK_p:
6762         case XK_Print:
6763           return(PrintCommand);
6764         case XK_space:
6765           return(NextCommand);
6766         case XK_q:
6767         case XK_Escape:
6768           return(QuitCommand);
6769         default:
6770           break;
6771       }
6772       return(NullCommand);
6773     }
6774   switch ((int) key_symbol)
6775   {
6776     case XK_o:
6777     {
6778       if ((state & ControlMask) == 0)
6779         break;
6780       return(OpenCommand);
6781     }
6782     case XK_space:
6783       return(NextCommand);
6784     case XK_BackSpace:
6785       return(FormerCommand);
6786     case XK_s:
6787     {
6788       if ((state & Mod1Mask) != 0)
6789         return(SwirlCommand);
6790       if ((state & ControlMask) == 0)
6791         return(ShearCommand);
6792       return(SaveCommand);
6793     }
6794     case XK_p:
6795     case XK_Print:
6796     {
6797       if ((state & Mod1Mask) != 0)
6798         return(OilPaintCommand);
6799       if ((state & Mod4Mask) != 0)
6800         return(ColorCommand);
6801       if ((state & ControlMask) == 0)
6802         return(NullCommand);
6803       return(PrintCommand);
6804     }
6805     case XK_d:
6806     {
6807       if ((state & Mod4Mask) != 0)
6808         return(DrawCommand);
6809       if ((state & ControlMask) == 0)
6810         return(NullCommand);
6811       return(DeleteCommand);
6812     }
6813     case XK_Select:
6814     {
6815       if ((state & ControlMask) == 0)
6816         return(NullCommand);
6817       return(SelectCommand);
6818     }
6819     case XK_n:
6820     {
6821       if ((state & ControlMask) == 0)
6822         return(NullCommand);
6823       return(NewCommand);
6824     }
6825     case XK_q:
6826     case XK_Escape:
6827       return(QuitCommand);
6828     case XK_z:
6829     case XK_Undo:
6830     {
6831       if ((state & ControlMask) == 0)
6832         return(NullCommand);
6833       return(UndoCommand);
6834     }
6835     case XK_r:
6836     case XK_Redo:
6837     {
6838       if ((state & ControlMask) == 0)
6839         return(RollCommand);
6840       return(RedoCommand);
6841     }
6842     case XK_x:
6843     {
6844       if ((state & ControlMask) == 0)
6845         return(NullCommand);
6846       return(CutCommand);
6847     }
6848     case XK_c:
6849     {
6850       if ((state & Mod1Mask) != 0)
6851         return(CharcoalDrawCommand);
6852       if ((state & ControlMask) == 0)
6853         return(CropCommand);
6854       return(CopyCommand);
6855     }
6856     case XK_v:
6857     case XK_Insert:
6858     {
6859       if ((state & Mod4Mask) != 0)
6860         return(CompositeCommand);
6861       if ((state & ControlMask) == 0)
6862         return(FlipCommand);
6863       return(PasteCommand);
6864     }
6865     case XK_less:
6866       return(HalfSizeCommand);
6867     case XK_minus:
6868       return(OriginalSizeCommand);
6869     case XK_greater:
6870       return(DoubleSizeCommand);
6871     case XK_percent:
6872       return(ResizeCommand);
6873     case XK_at:
6874       return(RefreshCommand);
6875     case XK_bracketleft:
6876       return(ChopCommand);
6877     case XK_h:
6878       return(FlopCommand);
6879     case XK_slash:
6880       return(RotateRightCommand);
6881     case XK_backslash:
6882       return(RotateLeftCommand);
6883     case XK_asterisk:
6884       return(RotateCommand);
6885     case XK_t:
6886       return(TrimCommand);
6887     case XK_H:
6888       return(HueCommand);
6889     case XK_S:
6890       return(SaturationCommand);
6891     case XK_L:
6892       return(BrightnessCommand);
6893     case XK_G:
6894       return(GammaCommand);
6895     case XK_C:
6896       return(SpiffCommand);
6897     case XK_Z:
6898       return(DullCommand);
6899     case XK_N:
6900       return(NormalizeCommand);
6901     case XK_equal:
6902       return(EqualizeCommand);
6903     case XK_asciitilde:
6904       return(NegateCommand);
6905     case XK_period:
6906       return(GrayscaleCommand);
6907     case XK_numbersign:
6908       return(QuantizeCommand);
6909     case XK_F2:
6910       return(DespeckleCommand);
6911     case XK_F3:
6912       return(EmbossCommand);
6913     case XK_F4:
6914       return(ReduceNoiseCommand);
6915     case XK_F5:
6916       return(AddNoiseCommand);
6917     case XK_F6:
6918       return(SharpenCommand);
6919     case XK_F7:
6920       return(BlurCommand);
6921     case XK_F8:
6922       return(ThresholdCommand);
6923     case XK_F9:
6924       return(EdgeDetectCommand);
6925     case XK_F10:
6926       return(SpreadCommand);
6927     case XK_F11:
6928       return(ShadeCommand);
6929     case XK_F12:
6930       return(RaiseCommand);
6931     case XK_F13:
6932       return(SegmentCommand);
6933     case XK_i:
6934     {
6935       if ((state & Mod1Mask) == 0)
6936         return(NullCommand);
6937       return(ImplodeCommand);
6938     }
6939     case XK_w:
6940     {
6941       if ((state & Mod1Mask) == 0)
6942         return(NullCommand);
6943       return(WaveCommand);
6944     }
6945     case XK_m:
6946     {
6947       if ((state & Mod4Mask) == 0)
6948         return(NullCommand);
6949       return(MatteCommand);
6950     }
6951     case XK_b:
6952     {
6953       if ((state & Mod4Mask) == 0)
6954         return(NullCommand);
6955       return(AddBorderCommand);
6956     }
6957     case XK_f:
6958     {
6959       if ((state & Mod4Mask) == 0)
6960         return(NullCommand);
6961       return(AddFrameCommand);
6962     }
6963     case XK_exclam:
6964     {
6965       if ((state & Mod4Mask) == 0)
6966         return(NullCommand);
6967       return(CommentCommand);
6968     }
6969     case XK_a:
6970     {
6971       if ((state & Mod1Mask) != 0)
6972         return(ApplyCommand);
6973       if ((state & Mod4Mask) != 0)
6974         return(AnnotateCommand);
6975       if ((state & ControlMask) == 0)
6976         return(NullCommand);
6977       return(RegionofInterestCommand);
6978     }
6979     case XK_question:
6980       return(InfoCommand);
6981     case XK_plus:
6982       return(ZoomCommand);
6983     case XK_P:
6984     {
6985       if ((state & ShiftMask) == 0)
6986         return(NullCommand);
6987       return(ShowPreviewCommand);
6988     }
6989     case XK_Execute:
6990       return(LaunchCommand);
6991     case XK_F1:
6992       return(HelpCommand);
6993     case XK_Find:
6994       return(BrowseDocumentationCommand);
6995     case XK_Menu:
6996     {
6997       (void) XMapRaised(display,windows->command.id);
6998       return(NullCommand);
6999     }
7000     case XK_Next:
7001     case XK_Prior:
7002     case XK_Home:
7003     case XK_KP_Home:
7004     {
7005       XTranslateImage(display,windows,*image,key_symbol);
7006       return(NullCommand);
7007     }
7008     case XK_Up:
7009     case XK_KP_Up:
7010     case XK_Down:
7011     case XK_KP_Down:
7012     case XK_Left:
7013     case XK_KP_Left:
7014     case XK_Right:
7015     case XK_KP_Right:
7016     {
7017       if ((state & Mod1Mask) != 0)
7018         {
7019           RectangleInfo
7020             crop_info;
7021
7022           /*
7023             Trim one pixel from edge of image.
7024           */
7025           crop_info.x=0;
7026           crop_info.y=0;
7027           crop_info.width=(size_t) windows->image.ximage->width;
7028           crop_info.height=(size_t) windows->image.ximage->height;
7029           if ((key_symbol == XK_Up) || (key_symbol == XK_KP_Up))
7030             {
7031               if (resource_info->quantum >= (int) crop_info.height)
7032                 resource_info->quantum=(int) crop_info.height-1;
7033               crop_info.height-=resource_info->quantum;
7034             }
7035           if ((key_symbol == XK_Down) || (key_symbol == XK_KP_Down))
7036             {
7037               if (resource_info->quantum >= (int) (crop_info.height-crop_info.y))
7038                 resource_info->quantum=(int) (crop_info.height-crop_info.y-1);
7039               crop_info.y+=resource_info->quantum;
7040               crop_info.height-=resource_info->quantum;
7041             }
7042           if ((key_symbol == XK_Left) || (key_symbol == XK_KP_Left))
7043             {
7044               if (resource_info->quantum >= (int) crop_info.width)
7045                 resource_info->quantum=(int) crop_info.width-1;
7046               crop_info.width-=resource_info->quantum;
7047             }
7048           if ((key_symbol == XK_Right) || (key_symbol == XK_KP_Right))
7049             {
7050               if (resource_info->quantum >= (int) (crop_info.width-crop_info.x))
7051                 resource_info->quantum=(int) (crop_info.width-crop_info.x-1);
7052               crop_info.x+=resource_info->quantum;
7053               crop_info.width-=resource_info->quantum;
7054             }
7055           if ((int) (windows->image.x+windows->image.width) >
7056               (int) crop_info.width)
7057             windows->image.x=(int) (crop_info.width-windows->image.width);
7058           if ((int) (windows->image.y+windows->image.height) >
7059               (int) crop_info.height)
7060             windows->image.y=(int) (crop_info.height-windows->image.height);
7061           XSetCropGeometry(display,windows,&crop_info,*image);
7062           windows->image.window_changes.width=(int) crop_info.width;
7063           windows->image.window_changes.height=(int) crop_info.height;
7064           (void) XSetWindowBackgroundPixmap(display,windows->image.id,None);
7065           (void) XConfigureImage(display,resource_info,windows,*image,
7066             exception);
7067           return(NullCommand);
7068         }
7069       XTranslateImage(display,windows,*image,key_symbol);
7070       return(NullCommand);
7071     }
7072     default:
7073       return(NullCommand);
7074   }
7075   return(NullCommand);
7076 }
7077 \f
7078 /*
7079 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7080 %                                                                             %
7081 %                                                                             %
7082 %                                                                             %
7083 +   X M a g i c k C o m m a n d                                               %
7084 %                                                                             %
7085 %                                                                             %
7086 %                                                                             %
7087 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7088 %
7089 %  XMagickCommand() makes a transform to the image or Image window as
7090 %  specified by a user menu button or keyboard command.
7091 %
7092 %  The format of the XMagickCommand method is:
7093 %
7094 %      Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7095 %        XWindows *windows,const CommandType command,Image **image,
7096 %        ExceptionInfo *exception)
7097 %
7098 %  A description of each parameter follows:
7099 %
7100 %    o display: Specifies a connection to an X server; returned from
7101 %      XOpenDisplay.
7102 %
7103 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
7104 %
7105 %    o windows: Specifies a pointer to a XWindows structure.
7106 %
7107 %    o command: Specifies a command to perform.
7108 %
7109 %    o image: the image;  XMagickCommand may transform the image and return a
7110 %      new image pointer.
7111 %
7112 %    o exception: return any errors or warnings in this structure.
7113 %
7114 */
7115 static Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7116   XWindows *windows,const CommandType command,Image **image,
7117   ExceptionInfo *exception)
7118 {
7119   char
7120     filename[MaxTextExtent],
7121     geometry[MaxTextExtent],
7122     modulate_factors[MaxTextExtent];
7123
7124   GeometryInfo
7125     geometry_info;
7126
7127   Image
7128     *nexus;
7129
7130   ImageInfo
7131     *image_info;
7132
7133   int
7134     x,
7135     y;
7136
7137   MagickStatusType
7138     flags,
7139     status;
7140
7141   QuantizeInfo
7142     quantize_info;
7143
7144   RectangleInfo
7145     page_geometry;
7146
7147   register int
7148     i;
7149
7150   static char
7151     color[MaxTextExtent] = "gray";
7152
7153   unsigned int
7154     height,
7155     width;
7156
7157   /*
7158     Process user command.
7159   */
7160   XCheckRefreshWindows(display,windows);
7161   XImageCache(display,resource_info,windows,command,image,exception);
7162   nexus=NewImageList();
7163   windows->image.window_changes.width=windows->image.ximage->width;
7164   windows->image.window_changes.height=windows->image.ximage->height;
7165   image_info=CloneImageInfo(resource_info->image_info);
7166   SetGeometryInfo(&geometry_info);
7167   GetQuantizeInfo(&quantize_info);
7168   switch (command)
7169   {
7170     case OpenCommand:
7171     {
7172       /*
7173         Load image.
7174       */
7175       nexus=XOpenImage(display,resource_info,windows,MagickFalse);
7176       break;
7177     }
7178     case NextCommand:
7179     {
7180       /*
7181         Display next image.
7182       */
7183       for (i=0; i < resource_info->quantum; i++)
7184         XClientMessage(display,windows->image.id,windows->im_protocols,
7185           windows->im_next_image,CurrentTime);
7186       break;
7187     }
7188     case FormerCommand:
7189     {
7190       /*
7191         Display former image.
7192       */
7193       for (i=0; i < resource_info->quantum; i++)
7194         XClientMessage(display,windows->image.id,windows->im_protocols,
7195           windows->im_former_image,CurrentTime);
7196       break;
7197     }
7198     case SelectCommand:
7199     {
7200       int
7201         status;
7202
7203       /*
7204         Select image.
7205       */
7206       status=chdir(resource_info->home_directory);
7207       if (status == -1)
7208         (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
7209           "UnableToOpenFile","%s",resource_info->home_directory);
7210       nexus=XOpenImage(display,resource_info,windows,MagickTrue);
7211       break;
7212     }
7213     case SaveCommand:
7214     {
7215       /*
7216         Save image.
7217       */
7218       status=XSaveImage(display,resource_info,windows,*image,exception);
7219       if (status == MagickFalse)
7220         {
7221           char
7222             message[MaxTextExtent];
7223
7224           (void) FormatLocaleString(message,MaxTextExtent,"%s:%s",
7225             exception->reason != (char *) NULL ? exception->reason : "",
7226             exception->description != (char *) NULL ? exception->description :
7227             "");
7228           XNoticeWidget(display,windows,"Unable to save file:",message);
7229           break;
7230         }
7231       break;
7232     }
7233     case PrintCommand:
7234     {
7235       /*
7236         Print image.
7237       */
7238       status=XPrintImage(display,resource_info,windows,*image,exception);
7239       if (status == MagickFalse)
7240         {
7241           char
7242             message[MaxTextExtent];
7243
7244           (void) FormatLocaleString(message,MaxTextExtent,"%s:%s",
7245             exception->reason != (char *) NULL ? exception->reason : "",
7246             exception->description != (char *) NULL ? exception->description :
7247             "");
7248           XNoticeWidget(display,windows,"Unable to print file:",message);
7249           break;
7250         }
7251       break;
7252     }
7253     case DeleteCommand:
7254     {
7255       static char
7256         filename[MaxTextExtent] = "\0";
7257
7258       /*
7259         Delete image file.
7260       */
7261       XFileBrowserWidget(display,windows,"Delete",filename);
7262       if (*filename == '\0')
7263         break;
7264       status=remove_utf8(filename) != 0 ? MagickTrue : MagickFalse;
7265       if (status != MagickFalse)
7266         XNoticeWidget(display,windows,"Unable to delete image file:",filename);
7267       break;
7268     }
7269     case NewCommand:
7270     {
7271       int
7272         status;
7273
7274       static char
7275         color[MaxTextExtent] = "gray",
7276         geometry[MaxTextExtent] = "640x480";
7277
7278       static const char
7279         *format = "gradient";
7280
7281       /*
7282         Query user for canvas geometry.
7283       */
7284       status=XDialogWidget(display,windows,"New","Enter image geometry:",
7285         geometry);
7286       if (*geometry == '\0')
7287         break;
7288       if (status == 0)
7289         format="xc";
7290       XColorBrowserWidget(display,windows,"Select",color);
7291       if (*color == '\0')
7292         break;
7293       /*
7294         Create canvas.
7295       */
7296       (void) FormatLocaleString(image_info->filename,MaxTextExtent,
7297         "%s:%s",format,color);
7298       (void) CloneString(&image_info->size,geometry);
7299       nexus=ReadImage(image_info,exception);
7300       CatchException(exception);
7301       XClientMessage(display,windows->image.id,windows->im_protocols,
7302         windows->im_next_image,CurrentTime);
7303       break;
7304     }
7305     case VisualDirectoryCommand:
7306     {
7307       /*
7308         Visual Image directory.
7309       */
7310       nexus=XVisualDirectoryImage(display,resource_info,windows,exception);
7311       break;
7312     }
7313     case QuitCommand:
7314     {
7315       /*
7316         exit program.
7317       */
7318       if (resource_info->confirm_exit == MagickFalse)
7319         XClientMessage(display,windows->image.id,windows->im_protocols,
7320           windows->im_exit,CurrentTime);
7321       else
7322         {
7323           int
7324             status;
7325
7326           /*
7327             Confirm program exit.
7328           */
7329           status=XConfirmWidget(display,windows,"Do you really want to exit",
7330             resource_info->client_name);
7331           if (status > 0)
7332             XClientMessage(display,windows->image.id,windows->im_protocols,
7333               windows->im_exit,CurrentTime);
7334         }
7335       break;
7336     }
7337     case CutCommand:
7338     {
7339       /*
7340         Cut image.
7341       */
7342       (void) XCropImage(display,resource_info,windows,*image,CutMode,exception);
7343       break;
7344     }
7345     case CopyCommand:
7346     {
7347       /*
7348         Copy image.
7349       */
7350       (void) XCropImage(display,resource_info,windows,*image,CopyMode,
7351         exception);
7352       break;
7353     }
7354     case PasteCommand:
7355     {
7356       /*
7357         Paste image.
7358       */
7359       status=XPasteImage(display,resource_info,windows,*image,exception);
7360       if (status == MagickFalse)
7361         {
7362           XNoticeWidget(display,windows,"Unable to paste X image",
7363             (*image)->filename);
7364           break;
7365         }
7366       break;
7367     }
7368     case HalfSizeCommand:
7369     {
7370       /*
7371         Half image size.
7372       */
7373       windows->image.window_changes.width=windows->image.ximage->width/2;
7374       windows->image.window_changes.height=windows->image.ximage->height/2;
7375       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7376       break;
7377     }
7378     case OriginalSizeCommand:
7379     {
7380       /*
7381         Original image size.
7382       */
7383       windows->image.window_changes.width=(int) (*image)->columns;
7384       windows->image.window_changes.height=(int) (*image)->rows;
7385       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7386       break;
7387     }
7388     case DoubleSizeCommand:
7389     {
7390       /*
7391         Double the image size.
7392       */
7393       windows->image.window_changes.width=windows->image.ximage->width << 1;
7394       windows->image.window_changes.height=windows->image.ximage->height << 1;
7395       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7396       break;
7397     }
7398     case ResizeCommand:
7399     {
7400       int
7401         status;
7402
7403       size_t
7404         height,
7405         width;
7406
7407       ssize_t
7408         x,
7409         y;
7410
7411       /*
7412         Resize image.
7413       */
7414       width=(size_t) windows->image.ximage->width;
7415       height=(size_t) windows->image.ximage->height;
7416       x=0;
7417       y=0;
7418       (void) FormatLocaleString(geometry,MaxTextExtent,"%.20gx%.20g+0+0",
7419         (double) width,(double) height);
7420       status=XDialogWidget(display,windows,"Resize",
7421         "Enter resize geometry (e.g. 640x480, 200%):",geometry);
7422       if (*geometry == '\0')
7423         break;
7424       if (status == 0)
7425         (void) ConcatenateMagickString(geometry,"!",MaxTextExtent);
7426       (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
7427       windows->image.window_changes.width=(int) width;
7428       windows->image.window_changes.height=(int) height;
7429       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7430       break;
7431     }
7432     case ApplyCommand:
7433     {
7434       char
7435         image_geometry[MaxTextExtent];
7436
7437       if ((windows->image.crop_geometry == (char *) NULL) &&
7438           ((int) (*image)->columns == windows->image.ximage->width) &&
7439           ((int) (*image)->rows == windows->image.ximage->height))
7440         break;
7441       /*
7442         Apply size transforms to image.
7443       */
7444       XSetCursorState(display,windows,MagickTrue);
7445       XCheckRefreshWindows(display,windows);
7446       /*
7447         Crop and/or scale displayed image.
7448       */
7449       (void) FormatLocaleString(image_geometry,MaxTextExtent,"%dx%d!",
7450         windows->image.ximage->width,windows->image.ximage->height);
7451       (void) TransformImage(image,windows->image.crop_geometry,image_geometry,
7452         exception);
7453       if (windows->image.crop_geometry != (char *) NULL)
7454         windows->image.crop_geometry=(char *) RelinquishMagickMemory(
7455           windows->image.crop_geometry);
7456       windows->image.x=0;
7457       windows->image.y=0;
7458       XConfigureImageColormap(display,resource_info,windows,*image);
7459       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7460       break;
7461     }
7462     case RefreshCommand:
7463     {
7464       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7465       break;
7466     }
7467     case RestoreCommand:
7468     {
7469       /*
7470         Restore Image window to its original size.
7471       */
7472       if ((windows->image.width == (unsigned int) (*image)->columns) &&
7473           (windows->image.height == (unsigned int) (*image)->rows) &&
7474           (windows->image.crop_geometry == (char *) NULL))
7475         {
7476           (void) XBell(display,0);
7477           break;
7478         }
7479       windows->image.window_changes.width=(int) (*image)->columns;
7480       windows->image.window_changes.height=(int) (*image)->rows;
7481       if (windows->image.crop_geometry != (char *) NULL)
7482         {
7483           windows->image.crop_geometry=(char *)
7484             RelinquishMagickMemory(windows->image.crop_geometry);
7485           windows->image.crop_geometry=(char *) NULL;
7486           windows->image.x=0;
7487           windows->image.y=0;
7488         }
7489       XConfigureImageColormap(display,resource_info,windows,*image);
7490       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7491       break;
7492     }
7493     case CropCommand:
7494     {
7495       /*
7496         Crop image.
7497       */
7498       (void) XCropImage(display,resource_info,windows,*image,CropMode,
7499         exception);
7500       break;
7501     }
7502     case ChopCommand:
7503     {
7504       /*
7505         Chop image.
7506       */
7507       status=XChopImage(display,resource_info,windows,image,exception);
7508       if (status == MagickFalse)
7509         {
7510           XNoticeWidget(display,windows,"Unable to cut X image",
7511             (*image)->filename);
7512           break;
7513         }
7514       break;
7515     }
7516     case FlopCommand:
7517     {
7518       Image
7519         *flop_image;
7520
7521       /*
7522         Flop image scanlines.
7523       */
7524       XSetCursorState(display,windows,MagickTrue);
7525       XCheckRefreshWindows(display,windows);
7526       flop_image=FlopImage(*image,exception);
7527       if (flop_image != (Image *) NULL)
7528         {
7529           *image=DestroyImage(*image);
7530           *image=flop_image;
7531         }
7532       CatchException(exception);
7533       XSetCursorState(display,windows,MagickFalse);
7534       if (windows->image.crop_geometry != (char *) NULL)
7535         {
7536           /*
7537             Flop crop geometry.
7538           */
7539           width=(unsigned int) (*image)->columns;
7540           height=(unsigned int) (*image)->rows;
7541           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7542             &width,&height);
7543           (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
7544             "%ux%u%+d%+d",width,height,(int) (*image)->columns-(int) width-x,y);
7545         }
7546       if (windows->image.orphan != MagickFalse)
7547         break;
7548       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7549       break;
7550     }
7551     case FlipCommand:
7552     {
7553       Image
7554         *flip_image;
7555
7556       /*
7557         Flip image scanlines.
7558       */
7559       XSetCursorState(display,windows,MagickTrue);
7560       XCheckRefreshWindows(display,windows);
7561       flip_image=FlipImage(*image,exception);
7562       if (flip_image != (Image *) NULL)
7563         {
7564           *image=DestroyImage(*image);
7565           *image=flip_image;
7566         }
7567       CatchException(exception);
7568       XSetCursorState(display,windows,MagickFalse);
7569       if (windows->image.crop_geometry != (char *) NULL)
7570         {
7571           /*
7572             Flip crop geometry.
7573           */
7574           width=(unsigned int) (*image)->columns;
7575           height=(unsigned int) (*image)->rows;
7576           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7577             &width,&height);
7578           (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
7579             "%ux%u%+d%+d",width,height,x,(int) (*image)->rows-(int) height-y);
7580         }
7581       if (windows->image.orphan != MagickFalse)
7582         break;
7583       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7584       break;
7585     }
7586     case RotateRightCommand:
7587     {
7588       /*
7589         Rotate image 90 degrees clockwise.
7590       */
7591       status=XRotateImage(display,resource_info,windows,90.0,image,exception);
7592       if (status == MagickFalse)
7593         {
7594           XNoticeWidget(display,windows,"Unable to rotate X image",
7595             (*image)->filename);
7596           break;
7597         }
7598       break;
7599     }
7600     case RotateLeftCommand:
7601     {
7602       /*
7603         Rotate image 90 degrees counter-clockwise.
7604       */
7605       status=XRotateImage(display,resource_info,windows,-90.0,image,exception);
7606       if (status == MagickFalse)
7607         {
7608           XNoticeWidget(display,windows,"Unable to rotate X image",
7609             (*image)->filename);
7610           break;
7611         }
7612       break;
7613     }
7614     case RotateCommand:
7615     {
7616       /*
7617         Rotate image.
7618       */
7619       status=XRotateImage(display,resource_info,windows,0.0,image,exception);
7620       if (status == MagickFalse)
7621         {
7622           XNoticeWidget(display,windows,"Unable to rotate X image",
7623             (*image)->filename);
7624           break;
7625         }
7626       break;
7627     }
7628     case ShearCommand:
7629     {
7630       Image
7631         *shear_image;
7632
7633       static char
7634         geometry[MaxTextExtent] = "45.0x45.0";
7635
7636       /*
7637         Query user for shear color and geometry.
7638       */
7639       XColorBrowserWidget(display,windows,"Select",color);
7640       if (*color == '\0')
7641         break;
7642       (void) XDialogWidget(display,windows,"Shear","Enter shear geometry:",
7643         geometry);
7644       if (*geometry == '\0')
7645         break;
7646       /*
7647         Shear image.
7648       */
7649       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
7650         exception);
7651       XSetCursorState(display,windows,MagickTrue);
7652       XCheckRefreshWindows(display,windows);
7653       (void) QueryColorCompliance(color,AllCompliance,
7654         &(*image)->background_color,exception);
7655       flags=ParseGeometry(geometry,&geometry_info);
7656       if ((flags & SigmaValue) == 0)
7657         geometry_info.sigma=geometry_info.rho;
7658       shear_image=ShearImage(*image,geometry_info.rho,geometry_info.sigma,
7659         exception);
7660       if (shear_image != (Image *) NULL)
7661         {
7662           *image=DestroyImage(*image);
7663           *image=shear_image;
7664         }
7665       CatchException(exception);
7666       XSetCursorState(display,windows,MagickFalse);
7667       if (windows->image.orphan != MagickFalse)
7668         break;
7669       windows->image.window_changes.width=(int) (*image)->columns;
7670       windows->image.window_changes.height=(int) (*image)->rows;
7671       XConfigureImageColormap(display,resource_info,windows,*image);
7672       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7673       break;
7674     }
7675     case RollCommand:
7676     {
7677       Image
7678         *roll_image;
7679
7680       static char
7681         geometry[MaxTextExtent] = "+2+2";
7682
7683       /*
7684         Query user for the roll geometry.
7685       */
7686       (void) XDialogWidget(display,windows,"Roll","Enter roll geometry:",
7687         geometry);
7688       if (*geometry == '\0')
7689         break;
7690       /*
7691         Roll image.
7692       */
7693       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
7694         exception);
7695       XSetCursorState(display,windows,MagickTrue);
7696       XCheckRefreshWindows(display,windows);
7697       (void) ParsePageGeometry(*image,geometry,&page_geometry,
7698         exception);
7699       roll_image=RollImage(*image,page_geometry.x,page_geometry.y,
7700         exception);
7701       if (roll_image != (Image *) NULL)
7702         {
7703           *image=DestroyImage(*image);
7704           *image=roll_image;
7705         }
7706       CatchException(exception);
7707       XSetCursorState(display,windows,MagickFalse);
7708       if (windows->image.orphan != MagickFalse)
7709         break;
7710       windows->image.window_changes.width=(int) (*image)->columns;
7711       windows->image.window_changes.height=(int) (*image)->rows;
7712       XConfigureImageColormap(display,resource_info,windows,*image);
7713       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7714       break;
7715     }
7716     case TrimCommand:
7717     {
7718       static char
7719         fuzz[MaxTextExtent];
7720
7721       /*
7722         Query user for the fuzz factor.
7723       */
7724       (void) FormatLocaleString(fuzz,MaxTextExtent,"%g%%",100.0*
7725         (*image)->fuzz/(QuantumRange+1.0));
7726       (void) XDialogWidget(display,windows,"Trim","Enter fuzz factor:",fuzz);
7727       if (*fuzz == '\0')
7728         break;
7729       (*image)->fuzz=SiPrefixToDouble(fuzz,(double) QuantumRange+1.0);
7730       /*
7731         Trim image.
7732       */
7733       status=XTrimImage(display,resource_info,windows,*image,exception);
7734       if (status == MagickFalse)
7735         {
7736           XNoticeWidget(display,windows,"Unable to trim X image",
7737             (*image)->filename);
7738           break;
7739         }
7740       break;
7741     }
7742     case HueCommand:
7743     {
7744       static char
7745         hue_percent[MaxTextExtent] = "110";
7746
7747       /*
7748         Query user for percent hue change.
7749       */
7750       (void) XDialogWidget(display,windows,"Apply",
7751         "Enter percent change in image hue (0-200):",hue_percent);
7752       if (*hue_percent == '\0')
7753         break;
7754       /*
7755         Vary the image hue.
7756       */
7757       XSetCursorState(display,windows,MagickTrue);
7758       XCheckRefreshWindows(display,windows);
7759       (void) CopyMagickString(modulate_factors,"100.0/100.0/",MaxTextExtent);
7760       (void) ConcatenateMagickString(modulate_factors,hue_percent,
7761         MaxTextExtent);
7762       (void) ModulateImage(*image,modulate_factors,exception);
7763       XSetCursorState(display,windows,MagickFalse);
7764       if (windows->image.orphan != MagickFalse)
7765         break;
7766       XConfigureImageColormap(display,resource_info,windows,*image);
7767       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7768       break;
7769     }
7770     case SaturationCommand:
7771     {
7772       static char
7773         saturation_percent[MaxTextExtent] = "110";
7774
7775       /*
7776         Query user for percent saturation change.
7777       */
7778       (void) XDialogWidget(display,windows,"Apply",
7779         "Enter percent change in color saturation (0-200):",saturation_percent);
7780       if (*saturation_percent == '\0')
7781         break;
7782       /*
7783         Vary color saturation.
7784       */
7785       XSetCursorState(display,windows,MagickTrue);
7786       XCheckRefreshWindows(display,windows);
7787       (void) CopyMagickString(modulate_factors,"100.0/",MaxTextExtent);
7788       (void) ConcatenateMagickString(modulate_factors,saturation_percent,
7789         MaxTextExtent);
7790       (void) ModulateImage(*image,modulate_factors,exception);
7791       XSetCursorState(display,windows,MagickFalse);
7792       if (windows->image.orphan != MagickFalse)
7793         break;
7794       XConfigureImageColormap(display,resource_info,windows,*image);
7795       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7796       break;
7797     }
7798     case BrightnessCommand:
7799     {
7800       static char
7801         brightness_percent[MaxTextExtent] = "110";
7802
7803       /*
7804         Query user for percent brightness change.
7805       */
7806       (void) XDialogWidget(display,windows,"Apply",
7807         "Enter percent change in color brightness (0-200):",brightness_percent);
7808       if (*brightness_percent == '\0')
7809         break;
7810       /*
7811         Vary the color brightness.
7812       */
7813       XSetCursorState(display,windows,MagickTrue);
7814       XCheckRefreshWindows(display,windows);
7815       (void) CopyMagickString(modulate_factors,brightness_percent,
7816         MaxTextExtent);
7817       (void) ModulateImage(*image,modulate_factors,exception);
7818       XSetCursorState(display,windows,MagickFalse);
7819       if (windows->image.orphan != MagickFalse)
7820         break;
7821       XConfigureImageColormap(display,resource_info,windows,*image);
7822       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7823       break;
7824     }
7825     case GammaCommand:
7826     {
7827       static char
7828         factor[MaxTextExtent] = "1.6";
7829
7830       /*
7831         Query user for gamma value.
7832       */
7833       (void) XDialogWidget(display,windows,"Gamma",
7834         "Enter gamma value (e.g. 1.2):",factor);
7835       if (*factor == '\0')
7836         break;
7837       /*
7838         Gamma correct image.
7839       */
7840       XSetCursorState(display,windows,MagickTrue);
7841       XCheckRefreshWindows(display,windows);
7842       (void) GammaImage(*image,atof(factor),exception);
7843       XSetCursorState(display,windows,MagickFalse);
7844       if (windows->image.orphan != MagickFalse)
7845         break;
7846       XConfigureImageColormap(display,resource_info,windows,*image);
7847       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7848       break;
7849     }
7850     case SpiffCommand:
7851     {
7852       /*
7853         Sharpen the image contrast.
7854       */
7855       XSetCursorState(display,windows,MagickTrue);
7856       XCheckRefreshWindows(display,windows);
7857       (void) ContrastImage(*image,MagickTrue,exception);
7858       XSetCursorState(display,windows,MagickFalse);
7859       if (windows->image.orphan != MagickFalse)
7860         break;
7861       XConfigureImageColormap(display,resource_info,windows,*image);
7862       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7863       break;
7864     }
7865     case DullCommand:
7866     {
7867       /*
7868         Dull the image contrast.
7869       */
7870       XSetCursorState(display,windows,MagickTrue);
7871       XCheckRefreshWindows(display,windows);
7872       (void) ContrastImage(*image,MagickFalse,exception);
7873       XSetCursorState(display,windows,MagickFalse);
7874       if (windows->image.orphan != MagickFalse)
7875         break;
7876       XConfigureImageColormap(display,resource_info,windows,*image);
7877       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7878       break;
7879     }
7880     case ContrastStretchCommand:
7881     {
7882       double
7883         black_point,
7884         white_point;
7885
7886       static char
7887         levels[MaxTextExtent] = "1%";
7888
7889       /*
7890         Query user for gamma value.
7891       */
7892       (void) XDialogWidget(display,windows,"Contrast Stretch",
7893         "Enter black and white points:",levels);
7894       if (*levels == '\0')
7895         break;
7896       /*
7897         Contrast stretch image.
7898       */
7899       XSetCursorState(display,windows,MagickTrue);
7900       XCheckRefreshWindows(display,windows);
7901       flags=ParseGeometry(levels,&geometry_info);
7902       black_point=geometry_info.rho;
7903       white_point=(flags & SigmaValue) != 0 ? geometry_info.sigma : black_point;
7904       if ((flags & PercentValue) != 0)
7905         {
7906           black_point*=(double) (*image)->columns*(*image)->rows/100.0;
7907           white_point*=(double) (*image)->columns*(*image)->rows/100.0;
7908         }
7909       white_point=(MagickRealType) (*image)->columns*(*image)->rows-white_point;
7910       (void) ContrastStretchImage(*image,black_point,white_point,
7911         exception);
7912       XSetCursorState(display,windows,MagickFalse);
7913       if (windows->image.orphan != MagickFalse)
7914         break;
7915       XConfigureImageColormap(display,resource_info,windows,*image);
7916       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7917       break;
7918     }
7919     case SigmoidalContrastCommand:
7920     {
7921       GeometryInfo
7922         geometry_info;
7923
7924       MagickStatusType
7925         flags;
7926
7927       static char
7928         levels[MaxTextExtent] = "3x50%";
7929
7930       /*
7931         Query user for gamma value.
7932       */
7933       (void) XDialogWidget(display,windows,"Sigmoidal Contrast",
7934         "Enter contrast and midpoint:",levels);
7935       if (*levels == '\0')
7936         break;
7937       /*
7938         Contrast stretch image.
7939       */
7940       XSetCursorState(display,windows,MagickTrue);
7941       XCheckRefreshWindows(display,windows);
7942       flags=ParseGeometry(levels,&geometry_info);
7943       if ((flags & SigmaValue) == 0)
7944         geometry_info.sigma=1.0*QuantumRange/2.0;
7945       if ((flags & PercentValue) != 0)
7946         geometry_info.sigma=1.0*QuantumRange*geometry_info.sigma/100.0;
7947       (void) SigmoidalContrastImage(*image,MagickTrue,geometry_info.rho,
7948         geometry_info.sigma,exception);
7949       XSetCursorState(display,windows,MagickFalse);
7950       if (windows->image.orphan != MagickFalse)
7951         break;
7952       XConfigureImageColormap(display,resource_info,windows,*image);
7953       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7954       break;
7955     }
7956     case NormalizeCommand:
7957     {
7958       /*
7959         Perform histogram normalization on the image.
7960       */
7961       XSetCursorState(display,windows,MagickTrue);
7962       XCheckRefreshWindows(display,windows);
7963       (void) NormalizeImage(*image,exception);
7964       XSetCursorState(display,windows,MagickFalse);
7965       if (windows->image.orphan != MagickFalse)
7966         break;
7967       XConfigureImageColormap(display,resource_info,windows,*image);
7968       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7969       break;
7970     }
7971     case EqualizeCommand:
7972     {
7973       /*
7974         Perform histogram equalization on the image.
7975       */
7976       XSetCursorState(display,windows,MagickTrue);
7977       XCheckRefreshWindows(display,windows);
7978       (void) EqualizeImage(*image,exception);
7979       XSetCursorState(display,windows,MagickFalse);
7980       if (windows->image.orphan != MagickFalse)
7981         break;
7982       XConfigureImageColormap(display,resource_info,windows,*image);
7983       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7984       break;
7985     }
7986     case NegateCommand:
7987     {
7988       /*
7989         Negate colors in image.
7990       */
7991       XSetCursorState(display,windows,MagickTrue);
7992       XCheckRefreshWindows(display,windows);
7993       (void) NegateImage(*image,MagickFalse,exception);
7994       XSetCursorState(display,windows,MagickFalse);
7995       if (windows->image.orphan != MagickFalse)
7996         break;
7997       XConfigureImageColormap(display,resource_info,windows,*image);
7998       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7999       break;
8000     }
8001     case GrayscaleCommand:
8002     {
8003       /*
8004         Convert image to grayscale.
8005       */
8006       XSetCursorState(display,windows,MagickTrue);
8007       XCheckRefreshWindows(display,windows);
8008       (void) SetImageType(*image,(*image)->matte == MagickFalse ?
8009         GrayscaleType : GrayscaleMatteType,exception);
8010       XSetCursorState(display,windows,MagickFalse);
8011       if (windows->image.orphan != MagickFalse)
8012         break;
8013       XConfigureImageColormap(display,resource_info,windows,*image);
8014       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8015       break;
8016     }
8017     case MapCommand:
8018     {
8019       Image
8020         *affinity_image;
8021
8022       static char
8023         filename[MaxTextExtent] = "\0";
8024
8025       /*
8026         Request image file name from user.
8027       */
8028       XFileBrowserWidget(display,windows,"Map",filename);
8029       if (*filename == '\0')
8030         break;
8031       /*
8032         Map image.
8033       */
8034       XSetCursorState(display,windows,MagickTrue);
8035       XCheckRefreshWindows(display,windows);
8036       (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
8037       affinity_image=ReadImage(image_info,exception);
8038       if (affinity_image != (Image *) NULL)
8039         {
8040           (void) RemapImage(&quantize_info,*image,affinity_image,exception);
8041           affinity_image=DestroyImage(affinity_image);
8042         }
8043       CatchException(exception);
8044       XSetCursorState(display,windows,MagickFalse);
8045       if (windows->image.orphan != MagickFalse)
8046         break;
8047       XConfigureImageColormap(display,resource_info,windows,*image);
8048       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8049       break;
8050     }
8051     case QuantizeCommand:
8052     {
8053       int
8054         status;
8055
8056       static char
8057         colors[MaxTextExtent] = "256";
8058
8059       /*
8060         Query user for maximum number of colors.
8061       */
8062       status=XDialogWidget(display,windows,"Quantize",
8063         "Maximum number of colors:",colors);
8064       if (*colors == '\0')
8065         break;
8066       /*
8067         Color reduce the image.
8068       */
8069       XSetCursorState(display,windows,MagickTrue);
8070       XCheckRefreshWindows(display,windows);
8071       quantize_info.number_colors=StringToUnsignedLong(colors);
8072       quantize_info.dither=status != 0 ? MagickTrue : MagickFalse;
8073       (void) QuantizeImage(&quantize_info,*image,exception);
8074       XSetCursorState(display,windows,MagickFalse);
8075       if (windows->image.orphan != MagickFalse)
8076         break;
8077       XConfigureImageColormap(display,resource_info,windows,*image);
8078       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8079       break;
8080     }
8081     case DespeckleCommand:
8082     {
8083       Image
8084         *despeckle_image;
8085
8086       /*
8087         Despeckle image.
8088       */
8089       XSetCursorState(display,windows,MagickTrue);
8090       XCheckRefreshWindows(display,windows);
8091       despeckle_image=DespeckleImage(*image,exception);
8092       if (despeckle_image != (Image *) NULL)
8093         {
8094           *image=DestroyImage(*image);
8095           *image=despeckle_image;
8096         }
8097       CatchException(exception);
8098       XSetCursorState(display,windows,MagickFalse);
8099       if (windows->image.orphan != MagickFalse)
8100         break;
8101       XConfigureImageColormap(display,resource_info,windows,*image);
8102       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8103       break;
8104     }
8105     case EmbossCommand:
8106     {
8107       Image
8108         *emboss_image;
8109
8110       static char
8111         radius[MaxTextExtent] = "0.0x1.0";
8112
8113       /*
8114         Query user for emboss radius.
8115       */
8116       (void) XDialogWidget(display,windows,"Emboss",
8117         "Enter the emboss radius and standard deviation:",radius);
8118       if (*radius == '\0')
8119         break;
8120       /*
8121         Reduce noise in the image.
8122       */
8123       XSetCursorState(display,windows,MagickTrue);
8124       XCheckRefreshWindows(display,windows);
8125       flags=ParseGeometry(radius,&geometry_info);
8126       if ((flags & SigmaValue) == 0)
8127         geometry_info.sigma=1.0;
8128       emboss_image=EmbossImage(*image,geometry_info.rho,geometry_info.sigma,
8129         exception);
8130       if (emboss_image != (Image *) NULL)
8131         {
8132           *image=DestroyImage(*image);
8133           *image=emboss_image;
8134         }
8135       CatchException(exception);
8136       XSetCursorState(display,windows,MagickFalse);
8137       if (windows->image.orphan != MagickFalse)
8138         break;
8139       XConfigureImageColormap(display,resource_info,windows,*image);
8140       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8141       break;
8142     }
8143     case ReduceNoiseCommand:
8144     {
8145       Image
8146         *noise_image;
8147
8148       static char
8149         radius[MaxTextExtent] = "0";
8150
8151       /*
8152         Query user for noise radius.
8153       */
8154       (void) XDialogWidget(display,windows,"Reduce Noise",
8155         "Enter the noise radius:",radius);
8156       if (*radius == '\0')
8157         break;
8158       /*
8159         Reduce noise in the image.
8160       */
8161       XSetCursorState(display,windows,MagickTrue);
8162       XCheckRefreshWindows(display,windows);
8163       flags=ParseGeometry(radius,&geometry_info);
8164       noise_image=StatisticImage(*image,NonpeakStatistic,(size_t)
8165         geometry_info.rho,(size_t) geometry_info.rho,exception);
8166       if (noise_image != (Image *) NULL)
8167         {
8168           *image=DestroyImage(*image);
8169           *image=noise_image;
8170         }
8171       CatchException(exception);
8172       XSetCursorState(display,windows,MagickFalse);
8173       if (windows->image.orphan != MagickFalse)
8174         break;
8175       XConfigureImageColormap(display,resource_info,windows,*image);
8176       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8177       break;
8178     }
8179     case AddNoiseCommand:
8180     {
8181       char
8182         **noises;
8183
8184       Image
8185         *noise_image;
8186
8187       static char
8188         noise_type[MaxTextExtent] = "Gaussian";
8189
8190       /*
8191         Add noise to the image.
8192       */
8193       noises=GetCommandOptions(MagickNoiseOptions);
8194       if (noises == (char **) NULL)
8195         break;
8196       XListBrowserWidget(display,windows,&windows->widget,
8197         (const char **) noises,"Add Noise",
8198         "Select a type of noise to add to your image:",noise_type);
8199       noises=DestroyStringList(noises);
8200       if (*noise_type == '\0')
8201         break;
8202       XSetCursorState(display,windows,MagickTrue);
8203       XCheckRefreshWindows(display,windows);
8204       noise_image=AddNoiseImage(*image,(NoiseType) ParseCommandOption(
8205         MagickNoiseOptions,MagickFalse,noise_type),1.0,exception);
8206       if (noise_image != (Image *) NULL)
8207         {
8208           *image=DestroyImage(*image);
8209           *image=noise_image;
8210         }
8211       CatchException(exception);
8212       XSetCursorState(display,windows,MagickFalse);
8213       if (windows->image.orphan != MagickFalse)
8214         break;
8215       XConfigureImageColormap(display,resource_info,windows,*image);
8216       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8217       break;
8218     }
8219     case SharpenCommand:
8220     {
8221       Image
8222         *sharp_image;
8223
8224       static char
8225         radius[MaxTextExtent] = "0.0x1.0";
8226
8227       /*
8228         Query user for sharpen radius.
8229       */
8230       (void) XDialogWidget(display,windows,"Sharpen",
8231         "Enter the sharpen radius and standard deviation:",radius);
8232       if (*radius == '\0')
8233         break;
8234       /*
8235         Sharpen image scanlines.
8236       */
8237       XSetCursorState(display,windows,MagickTrue);
8238       XCheckRefreshWindows(display,windows);
8239       flags=ParseGeometry(radius,&geometry_info);
8240       sharp_image=SharpenImage(*image,geometry_info.rho,geometry_info.sigma,
8241         geometry_info.xi,exception);
8242       if (sharp_image != (Image *) NULL)
8243         {
8244           *image=DestroyImage(*image);
8245           *image=sharp_image;
8246         }
8247       CatchException(exception);
8248       XSetCursorState(display,windows,MagickFalse);
8249       if (windows->image.orphan != MagickFalse)
8250         break;
8251       XConfigureImageColormap(display,resource_info,windows,*image);
8252       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8253       break;
8254     }
8255     case BlurCommand:
8256     {
8257       Image
8258         *blur_image;
8259
8260       static char
8261         radius[MaxTextExtent] = "0.0x1.0";
8262
8263       /*
8264         Query user for blur radius.
8265       */
8266       (void) XDialogWidget(display,windows,"Blur",
8267         "Enter the blur radius and standard deviation:",radius);
8268       if (*radius == '\0')
8269         break;
8270       /*
8271         Blur an image.
8272       */
8273       XSetCursorState(display,windows,MagickTrue);
8274       XCheckRefreshWindows(display,windows);
8275       flags=ParseGeometry(radius,&geometry_info);
8276       blur_image=BlurImage(*image,geometry_info.rho,geometry_info.sigma,
8277         geometry_info.xi,exception);
8278       if (blur_image != (Image *) NULL)
8279         {
8280           *image=DestroyImage(*image);
8281           *image=blur_image;
8282         }
8283       CatchException(exception);
8284       XSetCursorState(display,windows,MagickFalse);
8285       if (windows->image.orphan != MagickFalse)
8286         break;
8287       XConfigureImageColormap(display,resource_info,windows,*image);
8288       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8289       break;
8290     }
8291     case ThresholdCommand:
8292     {
8293       double
8294         threshold;
8295
8296       static char
8297         factor[MaxTextExtent] = "128";
8298
8299       /*
8300         Query user for threshold value.
8301       */
8302       (void) XDialogWidget(display,windows,"Threshold",
8303         "Enter threshold value:",factor);
8304       if (*factor == '\0')
8305         break;
8306       /*
8307         Gamma correct image.
8308       */
8309       XSetCursorState(display,windows,MagickTrue);
8310       XCheckRefreshWindows(display,windows);
8311       threshold=SiPrefixToDouble(factor,QuantumRange);
8312       (void) BilevelImage(*image,threshold,exception);
8313       XSetCursorState(display,windows,MagickFalse);
8314       if (windows->image.orphan != MagickFalse)
8315         break;
8316       XConfigureImageColormap(display,resource_info,windows,*image);
8317       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8318       break;
8319     }
8320     case EdgeDetectCommand:
8321     {
8322       Image
8323         *edge_image;
8324
8325       static char
8326         radius[MaxTextExtent] = "0";
8327
8328       /*
8329         Query user for edge factor.
8330       */
8331       (void) XDialogWidget(display,windows,"Detect Edges",
8332         "Enter the edge detect radius:",radius);
8333       if (*radius == '\0')
8334         break;
8335       /*
8336         Detect edge in image.
8337       */
8338       XSetCursorState(display,windows,MagickTrue);
8339       XCheckRefreshWindows(display,windows);
8340       flags=ParseGeometry(radius,&geometry_info);
8341       edge_image=EdgeImage(*image,geometry_info.rho,geometry_info.sigma,
8342         exception);
8343       if (edge_image != (Image *) NULL)
8344         {
8345           *image=DestroyImage(*image);
8346           *image=edge_image;
8347         }
8348       CatchException(exception);
8349       XSetCursorState(display,windows,MagickFalse);
8350       if (windows->image.orphan != MagickFalse)
8351         break;
8352       XConfigureImageColormap(display,resource_info,windows,*image);
8353       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8354       break;
8355     }
8356     case SpreadCommand:
8357     {
8358       Image
8359         *spread_image;
8360
8361       static char
8362         amount[MaxTextExtent] = "2";
8363
8364       /*
8365         Query user for spread amount.
8366       */
8367       (void) XDialogWidget(display,windows,"Spread",
8368         "Enter the displacement amount:",amount);
8369       if (*amount == '\0')
8370         break;
8371       /*
8372         Displace image pixels by a random amount.
8373       */
8374       XSetCursorState(display,windows,MagickTrue);
8375       XCheckRefreshWindows(display,windows);
8376       flags=ParseGeometry(amount,&geometry_info);
8377       spread_image=EdgeImage(*image,geometry_info.rho,geometry_info.sigma,
8378         exception);
8379       if (spread_image != (Image *) NULL)
8380         {
8381           *image=DestroyImage(*image);
8382           *image=spread_image;
8383         }
8384       CatchException(exception);
8385       XSetCursorState(display,windows,MagickFalse);
8386       if (windows->image.orphan != MagickFalse)
8387         break;
8388       XConfigureImageColormap(display,resource_info,windows,*image);
8389       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8390       break;
8391     }
8392     case ShadeCommand:
8393     {
8394       Image
8395         *shade_image;
8396
8397       int
8398         status;
8399
8400       static char
8401         geometry[MaxTextExtent] = "30x30";
8402
8403       /*
8404         Query user for the shade geometry.
8405       */
8406       status=XDialogWidget(display,windows,"Shade",
8407         "Enter the azimuth and elevation of the light source:",geometry);
8408       if (*geometry == '\0')
8409         break;
8410       /*
8411         Shade image pixels.
8412       */
8413       XSetCursorState(display,windows,MagickTrue);
8414       XCheckRefreshWindows(display,windows);
8415       flags=ParseGeometry(geometry,&geometry_info);
8416       if ((flags & SigmaValue) == 0)
8417         geometry_info.sigma=1.0;
8418       shade_image=ShadeImage(*image,status != 0 ? MagickFalse : MagickTrue,
8419         geometry_info.rho,geometry_info.sigma,exception);
8420       if (shade_image != (Image *) NULL)
8421         {
8422           *image=DestroyImage(*image);
8423           *image=shade_image;
8424         }
8425       CatchException(exception);
8426       XSetCursorState(display,windows,MagickFalse);
8427       if (windows->image.orphan != MagickFalse)
8428         break;
8429       XConfigureImageColormap(display,resource_info,windows,*image);
8430       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8431       break;
8432     }
8433     case RaiseCommand:
8434     {
8435       static char
8436         bevel_width[MaxTextExtent] = "10";
8437
8438       /*
8439         Query user for bevel width.
8440       */
8441       (void) XDialogWidget(display,windows,"Raise","Bevel width:",bevel_width);
8442       if (*bevel_width == '\0')
8443         break;
8444       /*
8445         Raise an image.
8446       */
8447       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8448         exception);
8449       XSetCursorState(display,windows,MagickTrue);
8450       XCheckRefreshWindows(display,windows);
8451       (void) ParsePageGeometry(*image,bevel_width,&page_geometry,
8452         exception);
8453       (void) RaiseImage(*image,&page_geometry,MagickTrue,exception);
8454       XSetCursorState(display,windows,MagickFalse);
8455       if (windows->image.orphan != MagickFalse)
8456         break;
8457       XConfigureImageColormap(display,resource_info,windows,*image);
8458       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8459       break;
8460     }
8461     case SegmentCommand:
8462     {
8463       static char
8464         threshold[MaxTextExtent] = "1.0x1.5";
8465
8466       /*
8467         Query user for smoothing threshold.
8468       */
8469       (void) XDialogWidget(display,windows,"Segment","Smooth threshold:",
8470         threshold);
8471       if (*threshold == '\0')
8472         break;
8473       /*
8474         Segment an image.
8475       */
8476       XSetCursorState(display,windows,MagickTrue);
8477       XCheckRefreshWindows(display,windows);
8478       flags=ParseGeometry(threshold,&geometry_info);
8479       if ((flags & SigmaValue) == 0)
8480         geometry_info.sigma=1.0;
8481       (void) SegmentImage(*image,RGBColorspace,MagickFalse,geometry_info.rho,
8482         geometry_info.sigma,exception);
8483       XSetCursorState(display,windows,MagickFalse);
8484       if (windows->image.orphan != MagickFalse)
8485         break;
8486       XConfigureImageColormap(display,resource_info,windows,*image);
8487       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8488       break;
8489     }
8490     case SepiaToneCommand:
8491     {
8492       double
8493         threshold;
8494
8495       Image
8496         *sepia_image;
8497
8498       static char
8499         factor[MaxTextExtent] = "80%";
8500
8501       /*
8502         Query user for sepia-tone factor.
8503       */
8504       (void) XDialogWidget(display,windows,"Sepia Tone",
8505         "Enter the sepia tone factor (0 - 99.9%):",factor);
8506       if (*factor == '\0')
8507         break;
8508       /*
8509         Sepia tone image pixels.
8510       */
8511       XSetCursorState(display,windows,MagickTrue);
8512       XCheckRefreshWindows(display,windows);
8513       threshold=SiPrefixToDouble(factor,QuantumRange);
8514       sepia_image=SepiaToneImage(*image,threshold,exception);
8515       if (sepia_image != (Image *) NULL)
8516         {
8517           *image=DestroyImage(*image);
8518           *image=sepia_image;
8519         }
8520       CatchException(exception);
8521       XSetCursorState(display,windows,MagickFalse);
8522       if (windows->image.orphan != MagickFalse)
8523         break;
8524       XConfigureImageColormap(display,resource_info,windows,*image);
8525       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8526       break;
8527     }
8528     case SolarizeCommand:
8529     {
8530       double
8531         threshold;
8532
8533       static char
8534         factor[MaxTextExtent] = "60%";
8535
8536       /*
8537         Query user for solarize factor.
8538       */
8539       (void) XDialogWidget(display,windows,"Solarize",
8540         "Enter the solarize factor (0 - 99.9%):",factor);
8541       if (*factor == '\0')
8542         break;
8543       /*
8544         Solarize image pixels.
8545       */
8546       XSetCursorState(display,windows,MagickTrue);
8547       XCheckRefreshWindows(display,windows);
8548       threshold=SiPrefixToDouble(factor,QuantumRange);
8549       (void) SolarizeImage(*image,threshold,exception);
8550       XSetCursorState(display,windows,MagickFalse);
8551       if (windows->image.orphan != MagickFalse)
8552         break;
8553       XConfigureImageColormap(display,resource_info,windows,*image);
8554       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8555       break;
8556     }
8557     case SwirlCommand:
8558     {
8559       Image
8560         *swirl_image;
8561
8562       static char
8563         degrees[MaxTextExtent] = "60";
8564
8565       /*
8566         Query user for swirl angle.
8567       */
8568       (void) XDialogWidget(display,windows,"Swirl","Enter the swirl angle:",
8569         degrees);
8570       if (*degrees == '\0')
8571         break;
8572       /*
8573         Swirl image pixels about the center.
8574       */
8575       XSetCursorState(display,windows,MagickTrue);
8576       XCheckRefreshWindows(display,windows);
8577       flags=ParseGeometry(degrees,&geometry_info);
8578       swirl_image=SwirlImage(*image,geometry_info.rho,(*image)->interpolate,
8579         exception);
8580       if (swirl_image != (Image *) NULL)
8581         {
8582           *image=DestroyImage(*image);
8583           *image=swirl_image;
8584         }
8585       CatchException(exception);
8586       XSetCursorState(display,windows,MagickFalse);
8587       if (windows->image.orphan != MagickFalse)
8588         break;
8589       XConfigureImageColormap(display,resource_info,windows,*image);
8590       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8591       break;
8592     }
8593     case ImplodeCommand:
8594     {
8595       Image
8596         *implode_image;
8597
8598       static char
8599         factor[MaxTextExtent] = "0.3";
8600
8601       /*
8602         Query user for implode factor.
8603       */
8604       (void) XDialogWidget(display,windows,"Implode",
8605         "Enter the implosion/explosion factor (-1.0 - 1.0):",factor);
8606       if (*factor == '\0')
8607         break;
8608       /*
8609         Implode image pixels about the center.
8610       */
8611       XSetCursorState(display,windows,MagickTrue);
8612       XCheckRefreshWindows(display,windows);
8613       flags=ParseGeometry(factor,&geometry_info);
8614       implode_image=ImplodeImage(*image,geometry_info.rho,(*image)->interpolate,
8615         exception);
8616       if (implode_image != (Image *) NULL)
8617         {
8618           *image=DestroyImage(*image);
8619           *image=implode_image;
8620         }
8621       CatchException(exception);
8622       XSetCursorState(display,windows,MagickFalse);
8623       if (windows->image.orphan != MagickFalse)
8624         break;
8625       XConfigureImageColormap(display,resource_info,windows,*image);
8626       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8627       break;
8628     }
8629     case VignetteCommand:
8630     {
8631       Image
8632         *vignette_image;
8633
8634       static char
8635         geometry[MaxTextExtent] = "0x20";
8636
8637       /*
8638         Query user for the vignette geometry.
8639       */
8640       (void) XDialogWidget(display,windows,"Vignette",
8641         "Enter the radius, sigma, and x and y offsets:",geometry);
8642       if (*geometry == '\0')
8643         break;
8644       /*
8645         Soften the edges of the image in vignette style
8646       */
8647       XSetCursorState(display,windows,MagickTrue);
8648       XCheckRefreshWindows(display,windows);
8649       flags=ParseGeometry(geometry,&geometry_info);
8650       if ((flags & SigmaValue) == 0)
8651         geometry_info.sigma=1.0;
8652       if ((flags & XiValue) == 0)
8653         geometry_info.xi=0.1*(*image)->columns;
8654       if ((flags & PsiValue) == 0)
8655         geometry_info.psi=0.1*(*image)->rows;
8656       vignette_image=VignetteImage(*image,geometry_info.rho,geometry_info.sigma,
8657         (ssize_t) ceil(geometry_info.xi-0.5),(ssize_t) ceil(geometry_info.psi-
8658         0.5),exception);
8659       if (vignette_image != (Image *) NULL)
8660         {
8661           *image=DestroyImage(*image);
8662           *image=vignette_image;
8663         }
8664       CatchException(exception);
8665       XSetCursorState(display,windows,MagickFalse);
8666       if (windows->image.orphan != MagickFalse)
8667         break;
8668       XConfigureImageColormap(display,resource_info,windows,*image);
8669       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8670       break;
8671     }
8672     case WaveCommand:
8673     {
8674       Image
8675         *wave_image;
8676
8677       static char
8678         geometry[MaxTextExtent] = "25x150";
8679
8680       /*
8681         Query user for the wave geometry.
8682       */
8683       (void) XDialogWidget(display,windows,"Wave",
8684         "Enter the amplitude and length of the wave:",geometry);
8685       if (*geometry == '\0')
8686         break;
8687       /*
8688         Alter an image along a sine wave.
8689       */
8690       XSetCursorState(display,windows,MagickTrue);
8691       XCheckRefreshWindows(display,windows);
8692       flags=ParseGeometry(geometry,&geometry_info);
8693       if ((flags & SigmaValue) == 0)
8694         geometry_info.sigma=1.0;
8695       wave_image=WaveImage(*image,geometry_info.rho,geometry_info.sigma,
8696         (*image)->interpolate,exception);
8697       if (wave_image != (Image *) NULL)
8698         {
8699           *image=DestroyImage(*image);
8700           *image=wave_image;
8701         }
8702       CatchException(exception);
8703       XSetCursorState(display,windows,MagickFalse);
8704       if (windows->image.orphan != MagickFalse)
8705         break;
8706       XConfigureImageColormap(display,resource_info,windows,*image);
8707       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8708       break;
8709     }
8710     case OilPaintCommand:
8711     {
8712       Image
8713         *paint_image;
8714
8715       static char
8716         radius[MaxTextExtent] = "0";
8717
8718       /*
8719         Query user for circular neighborhood radius.
8720       */
8721       (void) XDialogWidget(display,windows,"Oil Paint",
8722         "Enter the mask radius:",radius);
8723       if (*radius == '\0')
8724         break;
8725       /*
8726         OilPaint image scanlines.
8727       */
8728       XSetCursorState(display,windows,MagickTrue);
8729       XCheckRefreshWindows(display,windows);
8730       flags=ParseGeometry(radius,&geometry_info);
8731       paint_image=OilPaintImage(*image,geometry_info.rho,geometry_info.sigma,
8732         exception);
8733       if (paint_image != (Image *) NULL)
8734         {
8735           *image=DestroyImage(*image);
8736           *image=paint_image;
8737         }
8738       CatchException(exception);
8739       XSetCursorState(display,windows,MagickFalse);
8740       if (windows->image.orphan != MagickFalse)
8741         break;
8742       XConfigureImageColormap(display,resource_info,windows,*image);
8743       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8744       break;
8745     }
8746     case CharcoalDrawCommand:
8747     {
8748       Image
8749         *charcoal_image;
8750
8751       static char
8752         radius[MaxTextExtent] = "0x1";
8753
8754       /*
8755         Query user for charcoal radius.
8756       */
8757       (void) XDialogWidget(display,windows,"Charcoal Draw",
8758         "Enter the charcoal radius and sigma:",radius);
8759       if (*radius == '\0')
8760         break;
8761       /*
8762         Charcoal the image.
8763       */
8764       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8765         exception);
8766       XSetCursorState(display,windows,MagickTrue);
8767       XCheckRefreshWindows(display,windows);
8768       flags=ParseGeometry(radius,&geometry_info);
8769       if ((flags & SigmaValue) == 0)
8770         geometry_info.sigma=geometry_info.rho;
8771       charcoal_image=CharcoalImage(*image,geometry_info.rho,geometry_info.sigma,
8772         geometry_info.xi,exception);
8773       if (charcoal_image != (Image *) NULL)
8774         {
8775           *image=DestroyImage(*image);
8776           *image=charcoal_image;
8777         }
8778       CatchException(exception);
8779       XSetCursorState(display,windows,MagickFalse);
8780       if (windows->image.orphan != MagickFalse)
8781         break;
8782       XConfigureImageColormap(display,resource_info,windows,*image);
8783       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8784       break;
8785     }
8786     case AnnotateCommand:
8787     {
8788       /*
8789         Annotate the image with text.
8790       */
8791       status=XAnnotateEditImage(display,resource_info,windows,*image,exception);
8792       if (status == MagickFalse)
8793         {
8794           XNoticeWidget(display,windows,"Unable to annotate X image",
8795             (*image)->filename);
8796           break;
8797         }
8798       break;
8799     }
8800     case DrawCommand:
8801     {
8802       /*
8803         Draw image.
8804       */
8805       status=XDrawEditImage(display,resource_info,windows,image,exception);
8806       if (status == MagickFalse)
8807         {
8808           XNoticeWidget(display,windows,"Unable to draw on the X image",
8809             (*image)->filename);
8810           break;
8811         }
8812       break;
8813     }
8814     case ColorCommand:
8815     {
8816       /*
8817         Color edit.
8818       */
8819       status=XColorEditImage(display,resource_info,windows,image,exception);
8820       if (status == MagickFalse)
8821         {
8822           XNoticeWidget(display,windows,"Unable to pixel edit X image",
8823             (*image)->filename);
8824           break;
8825         }
8826       break;
8827     }
8828     case MatteCommand:
8829     {
8830       /*
8831         Matte edit.
8832       */
8833       status=XMatteEditImage(display,resource_info,windows,image,exception);
8834       if (status == MagickFalse)
8835         {
8836           XNoticeWidget(display,windows,"Unable to matte edit X image",
8837             (*image)->filename);
8838           break;
8839         }
8840       break;
8841     }
8842     case CompositeCommand:
8843     {
8844       /*
8845         Composite image.
8846       */
8847       status=XCompositeImage(display,resource_info,windows,*image,
8848         exception);
8849       if (status == MagickFalse)
8850         {
8851           XNoticeWidget(display,windows,"Unable to composite X image",
8852             (*image)->filename);
8853           break;
8854         }
8855       break;
8856     }
8857     case AddBorderCommand:
8858     {
8859       Image
8860         *border_image;
8861
8862       static char
8863         geometry[MaxTextExtent] = "6x6";
8864
8865       /*
8866         Query user for border color and geometry.
8867       */
8868       XColorBrowserWidget(display,windows,"Select",color);
8869       if (*color == '\0')
8870         break;
8871       (void) XDialogWidget(display,windows,"Add Border",
8872         "Enter border geometry:",geometry);
8873       if (*geometry == '\0')
8874         break;
8875       /*
8876         Add a border to the image.
8877       */
8878       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8879         exception);
8880       XSetCursorState(display,windows,MagickTrue);
8881       XCheckRefreshWindows(display,windows);
8882       (void) QueryColorCompliance(color,AllCompliance,&(*image)->border_color,
8883         exception);
8884       (void) ParsePageGeometry(*image,geometry,&page_geometry,
8885         exception);
8886       border_image=BorderImage(*image,&page_geometry,(*image)->compose,
8887         exception);
8888       if (border_image != (Image *) NULL)
8889         {
8890           *image=DestroyImage(*image);
8891           *image=border_image;
8892         }
8893       CatchException(exception);
8894       XSetCursorState(display,windows,MagickFalse);
8895       if (windows->image.orphan != MagickFalse)
8896         break;
8897       windows->image.window_changes.width=(int) (*image)->columns;
8898       windows->image.window_changes.height=(int) (*image)->rows;
8899       XConfigureImageColormap(display,resource_info,windows,*image);
8900       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8901       break;
8902     }
8903     case AddFrameCommand:
8904     {
8905       FrameInfo
8906         frame_info;
8907
8908       Image
8909         *frame_image;
8910
8911       static char
8912         geometry[MaxTextExtent] = "6x6";
8913
8914       /*
8915         Query user for frame color and geometry.
8916       */
8917       XColorBrowserWidget(display,windows,"Select",color);
8918       if (*color == '\0')
8919         break;
8920       (void) XDialogWidget(display,windows,"Add Frame","Enter frame geometry:",
8921         geometry);
8922       if (*geometry == '\0')
8923         break;
8924       /*
8925         Surround image with an ornamental border.
8926       */
8927       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8928         exception);
8929       XSetCursorState(display,windows,MagickTrue);
8930       XCheckRefreshWindows(display,windows);
8931       (void) QueryColorCompliance(color,AllCompliance,&(*image)->matte_color,
8932         exception);
8933       (void) ParsePageGeometry(*image,geometry,&page_geometry,
8934         exception);
8935       frame_info.width=page_geometry.width;
8936       frame_info.height=page_geometry.height;
8937       frame_info.outer_bevel=page_geometry.x;
8938       frame_info.inner_bevel=page_geometry.y;
8939       frame_info.x=(ssize_t) frame_info.width;
8940       frame_info.y=(ssize_t) frame_info.height;
8941       frame_info.width=(*image)->columns+2*frame_info.width;
8942       frame_info.height=(*image)->rows+2*frame_info.height;
8943       frame_image=FrameImage(*image,&frame_info,(*image)->compose,exception);
8944       if (frame_image != (Image *) NULL)
8945         {
8946           *image=DestroyImage(*image);
8947           *image=frame_image;
8948         }
8949       CatchException(exception);
8950       XSetCursorState(display,windows,MagickFalse);
8951       if (windows->image.orphan != MagickFalse)
8952         break;
8953       windows->image.window_changes.width=(int) (*image)->columns;
8954       windows->image.window_changes.height=(int) (*image)->rows;
8955       XConfigureImageColormap(display,resource_info,windows,*image);
8956       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8957       break;
8958     }
8959     case CommentCommand:
8960     {
8961       const char
8962         *value;
8963
8964       FILE
8965         *file;
8966
8967       int
8968         unique_file;
8969
8970       /*
8971         Edit image comment.
8972       */
8973       unique_file=AcquireUniqueFileResource(image_info->filename);
8974       if (unique_file == -1)
8975         XNoticeWidget(display,windows,"Unable to edit image comment",
8976           image_info->filename);
8977       value=GetImageProperty(*image,"comment",exception);
8978       if (value == (char *) NULL)
8979         unique_file=close(unique_file)-1;
8980       else
8981         {
8982           register const char
8983             *p;
8984
8985           file=fdopen(unique_file,"w");
8986           if (file == (FILE *) NULL)
8987             {
8988               XNoticeWidget(display,windows,"Unable to edit image comment",
8989                 image_info->filename);
8990               break;
8991             }
8992           for (p=value; *p != '\0'; p++)
8993             (void) fputc((int) *p,file);
8994           (void) fputc('\n',file);
8995           (void) fclose(file);
8996         }
8997       XSetCursorState(display,windows,MagickTrue);
8998       XCheckRefreshWindows(display,windows);
8999       status=InvokeDelegate(image_info,*image,"edit",(char *) NULL,
9000         exception);
9001       if (status == MagickFalse)
9002         XNoticeWidget(display,windows,"Unable to edit image comment",
9003           (char *) NULL);
9004       else
9005         {
9006           char
9007             *comment;
9008
9009           comment=FileToString(image_info->filename,~0UL,exception);
9010           if (comment != (char *) NULL)
9011             {
9012               (void) SetImageProperty(*image,"comment",comment,exception);
9013               (*image)->taint=MagickTrue;
9014             }
9015         }
9016       (void) RelinquishUniqueFileResource(image_info->filename);
9017       XSetCursorState(display,windows,MagickFalse);
9018       break;
9019     }
9020     case LaunchCommand:
9021     {
9022       /*
9023         Launch program.
9024       */
9025       XSetCursorState(display,windows,MagickTrue);
9026       XCheckRefreshWindows(display,windows);
9027       (void) AcquireUniqueFilename(filename);
9028       (void) FormatLocaleString((*image)->filename,MaxTextExtent,"launch:%s",
9029         filename);
9030       status=WriteImage(image_info,*image,exception);
9031       if (status == MagickFalse)
9032         XNoticeWidget(display,windows,"Unable to launch image editor",
9033           (char *) NULL);
9034       else
9035         {
9036           nexus=ReadImage(resource_info->image_info,exception);
9037           CatchException(exception);
9038           XClientMessage(display,windows->image.id,windows->im_protocols,
9039             windows->im_next_image,CurrentTime);
9040         }
9041       (void) RelinquishUniqueFileResource(filename);
9042       XSetCursorState(display,windows,MagickFalse);
9043       break;
9044     }
9045     case RegionofInterestCommand:
9046     {
9047       /*
9048         Apply an image processing technique to a region of interest.
9049       */
9050       (void) XROIImage(display,resource_info,windows,image,exception);
9051       break;
9052     }
9053     case InfoCommand:
9054       break;
9055     case ZoomCommand:
9056     {
9057       /*
9058         Zoom image.
9059       */
9060       if (windows->magnify.mapped != MagickFalse)
9061         (void) XRaiseWindow(display,windows->magnify.id);
9062       else
9063         {
9064           /*
9065             Make magnify image.
9066           */
9067           XSetCursorState(display,windows,MagickTrue);
9068           (void) XMapRaised(display,windows->magnify.id);
9069           XSetCursorState(display,windows,MagickFalse);
9070         }
9071       break;
9072     }
9073     case ShowPreviewCommand:
9074     {
9075       char
9076         **previews;
9077
9078       Image
9079         *preview_image;
9080
9081       static char
9082         preview_type[MaxTextExtent] = "Gamma";
9083
9084       /*
9085         Select preview type from menu.
9086       */
9087       previews=GetCommandOptions(MagickPreviewOptions);
9088       if (previews == (char **) NULL)
9089         break;
9090       XListBrowserWidget(display,windows,&windows->widget,
9091         (const char **) previews,"Preview",
9092         "Select an enhancement, effect, or F/X:",preview_type);
9093       previews=DestroyStringList(previews);
9094       if (*preview_type == '\0')
9095         break;
9096       /*
9097         Show image preview.
9098       */
9099       XSetCursorState(display,windows,MagickTrue);
9100       XCheckRefreshWindows(display,windows);
9101       image_info->preview_type=(PreviewType)
9102         ParseCommandOption(MagickPreviewOptions,MagickFalse,preview_type);
9103       image_info->group=(ssize_t) windows->image.id;
9104       (void) DeleteImageProperty(*image,"label");
9105       (void) SetImageProperty(*image,"label","Preview",exception);
9106       (void) AcquireUniqueFilename(filename);
9107       (void) FormatLocaleString((*image)->filename,MaxTextExtent,"preview:%s",
9108         filename);
9109       status=WriteImage(image_info,*image,exception);
9110       (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9111       preview_image=ReadImage(image_info,exception);
9112       (void) RelinquishUniqueFileResource(filename);
9113       if (preview_image == (Image *) NULL)
9114         break;
9115       (void) FormatLocaleString(preview_image->filename,MaxTextExtent,"show:%s",
9116         filename);
9117       status=WriteImage(image_info,preview_image,exception);
9118       preview_image=DestroyImage(preview_image);
9119       if (status == MagickFalse)
9120         XNoticeWidget(display,windows,"Unable to show image preview",
9121           (*image)->filename);
9122       XDelay(display,1500);
9123       XSetCursorState(display,windows,MagickFalse);
9124       break;
9125     }
9126     case ShowHistogramCommand:
9127     {
9128       Image
9129         *histogram_image;
9130
9131       /*
9132         Show image histogram.
9133       */
9134       XSetCursorState(display,windows,MagickTrue);
9135       XCheckRefreshWindows(display,windows);
9136       image_info->group=(ssize_t) windows->image.id;
9137       (void) DeleteImageProperty(*image,"label");
9138       (void) SetImageProperty(*image,"label","Histogram",exception);
9139       (void) AcquireUniqueFilename(filename);
9140       (void) FormatLocaleString((*image)->filename,MaxTextExtent,"histogram:%s",
9141         filename);
9142       status=WriteImage(image_info,*image,exception);
9143       (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9144       histogram_image=ReadImage(image_info,exception);
9145       (void) RelinquishUniqueFileResource(filename);
9146       if (histogram_image == (Image *) NULL)
9147         break;
9148       (void) FormatLocaleString(histogram_image->filename,MaxTextExtent,
9149         "show:%s",filename);
9150       status=WriteImage(image_info,histogram_image,exception);
9151       histogram_image=DestroyImage(histogram_image);
9152       if (status == MagickFalse)
9153         XNoticeWidget(display,windows,"Unable to show histogram",
9154           (*image)->filename);
9155       XDelay(display,1500);
9156       XSetCursorState(display,windows,MagickFalse);
9157       break;
9158     }
9159     case ShowMatteCommand:
9160     {
9161       Image
9162         *matte_image;
9163
9164       if ((*image)->matte == MagickFalse)
9165         {
9166           XNoticeWidget(display,windows,
9167             "Image does not have any matte information",(*image)->filename);
9168           break;
9169         }
9170       /*
9171         Show image matte.
9172       */
9173       XSetCursorState(display,windows,MagickTrue);
9174       XCheckRefreshWindows(display,windows);
9175       image_info->group=(ssize_t) windows->image.id;
9176       (void) DeleteImageProperty(*image,"label");
9177       (void) SetImageProperty(*image,"label","Matte",exception);
9178       (void) AcquireUniqueFilename(filename);
9179       (void) FormatLocaleString((*image)->filename,MaxTextExtent,"matte:%s",
9180         filename);
9181       status=WriteImage(image_info,*image,exception);
9182       (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9183       matte_image=ReadImage(image_info,exception);
9184       (void) RelinquishUniqueFileResource(filename);
9185       if (matte_image == (Image *) NULL)
9186         break;
9187       (void) FormatLocaleString(matte_image->filename,MaxTextExtent,"show:%s",
9188         filename);
9189       status=WriteImage(image_info,matte_image,exception);
9190       matte_image=DestroyImage(matte_image);
9191       if (status == MagickFalse)
9192         XNoticeWidget(display,windows,"Unable to show matte",
9193           (*image)->filename);
9194       XDelay(display,1500);
9195       XSetCursorState(display,windows,MagickFalse);
9196       break;
9197     }
9198     case BackgroundCommand:
9199     {
9200       /*
9201         Background image.
9202       */
9203       status=XBackgroundImage(display,resource_info,windows,image,exception);
9204       if (status == MagickFalse)
9205         break;
9206       nexus=CloneImage(*image,0,0,MagickTrue,exception);
9207       if (nexus != (Image *) NULL)
9208         XClientMessage(display,windows->image.id,windows->im_protocols,
9209           windows->im_next_image,CurrentTime);
9210       break;
9211     }
9212     case SlideShowCommand:
9213     {
9214       static char
9215         delay[MaxTextExtent] = "5";
9216
9217       /*
9218         Display next image after pausing.
9219       */
9220       (void) XDialogWidget(display,windows,"Slide Show",
9221         "Pause how many 1/100ths of a second between images:",delay);
9222       if (*delay == '\0')
9223         break;
9224       resource_info->delay=StringToUnsignedLong(delay);
9225       XClientMessage(display,windows->image.id,windows->im_protocols,
9226         windows->im_next_image,CurrentTime);
9227       break;
9228     }
9229     case PreferencesCommand:
9230     {
9231       /*
9232         Set user preferences.
9233       */
9234       status=XPreferencesWidget(display,resource_info,windows);
9235       if (status == MagickFalse)
9236         break;
9237       nexus=CloneImage(*image,0,0,MagickTrue,exception);
9238       if (nexus != (Image *) NULL)
9239         XClientMessage(display,windows->image.id,windows->im_protocols,
9240           windows->im_next_image,CurrentTime);
9241       break;
9242     }
9243     case HelpCommand:
9244     {
9245       /*
9246         User requested help.
9247       */
9248       XTextViewWidget(display,resource_info,windows,MagickFalse,
9249         "Help Viewer - Display",DisplayHelp);
9250       break;
9251     }
9252     case BrowseDocumentationCommand:
9253     {
9254       Atom
9255         mozilla_atom;
9256
9257       Window
9258         mozilla_window,
9259         root_window;
9260
9261       /*
9262         Browse the ImageMagick documentation.
9263       */
9264       root_window=XRootWindow(display,XDefaultScreen(display));
9265       mozilla_atom=XInternAtom(display,"_MOZILLA_VERSION",MagickFalse);
9266       mozilla_window=XWindowByProperty(display,root_window,mozilla_atom);
9267       if (mozilla_window != (Window) NULL)
9268         {
9269           char
9270             command[MaxTextExtent],
9271             *url;
9272
9273           /*
9274             Display documentation using Netscape remote control.
9275           */
9276           url=GetMagickHomeURL();
9277           (void) FormatLocaleString(command,MaxTextExtent,
9278             "openurl(%s,new-tab)",url);
9279           url=DestroyString(url);
9280           mozilla_atom=XInternAtom(display,"_MOZILLA_COMMAND",MagickFalse);
9281           (void) XChangeProperty(display,mozilla_window,mozilla_atom,XA_STRING,
9282             8,PropModeReplace,(unsigned char *) command,(int) strlen(command));
9283           XSetCursorState(display,windows,MagickFalse);
9284           break;
9285         }
9286       XSetCursorState(display,windows,MagickTrue);
9287       XCheckRefreshWindows(display,windows);
9288       status=InvokeDelegate(image_info,*image,"browse",(char *) NULL,
9289         exception);
9290       if (status == MagickFalse)
9291         XNoticeWidget(display,windows,"Unable to browse documentation",
9292           (char *) NULL);
9293       XDelay(display,1500);
9294       XSetCursorState(display,windows,MagickFalse);
9295       break;
9296     }
9297     case VersionCommand:
9298     {
9299       XNoticeWidget(display,windows,GetMagickVersion((size_t *) NULL),
9300         GetMagickCopyright());
9301       break;
9302     }
9303     case SaveToUndoBufferCommand:
9304       break;
9305     default:
9306     {
9307       (void) XBell(display,0);
9308       break;
9309     }
9310   }
9311   image_info=DestroyImageInfo(image_info);
9312   return(nexus);
9313 }
9314 \f
9315 /*
9316 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9317 %                                                                             %
9318 %                                                                             %
9319 %                                                                             %
9320 +   X M a g n i f y I m a g e                                                 %
9321 %                                                                             %
9322 %                                                                             %
9323 %                                                                             %
9324 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9325 %
9326 %  XMagnifyImage() magnifies portions of the image as indicated by the pointer.
9327 %  The magnified portion is displayed in a separate window.
9328 %
9329 %  The format of the XMagnifyImage method is:
9330 %
9331 %      void XMagnifyImage(Display *display,XWindows *windows,XEvent *event)
9332 %
9333 %  A description of each parameter follows:
9334 %
9335 %    o display: Specifies a connection to an X server;  returned from
9336 %      XOpenDisplay.
9337 %
9338 %    o windows: Specifies a pointer to a XWindows structure.
9339 %
9340 %    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
9341 %      the entire image is refreshed.
9342 %
9343 */
9344 static void XMagnifyImage(Display *display,XWindows *windows,XEvent *event)
9345 {
9346   char
9347     text[MaxTextExtent];
9348
9349   register int
9350     x,
9351     y;
9352
9353   size_t
9354     state;
9355
9356   /*
9357     Update magnified image until the mouse button is released.
9358   */
9359   (void) XCheckDefineCursor(display,windows->image.id,windows->magnify.cursor);
9360   state=DefaultState;
9361   x=event->xbutton.x;
9362   y=event->xbutton.y;
9363   windows->magnify.x=(int) windows->image.x+x;
9364   windows->magnify.y=(int) windows->image.y+y;
9365   do
9366   {
9367     /*
9368       Map and unmap Info widget as text cursor crosses its boundaries.
9369     */
9370     if (windows->info.mapped != MagickFalse)
9371       {
9372         if ((x < (int) (windows->info.x+windows->info.width)) &&
9373             (y < (int) (windows->info.y+windows->info.height)))
9374           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
9375       }
9376     else
9377       if ((x > (int) (windows->info.x+windows->info.width)) ||
9378           (y > (int) (windows->info.y+windows->info.height)))
9379         (void) XMapWindow(display,windows->info.id);
9380     if (windows->info.mapped != MagickFalse)
9381       {
9382         /*
9383           Display pointer position.
9384         */
9385         (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
9386           windows->magnify.x,windows->magnify.y);
9387         XInfoWidget(display,windows,text);
9388       }
9389     /*
9390       Wait for next event.
9391     */
9392     XScreenEvent(display,windows,event);
9393     switch (event->type)
9394     {
9395       case ButtonPress:
9396         break;
9397       case ButtonRelease:
9398       {
9399         /*
9400           User has finished magnifying image.
9401         */
9402         x=event->xbutton.x;
9403         y=event->xbutton.y;
9404         state|=ExitState;
9405         break;
9406       }
9407       case Expose:
9408         break;
9409       case MotionNotify:
9410       {
9411         x=event->xmotion.x;
9412         y=event->xmotion.y;
9413         break;
9414       }
9415       default:
9416         break;
9417     }
9418     /*
9419       Check boundary conditions.
9420     */
9421     if (x < 0)
9422       x=0;
9423     else
9424       if (x >= (int) windows->image.width)
9425         x=(int) windows->image.width-1;
9426     if (y < 0)
9427       y=0;
9428     else
9429      if (y >= (int) windows->image.height)
9430        y=(int) windows->image.height-1;
9431   } while ((state & ExitState) == 0);
9432   /*
9433     Display magnified image.
9434   */
9435   XSetCursorState(display,windows,MagickFalse);
9436 }
9437 \f
9438 /*
9439 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9440 %                                                                             %
9441 %                                                                             %
9442 %                                                                             %
9443 +   X M a g n i f y W i n d o w C o m m a n d                                 %
9444 %                                                                             %
9445 %                                                                             %
9446 %                                                                             %
9447 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9448 %
9449 %  XMagnifyWindowCommand() moves the image within an Magnify window by one
9450 %  pixel as specified by the key symbol.
9451 %
9452 %  The format of the XMagnifyWindowCommand method is:
9453 %
9454 %      void XMagnifyWindowCommand(Display *display,XWindows *windows,
9455 %        const MagickStatusType state,const KeySym key_symbol)
9456 %
9457 %  A description of each parameter follows:
9458 %
9459 %    o display: Specifies a connection to an X server; returned from
9460 %      XOpenDisplay.
9461 %
9462 %    o windows: Specifies a pointer to a XWindows structure.
9463 %
9464 %    o state: key mask.
9465 %
9466 %    o key_symbol: Specifies a KeySym which indicates which side of the image
9467 %      to trim.
9468 %
9469 */
9470 static void XMagnifyWindowCommand(Display *display,XWindows *windows,
9471   const MagickStatusType state,const KeySym key_symbol)
9472 {
9473   unsigned int
9474     quantum;
9475
9476   /*
9477     User specified a magnify factor or position.
9478   */
9479   quantum=1;
9480   if ((state & Mod1Mask) != 0)
9481     quantum=10;
9482   switch ((int) key_symbol)
9483   {
9484     case QuitCommand:
9485     {
9486       (void) XWithdrawWindow(display,windows->magnify.id,
9487         windows->magnify.screen);
9488       break;
9489     }
9490     case XK_Home:
9491     case XK_KP_Home:
9492     {
9493       windows->magnify.x=(int) windows->image.width/2;
9494       windows->magnify.y=(int) windows->image.height/2;
9495       break;
9496     }
9497     case XK_Left:
9498     case XK_KP_Left:
9499     {
9500       if (windows->magnify.x > 0)
9501         windows->magnify.x-=quantum;
9502       break;
9503     }
9504     case XK_Up:
9505     case XK_KP_Up:
9506     {
9507       if (windows->magnify.y > 0)
9508         windows->magnify.y-=quantum;
9509       break;
9510     }
9511     case XK_Right:
9512     case XK_KP_Right:
9513     {
9514       if (windows->magnify.x < (int) (windows->image.ximage->width-1))
9515         windows->magnify.x+=quantum;
9516       break;
9517     }
9518     case XK_Down:
9519     case XK_KP_Down:
9520     {
9521       if (windows->magnify.y < (int) (windows->image.ximage->height-1))
9522         windows->magnify.y+=quantum;
9523       break;
9524     }
9525     case XK_0:
9526     case XK_1:
9527     case XK_2:
9528     case XK_3:
9529     case XK_4:
9530     case XK_5:
9531     case XK_6:
9532     case XK_7:
9533     case XK_8:
9534     case XK_9:
9535     {
9536       windows->magnify.data=(key_symbol-XK_0);
9537       break;
9538     }
9539     case XK_KP_0:
9540     case XK_KP_1:
9541     case XK_KP_2:
9542     case XK_KP_3:
9543     case XK_KP_4:
9544     case XK_KP_5:
9545     case XK_KP_6:
9546     case XK_KP_7:
9547     case XK_KP_8:
9548     case XK_KP_9:
9549     {
9550       windows->magnify.data=(key_symbol-XK_KP_0);
9551       break;
9552     }
9553     default:
9554       break;
9555   }
9556   XMakeMagnifyImage(display,windows);
9557 }
9558 \f
9559 /*
9560 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9561 %                                                                             %
9562 %                                                                             %
9563 %                                                                             %
9564 +   X M a k e P a n I m a g e                                                 %
9565 %                                                                             %
9566 %                                                                             %
9567 %                                                                             %
9568 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9569 %
9570 %  XMakePanImage() creates a thumbnail of the image and displays it in the Pan
9571 %  icon window.
9572 %
9573 %  The format of the XMakePanImage method is:
9574 %
9575 %        void XMakePanImage(Display *display,XResourceInfo *resource_info,
9576 %          XWindows *windows,Image *image,ExceptionInfo *exception)
9577 %
9578 %  A description of each parameter follows:
9579 %
9580 %    o display: Specifies a connection to an X server;  returned from
9581 %      XOpenDisplay.
9582 %
9583 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9584 %
9585 %    o windows: Specifies a pointer to a XWindows structure.
9586 %
9587 %    o image: the image.
9588 %
9589 %    o exception: return any errors or warnings in this structure.
9590 %
9591 */
9592 static void XMakePanImage(Display *display,XResourceInfo *resource_info,
9593   XWindows *windows,Image *image,ExceptionInfo *exception)
9594 {
9595   MagickStatusType
9596     status;
9597
9598   /*
9599     Create and display image for panning icon.
9600   */
9601   XSetCursorState(display,windows,MagickTrue);
9602   XCheckRefreshWindows(display,windows);
9603   windows->pan.x=(int) windows->image.x;
9604   windows->pan.y=(int) windows->image.y;
9605   status=XMakeImage(display,resource_info,&windows->pan,image,
9606     windows->pan.width,windows->pan.height,exception);
9607   if (status == MagickFalse)
9608     ThrowXWindowFatalException(ResourceLimitError,
9609      "MemoryAllocationFailed",image->filename);
9610   (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
9611     windows->pan.pixmap);
9612   (void) XClearWindow(display,windows->pan.id);
9613   XDrawPanRectangle(display,windows);
9614   XSetCursorState(display,windows,MagickFalse);
9615 }
9616 \f
9617 /*
9618 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9619 %                                                                             %
9620 %                                                                             %
9621 %                                                                             %
9622 +   X M a t t a E d i t I m a g e                                             %
9623 %                                                                             %
9624 %                                                                             %
9625 %                                                                             %
9626 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9627 %
9628 %  XMatteEditImage() allows the user to interactively change the Matte channel
9629 %  of an image.  If the image is PseudoClass it is promoted to DirectClass
9630 %  before the matte information is stored.
9631 %
9632 %  The format of the XMatteEditImage method is:
9633 %
9634 %      MagickBooleanType XMatteEditImage(Display *display,
9635 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
9636 %        ExceptionInfo *exception)
9637 %
9638 %  A description of each parameter follows:
9639 %
9640 %    o display: Specifies a connection to an X server;  returned from
9641 %      XOpenDisplay.
9642 %
9643 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9644 %
9645 %    o windows: Specifies a pointer to a XWindows structure.
9646 %
9647 %    o image: the image; returned from ReadImage.
9648 %
9649 %    o exception: return any errors or warnings in this structure.
9650 %
9651 */
9652 static MagickBooleanType XMatteEditImage(Display *display,
9653   XResourceInfo *resource_info,XWindows *windows,Image **image,
9654   ExceptionInfo *exception)
9655 {
9656   static char
9657     matte[MaxTextExtent] = "0";
9658
9659   static const char
9660     *MatteEditMenu[] =
9661     {
9662       "Method",
9663       "Border Color",
9664       "Fuzz",
9665       "Matte Value",
9666       "Undo",
9667       "Help",
9668       "Dismiss",
9669       (char *) NULL
9670     };
9671
9672   static const ModeType
9673     MatteEditCommands[] =
9674     {
9675       MatteEditMethod,
9676       MatteEditBorderCommand,
9677       MatteEditFuzzCommand,
9678       MatteEditValueCommand,
9679       MatteEditUndoCommand,
9680       MatteEditHelpCommand,
9681       MatteEditDismissCommand
9682     };
9683
9684   static PaintMethod
9685     method = PointMethod;
9686
9687   static XColor
9688     border_color = { 0, 0, 0, 0, 0, 0 };
9689
9690   char
9691     command[MaxTextExtent],
9692     text[MaxTextExtent];
9693
9694   Cursor
9695     cursor;
9696
9697   int
9698     entry,
9699     id,
9700     x,
9701     x_offset,
9702     y,
9703     y_offset;
9704
9705   register int
9706     i;
9707
9708   register Quantum
9709     *q;
9710
9711   unsigned int
9712     height,
9713     width;
9714
9715   size_t
9716     state;
9717
9718   XEvent
9719     event;
9720
9721   /*
9722     Map Command widget.
9723   */
9724   (void) CloneString(&windows->command.name,"Matte Edit");
9725   windows->command.data=4;
9726   (void) XCommandWidget(display,windows,MatteEditMenu,(XEvent *) NULL);
9727   (void) XMapRaised(display,windows->command.id);
9728   XClientMessage(display,windows->image.id,windows->im_protocols,
9729     windows->im_update_widget,CurrentTime);
9730   /*
9731     Make cursor.
9732   */
9733   cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
9734     resource_info->background_color,resource_info->foreground_color);
9735   (void) XCheckDefineCursor(display,windows->image.id,cursor);
9736   /*
9737     Track pointer until button 1 is pressed.
9738   */
9739   XQueryPosition(display,windows->image.id,&x,&y);
9740   (void) XSelectInput(display,windows->image.id,
9741     windows->image.attributes.event_mask | PointerMotionMask);
9742   state=DefaultState;
9743   do
9744   {
9745     if (windows->info.mapped != MagickFalse)
9746       {
9747         /*
9748           Display pointer position.
9749         */
9750         (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
9751           x+windows->image.x,y+windows->image.y);
9752         XInfoWidget(display,windows,text);
9753       }
9754     /*
9755       Wait for next event.
9756     */
9757     XScreenEvent(display,windows,&event);
9758     if (event.xany.window == windows->command.id)
9759       {
9760         /*
9761           Select a command from the Command widget.
9762         */
9763         id=XCommandWidget(display,windows,MatteEditMenu,&event);
9764         if (id < 0)
9765           {
9766             (void) XCheckDefineCursor(display,windows->image.id,cursor);
9767             continue;
9768           }
9769         switch (MatteEditCommands[id])
9770         {
9771           case MatteEditMethod:
9772           {
9773             char
9774               **methods;
9775
9776             /*
9777               Select a method from the pop-up menu.
9778             */
9779             methods=GetCommandOptions(MagickMethodOptions);
9780             if (methods == (char **) NULL)
9781               break;
9782             entry=XMenuWidget(display,windows,MatteEditMenu[id],
9783               (const char **) methods,command);
9784             if (entry >= 0)
9785               method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
9786                 MagickFalse,methods[entry]);
9787             methods=DestroyStringList(methods);
9788             break;
9789           }
9790           case MatteEditBorderCommand:
9791           {
9792             const char
9793               *ColorMenu[MaxNumberPens];
9794
9795             int
9796               pen_number;
9797
9798             /*
9799               Initialize menu selections.
9800             */
9801             for (i=0; i < (int) (MaxNumberPens-2); i++)
9802               ColorMenu[i]=resource_info->pen_colors[i];
9803             ColorMenu[MaxNumberPens-2]="Browser...";
9804             ColorMenu[MaxNumberPens-1]=(const char *) NULL;
9805             /*
9806               Select a pen color from the pop-up menu.
9807             */
9808             pen_number=XMenuWidget(display,windows,MatteEditMenu[id],
9809               (const char **) ColorMenu,command);
9810             if (pen_number < 0)
9811               break;
9812             if (pen_number == (MaxNumberPens-2))
9813               {
9814                 static char
9815                   color_name[MaxTextExtent] = "gray";
9816
9817                 /*
9818                   Select a pen color from a dialog.
9819                 */
9820                 resource_info->pen_colors[pen_number]=color_name;
9821                 XColorBrowserWidget(display,windows,"Select",color_name);
9822                 if (*color_name == '\0')
9823                   break;
9824               }
9825             /*
9826               Set border color.
9827             */
9828             (void) XParseColor(display,windows->map_info->colormap,
9829               resource_info->pen_colors[pen_number],&border_color);
9830             break;
9831           }
9832           case MatteEditFuzzCommand:
9833           {
9834             static char
9835               fuzz[MaxTextExtent];
9836
9837             static const char
9838               *FuzzMenu[] =
9839               {
9840                 "0%",
9841                 "2%",
9842                 "5%",
9843                 "10%",
9844                 "15%",
9845                 "Dialog...",
9846                 (char *) NULL,
9847               };
9848
9849             /*
9850               Select a command from the pop-up menu.
9851             */
9852             entry=XMenuWidget(display,windows,MatteEditMenu[id],FuzzMenu,
9853               command);
9854             if (entry < 0)
9855               break;
9856             if (entry != 5)
9857               {
9858                 (*image)->fuzz=SiPrefixToDouble(FuzzMenu[entry],(double)
9859                   QuantumRange+1.0);
9860                 break;
9861               }
9862             (void) CopyMagickString(fuzz,"20%",MaxTextExtent);
9863             (void) XDialogWidget(display,windows,"Ok",
9864               "Enter fuzz factor (0.0 - 99.9%):",fuzz);
9865             if (*fuzz == '\0')
9866               break;
9867             (void) ConcatenateMagickString(fuzz,"%",MaxTextExtent);
9868             (*image)->fuzz=SiPrefixToDouble(fuzz,(double) QuantumRange+1.0);
9869             break;
9870           }
9871           case MatteEditValueCommand:
9872           {
9873             static char
9874               message[MaxTextExtent];
9875
9876             static const char
9877               *MatteMenu[] =
9878               {
9879                 "Opaque",
9880                 "Transparent",
9881                 "Dialog...",
9882                 (char *) NULL,
9883               };
9884
9885             /*
9886               Select a command from the pop-up menu.
9887             */
9888             entry=XMenuWidget(display,windows,MatteEditMenu[id],MatteMenu,
9889               command);
9890             if (entry < 0)
9891               break;
9892             if (entry != 2)
9893               {
9894                 (void) FormatLocaleString(matte,MaxTextExtent,QuantumFormat,
9895                   OpaqueAlpha);
9896                 if (LocaleCompare(MatteMenu[entry],"Transparent") == 0)
9897                   (void) FormatLocaleString(matte,MaxTextExtent,QuantumFormat,
9898                     (Quantum) TransparentAlpha);
9899                 break;
9900               }
9901             (void) FormatLocaleString(message,MaxTextExtent,
9902               "Enter matte value (0 - " QuantumFormat "):",(Quantum)
9903               QuantumRange);
9904             (void) XDialogWidget(display,windows,"Matte",message,matte);
9905             if (*matte == '\0')
9906               break;
9907             break;
9908           }
9909           case MatteEditUndoCommand:
9910           {
9911             (void) XMagickCommand(display,resource_info,windows,UndoCommand,
9912               image,exception);
9913             break;
9914           }
9915           case MatteEditHelpCommand:
9916           {
9917             XTextViewWidget(display,resource_info,windows,MagickFalse,
9918               "Help Viewer - Matte Edit",ImageMatteEditHelp);
9919             break;
9920           }
9921           case MatteEditDismissCommand:
9922           {
9923             /*
9924               Prematurely exit.
9925             */
9926             state|=EscapeState;
9927             state|=ExitState;
9928             break;
9929           }
9930           default:
9931             break;
9932         }
9933         (void) XCheckDefineCursor(display,windows->image.id,cursor);
9934         continue;
9935       }
9936     switch (event.type)
9937     {
9938       case ButtonPress:
9939       {
9940         if (event.xbutton.button != Button1)
9941           break;
9942         if ((event.xbutton.window != windows->image.id) &&
9943             (event.xbutton.window != windows->magnify.id))
9944           break;
9945         /*
9946           Update matte data.
9947         */
9948         x=event.xbutton.x;
9949         y=event.xbutton.y;
9950         (void) XMagickCommand(display,resource_info,windows,
9951           SaveToUndoBufferCommand,image,exception);
9952         state|=UpdateConfigurationState;
9953         break;
9954       }
9955       case ButtonRelease:
9956       {
9957         if (event.xbutton.button != Button1)
9958           break;
9959         if ((event.xbutton.window != windows->image.id) &&
9960             (event.xbutton.window != windows->magnify.id))
9961           break;
9962         /*
9963           Update colormap information.
9964         */
9965         x=event.xbutton.x;
9966         y=event.xbutton.y;
9967         XConfigureImageColormap(display,resource_info,windows,*image);
9968         (void) XConfigureImage(display,resource_info,windows,*image,exception);
9969         XInfoWidget(display,windows,text);
9970         (void) XCheckDefineCursor(display,windows->image.id,cursor);
9971         state&=(~UpdateConfigurationState);
9972         break;
9973       }
9974       case Expose:
9975         break;
9976       case KeyPress:
9977       {
9978         char
9979           command[MaxTextExtent];
9980
9981         KeySym
9982           key_symbol;
9983
9984         if (event.xkey.window == windows->magnify.id)
9985           {
9986             Window
9987               window;
9988
9989             window=windows->magnify.id;
9990             while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
9991           }
9992         if (event.xkey.window != windows->image.id)
9993           break;
9994         /*
9995           Respond to a user key press.
9996         */
9997         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
9998           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
9999         switch ((int) key_symbol)
10000         {
10001           case XK_Escape:
10002           case XK_F20:
10003           {
10004             /*
10005               Prematurely exit.
10006             */
10007             state|=ExitState;
10008             break;
10009           }
10010           case XK_F1:
10011           case XK_Help:
10012           {
10013             XTextViewWidget(display,resource_info,windows,MagickFalse,
10014               "Help Viewer - Matte Edit",ImageMatteEditHelp);
10015             break;
10016           }
10017           default:
10018           {
10019             (void) XBell(display,0);
10020             break;
10021           }
10022         }
10023         break;
10024       }
10025       case MotionNotify:
10026       {
10027         /*
10028           Map and unmap Info widget as cursor crosses its boundaries.
10029         */
10030         x=event.xmotion.x;
10031         y=event.xmotion.y;
10032         if (windows->info.mapped != MagickFalse)
10033           {
10034             if ((x < (int) (windows->info.x+windows->info.width)) &&
10035                 (y < (int) (windows->info.y+windows->info.height)))
10036               (void) XWithdrawWindow(display,windows->info.id,
10037                 windows->info.screen);
10038           }
10039         else
10040           if ((x > (int) (windows->info.x+windows->info.width)) ||
10041               (y > (int) (windows->info.y+windows->info.height)))
10042             (void) XMapWindow(display,windows->info.id);
10043         break;
10044       }
10045       default:
10046         break;
10047     }
10048     if (event.xany.window == windows->magnify.id)
10049       {
10050         x=windows->magnify.x-windows->image.x;
10051         y=windows->magnify.y-windows->image.y;
10052       }
10053     x_offset=x;
10054     y_offset=y;
10055     if ((state & UpdateConfigurationState) != 0)
10056       {
10057         CacheView
10058           *image_view;
10059
10060         int
10061           x,
10062           y;
10063
10064         /*
10065           Matte edit is relative to image configuration.
10066         */
10067         (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
10068           MagickTrue);
10069         XPutPixel(windows->image.ximage,x_offset,y_offset,
10070           windows->pixel_info->background_color.pixel);
10071         width=(unsigned int) (*image)->columns;
10072         height=(unsigned int) (*image)->rows;
10073         x=0;
10074         y=0;
10075         if (windows->image.crop_geometry != (char *) NULL)
10076           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,
10077             &height);
10078         x_offset=(int) (width*(windows->image.x+x_offset)/
10079           windows->image.ximage->width+x);
10080         y_offset=(int) (height*(windows->image.y+y_offset)/
10081           windows->image.ximage->height+y);
10082         if ((x_offset < 0) || (y_offset < 0))
10083           continue;
10084         if ((x_offset >= (int) (*image)->columns) ||
10085             (y_offset >= (int) (*image)->rows))
10086           continue;
10087         if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
10088           return(MagickFalse);
10089         (*image)->matte=MagickTrue;
10090         image_view=AcquireCacheView(*image);
10091         switch (method)
10092         {
10093           case PointMethod:
10094           default:
10095           {
10096             /*
10097               Update matte information using point algorithm.
10098             */
10099             q=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,
10100               (ssize_t) y_offset,1,1,exception);
10101             if (q == (Quantum *) NULL)
10102               break;
10103             SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10104             (void) SyncCacheViewAuthenticPixels(image_view,exception);
10105             break;
10106           }
10107           case ReplaceMethod:
10108           {
10109             PixelInfo
10110               pixel,
10111               target;
10112
10113             Quantum
10114               virtual_pixel[MaxPixelChannels];
10115
10116             /*
10117               Update matte information using replace algorithm.
10118             */
10119             (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t) x_offset,
10120               (ssize_t) y_offset,virtual_pixel,exception);
10121             target.red=virtual_pixel[RedPixelChannel];
10122             target.green=virtual_pixel[GreenPixelChannel];
10123             target.blue=virtual_pixel[BluePixelChannel];
10124             target.alpha=virtual_pixel[AlphaPixelChannel];
10125             for (y=0; y < (int) (*image)->rows; y++)
10126             {
10127               q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10128                 (*image)->columns,1,exception);
10129               if (q == (Quantum *) NULL)
10130                 break;
10131               for (x=0; x < (int) (*image)->columns; x++)
10132               {
10133                 GetPixelInfoPixel(*image,q,&pixel);
10134                 if (IsFuzzyEquivalencePixelInfo(&pixel,&target))
10135                   SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10136                 q+=GetPixelChannels(*image);
10137               }
10138               if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
10139                 break;
10140             }
10141             break;
10142           }
10143           case FloodfillMethod:
10144           case FillToBorderMethod:
10145           {
10146             ChannelType
10147               channel_mask;
10148
10149             DrawInfo
10150               *draw_info;
10151
10152             PixelInfo
10153               target;
10154
10155             /*
10156               Update matte information using floodfill algorithm.
10157             */
10158             (void) GetOneVirtualMagickPixel(*image,(ssize_t) x_offset,
10159               (ssize_t) y_offset,&target,exception);
10160             if (method == FillToBorderMethod)
10161               {
10162                 target.red=(MagickRealType) ScaleShortToQuantum(
10163                   border_color.red);
10164                 target.green=(MagickRealType) ScaleShortToQuantum(
10165                   border_color.green);
10166                 target.blue=(MagickRealType) ScaleShortToQuantum(
10167                   border_color.blue);
10168               }
10169             draw_info=CloneDrawInfo(resource_info->image_info,
10170               (DrawInfo *) NULL);
10171             draw_info->fill.alpha=ClampToQuantum(InterpretLocaleValue(matte,
10172               (char **) NULL));
10173             channel_mask=SetPixelChannelMask(*image,AlphaChannel); 
10174             (void) FloodfillPaintImage(*image,draw_info,&target,(ssize_t)
10175               x_offset,(ssize_t) y_offset,method == FloodfillMethod ?
10176               MagickFalse : MagickTrue,exception);
10177             (void) SetPixelChannelMap(*image,channel_mask);
10178             draw_info=DestroyDrawInfo(draw_info);
10179             break;
10180           }
10181           case ResetMethod:
10182           {
10183             /*
10184               Update matte information using reset algorithm.
10185             */
10186             if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
10187               return(MagickFalse);
10188             for (y=0; y < (int) (*image)->rows; y++)
10189             {
10190               q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10191                 (*image)->columns,1,exception);
10192               if (q == (Quantum *) NULL)
10193                 break;
10194               for (x=0; x < (int) (*image)->columns; x++)
10195               {
10196                 SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10197                 q+=GetPixelChannels(*image);
10198               }
10199               if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
10200                 break;
10201             }
10202             if (StringToLong(matte) == (long) OpaqueAlpha)
10203               (*image)->matte=MagickFalse;
10204             break;
10205           }
10206         }
10207         image_view=DestroyCacheView(image_view);
10208         state&=(~UpdateConfigurationState);
10209       }
10210   } while ((state & ExitState) == 0);
10211   (void) XSelectInput(display,windows->image.id,
10212     windows->image.attributes.event_mask);
10213   XSetCursorState(display,windows,MagickFalse);
10214   (void) XFreeCursor(display,cursor);
10215   return(MagickTrue);
10216 }
10217 \f
10218 /*
10219 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10220 %                                                                             %
10221 %                                                                             %
10222 %                                                                             %
10223 +   X O p e n I m a g e                                                       %
10224 %                                                                             %
10225 %                                                                             %
10226 %                                                                             %
10227 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10228 %
10229 %  XOpenImage() loads an image from a file.
10230 %
10231 %  The format of the XOpenImage method is:
10232 %
10233 %     Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10234 %       XWindows *windows,const unsigned int command)
10235 %
10236 %  A description of each parameter follows:
10237 %
10238 %    o display: Specifies a connection to an X server; returned from
10239 %      XOpenDisplay.
10240 %
10241 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10242 %
10243 %    o windows: Specifies a pointer to a XWindows structure.
10244 %
10245 %    o command: A value other than zero indicates that the file is selected
10246 %      from the command line argument list.
10247 %
10248 */
10249 static Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10250   XWindows *windows,const MagickBooleanType command)
10251 {
10252   const MagickInfo
10253     *magick_info;
10254
10255   ExceptionInfo
10256     *exception;
10257
10258   Image
10259     *nexus;
10260
10261   ImageInfo
10262     *image_info;
10263
10264   static char
10265     filename[MaxTextExtent] = "\0";
10266
10267   /*
10268     Request file name from user.
10269   */
10270   if (command == MagickFalse)
10271     XFileBrowserWidget(display,windows,"Open",filename);
10272   else
10273     {
10274       char
10275         **filelist,
10276         **files;
10277
10278       int
10279         count,
10280         status;
10281
10282       register int
10283         i,
10284         j;
10285
10286       /*
10287         Select next image from the command line.
10288       */
10289       status=XGetCommand(display,windows->image.id,&files,&count);
10290       if (status == 0)
10291         {
10292           ThrowXWindowFatalException(XServerError,"UnableToGetProperty","...");
10293           return((Image *) NULL);
10294         }
10295       filelist=(char **) AcquireQuantumMemory((size_t) count,sizeof(*filelist));
10296       if (filelist == (char **) NULL)
10297         {
10298           ThrowXWindowFatalException(ResourceLimitError,
10299             "MemoryAllocationFailed","...");
10300           (void) XFreeStringList(files);
10301           return((Image *) NULL);
10302         }
10303       j=0;
10304       for (i=1; i < count; i++)
10305         if (*files[i] != '-')
10306           filelist[j++]=files[i];
10307       filelist[j]=(char *) NULL;
10308       XListBrowserWidget(display,windows,&windows->widget,
10309         (const char **) filelist,"Load","Select Image to Load:",filename);
10310       filelist=(char **) RelinquishMagickMemory(filelist);
10311       (void) XFreeStringList(files);
10312     }
10313   if (*filename == '\0')
10314     return((Image *) NULL);
10315   image_info=CloneImageInfo(resource_info->image_info);
10316   (void) SetImageInfoProgressMonitor(image_info,(MagickProgressMonitor) NULL,
10317     (void *) NULL);
10318   (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
10319   exception=AcquireExceptionInfo();
10320   (void) SetImageInfo(image_info,0,exception);
10321   if (LocaleCompare(image_info->magick,"X") == 0)
10322     {
10323       char
10324         seconds[MaxTextExtent];
10325
10326       /*
10327         User may want to delay the X server screen grab.
10328       */
10329       (void) CopyMagickString(seconds,"0",MaxTextExtent);
10330       (void) XDialogWidget(display,windows,"Grab","Enter any delay in seconds:",
10331         seconds);
10332       if (*seconds == '\0')
10333         return((Image *) NULL);
10334       XDelay(display,(size_t) (1000*StringToLong(seconds)));
10335     }
10336   magick_info=GetMagickInfo(image_info->magick,exception);
10337   if ((magick_info != (const MagickInfo *) NULL) &&
10338       (magick_info->raw != MagickFalse))
10339     {
10340       char
10341         geometry[MaxTextExtent];
10342
10343       /*
10344         Request image size from the user.
10345       */
10346       (void) CopyMagickString(geometry,"512x512",MaxTextExtent);
10347       if (image_info->size != (char *) NULL)
10348         (void) CopyMagickString(geometry,image_info->size,MaxTextExtent);
10349       (void) XDialogWidget(display,windows,"Load","Enter the image geometry:",
10350         geometry);
10351       (void) CloneString(&image_info->size,geometry);
10352     }
10353   /*
10354     Load the image.
10355   */
10356   XSetCursorState(display,windows,MagickTrue);
10357   XCheckRefreshWindows(display,windows);
10358   (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
10359   nexus=ReadImage(image_info,exception);
10360   CatchException(exception);
10361   XSetCursorState(display,windows,MagickFalse);
10362   if (nexus != (Image *) NULL)
10363     XClientMessage(display,windows->image.id,windows->im_protocols,
10364       windows->im_next_image,CurrentTime);
10365   else
10366     {
10367       char
10368         *text,
10369         **textlist;
10370
10371       /*
10372         Unknown image format.
10373       */
10374       text=FileToString(filename,~0,exception);
10375       if (text == (char *) NULL)
10376         return((Image *) NULL);
10377       textlist=StringToList(text);
10378       if (textlist != (char **) NULL)
10379         {
10380           char
10381             title[MaxTextExtent];
10382
10383           register int
10384             i;
10385
10386           (void) FormatLocaleString(title,MaxTextExtent,
10387             "Unknown format: %s",filename);
10388           XTextViewWidget(display,resource_info,windows,MagickTrue,title,
10389             (const char **) textlist);
10390           for (i=0; textlist[i] != (char *) NULL; i++)
10391             textlist[i]=DestroyString(textlist[i]);
10392           textlist=(char **) RelinquishMagickMemory(textlist);
10393         }
10394       text=DestroyString(text);
10395     }
10396   exception=DestroyExceptionInfo(exception);
10397   image_info=DestroyImageInfo(image_info);
10398   return(nexus);
10399 }
10400 \f
10401 /*
10402 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10403 %                                                                             %
10404 %                                                                             %
10405 %                                                                             %
10406 +   X P a n I m a g e                                                         %
10407 %                                                                             %
10408 %                                                                             %
10409 %                                                                             %
10410 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10411 %
10412 %  XPanImage() pans the image until the mouse button is released.
10413 %
10414 %  The format of the XPanImage method is:
10415 %
10416 %      void XPanImage(Display *display,XWindows *windows,XEvent *event)
10417 %
10418 %  A description of each parameter follows:
10419 %
10420 %    o display: Specifies a connection to an X server;  returned from
10421 %      XOpenDisplay.
10422 %
10423 %    o windows: Specifies a pointer to a XWindows structure.
10424 %
10425 %    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
10426 %      the entire image is refreshed.
10427 %
10428 */
10429 static void XPanImage(Display *display,XWindows *windows,XEvent *event)
10430 {
10431   char
10432     text[MaxTextExtent];
10433
10434   Cursor
10435     cursor;
10436
10437   MagickRealType
10438     x_factor,
10439     y_factor;
10440
10441   RectangleInfo
10442     pan_info;
10443
10444   size_t
10445     state;
10446
10447   /*
10448     Define cursor.
10449   */
10450   if ((windows->image.ximage->width > (int) windows->image.width) &&
10451       (windows->image.ximage->height > (int) windows->image.height))
10452     cursor=XCreateFontCursor(display,XC_fleur);
10453   else
10454     if (windows->image.ximage->width > (int) windows->image.width)
10455       cursor=XCreateFontCursor(display,XC_sb_h_double_arrow);
10456     else
10457       if (windows->image.ximage->height > (int) windows->image.height)
10458         cursor=XCreateFontCursor(display,XC_sb_v_double_arrow);
10459       else
10460         cursor=XCreateFontCursor(display,XC_arrow);
10461   (void) XCheckDefineCursor(display,windows->pan.id,cursor);
10462   /*
10463     Pan image as pointer moves until the mouse button is released.
10464   */
10465   x_factor=(MagickRealType) windows->image.ximage->width/windows->pan.width;
10466   y_factor=(MagickRealType) windows->image.ximage->height/windows->pan.height;
10467   pan_info.width=windows->pan.width*windows->image.width/
10468     windows->image.ximage->width;
10469   pan_info.height=windows->pan.height*windows->image.height/
10470     windows->image.ximage->height;
10471   pan_info.x=0;
10472   pan_info.y=0;
10473   state=UpdateConfigurationState;
10474   do
10475   {
10476     switch (event->type)
10477     {
10478       case ButtonPress:
10479       {
10480         /*
10481           User choose an initial pan location.
10482         */
10483         pan_info.x=(ssize_t) event->xbutton.x;
10484         pan_info.y=(ssize_t) event->xbutton.y;
10485         state|=UpdateConfigurationState;
10486         break;
10487       }
10488       case ButtonRelease:
10489       {
10490         /*
10491           User has finished panning the image.
10492         */
10493         pan_info.x=(ssize_t) event->xbutton.x;
10494         pan_info.y=(ssize_t) event->xbutton.y;
10495         state|=UpdateConfigurationState | ExitState;
10496         break;
10497       }
10498       case MotionNotify:
10499       {
10500         pan_info.x=(ssize_t) event->xmotion.x;
10501         pan_info.y=(ssize_t) event->xmotion.y;
10502         state|=UpdateConfigurationState;
10503       }
10504       default:
10505         break;
10506     }
10507     if ((state & UpdateConfigurationState) != 0)
10508       {
10509         /*
10510           Check boundary conditions.
10511         */
10512         if (pan_info.x < (ssize_t) (pan_info.width/2))
10513           pan_info.x=0;
10514         else
10515           pan_info.x=(ssize_t) (x_factor*(pan_info.x-(pan_info.width/2)));
10516         if (pan_info.x < 0)
10517           pan_info.x=0;
10518         else
10519           if ((int) (pan_info.x+windows->image.width) >
10520               windows->image.ximage->width)
10521             pan_info.x=(ssize_t)
10522               (windows->image.ximage->width-windows->image.width);
10523         if (pan_info.y < (ssize_t) (pan_info.height/2))
10524           pan_info.y=0;
10525         else
10526           pan_info.y=(ssize_t) (y_factor*(pan_info.y-(pan_info.height/2)));
10527         if (pan_info.y < 0)
10528           pan_info.y=0;
10529         else
10530           if ((int) (pan_info.y+windows->image.height) >
10531               windows->image.ximage->height)
10532             pan_info.y=(ssize_t)
10533               (windows->image.ximage->height-windows->image.height);
10534         if ((windows->image.x != (int) pan_info.x) ||
10535             (windows->image.y != (int) pan_info.y))
10536           {
10537             /*
10538               Display image pan offset.
10539             */
10540             windows->image.x=(int) pan_info.x;
10541             windows->image.y=(int) pan_info.y;
10542             (void) FormatLocaleString(text,MaxTextExtent," %ux%u%+d%+d ",
10543               windows->image.width,windows->image.height,windows->image.x,
10544               windows->image.y);
10545             XInfoWidget(display,windows,text);
10546             /*
10547               Refresh Image window.
10548             */
10549             XDrawPanRectangle(display,windows);
10550             XRefreshWindow(display,&windows->image,(XEvent *) NULL);
10551           }
10552         state&=(~UpdateConfigurationState);
10553       }
10554     /*
10555       Wait for next event.
10556     */
10557     if ((state & ExitState) == 0)
10558       XScreenEvent(display,windows,event);
10559   } while ((state & ExitState) == 0);
10560   /*
10561     Restore cursor.
10562   */
10563   (void) XCheckDefineCursor(display,windows->pan.id,windows->pan.cursor);
10564   (void) XFreeCursor(display,cursor);
10565   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
10566 }
10567 \f
10568 /*
10569 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10570 %                                                                             %
10571 %                                                                             %
10572 %                                                                             %
10573 +   X P a s t e I m a g e                                                     %
10574 %                                                                             %
10575 %                                                                             %
10576 %                                                                             %
10577 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10578 %
10579 %  XPasteImage() pastes an image previously saved with XCropImage in the X
10580 %  window image at a location the user chooses with the pointer.
10581 %
10582 %  The format of the XPasteImage method is:
10583 %
10584 %      MagickBooleanType XPasteImage(Display *display,
10585 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
10586 %        ExceptionInfo *exception)
10587 %
10588 %  A description of each parameter follows:
10589 %
10590 %    o display: Specifies a connection to an X server;  returned from
10591 %      XOpenDisplay.
10592 %
10593 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10594 %
10595 %    o windows: Specifies a pointer to a XWindows structure.
10596 %
10597 %    o image: the image; returned from ReadImage.
10598 %
10599 %    o exception: return any errors or warnings in this structure.
10600 %
10601 */
10602 static MagickBooleanType XPasteImage(Display *display,
10603   XResourceInfo *resource_info,XWindows *windows,Image *image,
10604   ExceptionInfo *exception)
10605 {
10606   static const char
10607     *PasteMenu[] =
10608     {
10609       "Operator",
10610       "Help",
10611       "Dismiss",
10612       (char *) NULL
10613     };
10614
10615   static const ModeType
10616     PasteCommands[] =
10617     {
10618       PasteOperatorsCommand,
10619       PasteHelpCommand,
10620       PasteDismissCommand
10621     };
10622
10623   static CompositeOperator
10624     compose = CopyCompositeOp;
10625
10626   char
10627     text[MaxTextExtent];
10628
10629   Cursor
10630     cursor;
10631
10632   Image
10633     *paste_image;
10634
10635   int
10636     entry,
10637     id,
10638     x,
10639     y;
10640
10641   MagickRealType
10642     scale_factor;
10643
10644   RectangleInfo
10645     highlight_info,
10646     paste_info;
10647
10648   unsigned int
10649     height,
10650     width;
10651
10652   size_t
10653     state;
10654
10655   XEvent
10656     event;
10657
10658   /*
10659     Copy image.
10660   */
10661   if (resource_info->copy_image == (Image *) NULL)
10662     return(MagickFalse);
10663   paste_image=CloneImage(resource_info->copy_image,0,0,MagickTrue,exception);
10664   /*
10665     Map Command widget.
10666   */
10667   (void) CloneString(&windows->command.name,"Paste");
10668   windows->command.data=1;
10669   (void) XCommandWidget(display,windows,PasteMenu,(XEvent *) NULL);
10670   (void) XMapRaised(display,windows->command.id);
10671   XClientMessage(display,windows->image.id,windows->im_protocols,
10672     windows->im_update_widget,CurrentTime);
10673   /*
10674     Track pointer until button 1 is pressed.
10675   */
10676   XSetCursorState(display,windows,MagickFalse);
10677   XQueryPosition(display,windows->image.id,&x,&y);
10678   (void) XSelectInput(display,windows->image.id,
10679     windows->image.attributes.event_mask | PointerMotionMask);
10680   paste_info.x=(ssize_t) windows->image.x+x;
10681   paste_info.y=(ssize_t) windows->image.y+y;
10682   paste_info.width=0;
10683   paste_info.height=0;
10684   cursor=XCreateFontCursor(display,XC_ul_angle);
10685   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
10686   state=DefaultState;
10687   do
10688   {
10689     if (windows->info.mapped != MagickFalse)
10690       {
10691         /*
10692           Display pointer position.
10693         */
10694         (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
10695           (long) paste_info.x,(long) paste_info.y);
10696         XInfoWidget(display,windows,text);
10697       }
10698     highlight_info=paste_info;
10699     highlight_info.x=paste_info.x-windows->image.x;
10700     highlight_info.y=paste_info.y-windows->image.y;
10701     XHighlightRectangle(display,windows->image.id,
10702       windows->image.highlight_context,&highlight_info);
10703     /*
10704       Wait for next event.
10705     */
10706     XScreenEvent(display,windows,&event);
10707     XHighlightRectangle(display,windows->image.id,
10708       windows->image.highlight_context,&highlight_info);
10709     if (event.xany.window == windows->command.id)
10710       {
10711         /*
10712           Select a command from the Command widget.
10713         */
10714         id=XCommandWidget(display,windows,PasteMenu,&event);
10715         if (id < 0)
10716           continue;
10717         switch (PasteCommands[id])
10718         {
10719           case PasteOperatorsCommand:
10720           {
10721             char
10722               command[MaxTextExtent],
10723               **operators;
10724
10725             /*
10726               Select a command from the pop-up menu.
10727             */
10728             operators=GetCommandOptions(MagickComposeOptions);
10729             if (operators == (char **) NULL)
10730               break;
10731             entry=XMenuWidget(display,windows,PasteMenu[id],
10732               (const char **) operators,command);
10733             if (entry >= 0)
10734               compose=(CompositeOperator) ParseCommandOption(
10735                 MagickComposeOptions,MagickFalse,operators[entry]);
10736             operators=DestroyStringList(operators);
10737             break;
10738           }
10739           case PasteHelpCommand:
10740           {
10741             XTextViewWidget(display,resource_info,windows,MagickFalse,
10742               "Help Viewer - Image Composite",ImagePasteHelp);
10743             break;
10744           }
10745           case PasteDismissCommand:
10746           {
10747             /*
10748               Prematurely exit.
10749             */
10750             state|=EscapeState;
10751             state|=ExitState;
10752             break;
10753           }
10754           default:
10755             break;
10756         }
10757         continue;
10758       }
10759     switch (event.type)
10760     {
10761       case ButtonPress:
10762       {
10763         if (image->debug != MagickFalse)
10764           (void) LogMagickEvent(X11Event,GetMagickModule(),
10765             "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
10766             event.xbutton.button,event.xbutton.x,event.xbutton.y);
10767         if (event.xbutton.button != Button1)
10768           break;
10769         if (event.xbutton.window != windows->image.id)
10770           break;
10771         /*
10772           Paste rectangle is relative to image configuration.
10773         */
10774         width=(unsigned int) image->columns;
10775         height=(unsigned int) image->rows;
10776         x=0;
10777         y=0;
10778         if (windows->image.crop_geometry != (char *) NULL)
10779           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
10780             &width,&height);
10781         scale_factor=(MagickRealType) windows->image.ximage->width/width;
10782         paste_info.width=(unsigned int) (scale_factor*paste_image->columns+0.5);
10783         scale_factor=(MagickRealType) windows->image.ximage->height/height;
10784         paste_info.height=(unsigned int) (scale_factor*paste_image->rows+0.5);
10785         (void) XCheckDefineCursor(display,windows->image.id,cursor);
10786         paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10787         paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10788         break;
10789       }
10790       case ButtonRelease:
10791       {
10792         if (image->debug != MagickFalse)
10793           (void) LogMagickEvent(X11Event,GetMagickModule(),
10794             "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
10795             event.xbutton.button,event.xbutton.x,event.xbutton.y);
10796         if (event.xbutton.button != Button1)
10797           break;
10798         if (event.xbutton.window != windows->image.id)
10799           break;
10800         if ((paste_info.width != 0) && (paste_info.height != 0))
10801           {
10802             /*
10803               User has selected the location of the paste image.
10804             */
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             state|=ExitState;
10808           }
10809         break;
10810       }
10811       case Expose:
10812         break;
10813       case KeyPress:
10814       {
10815         char
10816           command[MaxTextExtent];
10817
10818         KeySym
10819           key_symbol;
10820
10821         int
10822           length;
10823
10824         if (event.xkey.window != windows->image.id)
10825           break;
10826         /*
10827           Respond to a user key press.
10828         */
10829         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
10830           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
10831         *(command+length)='\0';
10832         if (image->debug != MagickFalse)
10833           (void) LogMagickEvent(X11Event,GetMagickModule(),
10834             "Key press: 0x%lx (%s)",(long) key_symbol,command);
10835         switch ((int) key_symbol)
10836         {
10837           case XK_Escape:
10838           case XK_F20:
10839           {
10840             /*
10841               Prematurely exit.
10842             */
10843             paste_image=DestroyImage(paste_image);
10844             state|=EscapeState;
10845             state|=ExitState;
10846             break;
10847           }
10848           case XK_F1:
10849           case XK_Help:
10850           {
10851             (void) XSetFunction(display,windows->image.highlight_context,
10852               GXcopy);
10853             XTextViewWidget(display,resource_info,windows,MagickFalse,
10854               "Help Viewer - Image Composite",ImagePasteHelp);
10855             (void) XSetFunction(display,windows->image.highlight_context,
10856               GXinvert);
10857             break;
10858           }
10859           default:
10860           {
10861             (void) XBell(display,0);
10862             break;
10863           }
10864         }
10865         break;
10866       }
10867       case MotionNotify:
10868       {
10869         /*
10870           Map and unmap Info widget as text cursor crosses its boundaries.
10871         */
10872         x=event.xmotion.x;
10873         y=event.xmotion.y;
10874         if (windows->info.mapped != MagickFalse)
10875           {
10876             if ((x < (int) (windows->info.x+windows->info.width)) &&
10877                 (y < (int) (windows->info.y+windows->info.height)))
10878               (void) XWithdrawWindow(display,windows->info.id,
10879                 windows->info.screen);
10880           }
10881         else
10882           if ((x > (int) (windows->info.x+windows->info.width)) ||
10883               (y > (int) (windows->info.y+windows->info.height)))
10884             (void) XMapWindow(display,windows->info.id);
10885         paste_info.x=(ssize_t) windows->image.x+x;
10886         paste_info.y=(ssize_t) windows->image.y+y;
10887         break;
10888       }
10889       default:
10890       {
10891         if (image->debug != MagickFalse)
10892           (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
10893             event.type);
10894         break;
10895       }
10896     }
10897   } while ((state & ExitState) == 0);
10898   (void) XSelectInput(display,windows->image.id,
10899     windows->image.attributes.event_mask);
10900   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
10901   XSetCursorState(display,windows,MagickFalse);
10902   (void) XFreeCursor(display,cursor);
10903   if ((state & EscapeState) != 0)
10904     return(MagickTrue);
10905   /*
10906     Image pasting is relative to image configuration.
10907   */
10908   XSetCursorState(display,windows,MagickTrue);
10909   XCheckRefreshWindows(display,windows);
10910   width=(unsigned int) image->columns;
10911   height=(unsigned int) image->rows;
10912   x=0;
10913   y=0;
10914   if (windows->image.crop_geometry != (char *) NULL)
10915     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
10916   scale_factor=(MagickRealType) width/windows->image.ximage->width;
10917   paste_info.x+=x;
10918   paste_info.x=(ssize_t) (scale_factor*paste_info.x+0.5);
10919   paste_info.width=(unsigned int) (scale_factor*paste_info.width+0.5);
10920   scale_factor=(MagickRealType) height/windows->image.ximage->height;
10921   paste_info.y+=y;
10922   paste_info.y=(ssize_t) (scale_factor*paste_info.y*scale_factor+0.5);
10923   paste_info.height=(unsigned int) (scale_factor*paste_info.height+0.5);
10924   /*
10925     Paste image with X Image window.
10926   */
10927   (void) CompositeImage(image,compose,paste_image,paste_info.x,paste_info.y,
10928     exception);
10929   paste_image=DestroyImage(paste_image);
10930   XSetCursorState(display,windows,MagickFalse);
10931   /*
10932     Update image colormap.
10933   */
10934   XConfigureImageColormap(display,resource_info,windows,image);
10935   (void) XConfigureImage(display,resource_info,windows,image,exception);
10936   return(MagickTrue);
10937 }
10938 \f
10939 /*
10940 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10941 %                                                                             %
10942 %                                                                             %
10943 %                                                                             %
10944 +   X P r i n t I m a g e                                                     %
10945 %                                                                             %
10946 %                                                                             %
10947 %                                                                             %
10948 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10949 %
10950 %  XPrintImage() prints an image to a Postscript printer.
10951 %
10952 %  The format of the XPrintImage method is:
10953 %
10954 %      MagickBooleanType XPrintImage(Display *display,
10955 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
10956 %        ExceptionInfo *exception)
10957 %
10958 %  A description of each parameter follows:
10959 %
10960 %    o display: Specifies a connection to an X server; returned from
10961 %      XOpenDisplay.
10962 %
10963 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10964 %
10965 %    o windows: Specifies a pointer to a XWindows structure.
10966 %
10967 %    o image: the image.
10968 %
10969 %    o exception: return any errors or warnings in this structure.
10970 %
10971 */
10972 static MagickBooleanType XPrintImage(Display *display,
10973   XResourceInfo *resource_info,XWindows *windows,Image *image,
10974   ExceptionInfo *exception)
10975 {
10976   char
10977     filename[MaxTextExtent],
10978     geometry[MaxTextExtent];
10979
10980   Image
10981     *print_image;
10982
10983   ImageInfo
10984     *image_info;
10985
10986   MagickStatusType
10987     status;
10988
10989   /*
10990     Request Postscript page geometry from user.
10991   */
10992   image_info=CloneImageInfo(resource_info->image_info);
10993   (void) FormatLocaleString(geometry,MaxTextExtent,"Letter");
10994   if (image_info->page != (char *) NULL)
10995     (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
10996   XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
10997     "Select Postscript Page Geometry:",geometry);
10998   if (*geometry == '\0')
10999     return(MagickTrue);
11000   image_info->page=GetPageGeometry(geometry);
11001   /*
11002     Apply image transforms.
11003   */
11004   XSetCursorState(display,windows,MagickTrue);
11005   XCheckRefreshWindows(display,windows);
11006   print_image=CloneImage(image,0,0,MagickTrue,exception);
11007   if (print_image == (Image *) NULL)
11008     return(MagickFalse);
11009   (void) FormatLocaleString(geometry,MaxTextExtent,"%dx%d!",
11010     windows->image.ximage->width,windows->image.ximage->height);
11011   (void) TransformImage(&print_image,windows->image.crop_geometry,geometry,
11012     exception);
11013   /*
11014     Print image.
11015   */
11016   (void) AcquireUniqueFilename(filename);
11017   (void) FormatLocaleString(print_image->filename,MaxTextExtent,"print:%s",
11018     filename);
11019   status=WriteImage(image_info,print_image,exception);
11020   (void) RelinquishUniqueFileResource(filename);
11021   print_image=DestroyImage(print_image);
11022   image_info=DestroyImageInfo(image_info);
11023   XSetCursorState(display,windows,MagickFalse);
11024   return(status != 0 ? MagickTrue : MagickFalse);
11025 }
11026 \f
11027 /*
11028 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11029 %                                                                             %
11030 %                                                                             %
11031 %                                                                             %
11032 +   X R O I I m a g e                                                         %
11033 %                                                                             %
11034 %                                                                             %
11035 %                                                                             %
11036 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11037 %
11038 %  XROIImage() applies an image processing technique to a region of interest.
11039 %
11040 %  The format of the XROIImage method is:
11041 %
11042 %      MagickBooleanType XROIImage(Display *display,
11043 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
11044 %        ExceptionInfo *exception)
11045 %
11046 %  A description of each parameter follows:
11047 %
11048 %    o display: Specifies a connection to an X server; returned from
11049 %      XOpenDisplay.
11050 %
11051 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
11052 %
11053 %    o windows: Specifies a pointer to a XWindows structure.
11054 %
11055 %    o image: the image; returned from ReadImage.
11056 %
11057 %    o exception: return any errors or warnings in this structure.
11058 %
11059 */
11060 static MagickBooleanType XROIImage(Display *display,
11061   XResourceInfo *resource_info,XWindows *windows,Image **image,
11062   ExceptionInfo *exception)
11063 {
11064 #define ApplyMenus  7
11065
11066   static const char
11067     *ROIMenu[] =
11068     {
11069       "Help",
11070       "Dismiss",
11071       (char *) NULL
11072     },
11073     *ApplyMenu[] =
11074     {
11075       "File",
11076       "Edit",
11077       "Transform",
11078       "Enhance",
11079       "Effects",
11080       "F/X",
11081       "Miscellany",
11082       "Help",
11083       "Dismiss",
11084       (char *) NULL
11085     },
11086     *FileMenu[] =
11087     {
11088       "Save...",
11089       "Print...",
11090       (char *) NULL
11091     },
11092     *EditMenu[] =
11093     {
11094       "Undo",
11095       "Redo",
11096       (char *) NULL
11097     },
11098     *TransformMenu[] =
11099     {
11100       "Flop",
11101       "Flip",
11102       "Rotate Right",
11103       "Rotate Left",
11104       (char *) NULL
11105     },
11106     *EnhanceMenu[] =
11107     {
11108       "Hue...",
11109       "Saturation...",
11110       "Brightness...",
11111       "Gamma...",
11112       "Spiff",
11113       "Dull",
11114       "Contrast Stretch...",
11115       "Sigmoidal Contrast...",
11116       "Normalize",
11117       "Equalize",
11118       "Negate",
11119       "Grayscale",
11120       "Map...",
11121       "Quantize...",
11122       (char *) NULL
11123     },
11124     *EffectsMenu[] =
11125     {
11126       "Despeckle",
11127       "Emboss",
11128       "Reduce Noise",
11129       "Add Noise",
11130       "Sharpen...",
11131       "Blur...",
11132       "Threshold...",
11133       "Edge Detect...",
11134       "Spread...",
11135       "Shade...",
11136       "Raise...",
11137       "Segment...",
11138       (char *) NULL
11139     },
11140     *FXMenu[] =
11141     {
11142       "Solarize...",
11143       "Sepia Tone...",
11144       "Swirl...",
11145       "Implode...",
11146       "Vignette...",
11147       "Wave...",
11148       "Oil Paint...",
11149       "Charcoal Draw...",
11150       (char *) NULL
11151     },
11152     *MiscellanyMenu[] =
11153     {
11154       "Image Info",
11155       "Zoom Image",
11156       "Show Preview...",
11157       "Show Histogram",
11158       "Show Matte",
11159       (char *) NULL
11160     };
11161
11162   static const char
11163     **Menus[ApplyMenus] =
11164     {
11165       FileMenu,
11166       EditMenu,
11167       TransformMenu,
11168       EnhanceMenu,
11169       EffectsMenu,
11170       FXMenu,
11171       MiscellanyMenu
11172     };
11173
11174   static const CommandType
11175     ApplyCommands[] =
11176     {
11177       NullCommand,
11178       NullCommand,
11179       NullCommand,
11180       NullCommand,
11181       NullCommand,
11182       NullCommand,
11183       NullCommand,
11184       HelpCommand,
11185       QuitCommand
11186     },
11187     FileCommands[] =
11188     {
11189       SaveCommand,
11190       PrintCommand
11191     },
11192     EditCommands[] =
11193     {
11194       UndoCommand,
11195       RedoCommand
11196     },
11197     TransformCommands[] =
11198     {
11199       FlopCommand,
11200       FlipCommand,
11201       RotateRightCommand,
11202       RotateLeftCommand
11203     },
11204     EnhanceCommands[] =
11205     {
11206       HueCommand,
11207       SaturationCommand,
11208       BrightnessCommand,
11209       GammaCommand,
11210       SpiffCommand,
11211       DullCommand,
11212       ContrastStretchCommand,
11213       SigmoidalContrastCommand,
11214       NormalizeCommand,
11215       EqualizeCommand,
11216       NegateCommand,
11217       GrayscaleCommand,
11218       MapCommand,
11219       QuantizeCommand
11220     },
11221     EffectsCommands[] =
11222     {
11223       DespeckleCommand,
11224       EmbossCommand,
11225       ReduceNoiseCommand,
11226       AddNoiseCommand,
11227       SharpenCommand,
11228       BlurCommand,
11229       EdgeDetectCommand,
11230       SpreadCommand,
11231       ShadeCommand,
11232       RaiseCommand,
11233       SegmentCommand
11234     },
11235     FXCommands[] =
11236     {
11237       SolarizeCommand,
11238       SepiaToneCommand,
11239       SwirlCommand,
11240       ImplodeCommand,
11241       VignetteCommand,
11242       WaveCommand,
11243       OilPaintCommand,
11244       CharcoalDrawCommand
11245     },
11246     MiscellanyCommands[] =
11247     {
11248       InfoCommand,
11249       ZoomCommand,
11250       ShowPreviewCommand,
11251       ShowHistogramCommand,
11252       ShowMatteCommand
11253     },
11254     ROICommands[] =
11255     {
11256       ROIHelpCommand,
11257       ROIDismissCommand
11258     };
11259
11260   static const CommandType
11261     *Commands[ApplyMenus] =
11262     {
11263       FileCommands,
11264       EditCommands,
11265       TransformCommands,
11266       EnhanceCommands,
11267       EffectsCommands,
11268       FXCommands,
11269       MiscellanyCommands
11270     };
11271
11272   char
11273     command[MaxTextExtent],
11274     text[MaxTextExtent];
11275
11276   CommandType
11277     command_type;
11278
11279   Cursor
11280     cursor;
11281
11282   Image
11283     *roi_image;
11284
11285   int
11286     entry,
11287     id,
11288     x,
11289     y;
11290
11291   MagickRealType
11292     scale_factor;
11293
11294   MagickProgressMonitor
11295     progress_monitor;
11296
11297   RectangleInfo
11298     crop_info,
11299     highlight_info,
11300     roi_info;
11301
11302   unsigned int
11303     height,
11304     width;
11305
11306   size_t
11307     state;
11308
11309   XEvent
11310     event;
11311
11312   /*
11313     Map Command widget.
11314   */
11315   (void) CloneString(&windows->command.name,"ROI");
11316   windows->command.data=0;
11317   (void) XCommandWidget(display,windows,ROIMenu,(XEvent *) NULL);
11318   (void) XMapRaised(display,windows->command.id);
11319   XClientMessage(display,windows->image.id,windows->im_protocols,
11320     windows->im_update_widget,CurrentTime);
11321   /*
11322     Track pointer until button 1 is pressed.
11323   */
11324   XQueryPosition(display,windows->image.id,&x,&y);
11325   (void) XSelectInput(display,windows->image.id,
11326     windows->image.attributes.event_mask | PointerMotionMask);
11327   roi_info.x=(ssize_t) windows->image.x+x;
11328   roi_info.y=(ssize_t) windows->image.y+y;
11329   roi_info.width=0;
11330   roi_info.height=0;
11331   cursor=XCreateFontCursor(display,XC_fleur);
11332   state=DefaultState;
11333   do
11334   {
11335     if (windows->info.mapped != MagickFalse)
11336       {
11337         /*
11338           Display pointer position.
11339         */
11340         (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
11341           (long) roi_info.x,(long) roi_info.y);
11342         XInfoWidget(display,windows,text);
11343       }
11344     /*
11345       Wait for next event.
11346     */
11347     XScreenEvent(display,windows,&event);
11348     if (event.xany.window == windows->command.id)
11349       {
11350         /*
11351           Select a command from the Command widget.
11352         */
11353         id=XCommandWidget(display,windows,ROIMenu,&event);
11354         if (id < 0)
11355           continue;
11356         switch (ROICommands[id])
11357         {
11358           case ROIHelpCommand:
11359           {
11360             XTextViewWidget(display,resource_info,windows,MagickFalse,
11361               "Help Viewer - Region of Interest",ImageROIHelp);
11362             break;
11363           }
11364           case ROIDismissCommand:
11365           {
11366             /*
11367               Prematurely exit.
11368             */
11369             state|=EscapeState;
11370             state|=ExitState;
11371             break;
11372           }
11373           default:
11374             break;
11375         }
11376         continue;
11377       }
11378     switch (event.type)
11379     {
11380       case ButtonPress:
11381       {
11382         if (event.xbutton.button != Button1)
11383           break;
11384         if (event.xbutton.window != windows->image.id)
11385           break;
11386         /*
11387           Note first corner of region of interest rectangle-- exit loop.
11388         */
11389         (void) XCheckDefineCursor(display,windows->image.id,cursor);
11390         roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11391         roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11392         state|=ExitState;
11393         break;
11394       }
11395       case ButtonRelease:
11396         break;
11397       case Expose:
11398         break;
11399       case KeyPress:
11400       {
11401         KeySym
11402           key_symbol;
11403
11404         if (event.xkey.window != windows->image.id)
11405           break;
11406         /*
11407           Respond to a user key press.
11408         */
11409         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11410           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11411         switch ((int) key_symbol)
11412         {
11413           case XK_Escape:
11414           case XK_F20:
11415           {
11416             /*
11417               Prematurely exit.
11418             */
11419             state|=EscapeState;
11420             state|=ExitState;
11421             break;
11422           }
11423           case XK_F1:
11424           case XK_Help:
11425           {
11426             XTextViewWidget(display,resource_info,windows,MagickFalse,
11427               "Help Viewer - Region of Interest",ImageROIHelp);
11428             break;
11429           }
11430           default:
11431           {
11432             (void) XBell(display,0);
11433             break;
11434           }
11435         }
11436         break;
11437       }
11438       case MotionNotify:
11439       {
11440         /*
11441           Map and unmap Info widget as text cursor crosses its boundaries.
11442         */
11443         x=event.xmotion.x;
11444         y=event.xmotion.y;
11445         if (windows->info.mapped != MagickFalse)
11446           {
11447             if ((x < (int) (windows->info.x+windows->info.width)) &&
11448                 (y < (int) (windows->info.y+windows->info.height)))
11449               (void) XWithdrawWindow(display,windows->info.id,
11450                 windows->info.screen);
11451           }
11452         else
11453           if ((x > (int) (windows->info.x+windows->info.width)) ||
11454               (y > (int) (windows->info.y+windows->info.height)))
11455             (void) XMapWindow(display,windows->info.id);
11456         roi_info.x=(ssize_t) windows->image.x+x;
11457         roi_info.y=(ssize_t) windows->image.y+y;
11458         break;
11459       }
11460       default:
11461         break;
11462     }
11463   } while ((state & ExitState) == 0);
11464   (void) XSelectInput(display,windows->image.id,
11465     windows->image.attributes.event_mask);
11466   if ((state & EscapeState) != 0)
11467     {
11468       /*
11469         User want to exit without region of interest.
11470       */
11471       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11472       (void) XFreeCursor(display,cursor);
11473       return(MagickTrue);
11474     }
11475   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
11476   do
11477   {
11478     /*
11479       Size rectangle as pointer moves until the mouse button is released.
11480     */
11481     x=(int) roi_info.x;
11482     y=(int) roi_info.y;
11483     roi_info.width=0;
11484     roi_info.height=0;
11485     state=DefaultState;
11486     do
11487     {
11488       highlight_info=roi_info;
11489       highlight_info.x=roi_info.x-windows->image.x;
11490       highlight_info.y=roi_info.y-windows->image.y;
11491       if ((highlight_info.width > 3) && (highlight_info.height > 3))
11492         {
11493           /*
11494             Display info and draw region of interest rectangle.
11495           */
11496           if (windows->info.mapped == MagickFalse)
11497             (void) XMapWindow(display,windows->info.id);
11498           (void) FormatLocaleString(text,MaxTextExtent,
11499             " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11500             roi_info.height,(double) roi_info.x,(double) roi_info.y);
11501           XInfoWidget(display,windows,text);
11502           XHighlightRectangle(display,windows->image.id,
11503             windows->image.highlight_context,&highlight_info);
11504         }
11505       else
11506         if (windows->info.mapped != MagickFalse)
11507           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11508       /*
11509         Wait for next event.
11510       */
11511       XScreenEvent(display,windows,&event);
11512       if ((highlight_info.width > 3) && (highlight_info.height > 3))
11513         XHighlightRectangle(display,windows->image.id,
11514           windows->image.highlight_context,&highlight_info);
11515       switch (event.type)
11516       {
11517         case ButtonPress:
11518         {
11519           roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11520           roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11521           break;
11522         }
11523         case ButtonRelease:
11524         {
11525           /*
11526             User has committed to region of interest rectangle.
11527           */
11528           roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11529           roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11530           XSetCursorState(display,windows,MagickFalse);
11531           state|=ExitState;
11532           if (LocaleCompare(windows->command.name,"Apply") == 0)
11533             break;
11534           (void) CloneString(&windows->command.name,"Apply");
11535           windows->command.data=ApplyMenus;
11536           (void) XCommandWidget(display,windows,ApplyMenu,(XEvent *) NULL);
11537           break;
11538         }
11539         case Expose:
11540           break;
11541         case MotionNotify:
11542         {
11543           roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11544           roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11545         }
11546         default:
11547           break;
11548       }
11549       if ((((int) roi_info.x != x) && ((int) roi_info.y != y)) ||
11550           ((state & ExitState) != 0))
11551         {
11552           /*
11553             Check boundary conditions.
11554           */
11555           if (roi_info.x < 0)
11556             roi_info.x=0;
11557           else
11558             if (roi_info.x > (ssize_t) windows->image.ximage->width)
11559               roi_info.x=(ssize_t) windows->image.ximage->width;
11560           if ((int) roi_info.x < x)
11561             roi_info.width=(unsigned int) (x-roi_info.x);
11562           else
11563             {
11564               roi_info.width=(unsigned int) (roi_info.x-x);
11565               roi_info.x=(ssize_t) x;
11566             }
11567           if (roi_info.y < 0)
11568             roi_info.y=0;
11569           else
11570             if (roi_info.y > (ssize_t) windows->image.ximage->height)
11571               roi_info.y=(ssize_t) windows->image.ximage->height;
11572           if ((int) roi_info.y < y)
11573             roi_info.height=(unsigned int) (y-roi_info.y);
11574           else
11575             {
11576               roi_info.height=(unsigned int) (roi_info.y-y);
11577               roi_info.y=(ssize_t) y;
11578             }
11579         }
11580     } while ((state & ExitState) == 0);
11581     /*
11582       Wait for user to grab a corner of the rectangle or press return.
11583     */
11584     state=DefaultState;
11585     command_type=NullCommand;
11586     (void) XMapWindow(display,windows->info.id);
11587     do
11588     {
11589       if (windows->info.mapped != MagickFalse)
11590         {
11591           /*
11592             Display pointer position.
11593           */
11594           (void) FormatLocaleString(text,MaxTextExtent,
11595             " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11596             roi_info.height,(double) roi_info.x,(double) roi_info.y);
11597           XInfoWidget(display,windows,text);
11598         }
11599       highlight_info=roi_info;
11600       highlight_info.x=roi_info.x-windows->image.x;
11601       highlight_info.y=roi_info.y-windows->image.y;
11602       if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
11603         {
11604           state|=EscapeState;
11605           state|=ExitState;
11606           break;
11607         }
11608       if ((state & UpdateRegionState) != 0)
11609         {
11610           (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11611           switch (command_type)
11612           {
11613             case UndoCommand:
11614             case RedoCommand:
11615             {
11616               (void) XMagickCommand(display,resource_info,windows,command_type,
11617                 image,exception);
11618               break;
11619             }
11620             default:
11621             {
11622               /*
11623                 Region of interest is relative to image configuration.
11624               */
11625               progress_monitor=SetImageProgressMonitor(*image,
11626                 (MagickProgressMonitor) NULL,(*image)->client_data);
11627               crop_info=roi_info;
11628               width=(unsigned int) (*image)->columns;
11629               height=(unsigned int) (*image)->rows;
11630               x=0;
11631               y=0;
11632               if (windows->image.crop_geometry != (char *) NULL)
11633                 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
11634                   &width,&height);
11635               scale_factor=(MagickRealType) width/windows->image.ximage->width;
11636               crop_info.x+=x;
11637               crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
11638               crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
11639               scale_factor=(MagickRealType)
11640                 height/windows->image.ximage->height;
11641               crop_info.y+=y;
11642               crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
11643               crop_info.height=(unsigned int)
11644                 (scale_factor*crop_info.height+0.5);
11645               roi_image=CropImage(*image,&crop_info,exception);
11646               (void) SetImageProgressMonitor(*image,progress_monitor,
11647                 (*image)->client_data);
11648               if (roi_image == (Image *) NULL)
11649                 continue;
11650               /*
11651                 Apply image processing technique to the region of interest.
11652               */
11653               windows->image.orphan=MagickTrue;
11654               (void) XMagickCommand(display,resource_info,windows,command_type,
11655                 &roi_image,exception);
11656               progress_monitor=SetImageProgressMonitor(*image,
11657                 (MagickProgressMonitor) NULL,(*image)->client_data);
11658               (void) XMagickCommand(display,resource_info,windows,
11659                 SaveToUndoBufferCommand,image,exception);
11660               windows->image.orphan=MagickFalse;
11661               (void) CompositeImage(*image,CopyCompositeOp,roi_image,
11662                 crop_info.x,crop_info.y,exception);
11663               roi_image=DestroyImage(roi_image);
11664               (void) SetImageProgressMonitor(*image,progress_monitor,
11665                 (*image)->client_data);
11666               break;
11667             }
11668           }
11669           if (command_type != InfoCommand)
11670             {
11671               XConfigureImageColormap(display,resource_info,windows,*image);
11672               (void) XConfigureImage(display,resource_info,windows,*image,exception);
11673             }
11674           XCheckRefreshWindows(display,windows);
11675           XInfoWidget(display,windows,text);
11676           (void) XSetFunction(display,windows->image.highlight_context,
11677             GXinvert);
11678           state&=(~UpdateRegionState);
11679         }
11680       XHighlightRectangle(display,windows->image.id,
11681         windows->image.highlight_context,&highlight_info);
11682       XScreenEvent(display,windows,&event);
11683       if (event.xany.window == windows->command.id)
11684         {
11685           /*
11686             Select a command from the Command widget.
11687           */
11688           (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11689           command_type=NullCommand;
11690           id=XCommandWidget(display,windows,ApplyMenu,&event);
11691           if (id >= 0)
11692             {
11693               (void) CopyMagickString(command,ApplyMenu[id],MaxTextExtent);
11694               command_type=ApplyCommands[id];
11695               if (id < ApplyMenus)
11696                 {
11697                   /*
11698                     Select a command from a pop-up menu.
11699                   */
11700                   entry=XMenuWidget(display,windows,ApplyMenu[id],
11701                     (const char **) Menus[id],command);
11702                   if (entry >= 0)
11703                     {
11704                       (void) CopyMagickString(command,Menus[id][entry],
11705                         MaxTextExtent);
11706                       command_type=Commands[id][entry];
11707                     }
11708                 }
11709             }
11710           (void) XSetFunction(display,windows->image.highlight_context,
11711             GXinvert);
11712           XHighlightRectangle(display,windows->image.id,
11713             windows->image.highlight_context,&highlight_info);
11714           if (command_type == HelpCommand)
11715             {
11716               (void) XSetFunction(display,windows->image.highlight_context,
11717                 GXcopy);
11718               XTextViewWidget(display,resource_info,windows,MagickFalse,
11719                 "Help Viewer - Region of Interest",ImageROIHelp);
11720               (void) XSetFunction(display,windows->image.highlight_context,
11721                 GXinvert);
11722               continue;
11723             }
11724           if (command_type == QuitCommand)
11725             {
11726               /*
11727                 exit.
11728               */
11729               state|=EscapeState;
11730               state|=ExitState;
11731               continue;
11732             }
11733           if (command_type != NullCommand)
11734             state|=UpdateRegionState;
11735           continue;
11736         }
11737       XHighlightRectangle(display,windows->image.id,
11738         windows->image.highlight_context,&highlight_info);
11739       switch (event.type)
11740       {
11741         case ButtonPress:
11742         {
11743           x=windows->image.x;
11744           y=windows->image.y;
11745           if (event.xbutton.button != Button1)
11746             break;
11747           if (event.xbutton.window != windows->image.id)
11748             break;
11749           x=windows->image.x+event.xbutton.x;
11750           y=windows->image.y+event.xbutton.y;
11751           if ((x < (int) (roi_info.x+RoiDelta)) &&
11752               (x > (int) (roi_info.x-RoiDelta)) &&
11753               (y < (int) (roi_info.y+RoiDelta)) &&
11754               (y > (int) (roi_info.y-RoiDelta)))
11755             {
11756               roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11757               roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11758               state|=UpdateConfigurationState;
11759               break;
11760             }
11761           if ((x < (int) (roi_info.x+RoiDelta)) &&
11762               (x > (int) (roi_info.x-RoiDelta)) &&
11763               (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11764               (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11765             {
11766               roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11767               state|=UpdateConfigurationState;
11768               break;
11769             }
11770           if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11771               (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11772               (y < (int) (roi_info.y+RoiDelta)) &&
11773               (y > (int) (roi_info.y-RoiDelta)))
11774             {
11775               roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11776               state|=UpdateConfigurationState;
11777               break;
11778             }
11779           if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11780               (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11781               (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11782               (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11783             {
11784               state|=UpdateConfigurationState;
11785               break;
11786             }
11787         }
11788         case ButtonRelease:
11789         {
11790           if (event.xbutton.window == windows->pan.id)
11791             if ((highlight_info.x != crop_info.x-windows->image.x) ||
11792                 (highlight_info.y != crop_info.y-windows->image.y))
11793               XHighlightRectangle(display,windows->image.id,
11794                 windows->image.highlight_context,&highlight_info);
11795           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11796             event.xbutton.time);
11797           break;
11798         }
11799         case Expose:
11800         {
11801           if (event.xexpose.window == windows->image.id)
11802             if (event.xexpose.count == 0)
11803               {
11804                 event.xexpose.x=(int) highlight_info.x;
11805                 event.xexpose.y=(int) highlight_info.y;
11806                 event.xexpose.width=(int) highlight_info.width;
11807                 event.xexpose.height=(int) highlight_info.height;
11808                 XRefreshWindow(display,&windows->image,&event);
11809               }
11810           if (event.xexpose.window == windows->info.id)
11811             if (event.xexpose.count == 0)
11812               XInfoWidget(display,windows,text);
11813           break;
11814         }
11815         case KeyPress:
11816         {
11817           KeySym
11818             key_symbol;
11819
11820           if (event.xkey.window != windows->image.id)
11821             break;
11822           /*
11823             Respond to a user key press.
11824           */
11825           (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11826             sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11827           switch ((int) key_symbol)
11828           {
11829             case XK_Shift_L:
11830             case XK_Shift_R:
11831               break;
11832             case XK_Escape:
11833             case XK_F20:
11834               state|=EscapeState;
11835             case XK_Return:
11836             {
11837               state|=ExitState;
11838               break;
11839             }
11840             case XK_Home:
11841             case XK_KP_Home:
11842             {
11843               roi_info.x=(ssize_t) (windows->image.width/2L-roi_info.width/2L);
11844               roi_info.y=(ssize_t) (windows->image.height/2L-
11845                 roi_info.height/2L);
11846               break;
11847             }
11848             case XK_Left:
11849             case XK_KP_Left:
11850             {
11851               roi_info.x--;
11852               break;
11853             }
11854             case XK_Up:
11855             case XK_KP_Up:
11856             case XK_Next:
11857             {
11858               roi_info.y--;
11859               break;
11860             }
11861             case XK_Right:
11862             case XK_KP_Right:
11863             {
11864               roi_info.x++;
11865               break;
11866             }
11867             case XK_Prior:
11868             case XK_Down:
11869             case XK_KP_Down:
11870             {
11871               roi_info.y++;
11872               break;
11873             }
11874             case XK_F1:
11875             case XK_Help:
11876             {
11877               (void) XSetFunction(display,windows->image.highlight_context,
11878                 GXcopy);
11879               XTextViewWidget(display,resource_info,windows,MagickFalse,
11880                 "Help Viewer - Region of Interest",ImageROIHelp);
11881               (void) XSetFunction(display,windows->image.highlight_context,
11882                 GXinvert);
11883               break;
11884             }
11885             default:
11886             {
11887               command_type=XImageWindowCommand(display,resource_info,windows,
11888                 event.xkey.state,key_symbol,image,exception);
11889               if (command_type != NullCommand)
11890                 state|=UpdateRegionState;
11891               break;
11892             }
11893           }
11894           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11895             event.xkey.time);
11896           break;
11897         }
11898         case KeyRelease:
11899           break;
11900         case MotionNotify:
11901         {
11902           if (event.xbutton.window != windows->image.id)
11903             break;
11904           /*
11905             Map and unmap Info widget as text cursor crosses its boundaries.
11906           */
11907           x=event.xmotion.x;
11908           y=event.xmotion.y;
11909           if (windows->info.mapped != MagickFalse)
11910             {
11911               if ((x < (int) (windows->info.x+windows->info.width)) &&
11912                   (y < (int) (windows->info.y+windows->info.height)))
11913                 (void) XWithdrawWindow(display,windows->info.id,
11914                   windows->info.screen);
11915             }
11916           else
11917             if ((x > (int) (windows->info.x+windows->info.width)) ||
11918                 (y > (int) (windows->info.y+windows->info.height)))
11919               (void) XMapWindow(display,windows->info.id);
11920           roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11921           roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11922           break;
11923         }
11924         case SelectionRequest:
11925         {
11926           XSelectionEvent
11927             notify;
11928
11929           XSelectionRequestEvent
11930             *request;
11931
11932           /*
11933             Set primary selection.
11934           */
11935           (void) FormatLocaleString(text,MaxTextExtent,
11936             "%.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11937             roi_info.height,(double) roi_info.x,(double) roi_info.y);
11938           request=(&(event.xselectionrequest));
11939           (void) XChangeProperty(request->display,request->requestor,
11940             request->property,request->target,8,PropModeReplace,
11941             (unsigned char *) text,(int) strlen(text));
11942           notify.type=SelectionNotify;
11943           notify.display=request->display;
11944           notify.requestor=request->requestor;
11945           notify.selection=request->selection;
11946           notify.target=request->target;
11947           notify.time=request->time;
11948           if (request->property == None)
11949             notify.property=request->target;
11950           else
11951             notify.property=request->property;
11952           (void) XSendEvent(request->display,request->requestor,False,0,
11953             (XEvent *) &notify);
11954         }
11955         default:
11956           break;
11957       }
11958       if ((state & UpdateConfigurationState) != 0)
11959         {
11960           (void) XPutBackEvent(display,&event);
11961           (void) XCheckDefineCursor(display,windows->image.id,cursor);
11962           break;
11963         }
11964     } while ((state & ExitState) == 0);
11965   } while ((state & ExitState) == 0);
11966   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11967   XSetCursorState(display,windows,MagickFalse);
11968   if ((state & EscapeState) != 0)
11969     return(MagickTrue);
11970   return(MagickTrue);
11971 }
11972 \f
11973 /*
11974 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11975 %                                                                             %
11976 %                                                                             %
11977 %                                                                             %
11978 +   X R o t a t e I m a g e                                                   %
11979 %                                                                             %
11980 %                                                                             %
11981 %                                                                             %
11982 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11983 %
11984 %  XRotateImage() rotates the X image.  If the degrees parameter if zero, the
11985 %  rotation angle is computed from the slope of a line drawn by the user.
11986 %
11987 %  The format of the XRotateImage method is:
11988 %
11989 %      MagickBooleanType XRotateImage(Display *display,
11990 %        XResourceInfo *resource_info,XWindows *windows,double degrees,
11991 %        Image **image,ExceptionInfo *exception)
11992 %
11993 %  A description of each parameter follows:
11994 %
11995 %    o display: Specifies a connection to an X server; returned from
11996 %      XOpenDisplay.
11997 %
11998 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
11999 %
12000 %    o windows: Specifies a pointer to a XWindows structure.
12001 %
12002 %    o degrees: Specifies the number of degrees to rotate the image.
12003 %
12004 %    o image: the image.
12005 %
12006 %    o exception: return any errors or warnings in this structure.
12007 %
12008 */
12009 static MagickBooleanType XRotateImage(Display *display,
12010   XResourceInfo *resource_info,XWindows *windows,double degrees,Image **image,
12011   ExceptionInfo *exception)
12012 {
12013   static const char
12014     *RotateMenu[] =
12015     {
12016       "Pixel Color",
12017       "Direction",
12018       "Help",
12019       "Dismiss",
12020       (char *) NULL
12021     };
12022
12023   static ModeType
12024     direction = HorizontalRotateCommand;
12025
12026   static const ModeType
12027     DirectionCommands[] =
12028     {
12029       HorizontalRotateCommand,
12030       VerticalRotateCommand
12031     },
12032     RotateCommands[] =
12033     {
12034       RotateColorCommand,
12035       RotateDirectionCommand,
12036       RotateHelpCommand,
12037       RotateDismissCommand
12038     };
12039
12040   static unsigned int
12041     pen_id = 0;
12042
12043   char
12044     command[MaxTextExtent],
12045     text[MaxTextExtent];
12046
12047   Image
12048     *rotate_image;
12049
12050   int
12051     id,
12052     x,
12053     y;
12054
12055   MagickRealType
12056     normalized_degrees;
12057
12058   register int
12059     i;
12060
12061   unsigned int
12062     height,
12063     rotations,
12064     width;
12065
12066   if (degrees == 0.0)
12067     {
12068       unsigned int
12069         distance;
12070
12071       size_t
12072         state;
12073
12074       XEvent
12075         event;
12076
12077       XSegment
12078         rotate_info;
12079
12080       /*
12081         Map Command widget.
12082       */
12083       (void) CloneString(&windows->command.name,"Rotate");
12084       windows->command.data=2;
12085       (void) XCommandWidget(display,windows,RotateMenu,(XEvent *) NULL);
12086       (void) XMapRaised(display,windows->command.id);
12087       XClientMessage(display,windows->image.id,windows->im_protocols,
12088         windows->im_update_widget,CurrentTime);
12089       /*
12090         Wait for first button press.
12091       */
12092       (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12093       XQueryPosition(display,windows->image.id,&x,&y);
12094       rotate_info.x1=x;
12095       rotate_info.y1=y;
12096       rotate_info.x2=x;
12097       rotate_info.y2=y;
12098       state=DefaultState;
12099       do
12100       {
12101         XHighlightLine(display,windows->image.id,
12102           windows->image.highlight_context,&rotate_info);
12103         /*
12104           Wait for next event.
12105         */
12106         XScreenEvent(display,windows,&event);
12107         XHighlightLine(display,windows->image.id,
12108           windows->image.highlight_context,&rotate_info);
12109         if (event.xany.window == windows->command.id)
12110           {
12111             /*
12112               Select a command from the Command widget.
12113             */
12114             id=XCommandWidget(display,windows,RotateMenu,&event);
12115             if (id < 0)
12116               continue;
12117             (void) XSetFunction(display,windows->image.highlight_context,
12118               GXcopy);
12119             switch (RotateCommands[id])
12120             {
12121               case RotateColorCommand:
12122               {
12123                 const char
12124                   *ColorMenu[MaxNumberPens];
12125
12126                 int
12127                   pen_number;
12128
12129                 XColor
12130                   color;
12131
12132                 /*
12133                   Initialize menu selections.
12134                 */
12135                 for (i=0; i < (int) (MaxNumberPens-2); i++)
12136                   ColorMenu[i]=resource_info->pen_colors[i];
12137                 ColorMenu[MaxNumberPens-2]="Browser...";
12138                 ColorMenu[MaxNumberPens-1]=(const char *) NULL;
12139                 /*
12140                   Select a pen color from the pop-up menu.
12141                 */
12142                 pen_number=XMenuWidget(display,windows,RotateMenu[id],
12143                   (const char **) ColorMenu,command);
12144                 if (pen_number < 0)
12145                   break;
12146                 if (pen_number == (MaxNumberPens-2))
12147                   {
12148                     static char
12149                       color_name[MaxTextExtent] = "gray";
12150
12151                     /*
12152                       Select a pen color from a dialog.
12153                     */
12154                     resource_info->pen_colors[pen_number]=color_name;
12155                     XColorBrowserWidget(display,windows,"Select",color_name);
12156                     if (*color_name == '\0')
12157                       break;
12158                   }
12159                 /*
12160                   Set pen color.
12161                 */
12162                 (void) XParseColor(display,windows->map_info->colormap,
12163                   resource_info->pen_colors[pen_number],&color);
12164                 XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
12165                   (unsigned int) MaxColors,&color);
12166                 windows->pixel_info->pen_colors[pen_number]=color;
12167                 pen_id=(unsigned int) pen_number;
12168                 break;
12169               }
12170               case RotateDirectionCommand:
12171               {
12172                 static const char
12173                   *Directions[] =
12174                   {
12175                     "horizontal",
12176                     "vertical",
12177                     (char *) NULL,
12178                   };
12179
12180                 /*
12181                   Select a command from the pop-up menu.
12182                 */
12183                 id=XMenuWidget(display,windows,RotateMenu[id],
12184                   Directions,command);
12185                 if (id >= 0)
12186                   direction=DirectionCommands[id];
12187                 break;
12188               }
12189               case RotateHelpCommand:
12190               {
12191                 XTextViewWidget(display,resource_info,windows,MagickFalse,
12192                   "Help Viewer - Image Rotation",ImageRotateHelp);
12193                 break;
12194               }
12195               case RotateDismissCommand:
12196               {
12197                 /*
12198                   Prematurely exit.
12199                 */
12200                 state|=EscapeState;
12201                 state|=ExitState;
12202                 break;
12203               }
12204               default:
12205                 break;
12206             }
12207             (void) XSetFunction(display,windows->image.highlight_context,
12208               GXinvert);
12209             continue;
12210           }
12211         switch (event.type)
12212         {
12213           case ButtonPress:
12214           {
12215             if (event.xbutton.button != Button1)
12216               break;
12217             if (event.xbutton.window != windows->image.id)
12218               break;
12219             /*
12220               exit loop.
12221             */
12222             (void) XSetFunction(display,windows->image.highlight_context,
12223               GXcopy);
12224             rotate_info.x1=event.xbutton.x;
12225             rotate_info.y1=event.xbutton.y;
12226             state|=ExitState;
12227             break;
12228           }
12229           case ButtonRelease:
12230             break;
12231           case Expose:
12232             break;
12233           case KeyPress:
12234           {
12235             char
12236               command[MaxTextExtent];
12237
12238             KeySym
12239               key_symbol;
12240
12241             if (event.xkey.window != windows->image.id)
12242               break;
12243             /*
12244               Respond to a user key press.
12245             */
12246             (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
12247               sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12248             switch ((int) key_symbol)
12249             {
12250               case XK_Escape:
12251               case XK_F20:
12252               {
12253                 /*
12254                   Prematurely exit.
12255                 */
12256                 state|=EscapeState;
12257                 state|=ExitState;
12258                 break;
12259               }
12260               case XK_F1:
12261               case XK_Help:
12262               {
12263                 (void) XSetFunction(display,windows->image.highlight_context,
12264                   GXcopy);
12265                 XTextViewWidget(display,resource_info,windows,MagickFalse,
12266                   "Help Viewer - Image Rotation",ImageRotateHelp);
12267                 (void) XSetFunction(display,windows->image.highlight_context,
12268                   GXinvert);
12269                 break;
12270               }
12271               default:
12272               {
12273                 (void) XBell(display,0);
12274                 break;
12275               }
12276             }
12277             break;
12278           }
12279           case MotionNotify:
12280           {
12281             rotate_info.x1=event.xmotion.x;
12282             rotate_info.y1=event.xmotion.y;
12283           }
12284         }
12285         rotate_info.x2=rotate_info.x1;
12286         rotate_info.y2=rotate_info.y1;
12287         if (direction == HorizontalRotateCommand)
12288           rotate_info.x2+=32;
12289         else
12290           rotate_info.y2-=32;
12291       } while ((state & ExitState) == 0);
12292       (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12293       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12294       if ((state & EscapeState) != 0)
12295         return(MagickTrue);
12296       /*
12297         Draw line as pointer moves until the mouse button is released.
12298       */
12299       distance=0;
12300       (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12301       state=DefaultState;
12302       do
12303       {
12304         if (distance > 9)
12305           {
12306             /*
12307               Display info and draw rotation line.
12308             */
12309             if (windows->info.mapped == MagickFalse)
12310               (void) XMapWindow(display,windows->info.id);
12311             (void) FormatLocaleString(text,MaxTextExtent," %g",
12312               direction == VerticalRotateCommand ? degrees-90.0 : degrees);
12313             XInfoWidget(display,windows,text);
12314             XHighlightLine(display,windows->image.id,
12315               windows->image.highlight_context,&rotate_info);
12316           }
12317         else
12318           if (windows->info.mapped != MagickFalse)
12319             (void) XWithdrawWindow(display,windows->info.id,
12320               windows->info.screen);
12321         /*
12322           Wait for next event.
12323         */
12324         XScreenEvent(display,windows,&event);
12325         if (distance > 9)
12326           XHighlightLine(display,windows->image.id,
12327             windows->image.highlight_context,&rotate_info);
12328         switch (event.type)
12329         {
12330           case ButtonPress:
12331             break;
12332           case ButtonRelease:
12333           {
12334             /*
12335               User has committed to rotation line.
12336             */
12337             rotate_info.x2=event.xbutton.x;
12338             rotate_info.y2=event.xbutton.y;
12339             state|=ExitState;
12340             break;
12341           }
12342           case Expose:
12343             break;
12344           case MotionNotify:
12345           {
12346             rotate_info.x2=event.xmotion.x;
12347             rotate_info.y2=event.xmotion.y;
12348           }
12349           default:
12350             break;
12351         }
12352         /*
12353           Check boundary conditions.
12354         */
12355         if (rotate_info.x2 < 0)
12356           rotate_info.x2=0;
12357         else
12358           if (rotate_info.x2 > (int) windows->image.width)
12359             rotate_info.x2=(short) windows->image.width;
12360         if (rotate_info.y2 < 0)
12361           rotate_info.y2=0;
12362         else
12363           if (rotate_info.y2 > (int) windows->image.height)
12364             rotate_info.y2=(short) windows->image.height;
12365         /*
12366           Compute rotation angle from the slope of the line.
12367         */
12368         degrees=0.0;
12369         distance=(unsigned int)
12370           ((rotate_info.x2-rotate_info.x1+1)*(rotate_info.x2-rotate_info.x1+1))+
12371           ((rotate_info.y2-rotate_info.y1+1)*(rotate_info.y2-rotate_info.y1+1));
12372         if (distance > 9)
12373           degrees=RadiansToDegrees(-atan2((double) (rotate_info.y2-
12374             rotate_info.y1),(double) (rotate_info.x2-rotate_info.x1)));
12375       } while ((state & ExitState) == 0);
12376       (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12377       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12378       if (distance <= 9)
12379         return(MagickTrue);
12380     }
12381   if (direction == VerticalRotateCommand)
12382     degrees-=90.0;
12383   if (degrees == 0.0)
12384     return(MagickTrue);
12385   /*
12386     Rotate image.
12387   */
12388   normalized_degrees=degrees;
12389   while (normalized_degrees < -45.0)
12390     normalized_degrees+=360.0;
12391   for (rotations=0; normalized_degrees > 45.0; rotations++)
12392     normalized_degrees-=90.0;
12393   if (normalized_degrees != 0.0)
12394     (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
12395       exception);
12396   XSetCursorState(display,windows,MagickTrue);
12397   XCheckRefreshWindows(display,windows);
12398   (*image)->background_color.red=ScaleShortToQuantum(
12399     windows->pixel_info->pen_colors[pen_id].red);
12400   (*image)->background_color.green=ScaleShortToQuantum(
12401     windows->pixel_info->pen_colors[pen_id].green);
12402   (*image)->background_color.blue=ScaleShortToQuantum(
12403     windows->pixel_info->pen_colors[pen_id].blue);
12404   rotate_image=RotateImage(*image,degrees,exception);
12405   XSetCursorState(display,windows,MagickFalse);
12406   if (rotate_image == (Image *) NULL)
12407     return(MagickFalse);
12408   *image=DestroyImage(*image);
12409   *image=rotate_image;
12410   if (windows->image.crop_geometry != (char *) NULL)
12411     {
12412       /*
12413         Rotate crop geometry.
12414       */
12415       width=(unsigned int) (*image)->columns;
12416       height=(unsigned int) (*image)->rows;
12417       (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12418       switch (rotations % 4)
12419       {
12420         default:
12421         case 0:
12422           break;
12423         case 1:
12424         {
12425           /*
12426             Rotate 90 degrees.
12427           */
12428           (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12429             "%ux%u%+d%+d",height,width,(int) (*image)->columns-
12430             (int) height-y,x);
12431           break;
12432         }
12433         case 2:
12434         {
12435           /*
12436             Rotate 180 degrees.
12437           */
12438           (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12439             "%ux%u%+d%+d",width,height,(int) width-x,(int) height-y);
12440           break;
12441         }
12442         case 3:
12443         {
12444           /*
12445             Rotate 270 degrees.
12446           */
12447           (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12448             "%ux%u%+d%+d",height,width,y,(int) (*image)->rows-(int) width-x);
12449           break;
12450         }
12451       }
12452     }
12453   if (windows->image.orphan != MagickFalse)
12454     return(MagickTrue);
12455   if (normalized_degrees != 0.0)
12456     {
12457       /*
12458         Update image colormap.
12459       */
12460       windows->image.window_changes.width=(int) (*image)->columns;
12461       windows->image.window_changes.height=(int) (*image)->rows;
12462       if (windows->image.crop_geometry != (char *) NULL)
12463         {
12464           /*
12465             Obtain dimensions of image from crop geometry.
12466           */
12467           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
12468             &width,&height);
12469           windows->image.window_changes.width=(int) width;
12470           windows->image.window_changes.height=(int) height;
12471         }
12472       XConfigureImageColormap(display,resource_info,windows,*image);
12473     }
12474   else
12475     if (((rotations % 4) == 1) || ((rotations % 4) == 3))
12476       {
12477         windows->image.window_changes.width=windows->image.ximage->height;
12478         windows->image.window_changes.height=windows->image.ximage->width;
12479       }
12480   /*
12481     Update image configuration.
12482   */
12483   (void) XConfigureImage(display,resource_info,windows,*image,exception);
12484   return(MagickTrue);
12485 }
12486 \f
12487 /*
12488 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12489 %                                                                             %
12490 %                                                                             %
12491 %                                                                             %
12492 +   X S a v e I m a g e                                                       %
12493 %                                                                             %
12494 %                                                                             %
12495 %                                                                             %
12496 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12497 %
12498 %  XSaveImage() saves an image to a file.
12499 %
12500 %  The format of the XSaveImage method is:
12501 %
12502 %      MagickBooleanType XSaveImage(Display *display,
12503 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
12504 %        ExceptionInfo *exception)
12505 %
12506 %  A description of each parameter follows:
12507 %
12508 %    o display: Specifies a connection to an X server; returned from
12509 %      XOpenDisplay.
12510 %
12511 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12512 %
12513 %    o windows: Specifies a pointer to a XWindows structure.
12514 %
12515 %    o image: the image.
12516 %
12517 %    o exception: return any errors or warnings in this structure.
12518 %
12519 */
12520 static MagickBooleanType XSaveImage(Display *display,
12521   XResourceInfo *resource_info,XWindows *windows,Image *image,
12522   ExceptionInfo *exception)
12523 {
12524   char
12525     filename[MaxTextExtent],
12526     geometry[MaxTextExtent];
12527
12528   Image
12529     *save_image;
12530
12531   ImageInfo
12532     *image_info;
12533
12534   MagickStatusType
12535     status;
12536
12537   /*
12538     Request file name from user.
12539   */
12540   if (resource_info->write_filename != (char *) NULL)
12541     (void) CopyMagickString(filename,resource_info->write_filename,
12542       MaxTextExtent);
12543   else
12544     {
12545       char
12546         path[MaxTextExtent];
12547
12548       int
12549         status;
12550
12551       GetPathComponent(image->filename,HeadPath,path);
12552       GetPathComponent(image->filename,TailPath,filename);
12553       if (*path != '\0')
12554         {
12555           status=chdir(path);
12556           if (status == -1)
12557             (void) ThrowMagickException(exception,GetMagickModule(),
12558               FileOpenError,"UnableToOpenFile","%s",path);
12559         }
12560     }
12561   XFileBrowserWidget(display,windows,"Save",filename);
12562   if (*filename == '\0')
12563     return(MagickTrue);
12564   if (IsPathAccessible(filename) != MagickFalse)
12565     {
12566       int
12567         status;
12568
12569       /*
12570         File exists-- seek user's permission before overwriting.
12571       */
12572       status=XConfirmWidget(display,windows,"Overwrite",filename);
12573       if (status <= 0)
12574         return(MagickTrue);
12575     }
12576   image_info=CloneImageInfo(resource_info->image_info);
12577   (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
12578   (void) SetImageInfo(image_info,1,exception);
12579   if ((LocaleCompare(image_info->magick,"JPEG") == 0) ||
12580       (LocaleCompare(image_info->magick,"JPG") == 0))
12581     {
12582       char
12583         quality[MaxTextExtent];
12584
12585       int
12586         status;
12587
12588       /*
12589         Request JPEG quality from user.
12590       */
12591       (void) FormatLocaleString(quality,MaxTextExtent,"%.20g",(double)
12592         image->quality);
12593       status=XDialogWidget(display,windows,"Save","Enter JPEG quality:",
12594         quality);
12595       if (*quality == '\0')
12596         return(MagickTrue);
12597       image->quality=StringToUnsignedLong(quality);
12598       image_info->interlace=status != 0 ? NoInterlace : PlaneInterlace;
12599     }
12600   if ((LocaleCompare(image_info->magick,"EPS") == 0) ||
12601       (LocaleCompare(image_info->magick,"PDF") == 0) ||
12602       (LocaleCompare(image_info->magick,"PS") == 0) ||
12603       (LocaleCompare(image_info->magick,"PS2") == 0))
12604     {
12605       char
12606         geometry[MaxTextExtent];
12607
12608       /*
12609         Request page geometry from user.
12610       */
12611       (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
12612       if (LocaleCompare(image_info->magick,"PDF") == 0)
12613         (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
12614       if (image_info->page != (char *) NULL)
12615         (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
12616       XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
12617         "Select page geometry:",geometry);
12618       if (*geometry != '\0')
12619         image_info->page=GetPageGeometry(geometry);
12620     }
12621   /*
12622     Apply image transforms.
12623   */
12624   XSetCursorState(display,windows,MagickTrue);
12625   XCheckRefreshWindows(display,windows);
12626   save_image=CloneImage(image,0,0,MagickTrue,exception);
12627   if (save_image == (Image *) NULL)
12628     return(MagickFalse);
12629   (void) FormatLocaleString(geometry,MaxTextExtent,"%dx%d!",
12630     windows->image.ximage->width,windows->image.ximage->height);
12631   (void) TransformImage(&save_image,windows->image.crop_geometry,geometry,
12632     exception);
12633   /*
12634     Write image.
12635   */
12636   (void) CopyMagickString(save_image->filename,filename,MaxTextExtent);
12637   status=WriteImage(image_info,save_image,exception);
12638   if (status != MagickFalse)
12639     image->taint=MagickFalse;
12640   save_image=DestroyImage(save_image);
12641   image_info=DestroyImageInfo(image_info);
12642   XSetCursorState(display,windows,MagickFalse);
12643   return(status != 0 ? MagickTrue : MagickFalse);
12644 }
12645 \f
12646 /*
12647 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12648 %                                                                             %
12649 %                                                                             %
12650 %                                                                             %
12651 +   X S c r e e n E v e n t                                                   %
12652 %                                                                             %
12653 %                                                                             %
12654 %                                                                             %
12655 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12656 %
12657 %  XScreenEvent() handles global events associated with the Pan and Magnify
12658 %  windows.
12659 %
12660 %  The format of the XScreenEvent function is:
12661 %
12662 %      void XScreenEvent(Display *display,XWindows *windows,XEvent *event)
12663 %
12664 %  A description of each parameter follows:
12665 %
12666 %    o display: Specifies a pointer to the Display structure;  returned from
12667 %      XOpenDisplay.
12668 %
12669 %    o windows: Specifies a pointer to a XWindows structure.
12670 %
12671 %    o event: Specifies a pointer to a X11 XEvent structure.
12672 %
12673 %
12674 */
12675
12676 #if defined(__cplusplus) || defined(c_plusplus)
12677 extern "C" {
12678 #endif
12679
12680 static int XPredicate(Display *magick_unused(display),XEvent *event,char *data)
12681 {
12682   register XWindows
12683     *windows;
12684
12685   windows=(XWindows *) data;
12686   if ((event->type == ClientMessage) &&
12687       (event->xclient.window == windows->image.id))
12688     return(MagickFalse);
12689   return(MagickTrue);
12690 }
12691
12692 #if defined(__cplusplus) || defined(c_plusplus)
12693 }
12694 #endif
12695
12696 static void XScreenEvent(Display *display,XWindows *windows,XEvent *event)
12697 {
12698   register int
12699     x,
12700     y;
12701
12702   (void) XIfEvent(display,event,XPredicate,(char *) windows);
12703   if (event->xany.window == windows->command.id)
12704     return;
12705   switch (event->type)
12706   {
12707     case ButtonPress:
12708     case ButtonRelease:
12709     {
12710       if ((event->xbutton.button == Button3) &&
12711           (event->xbutton.state & Mod1Mask))
12712         {
12713           /*
12714             Convert Alt-Button3 to Button2.
12715           */
12716           event->xbutton.button=Button2;
12717           event->xbutton.state&=(~Mod1Mask);
12718         }
12719       if (event->xbutton.window == windows->backdrop.id)
12720         {
12721           (void) XSetInputFocus(display,event->xbutton.window,RevertToParent,
12722             event->xbutton.time);
12723           break;
12724         }
12725       if (event->xbutton.window == windows->pan.id)
12726         {
12727           XPanImage(display,windows,event);
12728           break;
12729         }
12730       if (event->xbutton.window == windows->image.id)
12731         if (event->xbutton.button == Button2)
12732           {
12733             /*
12734               Update magnified image.
12735             */
12736             x=event->xbutton.x;
12737             y=event->xbutton.y;
12738             if (x < 0)
12739               x=0;
12740             else
12741               if (x >= (int) windows->image.width)
12742                 x=(int) (windows->image.width-1);
12743             windows->magnify.x=(int) windows->image.x+x;
12744             if (y < 0)
12745               y=0;
12746             else
12747              if (y >= (int) windows->image.height)
12748                y=(int) (windows->image.height-1);
12749             windows->magnify.y=windows->image.y+y;
12750             if (windows->magnify.mapped == MagickFalse)
12751               (void) XMapRaised(display,windows->magnify.id);
12752             XMakeMagnifyImage(display,windows);
12753             if (event->type == ButtonRelease)
12754               (void) XWithdrawWindow(display,windows->info.id,
12755                 windows->info.screen);
12756             break;
12757           }
12758       break;
12759     }
12760     case ClientMessage:
12761     {
12762       /*
12763         If client window delete message, exit.
12764       */
12765       if (event->xclient.message_type != windows->wm_protocols)
12766         break;
12767       if (*event->xclient.data.l != (long) windows->wm_delete_window)
12768         break;
12769       if (event->xclient.window == windows->magnify.id)
12770         {
12771           (void) XWithdrawWindow(display,windows->magnify.id,
12772             windows->magnify.screen);
12773           break;
12774         }
12775       break;
12776     }
12777     case ConfigureNotify:
12778     {
12779       if (event->xconfigure.window == windows->magnify.id)
12780         {
12781           unsigned int
12782             magnify;
12783
12784           /*
12785             Magnify window has a new configuration.
12786           */
12787           windows->magnify.width=(unsigned int) event->xconfigure.width;
12788           windows->magnify.height=(unsigned int) event->xconfigure.height;
12789           if (windows->magnify.mapped == MagickFalse)
12790             break;
12791           magnify=1;
12792           while ((int) magnify <= event->xconfigure.width)
12793             magnify<<=1;
12794           while ((int) magnify <= event->xconfigure.height)
12795             magnify<<=1;
12796           magnify>>=1;
12797           if (((int) magnify != event->xconfigure.width) ||
12798               ((int) magnify != event->xconfigure.height))
12799             {
12800               XWindowChanges
12801                 window_changes;
12802
12803               window_changes.width=(int) magnify;
12804               window_changes.height=(int) magnify;
12805               (void) XReconfigureWMWindow(display,windows->magnify.id,
12806                 windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
12807                 &window_changes);
12808               break;
12809             }
12810           XMakeMagnifyImage(display,windows);
12811           break;
12812         }
12813       break;
12814     }
12815     case Expose:
12816     {
12817       if (event->xexpose.window == windows->image.id)
12818         {
12819           XRefreshWindow(display,&windows->image,event);
12820           break;
12821         }
12822       if (event->xexpose.window == windows->pan.id)
12823         if (event->xexpose.count == 0)
12824           {
12825             XDrawPanRectangle(display,windows);
12826             break;
12827           }
12828       if (event->xexpose.window == windows->magnify.id)
12829         if (event->xexpose.count == 0)
12830           {
12831             XMakeMagnifyImage(display,windows);
12832             break;
12833           }
12834       break;
12835     }
12836     case KeyPress:
12837     {
12838       char
12839         command[MaxTextExtent];
12840
12841       KeySym
12842         key_symbol;
12843
12844       if (event->xkey.window != windows->magnify.id)
12845         break;
12846       /*
12847         Respond to a user key press.
12848       */
12849       (void) XLookupString((XKeyEvent *) &event->xkey,command,(int)
12850         sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12851       XMagnifyWindowCommand(display,windows,event->xkey.state,key_symbol);
12852       break;
12853     }
12854     case MapNotify:
12855     {
12856       if (event->xmap.window == windows->magnify.id)
12857         {
12858           windows->magnify.mapped=MagickTrue;
12859           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12860           break;
12861         }
12862       if (event->xmap.window == windows->info.id)
12863         {
12864           windows->info.mapped=MagickTrue;
12865           break;
12866         }
12867       break;
12868     }
12869     case MotionNotify:
12870     {
12871       while (XCheckMaskEvent(display,ButtonMotionMask,event)) ;
12872       if (event->xmotion.window == windows->image.id)
12873         if (windows->magnify.mapped != MagickFalse)
12874           {
12875             /*
12876               Update magnified image.
12877             */
12878             x=event->xmotion.x;
12879             y=event->xmotion.y;
12880             if (x < 0)
12881               x=0;
12882             else
12883               if (x >= (int) windows->image.width)
12884                 x=(int) (windows->image.width-1);
12885             windows->magnify.x=(int) windows->image.x+x;
12886             if (y < 0)
12887               y=0;
12888             else
12889              if (y >= (int) windows->image.height)
12890                y=(int) (windows->image.height-1);
12891             windows->magnify.y=windows->image.y+y;
12892             XMakeMagnifyImage(display,windows);
12893           }
12894       break;
12895     }
12896     case UnmapNotify:
12897     {
12898       if (event->xunmap.window == windows->magnify.id)
12899         {
12900           windows->magnify.mapped=MagickFalse;
12901           break;
12902         }
12903       if (event->xunmap.window == windows->info.id)
12904         {
12905           windows->info.mapped=MagickFalse;
12906           break;
12907         }
12908       break;
12909     }
12910     default:
12911       break;
12912   }
12913 }
12914 \f
12915 /*
12916 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12917 %                                                                             %
12918 %                                                                             %
12919 %                                                                             %
12920 +   X S e t C r o p G e o m e t r y                                           %
12921 %                                                                             %
12922 %                                                                             %
12923 %                                                                             %
12924 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12925 %
12926 %  XSetCropGeometry() accepts a cropping geometry relative to the Image window
12927 %  and translates it to a cropping geometry relative to the image.
12928 %
12929 %  The format of the XSetCropGeometry method is:
12930 %
12931 %      void XSetCropGeometry(Display *display,XWindows *windows,
12932 %        RectangleInfo *crop_info,Image *image)
12933 %
12934 %  A description of each parameter follows:
12935 %
12936 %    o display: Specifies a connection to an X server; returned from
12937 %      XOpenDisplay.
12938 %
12939 %    o windows: Specifies a pointer to a XWindows structure.
12940 %
12941 %    o crop_info:  A pointer to a RectangleInfo that defines a region of the
12942 %      Image window to crop.
12943 %
12944 %    o image: the image.
12945 %
12946 */
12947 static void XSetCropGeometry(Display *display,XWindows *windows,
12948   RectangleInfo *crop_info,Image *image)
12949 {
12950   char
12951     text[MaxTextExtent];
12952
12953   int
12954     x,
12955     y;
12956
12957   MagickRealType
12958     scale_factor;
12959
12960   unsigned int
12961     height,
12962     width;
12963
12964   if (windows->info.mapped != MagickFalse)
12965     {
12966       /*
12967         Display info on cropping rectangle.
12968       */
12969       (void) FormatLocaleString(text,MaxTextExtent," %.20gx%.20g%+.20g%+.20g",
12970         (double) crop_info->width,(double) crop_info->height,(double)
12971         crop_info->x,(double) crop_info->y);
12972       XInfoWidget(display,windows,text);
12973     }
12974   /*
12975     Cropping geometry is relative to any previous crop geometry.
12976   */
12977   x=0;
12978   y=0;
12979   width=(unsigned int) image->columns;
12980   height=(unsigned int) image->rows;
12981   if (windows->image.crop_geometry != (char *) NULL)
12982     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12983   else
12984     windows->image.crop_geometry=AcquireString((char *) NULL);
12985   /*
12986     Define the crop geometry string from the cropping rectangle.
12987   */
12988   scale_factor=(MagickRealType) width/windows->image.ximage->width;
12989   if (crop_info->x > 0)
12990     x+=(int) (scale_factor*crop_info->x+0.5);
12991   width=(unsigned int) (scale_factor*crop_info->width+0.5);
12992   if (width == 0)
12993     width=1;
12994   scale_factor=(MagickRealType) height/windows->image.ximage->height;
12995   if (crop_info->y > 0)
12996     y+=(int) (scale_factor*crop_info->y+0.5);
12997   height=(unsigned int) (scale_factor*crop_info->height+0.5);
12998   if (height == 0)
12999     height=1;
13000   (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
13001     "%ux%u%+d%+d",width,height,x,y);
13002 }
13003 \f
13004 /*
13005 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13006 %                                                                             %
13007 %                                                                             %
13008 %                                                                             %
13009 +   X T i l e I m a g e                                                       %
13010 %                                                                             %
13011 %                                                                             %
13012 %                                                                             %
13013 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13014 %
13015 %  XTileImage() loads or deletes a selected tile from a visual image directory.
13016 %  The load or delete command is chosen from a menu.
13017 %
13018 %  The format of the XTileImage method is:
13019 %
13020 %      Image *XTileImage(Display *display,XResourceInfo *resource_info,
13021 %        XWindows *windows,Image *image,XEvent *event,ExceptionInfo *exception)
13022 %
13023 %  A description of each parameter follows:
13024 %
13025 %    o tile_image:  XTileImage reads or deletes the tile image
13026 %      and returns it.  A null image is returned if an error occurs.
13027 %
13028 %    o display: Specifies a connection to an X server;  returned from
13029 %      XOpenDisplay.
13030 %
13031 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13032 %
13033 %    o windows: Specifies a pointer to a XWindows structure.
13034 %
13035 %    o image: the image; returned from ReadImage.
13036 %
13037 %    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
13038 %      the entire image is refreshed.
13039 %
13040 %    o exception: return any errors or warnings in this structure.
13041 %
13042 */
13043 static Image *XTileImage(Display *display,XResourceInfo *resource_info,
13044   XWindows *windows,Image *image,XEvent *event,ExceptionInfo *exception)
13045 {
13046   static const char
13047     *VerbMenu[] =
13048     {
13049       "Load",
13050       "Next",
13051       "Former",
13052       "Delete",
13053       "Update",
13054       (char *) NULL,
13055     };
13056
13057   static const ModeType
13058     TileCommands[] =
13059     {
13060       TileLoadCommand,
13061       TileNextCommand,
13062       TileFormerCommand,
13063       TileDeleteCommand,
13064       TileUpdateCommand
13065     };
13066
13067   char
13068     command[MaxTextExtent],
13069     filename[MaxTextExtent];
13070
13071   Image
13072     *tile_image;
13073
13074   int
13075     id,
13076     status,
13077     tile,
13078     x,
13079     y;
13080
13081   MagickRealType
13082     scale_factor;
13083
13084   register char
13085     *p,
13086     *q;
13087
13088   register int
13089     i;
13090
13091   unsigned int
13092     height,
13093     width;
13094
13095   /*
13096     Tile image is relative to montage image configuration.
13097   */
13098   x=0;
13099   y=0;
13100   width=(unsigned int) image->columns;
13101   height=(unsigned int) image->rows;
13102   if (windows->image.crop_geometry != (char *) NULL)
13103     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
13104   scale_factor=(MagickRealType) width/windows->image.ximage->width;
13105   event->xbutton.x+=windows->image.x;
13106   event->xbutton.x=(int) (scale_factor*event->xbutton.x+x+0.5);
13107   scale_factor=(MagickRealType) height/windows->image.ximage->height;
13108   event->xbutton.y+=windows->image.y;
13109   event->xbutton.y=(int) (scale_factor*event->xbutton.y+y+0.5);
13110   /*
13111     Determine size and location of each tile in the visual image directory.
13112   */
13113   width=(unsigned int) image->columns;
13114   height=(unsigned int) image->rows;
13115   x=0;
13116   y=0;
13117   (void) XParseGeometry(image->montage,&x,&y,&width,&height);
13118   tile=((event->xbutton.y-y)/height)*(((int) image->columns-x)/width)+
13119     (event->xbutton.x-x)/width;
13120   if (tile < 0)
13121     {
13122       /*
13123         Button press is outside any tile.
13124       */
13125       (void) XBell(display,0);
13126       return((Image *) NULL);
13127     }
13128   /*
13129     Determine file name from the tile directory.
13130   */
13131   p=image->directory;
13132   for (i=tile; (i != 0) && (*p != '\0'); )
13133   {
13134     if (*p == '\n')
13135       i--;
13136     p++;
13137   }
13138   if (*p == '\0')
13139     {
13140       /*
13141         Button press is outside any tile.
13142       */
13143       (void) XBell(display,0);
13144       return((Image *) NULL);
13145     }
13146   /*
13147     Select a command from the pop-up menu.
13148   */
13149   id=XMenuWidget(display,windows,"Tile Verb",VerbMenu,command);
13150   if (id < 0)
13151     return((Image *) NULL);
13152   q=p;
13153   while ((*q != '\n') && (*q != '\0'))
13154     q++;
13155   (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13156   /*
13157     Perform command for the selected tile.
13158   */
13159   XSetCursorState(display,windows,MagickTrue);
13160   XCheckRefreshWindows(display,windows);
13161   tile_image=NewImageList();
13162   switch (TileCommands[id])
13163   {
13164     case TileLoadCommand:
13165     {
13166       /*
13167         Load tile image.
13168       */
13169       XCheckRefreshWindows(display,windows);
13170       (void) CopyMagickString(resource_info->image_info->magick,"MIFF",
13171         MaxTextExtent);
13172       (void) CopyMagickString(resource_info->image_info->filename,filename,
13173         MaxTextExtent);
13174       tile_image=ReadImage(resource_info->image_info,exception);
13175       CatchException(exception);
13176       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13177       break;
13178     }
13179     case TileNextCommand:
13180     {
13181       /*
13182         Display next image.
13183       */
13184       XClientMessage(display,windows->image.id,windows->im_protocols,
13185         windows->im_next_image,CurrentTime);
13186       break;
13187     }
13188     case TileFormerCommand:
13189     {
13190       /*
13191         Display former image.
13192       */
13193       XClientMessage(display,windows->image.id,windows->im_protocols,
13194         windows->im_former_image,CurrentTime);
13195       break;
13196     }
13197     case TileDeleteCommand:
13198     {
13199       /*
13200         Delete tile image.
13201       */
13202       if (IsPathAccessible(filename) == MagickFalse)
13203         {
13204           XNoticeWidget(display,windows,"Image file does not exist:",filename);
13205           break;
13206         }
13207       status=XConfirmWidget(display,windows,"Really delete tile",filename);
13208       if (status <= 0)
13209         break;
13210       status=remove_utf8(filename) != 0 ? MagickTrue : MagickFalse;
13211       if (status != MagickFalse)
13212         {
13213           XNoticeWidget(display,windows,"Unable to delete image file:",
13214             filename);
13215           break;
13216         }
13217     }
13218     case TileUpdateCommand:
13219     {
13220       int
13221         x_offset,
13222         y_offset;
13223
13224       PixelInfo
13225         pixel;
13226
13227       Quantum
13228         virtual_pixel[MaxPixelChannels];
13229
13230       register int
13231         j;
13232
13233       register Quantum
13234         *s;
13235
13236       /*
13237         Ensure all the images exist.
13238       */
13239       tile=0;
13240       GetPixelInfo(image,&pixel);
13241       for (p=image->directory; *p != '\0'; p++)
13242       {
13243         CacheView
13244           *image_view;
13245
13246         q=p;
13247         while ((*q != '\n') && (*q != '\0'))
13248           q++;
13249         (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13250         p=q;
13251         if (IsPathAccessible(filename) != MagickFalse)
13252           {
13253             tile++;
13254             continue;
13255           }
13256         /*
13257           Overwrite tile with background color.
13258         */
13259         x_offset=(int) (width*(tile % (((int) image->columns-x)/width))+x);
13260         y_offset=(int) (height*(tile/(((int) image->columns-x)/width))+y);
13261         image_view=AcquireCacheView(image);
13262         (void) GetOneCacheViewVirtualPixel(image_view,0,0,virtual_pixel,
13263           exception);
13264         pixel.red=virtual_pixel[RedPixelChannel];
13265         pixel.green=virtual_pixel[GreenPixelChannel];
13266         pixel.blue=virtual_pixel[BluePixelChannel];
13267         pixel.alpha=virtual_pixel[AlphaPixelChannel];
13268         for (i=0; i < (int) height; i++)
13269         {
13270           s=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,(ssize_t)
13271             y_offset+i,width,1,exception);
13272           if (s == (Quantum *) NULL)
13273             break;
13274           for (j=0; j < (int) width; j++)
13275           {
13276             SetPixelPixelInfo(image,&pixel,s);
13277             s+=GetPixelChannels(image);
13278           }
13279           if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
13280             break;
13281         }
13282         image_view=DestroyCacheView(image_view);
13283         tile++;
13284       }
13285       windows->image.window_changes.width=(int) image->columns;
13286       windows->image.window_changes.height=(int) image->rows;
13287       XConfigureImageColormap(display,resource_info,windows,image);
13288       (void) XConfigureImage(display,resource_info,windows,image,exception);
13289       break;
13290     }
13291     default:
13292       break;
13293   }
13294   XSetCursorState(display,windows,MagickFalse);
13295   return(tile_image);
13296 }
13297 \f
13298 /*
13299 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13300 %                                                                             %
13301 %                                                                             %
13302 %                                                                             %
13303 +   X T r a n s l a t e I m a g e                                             %
13304 %                                                                             %
13305 %                                                                             %
13306 %                                                                             %
13307 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13308 %
13309 %  XTranslateImage() translates the image within an Image window by one pixel
13310 %  as specified by the key symbol.  If the image has a `montage string the
13311 %  translation is respect to the width and height contained within the string.
13312 %
13313 %  The format of the XTranslateImage method is:
13314 %
13315 %      void XTranslateImage(Display *display,XWindows *windows,
13316 %        Image *image,const KeySym key_symbol)
13317 %
13318 %  A description of each parameter follows:
13319 %
13320 %    o display: Specifies a connection to an X server; returned from
13321 %      XOpenDisplay.
13322 %
13323 %    o windows: Specifies a pointer to a XWindows structure.
13324 %
13325 %    o image: the image.
13326 %
13327 %    o key_symbol: Specifies a KeySym which indicates which side of the image
13328 %      to trim.
13329 %
13330 */
13331 static void XTranslateImage(Display *display,XWindows *windows,
13332   Image *image,const KeySym key_symbol)
13333 {
13334   char
13335     text[MaxTextExtent];
13336
13337   int
13338     x,
13339     y;
13340
13341   unsigned int
13342     x_offset,
13343     y_offset;
13344
13345   /*
13346     User specified a pan position offset.
13347   */
13348   x_offset=windows->image.width;
13349   y_offset=windows->image.height;
13350   if (image->montage != (char *) NULL)
13351     (void) XParseGeometry(image->montage,&x,&y,&x_offset,&y_offset);
13352   switch ((int) key_symbol)
13353   {
13354     case XK_Home:
13355     case XK_KP_Home:
13356     {
13357       windows->image.x=(int) windows->image.width/2;
13358       windows->image.y=(int) windows->image.height/2;
13359       break;
13360     }
13361     case XK_Left:
13362     case XK_KP_Left:
13363     {
13364       windows->image.x-=x_offset;
13365       break;
13366     }
13367     case XK_Next:
13368     case XK_Up:
13369     case XK_KP_Up:
13370     {
13371       windows->image.y-=y_offset;
13372       break;
13373     }
13374     case XK_Right:
13375     case XK_KP_Right:
13376     {
13377       windows->image.x+=x_offset;
13378       break;
13379     }
13380     case XK_Prior:
13381     case XK_Down:
13382     case XK_KP_Down:
13383     {
13384       windows->image.y+=y_offset;
13385       break;
13386     }
13387     default:
13388       return;
13389   }
13390   /*
13391     Check boundary conditions.
13392   */
13393   if (windows->image.x < 0)
13394     windows->image.x=0;
13395   else
13396     if ((int) (windows->image.x+windows->image.width) >
13397         windows->image.ximage->width)
13398       windows->image.x=(int) windows->image.ximage->width-windows->image.width;
13399   if (windows->image.y < 0)
13400     windows->image.y=0;
13401   else
13402     if ((int) (windows->image.y+windows->image.height) >
13403         windows->image.ximage->height)
13404       windows->image.y=(int) windows->image.ximage->height-windows->image.height;
13405   /*
13406     Refresh Image window.
13407   */
13408   (void) FormatLocaleString(text,MaxTextExtent," %ux%u%+d%+d ",
13409     windows->image.width,windows->image.height,windows->image.x,
13410     windows->image.y);
13411   XInfoWidget(display,windows,text);
13412   XCheckRefreshWindows(display,windows);
13413   XDrawPanRectangle(display,windows);
13414   XRefreshWindow(display,&windows->image,(XEvent *) NULL);
13415   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13416 }
13417 \f
13418 /*
13419 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13420 %                                                                             %
13421 %                                                                             %
13422 %                                                                             %
13423 +   X T r i m I m a g e                                                       %
13424 %                                                                             %
13425 %                                                                             %
13426 %                                                                             %
13427 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13428 %
13429 %  XTrimImage() trims the edges from the Image window.
13430 %
13431 %  The format of the XTrimImage method is:
13432 %
13433 %      MagickBooleanType XTrimImage(Display *display,
13434 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
13435 %        ExceptionInfo *exception)
13436 %
13437 %  A description of each parameter follows:
13438 %
13439 %    o display: Specifies a connection to an X server; returned from
13440 %      XOpenDisplay.
13441 %
13442 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13443 %
13444 %    o windows: Specifies a pointer to a XWindows structure.
13445 %
13446 %    o image: the image.
13447 %
13448 %    o exception: return any errors or warnings in this structure.
13449 %
13450 */
13451 static MagickBooleanType XTrimImage(Display *display,
13452   XResourceInfo *resource_info,XWindows *windows,Image *image,
13453   ExceptionInfo *exception)
13454 {
13455   RectangleInfo
13456     trim_info;
13457
13458   register int
13459     x,
13460     y;
13461
13462   size_t
13463     background,
13464     pixel;
13465
13466   /*
13467     Trim edges from image.
13468   */
13469   XSetCursorState(display,windows,MagickTrue);
13470   XCheckRefreshWindows(display,windows);
13471   /*
13472     Crop the left edge.
13473   */
13474   background=XGetPixel(windows->image.ximage,0,0);
13475   trim_info.width=(size_t) windows->image.ximage->width;
13476   for (x=0; x < windows->image.ximage->width; x++)
13477   {
13478     for (y=0; y < windows->image.ximage->height; y++)
13479     {
13480       pixel=XGetPixel(windows->image.ximage,x,y);
13481       if (pixel != background)
13482         break;
13483     }
13484     if (y < windows->image.ximage->height)
13485       break;
13486   }
13487   trim_info.x=(ssize_t) x;
13488   if (trim_info.x == (ssize_t) windows->image.ximage->width)
13489     {
13490       XSetCursorState(display,windows,MagickFalse);
13491       return(MagickFalse);
13492     }
13493   /*
13494     Crop the right edge.
13495   */
13496   background=XGetPixel(windows->image.ximage,windows->image.ximage->width-1,0);
13497   for (x=windows->image.ximage->width-1; x != 0; x--)
13498   {
13499     for (y=0; y < windows->image.ximage->height; y++)
13500     {
13501       pixel=XGetPixel(windows->image.ximage,x,y);
13502       if (pixel != background)
13503         break;
13504     }
13505     if (y < windows->image.ximage->height)
13506       break;
13507   }
13508   trim_info.width=(size_t) (x-trim_info.x+1);
13509   /*
13510     Crop the top edge.
13511   */
13512   background=XGetPixel(windows->image.ximage,0,0);
13513   trim_info.height=(size_t) windows->image.ximage->height;
13514   for (y=0; y < windows->image.ximage->height; y++)
13515   {
13516     for (x=0; x < windows->image.ximage->width; x++)
13517     {
13518       pixel=XGetPixel(windows->image.ximage,x,y);
13519       if (pixel != background)
13520         break;
13521     }
13522     if (x < windows->image.ximage->width)
13523       break;
13524   }
13525   trim_info.y=(ssize_t) y;
13526   /*
13527     Crop the bottom edge.
13528   */
13529   background=XGetPixel(windows->image.ximage,0,windows->image.ximage->height-1);
13530   for (y=windows->image.ximage->height-1; y != 0; y--)
13531   {
13532     for (x=0; x < windows->image.ximage->width; x++)
13533     {
13534       pixel=XGetPixel(windows->image.ximage,x,y);
13535       if (pixel != background)
13536         break;
13537     }
13538     if (x < windows->image.ximage->width)
13539       break;
13540   }
13541   trim_info.height=(size_t) y-trim_info.y+1;
13542   if (((unsigned int) trim_info.width != windows->image.width) ||
13543       ((unsigned int) trim_info.height != windows->image.height))
13544     {
13545       /*
13546         Reconfigure Image window as defined by the trimming rectangle.
13547       */
13548       XSetCropGeometry(display,windows,&trim_info,image);
13549       windows->image.window_changes.width=(int) trim_info.width;
13550       windows->image.window_changes.height=(int) trim_info.height;
13551       (void) XConfigureImage(display,resource_info,windows,image,exception);
13552     }
13553   XSetCursorState(display,windows,MagickFalse);
13554   return(MagickTrue);
13555 }
13556 \f
13557 /*
13558 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13559 %                                                                             %
13560 %                                                                             %
13561 %                                                                             %
13562 +   X V i s u a l D i r e c t o r y I m a g e                                 %
13563 %                                                                             %
13564 %                                                                             %
13565 %                                                                             %
13566 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13567 %
13568 %  XVisualDirectoryImage() creates a Visual Image Directory.
13569 %
13570 %  The format of the XVisualDirectoryImage method is:
13571 %
13572 %      Image *XVisualDirectoryImage(Display *display,
13573 %        XResourceInfo *resource_info,XWindows *windows,
13574 %        ExceptionInfo *exception)
13575 %
13576 %  A description of each parameter follows:
13577 %
13578 %    o display: Specifies a connection to an X server; returned from
13579 %      XOpenDisplay.
13580 %
13581 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13582 %
13583 %    o windows: Specifies a pointer to a XWindows structure.
13584 %
13585 %    o exception: return any errors or warnings in this structure.
13586 %
13587 */
13588 static Image *XVisualDirectoryImage(Display *display,
13589   XResourceInfo *resource_info,XWindows *windows,ExceptionInfo *exception)
13590 {
13591 #define TileImageTag  "Scale/Image"
13592 #define XClientName  "montage"
13593
13594   char
13595     **filelist;
13596
13597   Image
13598     *images,
13599     *montage_image,
13600     *next_image,
13601     *thumbnail_image;
13602
13603   ImageInfo
13604     *read_info;
13605
13606   int
13607     number_files;
13608
13609   MagickBooleanType
13610     backdrop;
13611
13612   MagickStatusType
13613     status;
13614
13615   MontageInfo
13616     *montage_info;
13617
13618   RectangleInfo
13619     geometry;
13620
13621   register int
13622     i;
13623
13624   static char
13625     filename[MaxTextExtent] = "\0",
13626     filenames[MaxTextExtent] = "*";
13627
13628   XResourceInfo
13629     background_resources;
13630
13631   /*
13632     Request file name from user.
13633   */
13634   XFileBrowserWidget(display,windows,"Directory",filenames);
13635   if (*filenames == '\0')
13636     return((Image *) NULL);
13637   /*
13638     Expand the filenames.
13639   */
13640   filelist=(char **) AcquireMagickMemory(sizeof(*filelist));
13641   if (filelist == (char **) NULL)
13642     {
13643       ThrowXWindowFatalException(ResourceLimitError,"MemoryAllocationFailed",
13644         filenames);
13645       return((Image *) NULL);
13646     }
13647   number_files=1;
13648   filelist[0]=filenames;
13649   status=ExpandFilenames(&number_files,&filelist);
13650   if ((status == MagickFalse) || (number_files == 0))
13651     {
13652       if (number_files == 0)
13653         ThrowXWindowFatalException(ImageError,"NoImagesWereFound",filenames)
13654       else
13655         ThrowXWindowFatalException(ResourceLimitError,"MemoryAllocationFailed",
13656           filenames);
13657       return((Image *) NULL);
13658     }
13659   /*
13660     Set image background resources.
13661   */
13662   background_resources=(*resource_info);
13663   background_resources.window_id=AcquireString("");
13664   (void) FormatLocaleString(background_resources.window_id,MaxTextExtent,
13665     "0x%lx",windows->image.id);
13666   background_resources.backdrop=MagickTrue;
13667   /*
13668     Read each image and convert them to a tile.
13669   */
13670   backdrop=(windows->visual_info->klass == TrueColor) ||
13671     (windows->visual_info->klass == DirectColor) ? MagickTrue : MagickFalse;
13672   read_info=CloneImageInfo(resource_info->image_info);
13673   (void) SetImageOption(read_info,"jpeg:size","120x120");
13674   (void) CloneString(&read_info->size,DefaultTileGeometry);
13675   (void) SetImageInfoProgressMonitor(read_info,(MagickProgressMonitor) NULL,
13676     (void *) NULL);
13677   images=NewImageList();
13678   XSetCursorState(display,windows,MagickTrue);
13679   XCheckRefreshWindows(display,windows);
13680   for (i=0; i < (int) number_files; i++)
13681   {
13682     (void) CopyMagickString(read_info->filename,filelist[i],MaxTextExtent);
13683     filelist[i]=DestroyString(filelist[i]);
13684     *read_info->magick='\0';
13685     next_image=ReadImage(read_info,exception);
13686     CatchException(exception);
13687     if (next_image != (Image *) NULL)
13688       {
13689         (void) DeleteImageProperty(next_image,"label");
13690         (void) SetImageProperty(next_image,"label",InterpretImageProperties(
13691           read_info,next_image,DefaultTileLabel,exception),exception);
13692         (void) ParseRegionGeometry(next_image,read_info->size,&geometry,
13693           exception);
13694         thumbnail_image=ThumbnailImage(next_image,geometry.width,
13695           geometry.height,exception);
13696         if (thumbnail_image != (Image *) NULL)
13697           {
13698             next_image=DestroyImage(next_image);
13699             next_image=thumbnail_image;
13700           }
13701         if (backdrop)
13702           {
13703             (void) XDisplayBackgroundImage(display,&background_resources,
13704               next_image,exception);
13705             XSetCursorState(display,windows,MagickTrue);
13706           }
13707         AppendImageToList(&images,next_image);
13708         if (images->progress_monitor != (MagickProgressMonitor) NULL)
13709           {
13710             MagickBooleanType
13711               proceed;
13712
13713             proceed=SetImageProgress(images,LoadImageTag,(MagickOffsetType) i,
13714               (MagickSizeType) number_files);
13715             if (proceed == MagickFalse)
13716               break;
13717           }
13718       }
13719   }
13720   filelist=(char **) RelinquishMagickMemory(filelist);
13721   if (images == (Image *) NULL)
13722     {
13723       read_info=DestroyImageInfo(read_info);
13724       XSetCursorState(display,windows,MagickFalse);
13725       ThrowXWindowFatalException(ImageError,"NoImagesWereLoaded",filenames);
13726       return((Image *) NULL);
13727     }
13728   /*
13729     Create the Visual Image Directory.
13730   */
13731   montage_info=CloneMontageInfo(read_info,(MontageInfo *) NULL);
13732   montage_info->pointsize=10;
13733   if (resource_info->font != (char *) NULL)
13734     (void) CloneString(&montage_info->font,resource_info->font);
13735   (void) CopyMagickString(montage_info->filename,filename,MaxTextExtent);
13736   montage_image=MontageImageList(read_info,montage_info,GetFirstImageInList(
13737     images),exception);
13738   images=DestroyImageList(images);
13739   montage_info=DestroyMontageInfo(montage_info);
13740   read_info=DestroyImageInfo(read_info);
13741   XSetCursorState(display,windows,MagickFalse);
13742   if (montage_image == (Image *) NULL)
13743     return(montage_image);
13744   XClientMessage(display,windows->image.id,windows->im_protocols,
13745     windows->im_next_image,CurrentTime);
13746   return(montage_image);
13747 }
13748 \f
13749 /*
13750 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13751 %                                                                             %
13752 %                                                                             %
13753 %                                                                             %
13754 %   X D i s p l a y B a c k g r o u n d I m a g e                             %
13755 %                                                                             %
13756 %                                                                             %
13757 %                                                                             %
13758 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13759 %
13760 %  XDisplayBackgroundImage() displays an image in the background of a window.
13761 %
13762 %  The format of the XDisplayBackgroundImage method is:
13763 %
13764 %      MagickBooleanType XDisplayBackgroundImage(Display *display,
13765 %        XResourceInfo *resource_info,Image *image,ExceptionInfo *exception)
13766 %
13767 %  A description of each parameter follows:
13768 %
13769 %    o display: Specifies a connection to an X server;  returned from
13770 %      XOpenDisplay.
13771 %
13772 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13773 %
13774 %    o image: the image.
13775 %
13776 %    o exception: return any errors or warnings in this structure.
13777 %
13778 */
13779 MagickExport MagickBooleanType XDisplayBackgroundImage(Display *display,
13780   XResourceInfo *resource_info,Image *image,ExceptionInfo *exception)
13781 {
13782   char
13783     geometry[MaxTextExtent],
13784     visual_type[MaxTextExtent];
13785
13786   int
13787     height,
13788     status,
13789     width;
13790
13791   RectangleInfo
13792     geometry_info;
13793
13794   static XPixelInfo
13795     pixel;
13796
13797   static XStandardColormap
13798     *map_info;
13799
13800   static XVisualInfo
13801     *visual_info = (XVisualInfo *) NULL;
13802
13803   static XWindowInfo
13804     window_info;
13805
13806   size_t
13807     delay;
13808
13809   Window
13810     root_window;
13811
13812   XGCValues
13813     context_values;
13814
13815   XResourceInfo
13816     resources;
13817
13818   XWindowAttributes
13819     window_attributes;
13820
13821   /*
13822     Determine target window.
13823   */
13824   assert(image != (Image *) NULL);
13825   assert(image->signature == MagickSignature);
13826   if (image->debug != MagickFalse)
13827     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
13828   resources=(*resource_info);
13829   window_info.id=(Window) NULL;
13830   root_window=XRootWindow(display,XDefaultScreen(display));
13831   if (LocaleCompare(resources.window_id,"root") == 0)
13832     window_info.id=root_window;
13833   else
13834     {
13835       if (isdigit((unsigned char) *resources.window_id) != 0)
13836         window_info.id=XWindowByID(display,root_window,
13837           (Window) strtol((char *) resources.window_id,(char **) NULL,0));
13838       if (window_info.id == (Window) NULL)
13839         window_info.id=XWindowByName(display,root_window,resources.window_id);
13840     }
13841   if (window_info.id == (Window) NULL)
13842     {
13843       ThrowXWindowFatalException(XServerError,"NoWindowWithSpecifiedIDExists",
13844         resources.window_id);
13845       return(MagickFalse);
13846     }
13847   /*
13848     Determine window visual id.
13849   */
13850   window_attributes.width=XDisplayWidth(display,XDefaultScreen(display));
13851   window_attributes.height=XDisplayHeight(display,XDefaultScreen(display));
13852   (void) CopyMagickString(visual_type,"default",MaxTextExtent);
13853   status=XGetWindowAttributes(display,window_info.id,&window_attributes);
13854   if (status != 0)
13855     (void) FormatLocaleString(visual_type,MaxTextExtent,"0x%lx",
13856       XVisualIDFromVisual(window_attributes.visual));
13857   if (visual_info == (XVisualInfo *) NULL)
13858     {
13859       /*
13860         Allocate standard colormap.
13861       */
13862       map_info=XAllocStandardColormap();
13863       if (map_info == (XStandardColormap *) NULL)
13864         ThrowXWindowFatalException(XServerFatalError,"MemoryAllocationFailed",
13865           image->filename);
13866       map_info->colormap=(Colormap) NULL;
13867       pixel.pixels=(unsigned long *) NULL;
13868       /*
13869         Initialize visual info.
13870       */
13871       resources.map_type=(char *) NULL;
13872       resources.visual_type=visual_type;
13873       visual_info=XBestVisualInfo(display,map_info,&resources);
13874       if (visual_info == (XVisualInfo *) NULL)
13875         ThrowXWindowFatalException(XServerFatalError,"UnableToGetVisual",
13876           resources.visual_type);
13877       /*
13878         Initialize window info.
13879       */
13880       window_info.ximage=(XImage *) NULL;
13881       window_info.matte_image=(XImage *) NULL;
13882       window_info.pixmap=(Pixmap) NULL;
13883       window_info.matte_pixmap=(Pixmap) NULL;
13884     }
13885   /*
13886     Free previous root colors.
13887   */
13888   if (window_info.id == root_window)
13889     (void) XDestroyWindowColors(display,root_window);
13890   /*
13891     Initialize Standard Colormap.
13892   */
13893   resources.colormap=SharedColormap;
13894   XMakeStandardColormap(display,visual_info,&resources,image,map_info,&pixel);
13895   /*
13896     Graphic context superclass.
13897   */
13898   context_values.background=pixel.background_color.pixel;
13899   context_values.foreground=pixel.foreground_color.pixel;
13900   pixel.annotate_context=XCreateGC(display,window_info.id,
13901     (size_t) (GCBackground | GCForeground),&context_values);
13902   if (pixel.annotate_context == (GC) NULL)
13903     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
13904       image->filename);
13905   /*
13906     Initialize Image window attributes.
13907   */
13908   window_info.name=AcquireString("\0");
13909   window_info.icon_name=AcquireString("\0");
13910   XGetWindowInfo(display,visual_info,map_info,&pixel,(XFontStruct *) NULL,
13911     &resources,&window_info);
13912   /*
13913     Create the X image.
13914   */
13915   window_info.width=(unsigned int) image->columns;
13916   window_info.height=(unsigned int) image->rows;
13917   if ((image->columns != window_info.width) ||
13918       (image->rows != window_info.height))
13919     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13920       image->filename);
13921   (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>",
13922     window_attributes.width,window_attributes.height);
13923   geometry_info.width=window_info.width;
13924   geometry_info.height=window_info.height;
13925   geometry_info.x=(ssize_t) window_info.x;
13926   geometry_info.y=(ssize_t) window_info.y;
13927   (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
13928     &geometry_info.width,&geometry_info.height);
13929   window_info.width=(unsigned int) geometry_info.width;
13930   window_info.height=(unsigned int) geometry_info.height;
13931   window_info.x=(int) geometry_info.x;
13932   window_info.y=(int) geometry_info.y;
13933   status=XMakeImage(display,&resources,&window_info,image,window_info.width,
13934     window_info.height,exception);
13935   if (status == MagickFalse)
13936     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13937       image->filename);
13938   window_info.x=0;
13939   window_info.y=0;
13940   if (image->debug != MagickFalse)
13941     {
13942       (void) LogMagickEvent(X11Event,GetMagickModule(),
13943         "Image: %s[%.20g] %.20gx%.20g ",image->filename,(double) image->scene,
13944         (double) image->columns,(double) image->rows);
13945       if (image->colors != 0)
13946         (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
13947           image->colors);
13948       (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",image->magick);
13949     }
13950   /*
13951     Adjust image dimensions as specified by backdrop or geometry options.
13952   */
13953   width=(int) window_info.width;
13954   height=(int) window_info.height;
13955   if (resources.backdrop != MagickFalse)
13956     {
13957       /*
13958         Center image on window.
13959       */
13960       window_info.x=(window_attributes.width/2)-
13961         (window_info.ximage->width/2);
13962       window_info.y=(window_attributes.height/2)-
13963         (window_info.ximage->height/2);
13964       width=window_attributes.width;
13965       height=window_attributes.height;
13966     }
13967   if ((resources.image_geometry != (char *) NULL) &&
13968       (*resources.image_geometry != '\0'))
13969     {
13970       char
13971         default_geometry[MaxTextExtent];
13972
13973       int
13974         flags,
13975         gravity;
13976
13977       XSizeHints
13978         *size_hints;
13979
13980       /*
13981         User specified geometry.
13982       */
13983       size_hints=XAllocSizeHints();
13984       if (size_hints == (XSizeHints *) NULL)
13985         ThrowXWindowFatalException(ResourceLimitFatalError,
13986           "MemoryAllocationFailed",image->filename);
13987       size_hints->flags=0L;
13988       (void) FormatLocaleString(default_geometry,MaxTextExtent,"%dx%d",
13989         width,height);
13990       flags=XWMGeometry(display,visual_info->screen,resources.image_geometry,
13991         default_geometry,window_info.border_width,size_hints,&window_info.x,
13992         &window_info.y,&width,&height,&gravity);
13993       if (flags & (XValue | YValue))
13994         {
13995           width=window_attributes.width;
13996           height=window_attributes.height;
13997         }
13998       (void) XFree((void *) size_hints);
13999     }
14000   /*
14001     Create the X pixmap.
14002   */
14003   window_info.pixmap=XCreatePixmap(display,window_info.id,(unsigned int) width,
14004     (unsigned int) height,window_info.depth);
14005   if (window_info.pixmap == (Pixmap) NULL)
14006     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXPixmap",
14007       image->filename);
14008   /*
14009     Display pixmap on the window.
14010   */
14011   if (((unsigned int) width > window_info.width) ||
14012       ((unsigned int) height > window_info.height))
14013     (void) XFillRectangle(display,window_info.pixmap,
14014       window_info.annotate_context,0,0,(unsigned int) width,
14015       (unsigned int) height);
14016   (void) XPutImage(display,window_info.pixmap,window_info.annotate_context,
14017     window_info.ximage,0,0,window_info.x,window_info.y,(unsigned int)
14018     window_info.width,(unsigned int) window_info.height);
14019   (void) XSetWindowBackgroundPixmap(display,window_info.id,window_info.pixmap);
14020   (void) XClearWindow(display,window_info.id);
14021   delay=1000*image->delay/MagickMax(image->ticks_per_second,1L);
14022   XDelay(display,delay == 0UL ? 10UL : delay);
14023   (void) XSync(display,MagickFalse);
14024   return(window_info.id == root_window ? MagickTrue : MagickFalse);
14025 }
14026 \f
14027 /*
14028 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
14029 %                                                                             %
14030 %                                                                             %
14031 %                                                                             %
14032 +   X D i s p l a y I m a g e                                                 %
14033 %                                                                             %
14034 %                                                                             %
14035 %                                                                             %
14036 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
14037 %
14038 %  XDisplayImage() displays an image via X11.  A new image is created and
14039 %  returned if the user interactively transforms the displayed image.
14040 %
14041 %  The format of the XDisplayImage method is:
14042 %
14043 %      Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
14044 %        char **argv,int argc,Image **image,size_t *state,
14045 %        ExceptionInfo *exception)
14046 %
14047 %  A description of each parameter follows:
14048 %
14049 %    o nexus:  Method XDisplayImage returns an image when the
14050 %      user chooses 'Open Image' from the command menu or picks a tile
14051 %      from the image directory.  Otherwise a null image is returned.
14052 %
14053 %    o display: Specifies a connection to an X server;  returned from
14054 %      XOpenDisplay.
14055 %
14056 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
14057 %
14058 %    o argv: Specifies the application's argument list.
14059 %
14060 %    o argc: Specifies the number of arguments.
14061 %
14062 %    o image: Specifies an address to an address of an Image structure;
14063 %
14064 %    o exception: return any errors or warnings in this structure.
14065 %
14066 */
14067 MagickExport Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
14068   char **argv,int argc,Image **image,size_t *state,ExceptionInfo *exception)
14069 {
14070 #define MagnifySize  256  /* must be a power of 2 */
14071 #define MagickMenus  10
14072 #define MagickTitle  "Commands"
14073
14074   static const char
14075     *CommandMenu[] =
14076     {
14077       "File",
14078       "Edit",
14079       "View",
14080       "Transform",
14081       "Enhance",
14082       "Effects",
14083       "F/X",
14084       "Image Edit",
14085       "Miscellany",
14086       "Help",
14087       (char *) NULL
14088     },
14089     *FileMenu[] =
14090     {
14091       "Open...",
14092       "Next",
14093       "Former",
14094       "Select...",
14095       "Save...",
14096       "Print...",
14097       "Delete...",
14098       "New...",
14099       "Visual Directory...",
14100       "Quit",
14101       (char *) NULL
14102     },
14103     *EditMenu[] =
14104     {
14105       "Undo",
14106       "Redo",
14107       "Cut",
14108       "Copy",
14109       "Paste",
14110       (char *) NULL
14111     },
14112     *ViewMenu[] =
14113     {
14114       "Half Size",
14115       "Original Size",
14116       "Double Size",
14117       "Resize...",
14118       "Apply",
14119       "Refresh",
14120       "Restore",
14121       (char *) NULL
14122     },
14123     *TransformMenu[] =
14124     {
14125       "Crop",
14126       "Chop",
14127       "Flop",
14128       "Flip",
14129       "Rotate Right",
14130       "Rotate Left",
14131       "Rotate...",
14132       "Shear...",
14133       "Roll...",
14134       "Trim Edges",
14135       (char *) NULL
14136     },
14137     *EnhanceMenu[] =
14138     {
14139       "Hue...",
14140       "Saturation...",
14141       "Brightness...",
14142       "Gamma...",
14143       "Spiff",
14144       "Dull",
14145       "Contrast Stretch...",
14146       "Sigmoidal Contrast...",
14147       "Normalize",
14148       "Equalize",
14149       "Negate",
14150       "Grayscale",
14151       "Map...",
14152       "Quantize...",
14153       (char *) NULL
14154     },
14155     *EffectsMenu[] =
14156     {
14157       "Despeckle",
14158       "Emboss",
14159       "Reduce Noise",
14160       "Add Noise...",
14161       "Sharpen...",
14162       "Blur...",
14163       "Threshold...",
14164       "Edge Detect...",
14165       "Spread...",
14166       "Shade...",
14167       "Raise...",
14168       "Segment...",
14169       (char *) NULL
14170     },
14171     *FXMenu[] =
14172     {
14173       "Solarize...",
14174       "Sepia Tone...",
14175       "Swirl...",
14176       "Implode...",
14177       "Vignette...",
14178       "Wave...",
14179       "Oil Paint...",
14180       "Charcoal Draw...",
14181       (char *) NULL
14182     },
14183     *ImageEditMenu[] =
14184     {
14185       "Annotate...",
14186       "Draw...",
14187       "Color...",
14188       "Matte...",
14189       "Composite...",
14190       "Add Border...",
14191       "Add Frame...",
14192       "Comment...",
14193       "Launch...",
14194       "Region of Interest...",
14195       (char *) NULL
14196     },
14197     *MiscellanyMenu[] =
14198     {
14199       "Image Info",
14200       "Zoom Image",
14201       "Show Preview...",
14202       "Show Histogram",
14203       "Show Matte",
14204       "Background...",
14205       "Slide Show...",
14206       "Preferences...",
14207       (char *) NULL
14208     },
14209     *HelpMenu[] =
14210     {
14211       "Overview",
14212       "Browse Documentation",
14213       "About Display",
14214       (char *) NULL
14215     },
14216     *ShortCutsMenu[] =
14217     {
14218       "Next",
14219       "Former",
14220       "Open...",
14221       "Save...",
14222       "Print...",
14223       "Undo",
14224       "Restore",
14225       "Image Info",
14226       "Quit",
14227       (char *) NULL
14228     },
14229     *VirtualMenu[] =
14230     {
14231       "Image Info",
14232       "Print",
14233       "Next",
14234       "Quit",
14235       (char *) NULL
14236     };
14237
14238   static const char
14239     **Menus[MagickMenus] =
14240     {
14241       FileMenu,
14242       EditMenu,
14243       ViewMenu,
14244       TransformMenu,
14245       EnhanceMenu,
14246       EffectsMenu,
14247       FXMenu,
14248       ImageEditMenu,
14249       MiscellanyMenu,
14250       HelpMenu
14251     };
14252
14253   static CommandType
14254     CommandMenus[] =
14255     {
14256       NullCommand,
14257       NullCommand,
14258       NullCommand,
14259       NullCommand,
14260       NullCommand,
14261       NullCommand,
14262       NullCommand,
14263       NullCommand,
14264       NullCommand,
14265       NullCommand,
14266     },
14267     FileCommands[] =
14268     {
14269       OpenCommand,
14270       NextCommand,
14271       FormerCommand,
14272       SelectCommand,
14273       SaveCommand,
14274       PrintCommand,
14275       DeleteCommand,
14276       NewCommand,
14277       VisualDirectoryCommand,
14278       QuitCommand
14279     },
14280     EditCommands[] =
14281     {
14282       UndoCommand,
14283       RedoCommand,
14284       CutCommand,
14285       CopyCommand,
14286       PasteCommand
14287     },
14288     ViewCommands[] =
14289     {
14290       HalfSizeCommand,
14291       OriginalSizeCommand,
14292       DoubleSizeCommand,
14293       ResizeCommand,
14294       ApplyCommand,
14295       RefreshCommand,
14296       RestoreCommand
14297     },
14298     TransformCommands[] =
14299     {
14300       CropCommand,
14301       ChopCommand,
14302       FlopCommand,
14303       FlipCommand,
14304       RotateRightCommand,
14305       RotateLeftCommand,
14306       RotateCommand,
14307       ShearCommand,
14308       RollCommand,
14309       TrimCommand
14310     },
14311     EnhanceCommands[] =
14312     {
14313       HueCommand,
14314       SaturationCommand,
14315       BrightnessCommand,
14316       GammaCommand,
14317       SpiffCommand,
14318       DullCommand,
14319       ContrastStretchCommand,
14320       SigmoidalContrastCommand,
14321       NormalizeCommand,
14322       EqualizeCommand,
14323       NegateCommand,
14324       GrayscaleCommand,
14325       MapCommand,
14326       QuantizeCommand
14327     },
14328     EffectsCommands[] =
14329     {
14330       DespeckleCommand,
14331       EmbossCommand,
14332       ReduceNoiseCommand,
14333       AddNoiseCommand,
14334       SharpenCommand,
14335       BlurCommand,
14336       ThresholdCommand,
14337       EdgeDetectCommand,
14338       SpreadCommand,
14339       ShadeCommand,
14340       RaiseCommand,
14341       SegmentCommand
14342     },
14343     FXCommands[] =
14344     {
14345       SolarizeCommand,
14346       SepiaToneCommand,
14347       SwirlCommand,
14348       ImplodeCommand,
14349       VignetteCommand,
14350       WaveCommand,
14351       OilPaintCommand,
14352       CharcoalDrawCommand
14353     },
14354     ImageEditCommands[] =
14355     {
14356       AnnotateCommand,
14357       DrawCommand,
14358       ColorCommand,
14359       MatteCommand,
14360       CompositeCommand,
14361       AddBorderCommand,
14362       AddFrameCommand,
14363       CommentCommand,
14364       LaunchCommand,
14365       RegionofInterestCommand
14366     },
14367     MiscellanyCommands[] =
14368     {
14369       InfoCommand,
14370       ZoomCommand,
14371       ShowPreviewCommand,
14372       ShowHistogramCommand,
14373       ShowMatteCommand,
14374       BackgroundCommand,
14375       SlideShowCommand,
14376       PreferencesCommand
14377     },
14378     HelpCommands[] =
14379     {
14380       HelpCommand,
14381       BrowseDocumentationCommand,
14382       VersionCommand
14383     },
14384     ShortCutsCommands[] =
14385     {
14386       NextCommand,
14387       FormerCommand,
14388       OpenCommand,
14389       SaveCommand,
14390       PrintCommand,
14391       UndoCommand,
14392       RestoreCommand,
14393       InfoCommand,
14394       QuitCommand
14395     },
14396     VirtualCommands[] =
14397     {
14398       InfoCommand,
14399       PrintCommand,
14400       NextCommand,
14401       QuitCommand
14402     };
14403
14404   static CommandType
14405     *Commands[MagickMenus] =
14406     {
14407       FileCommands,
14408       EditCommands,
14409       ViewCommands,
14410       TransformCommands,
14411       EnhanceCommands,
14412       EffectsCommands,
14413       FXCommands,
14414       ImageEditCommands,
14415       MiscellanyCommands,
14416       HelpCommands
14417     };
14418
14419   char
14420     command[MaxTextExtent],
14421     *directory,
14422     geometry[MaxTextExtent],
14423     resource_name[MaxTextExtent];
14424
14425   CommandType
14426     command_type;
14427
14428   Image
14429     *display_image,
14430     *nexus;
14431
14432   int
14433     entry,
14434     id;
14435
14436   KeySym
14437     key_symbol;
14438
14439   MagickStatusType
14440     context_mask,
14441     status;
14442
14443   RectangleInfo
14444     geometry_info;
14445
14446   register int
14447     i;
14448
14449   static char
14450     working_directory[MaxTextExtent];
14451
14452   static XPoint
14453     vid_info;
14454
14455   static XWindowInfo
14456     *magick_windows[MaxXWindows];
14457
14458   static unsigned int
14459     number_windows;
14460
14461   struct stat
14462     attributes;
14463
14464   time_t
14465     timer,
14466     timestamp,
14467     update_time;
14468
14469   unsigned int
14470     height,
14471     width;
14472
14473   size_t
14474     delay;
14475
14476   WarningHandler
14477     warning_handler;
14478
14479   Window
14480     root_window;
14481
14482   XClassHint
14483     *class_hints;
14484
14485   XEvent
14486     event;
14487
14488   XFontStruct
14489     *font_info;
14490
14491   XGCValues
14492     context_values;
14493
14494   XPixelInfo
14495     *icon_pixel,
14496     *pixel;
14497
14498   XResourceInfo
14499     *icon_resources;
14500
14501   XStandardColormap
14502     *icon_map,
14503     *map_info;
14504
14505   XVisualInfo
14506     *icon_visual,
14507     *visual_info;
14508
14509   XWindowChanges
14510     window_changes;
14511
14512   XWindows
14513     *windows;
14514
14515   XWMHints
14516     *manager_hints;
14517
14518   assert(image != (Image **) NULL);
14519   assert((*image)->signature == MagickSignature);
14520   if ((*image)->debug != MagickFalse)
14521     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*image)->filename);
14522   display_image=(*image);
14523   warning_handler=(WarningHandler) NULL;
14524   windows=XSetWindows((XWindows *) ~0);
14525   if (windows != (XWindows *) NULL)
14526     {
14527       int
14528         status;
14529
14530       status=chdir(working_directory);
14531       if (status == -1)
14532         (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
14533           "UnableToOpenFile","%s",working_directory);
14534       warning_handler=resource_info->display_warnings ?
14535         SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
14536       warning_handler=resource_info->display_warnings ?
14537         SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
14538     }
14539   else
14540     {
14541       /*
14542         Allocate windows structure.
14543       */
14544       resource_info->colors=display_image->colors;
14545       windows=XSetWindows(XInitializeWindows(display,resource_info));
14546       if (windows == (XWindows *) NULL)
14547         ThrowXWindowFatalException(XServerFatalError,"UnableToCreateWindow",
14548           (*image)->filename);
14549       /*
14550         Initialize window id's.
14551       */
14552       number_windows=0;
14553       magick_windows[number_windows++]=(&windows->icon);
14554       magick_windows[number_windows++]=(&windows->backdrop);
14555       magick_windows[number_windows++]=(&windows->image);
14556       magick_windows[number_windows++]=(&windows->info);
14557       magick_windows[number_windows++]=(&windows->command);
14558       magick_windows[number_windows++]=(&windows->widget);
14559       magick_windows[number_windows++]=(&windows->popup);
14560       magick_windows[number_windows++]=(&windows->magnify);
14561       magick_windows[number_windows++]=(&windows->pan);
14562       for (i=0; i < (int) number_windows; i++)
14563         magick_windows[i]->id=(Window) NULL;
14564       vid_info.x=0;
14565       vid_info.y=0;
14566     }
14567   /*
14568     Initialize font info.
14569   */
14570   if (windows->font_info != (XFontStruct *) NULL)
14571     (void) XFreeFont(display,windows->font_info);
14572   windows->font_info=XBestFont(display,resource_info,MagickFalse);
14573   if (windows->font_info == (XFontStruct *) NULL)
14574     ThrowXWindowFatalException(XServerFatalError,"UnableToLoadFont",
14575       resource_info->font);
14576   /*
14577     Initialize Standard Colormap.
14578   */
14579   map_info=windows->map_info;
14580   icon_map=windows->icon_map;
14581   visual_info=windows->visual_info;
14582   icon_visual=windows->icon_visual;
14583   pixel=windows->pixel_info;
14584   icon_pixel=windows->icon_pixel;
14585   font_info=windows->font_info;
14586   icon_resources=windows->icon_resources;
14587   class_hints=windows->class_hints;
14588   manager_hints=windows->manager_hints;
14589   root_window=XRootWindow(display,visual_info->screen);
14590   nexus=NewImageList();
14591   if (display_image->debug != MagickFalse)
14592     {
14593       (void) LogMagickEvent(X11Event,GetMagickModule(),
14594         "Image: %s[%.20g] %.20gx%.20g ",display_image->filename,
14595         (double) display_image->scene,(double) display_image->columns,
14596         (double) display_image->rows);
14597       if (display_image->colors != 0)
14598         (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
14599           display_image->colors);
14600       (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",
14601         display_image->magick);
14602     }
14603   XMakeStandardColormap(display,visual_info,resource_info,display_image,
14604     map_info,pixel);
14605   display_image->taint=MagickFalse;
14606   /*
14607     Initialize graphic context.
14608   */
14609   windows->context.id=(Window) NULL;
14610   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14611     resource_info,&windows->context);
14612   (void) CloneString(&class_hints->res_name,resource_info->client_name);
14613   (void) CloneString(&class_hints->res_class,resource_info->client_name);
14614   class_hints->res_class[0]=(char) toupper((int) class_hints->res_class[0]);
14615   manager_hints->flags=InputHint | StateHint;
14616   manager_hints->input=MagickFalse;
14617   manager_hints->initial_state=WithdrawnState;
14618   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14619     &windows->context);
14620   if (display_image->debug != MagickFalse)
14621     (void) LogMagickEvent(X11Event,GetMagickModule(),
14622       "Window id: 0x%lx (context)",windows->context.id);
14623   context_values.background=pixel->background_color.pixel;
14624   context_values.font=font_info->fid;
14625   context_values.foreground=pixel->foreground_color.pixel;
14626   context_values.graphics_exposures=MagickFalse;
14627   context_mask=(MagickStatusType)
14628     (GCBackground | GCFont | GCForeground | GCGraphicsExposures);
14629   if (pixel->annotate_context != (GC) NULL)
14630     (void) XFreeGC(display,pixel->annotate_context);
14631   pixel->annotate_context=XCreateGC(display,windows->context.id,
14632     context_mask,&context_values);
14633   if (pixel->annotate_context == (GC) NULL)
14634     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14635       display_image->filename);
14636   context_values.background=pixel->depth_color.pixel;
14637   if (pixel->widget_context != (GC) NULL)
14638     (void) XFreeGC(display,pixel->widget_context);
14639   pixel->widget_context=XCreateGC(display,windows->context.id,context_mask,
14640     &context_values);
14641   if (pixel->widget_context == (GC) NULL)
14642     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14643       display_image->filename);
14644   context_values.background=pixel->foreground_color.pixel;
14645   context_values.foreground=pixel->background_color.pixel;
14646   context_values.plane_mask=context_values.background ^
14647     context_values.foreground;
14648   if (pixel->highlight_context != (GC) NULL)
14649     (void) XFreeGC(display,pixel->highlight_context);
14650   pixel->highlight_context=XCreateGC(display,windows->context.id,
14651     (size_t) (context_mask | GCPlaneMask),&context_values);
14652   if (pixel->highlight_context == (GC) NULL)
14653     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14654       display_image->filename);
14655   (void) XDestroyWindow(display,windows->context.id);
14656   /*
14657     Initialize icon window.
14658   */
14659   XGetWindowInfo(display,icon_visual,icon_map,icon_pixel,(XFontStruct *) NULL,
14660     icon_resources,&windows->icon);
14661   windows->icon.geometry=resource_info->icon_geometry;
14662   XBestIconSize(display,&windows->icon,display_image);
14663   windows->icon.attributes.colormap=XDefaultColormap(display,
14664     icon_visual->screen);
14665   windows->icon.attributes.event_mask=ExposureMask | StructureNotifyMask;
14666   manager_hints->flags=InputHint | StateHint;
14667   manager_hints->input=MagickFalse;
14668   manager_hints->initial_state=IconicState;
14669   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14670     &windows->icon);
14671   if (display_image->debug != MagickFalse)
14672     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (icon)",
14673       windows->icon.id);
14674   /*
14675     Initialize graphic context for icon window.
14676   */
14677   if (icon_pixel->annotate_context != (GC) NULL)
14678     (void) XFreeGC(display,icon_pixel->annotate_context);
14679   context_values.background=icon_pixel->background_color.pixel;
14680   context_values.foreground=icon_pixel->foreground_color.pixel;
14681   icon_pixel->annotate_context=XCreateGC(display,windows->icon.id,
14682     (size_t) (GCBackground | GCForeground),&context_values);
14683   if (icon_pixel->annotate_context == (GC) NULL)
14684     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14685       display_image->filename);
14686   windows->icon.annotate_context=icon_pixel->annotate_context;
14687   /*
14688     Initialize Image window.
14689   */
14690   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14691     &windows->image);
14692   windows->image.shape=MagickTrue;  /* non-rectangular shape hint */
14693   if (resource_info->use_shared_memory == MagickFalse)
14694     windows->image.shared_memory=MagickFalse;
14695   if ((resource_info->title != (char *) NULL) && !(*state & MontageImageState))
14696     {
14697       char
14698         *title;
14699
14700       title=InterpretImageProperties(resource_info->image_info,display_image,
14701         resource_info->title,exception);
14702       (void) CopyMagickString(windows->image.name,title,MaxTextExtent);
14703       (void) CopyMagickString(windows->image.icon_name,title,MaxTextExtent);
14704       title=DestroyString(title);
14705     }
14706   else
14707     {
14708       char
14709         filename[MaxTextExtent];
14710
14711       /*
14712         Window name is the base of the filename.
14713       */
14714       GetPathComponent(display_image->magick_filename,TailPath,filename);
14715       if (display_image->scene == 0)
14716         (void) FormatLocaleString(windows->image.name,MaxTextExtent,
14717           "%s: %s",MagickPackageName,filename);
14718       else
14719         (void) FormatLocaleString(windows->image.name,MaxTextExtent,
14720           "%s: %s[scene: %.20g frames: %.20g]",MagickPackageName,filename,
14721           (double) display_image->scene,(double) GetImageListLength(
14722           display_image));
14723       (void) CopyMagickString(windows->image.icon_name,filename,MaxTextExtent);
14724     }
14725   if (resource_info->immutable)
14726     windows->image.immutable=MagickTrue;
14727   windows->image.use_pixmap=resource_info->use_pixmap;
14728   windows->image.geometry=resource_info->image_geometry;
14729   (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>!",
14730     XDisplayWidth(display,visual_info->screen),
14731     XDisplayHeight(display,visual_info->screen));
14732   geometry_info.width=display_image->columns;
14733   geometry_info.height=display_image->rows;
14734   geometry_info.x=0;
14735   geometry_info.y=0;
14736   (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
14737     &geometry_info.width,&geometry_info.height);
14738   windows->image.width=(unsigned int) geometry_info.width;
14739   windows->image.height=(unsigned int) geometry_info.height;
14740   windows->image.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14741     ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14742     KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14743     PropertyChangeMask | StructureNotifyMask | SubstructureNotifyMask;
14744   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14745     resource_info,&windows->backdrop);
14746   if ((resource_info->backdrop) || (windows->backdrop.id != (Window) NULL))
14747     {
14748       /*
14749         Initialize backdrop window.
14750       */
14751       windows->backdrop.x=0;
14752       windows->backdrop.y=0;
14753       (void) CloneString(&windows->backdrop.name,"Backdrop");
14754       windows->backdrop.flags=(size_t) (USSize | USPosition);
14755       windows->backdrop.width=(unsigned int)
14756         XDisplayWidth(display,visual_info->screen);
14757       windows->backdrop.height=(unsigned int)
14758         XDisplayHeight(display,visual_info->screen);
14759       windows->backdrop.border_width=0;
14760       windows->backdrop.immutable=MagickTrue;
14761       windows->backdrop.attributes.do_not_propagate_mask=ButtonPressMask |
14762         ButtonReleaseMask;
14763       windows->backdrop.attributes.event_mask=ButtonPressMask | KeyPressMask |
14764         StructureNotifyMask;
14765       manager_hints->flags=IconWindowHint | InputHint | StateHint;
14766       manager_hints->icon_window=windows->icon.id;
14767       manager_hints->input=MagickTrue;
14768       manager_hints->initial_state=resource_info->iconic ? IconicState :
14769         NormalState;
14770       XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14771         &windows->backdrop);
14772       if (display_image->debug != MagickFalse)
14773         (void) LogMagickEvent(X11Event,GetMagickModule(),
14774           "Window id: 0x%lx (backdrop)",windows->backdrop.id);
14775       (void) XMapWindow(display,windows->backdrop.id);
14776       (void) XClearWindow(display,windows->backdrop.id);
14777       if (windows->image.id != (Window) NULL)
14778         {
14779           (void) XDestroyWindow(display,windows->image.id);
14780           windows->image.id=(Window) NULL;
14781         }
14782       /*
14783         Position image in the center the backdrop.
14784       */
14785       windows->image.flags|=USPosition;
14786       windows->image.x=(XDisplayWidth(display,visual_info->screen)/2)-
14787         (windows->image.width/2);
14788       windows->image.y=(XDisplayHeight(display,visual_info->screen)/2)-
14789         (windows->image.height/2);
14790     }
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   if (windows->group_leader.id != (Window) NULL)
14797     {
14798       /*
14799         Follow the leader.
14800       */
14801       manager_hints->flags|=WindowGroupHint;
14802       manager_hints->window_group=windows->group_leader.id;
14803       (void) XSelectInput(display,windows->group_leader.id,StructureNotifyMask);
14804       if (display_image->debug != MagickFalse)
14805         (void) LogMagickEvent(X11Event,GetMagickModule(),
14806           "Window id: 0x%lx (group leader)",windows->group_leader.id);
14807     }
14808   XMakeWindow(display,
14809     (Window) (resource_info->backdrop ? windows->backdrop.id : root_window),
14810     argv,argc,class_hints,manager_hints,&windows->image);
14811   (void) XChangeProperty(display,windows->image.id,windows->im_protocols,
14812     XA_STRING,8,PropModeReplace,(unsigned char *) NULL,0);
14813   if (windows->group_leader.id != (Window) NULL)
14814     (void) XSetTransientForHint(display,windows->image.id,
14815       windows->group_leader.id);
14816   if (display_image->debug != MagickFalse)
14817     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (image)",
14818       windows->image.id);
14819   /*
14820     Initialize Info widget.
14821   */
14822   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14823     &windows->info);
14824   (void) CloneString(&windows->info.name,"Info");
14825   (void) CloneString(&windows->info.icon_name,"Info");
14826   windows->info.border_width=1;
14827   windows->info.x=2;
14828   windows->info.y=2;
14829   windows->info.flags|=PPosition;
14830   windows->info.attributes.win_gravity=UnmapGravity;
14831   windows->info.attributes.event_mask=ButtonPressMask | ExposureMask |
14832     StructureNotifyMask;
14833   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14834   manager_hints->input=MagickFalse;
14835   manager_hints->initial_state=NormalState;
14836   manager_hints->window_group=windows->image.id;
14837   XMakeWindow(display,windows->image.id,argv,argc,class_hints,manager_hints,
14838     &windows->info);
14839   windows->info.highlight_stipple=XCreateBitmapFromData(display,
14840     windows->info.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14841   windows->info.shadow_stipple=XCreateBitmapFromData(display,
14842     windows->info.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14843   (void) XSetTransientForHint(display,windows->info.id,windows->image.id);
14844   if (windows->image.mapped != MagickFalse)
14845     (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14846   if (display_image->debug != MagickFalse)
14847     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (info)",
14848       windows->info.id);
14849   /*
14850     Initialize Command widget.
14851   */
14852   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14853     resource_info,&windows->command);
14854   windows->command.data=MagickMenus;
14855   (void) XCommandWidget(display,windows,CommandMenu,(XEvent *) NULL);
14856   (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.command",
14857     resource_info->client_name);
14858   windows->command.geometry=XGetResourceClass(resource_info->resource_database,
14859     resource_name,"geometry",(char *) NULL);
14860   (void) CloneString(&windows->command.name,MagickTitle);
14861   windows->command.border_width=0;
14862   windows->command.flags|=PPosition;
14863   windows->command.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14864     ButtonReleaseMask | EnterWindowMask | ExposureMask | LeaveWindowMask |
14865     OwnerGrabButtonMask | StructureNotifyMask;
14866   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14867   manager_hints->input=MagickTrue;
14868   manager_hints->initial_state=NormalState;
14869   manager_hints->window_group=windows->image.id;
14870   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14871     &windows->command);
14872   windows->command.highlight_stipple=XCreateBitmapFromData(display,
14873     windows->command.id,(char *) HighlightBitmap,HighlightWidth,
14874     HighlightHeight);
14875   windows->command.shadow_stipple=XCreateBitmapFromData(display,
14876     windows->command.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14877   (void) XSetTransientForHint(display,windows->command.id,windows->image.id);
14878   if (windows->command.mapped != MagickFalse)
14879     (void) XMapRaised(display,windows->command.id);
14880   if (display_image->debug != MagickFalse)
14881     (void) LogMagickEvent(X11Event,GetMagickModule(),
14882       "Window id: 0x%lx (command)",windows->command.id);
14883   /*
14884     Initialize Widget window.
14885   */
14886   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14887     resource_info,&windows->widget);
14888   (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.widget",
14889     resource_info->client_name);
14890   windows->widget.geometry=XGetResourceClass(resource_info->resource_database,
14891     resource_name,"geometry",(char *) NULL);
14892   windows->widget.border_width=0;
14893   windows->widget.flags|=PPosition;
14894   windows->widget.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14895     ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14896     KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14897     StructureNotifyMask;
14898   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14899   manager_hints->input=MagickTrue;
14900   manager_hints->initial_state=NormalState;
14901   manager_hints->window_group=windows->image.id;
14902   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14903     &windows->widget);
14904   windows->widget.highlight_stipple=XCreateBitmapFromData(display,
14905     windows->widget.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14906   windows->widget.shadow_stipple=XCreateBitmapFromData(display,
14907     windows->widget.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14908   (void) XSetTransientForHint(display,windows->widget.id,windows->image.id);
14909   if (display_image->debug != MagickFalse)
14910     (void) LogMagickEvent(X11Event,GetMagickModule(),
14911       "Window id: 0x%lx (widget)",windows->widget.id);
14912   /*
14913     Initialize popup window.
14914   */
14915   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14916     resource_info,&windows->popup);
14917   windows->popup.border_width=0;
14918   windows->popup.flags|=PPosition;
14919   windows->popup.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14920     ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14921     KeyReleaseMask | LeaveWindowMask | StructureNotifyMask;
14922   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14923   manager_hints->input=MagickTrue;
14924   manager_hints->initial_state=NormalState;
14925   manager_hints->window_group=windows->image.id;
14926   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14927     &windows->popup);
14928   windows->popup.highlight_stipple=XCreateBitmapFromData(display,
14929     windows->popup.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14930   windows->popup.shadow_stipple=XCreateBitmapFromData(display,
14931     windows->popup.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14932   (void) XSetTransientForHint(display,windows->popup.id,windows->image.id);
14933   if (display_image->debug != MagickFalse)
14934     (void) LogMagickEvent(X11Event,GetMagickModule(),
14935       "Window id: 0x%lx (pop up)",windows->popup.id);
14936   /*
14937     Initialize Magnify window and cursor.
14938   */
14939   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14940     resource_info,&windows->magnify);
14941   if (resource_info->use_shared_memory == MagickFalse)
14942     windows->magnify.shared_memory=MagickFalse;
14943   (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.magnify",
14944     resource_info->client_name);
14945   windows->magnify.geometry=XGetResourceClass(resource_info->resource_database,
14946     resource_name,"geometry",(char *) NULL);
14947   (void) FormatLocaleString(windows->magnify.name,MaxTextExtent,"Magnify %uX",
14948     resource_info->magnify);
14949   if (windows->magnify.cursor != (Cursor) NULL)
14950     (void) XFreeCursor(display,windows->magnify.cursor);
14951   windows->magnify.cursor=XMakeCursor(display,windows->image.id,
14952     map_info->colormap,resource_info->background_color,
14953     resource_info->foreground_color);
14954   if (windows->magnify.cursor == (Cursor) NULL)
14955     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateCursor",
14956       display_image->filename);
14957   windows->magnify.width=MagnifySize;
14958   windows->magnify.height=MagnifySize;
14959   windows->magnify.flags|=PPosition;
14960   windows->magnify.min_width=MagnifySize;
14961   windows->magnify.min_height=MagnifySize;
14962   windows->magnify.width_inc=MagnifySize;
14963   windows->magnify.height_inc=MagnifySize;
14964   windows->magnify.data=resource_info->magnify;
14965   windows->magnify.attributes.cursor=windows->magnify.cursor;
14966   windows->magnify.attributes.event_mask=ButtonPressMask | ButtonReleaseMask |
14967     ExposureMask | KeyPressMask | KeyReleaseMask | OwnerGrabButtonMask |
14968     StructureNotifyMask;
14969   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14970   manager_hints->input=MagickTrue;
14971   manager_hints->initial_state=NormalState;
14972   manager_hints->window_group=windows->image.id;
14973   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14974     &windows->magnify);
14975   if (display_image->debug != MagickFalse)
14976     (void) LogMagickEvent(X11Event,GetMagickModule(),
14977       "Window id: 0x%lx (magnify)",windows->magnify.id);
14978   (void) XSetTransientForHint(display,windows->magnify.id,windows->image.id);
14979   /*
14980     Initialize panning window.
14981   */
14982   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14983     resource_info,&windows->pan);
14984   (void) CloneString(&windows->pan.name,"Pan Icon");
14985   windows->pan.width=windows->icon.width;
14986   windows->pan.height=windows->icon.height;
14987   (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.pan",
14988     resource_info->client_name);
14989   windows->pan.geometry=XGetResourceClass(resource_info->resource_database,
14990     resource_name,"geometry",(char *) NULL);
14991   (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
14992     &windows->pan.width,&windows->pan.height);
14993   windows->pan.flags|=PPosition;
14994   windows->pan.immutable=MagickTrue;
14995   windows->pan.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14996     ButtonReleaseMask | ExposureMask | KeyPressMask | KeyReleaseMask |
14997     StructureNotifyMask;
14998   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14999   manager_hints->input=MagickFalse;
15000   manager_hints->initial_state=NormalState;
15001   manager_hints->window_group=windows->image.id;
15002   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
15003     &windows->pan);
15004   if (display_image->debug != MagickFalse)
15005     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (pan)",
15006       windows->pan.id);
15007   (void) XSetTransientForHint(display,windows->pan.id,windows->image.id);
15008   if (windows->info.mapped != MagickFalse)
15009     (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15010   if ((windows->image.mapped == MagickFalse) ||
15011       (windows->backdrop.id != (Window) NULL))
15012     (void) XMapWindow(display,windows->image.id);
15013   /*
15014     Set our progress monitor and warning handlers.
15015   */
15016   if (warning_handler == (WarningHandler) NULL)
15017     {
15018       warning_handler=resource_info->display_warnings ?
15019         SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
15020       warning_handler=resource_info->display_warnings ?
15021         SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
15022     }
15023   /*
15024     Initialize Image and Magnify X images.
15025   */
15026   windows->image.x=0;
15027   windows->image.y=0;
15028   windows->magnify.shape=MagickFalse;
15029   width=(unsigned int) display_image->columns;
15030   height=(unsigned int) display_image->rows;
15031   if ((display_image->columns != width) || (display_image->rows != height))
15032     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15033       display_image->filename);
15034   status=XMakeImage(display,resource_info,&windows->image,display_image,
15035     width,height,exception);
15036   if (status == MagickFalse)
15037     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15038       display_image->filename);
15039   status=XMakeImage(display,resource_info,&windows->magnify,(Image *) NULL,
15040     windows->magnify.width,windows->magnify.height,exception);
15041   if (status == MagickFalse)
15042     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15043       display_image->filename);
15044   if (windows->magnify.mapped != MagickFalse)
15045     (void) XMapRaised(display,windows->magnify.id);
15046   if (windows->pan.mapped != MagickFalse)
15047     (void) XMapRaised(display,windows->pan.id);
15048   windows->image.window_changes.width=(int) display_image->columns;
15049   windows->image.window_changes.height=(int) display_image->rows;
15050   (void) XConfigureImage(display,resource_info,windows,display_image,exception);
15051   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15052   (void) XSync(display,MagickFalse);
15053   /*
15054     Respond to events.
15055   */
15056   delay=display_image->delay/MagickMax(display_image->ticks_per_second,1L);
15057   timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15058   update_time=0;
15059   if (resource_info->update != MagickFalse)
15060     {
15061       MagickBooleanType
15062         status;
15063
15064       /*
15065         Determine when file data was last modified.
15066       */
15067       status=GetPathAttributes(display_image->filename,&attributes);
15068       if (status != MagickFalse)
15069         update_time=attributes.st_mtime;
15070     }
15071   *state&=(~FormerImageState);
15072   *state&=(~MontageImageState);
15073   *state&=(~NextImageState);
15074   do
15075   {
15076     /*
15077       Handle a window event.
15078     */
15079     if (windows->image.mapped != MagickFalse)
15080       if ((display_image->delay != 0) || (resource_info->update != 0))
15081         {
15082           if (timer < time((time_t *) NULL))
15083             {
15084               if (resource_info->update == MagickFalse)
15085                 *state|=NextImageState | ExitState;
15086               else
15087                 {
15088                   MagickBooleanType
15089                     status;
15090
15091                   /*
15092                     Determine if image file was modified.
15093                   */
15094                   status=GetPathAttributes(display_image->filename,&attributes);
15095                   if (status != MagickFalse)
15096                     if (update_time != attributes.st_mtime)
15097                       {
15098                         /*
15099                           Redisplay image.
15100                         */
15101                         (void) FormatLocaleString(
15102                           resource_info->image_info->filename,MaxTextExtent,
15103                           "%s:%s",display_image->magick,
15104                           display_image->filename);
15105                         nexus=ReadImage(resource_info->image_info,
15106                           &display_image->exception);
15107                         if (nexus != (Image *) NULL)
15108                           {
15109                             nexus=DestroyImage(nexus);
15110                             *state|=NextImageState | ExitState;
15111                           }
15112                       }
15113                   delay=display_image->delay/MagickMax(
15114                     display_image->ticks_per_second,1L);
15115                   timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15116                 }
15117             }
15118           if (XEventsQueued(display,QueuedAfterFlush) == 0)
15119             {
15120               /*
15121                 Do not block if delay > 0.
15122               */
15123               XDelay(display,SuspendTime << 2);
15124               continue;
15125             }
15126         }
15127     timestamp=time((time_t *) NULL);
15128     (void) XNextEvent(display,&event);
15129     if (windows->image.stasis == MagickFalse)
15130       windows->image.stasis=(time((time_t *) NULL)-timestamp) > 0 ?
15131         MagickTrue : MagickFalse;
15132     if (windows->magnify.stasis == MagickFalse)
15133       windows->magnify.stasis=(time((time_t *) NULL)-timestamp) > 0 ?
15134         MagickTrue : MagickFalse;
15135     if (event.xany.window == windows->command.id)
15136       {
15137         /*
15138           Select a command from the Command widget.
15139         */
15140         id=XCommandWidget(display,windows,CommandMenu,&event);
15141         if (id < 0)
15142           continue;
15143         (void) CopyMagickString(command,CommandMenu[id],MaxTextExtent);
15144         command_type=CommandMenus[id];
15145         if (id < MagickMenus)
15146           {
15147             /*
15148               Select a command from a pop-up menu.
15149             */
15150             entry=XMenuWidget(display,windows,CommandMenu[id],Menus[id],
15151               command);
15152             if (entry < 0)
15153               continue;
15154             (void) CopyMagickString(command,Menus[id][entry],MaxTextExtent);
15155             command_type=Commands[id][entry];
15156           }
15157         if (command_type != NullCommand)
15158           nexus=XMagickCommand(display,resource_info,windows,command_type,
15159             &display_image,exception);
15160         continue;
15161       }
15162     switch (event.type)
15163     {
15164       case ButtonPress:
15165       {
15166         if (display_image->debug != MagickFalse)
15167           (void) LogMagickEvent(X11Event,GetMagickModule(),
15168             "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
15169             event.xbutton.button,event.xbutton.x,event.xbutton.y);
15170         if ((event.xbutton.button == Button3) &&
15171             (event.xbutton.state & Mod1Mask))
15172           {
15173             /*
15174               Convert Alt-Button3 to Button2.
15175             */
15176             event.xbutton.button=Button2;
15177             event.xbutton.state&=(~Mod1Mask);
15178           }
15179         if (event.xbutton.window == windows->backdrop.id)
15180           {
15181             (void) XSetInputFocus(display,event.xbutton.window,RevertToParent,
15182               event.xbutton.time);
15183             break;
15184           }
15185         if (event.xbutton.window == windows->image.id)
15186           {
15187             switch (event.xbutton.button)
15188             {
15189               case Button1:
15190               {
15191                 if (resource_info->immutable)
15192                   {
15193                     /*
15194                       Select a command from the Virtual menu.
15195                     */
15196                     entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15197                       command);
15198                     if (entry >= 0)
15199                       nexus=XMagickCommand(display,resource_info,windows,
15200                         VirtualCommands[entry],&display_image,exception);
15201                     break;
15202                   }
15203                 /*
15204                   Map/unmap Command widget.
15205                 */
15206                 if (windows->command.mapped != MagickFalse)
15207                   (void) XWithdrawWindow(display,windows->command.id,
15208                     windows->command.screen);
15209                 else
15210                   {
15211                     (void) XCommandWidget(display,windows,CommandMenu,
15212                       (XEvent *) NULL);
15213                     (void) XMapRaised(display,windows->command.id);
15214                   }
15215                 break;
15216               }
15217               case Button2:
15218               {
15219                 /*
15220                   User pressed the image magnify button.
15221                 */
15222                 (void) XMagickCommand(display,resource_info,windows,ZoomCommand,
15223                   &display_image,exception);
15224                 XMagnifyImage(display,windows,&event);
15225                 break;
15226               }
15227               case Button3:
15228               {
15229                 if (resource_info->immutable)
15230                   {
15231                     /*
15232                       Select a command from the Virtual menu.
15233                     */
15234                     entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15235                       command);
15236                     if (entry >= 0)
15237                       nexus=XMagickCommand(display,resource_info,windows,
15238                         VirtualCommands[entry],&display_image,exception);
15239                     break;
15240                   }
15241                 if (display_image->montage != (char *) NULL)
15242                   {
15243                     /*
15244                       Open or delete a tile from a visual image directory.
15245                     */
15246                     nexus=XTileImage(display,resource_info,windows,
15247                       display_image,&event,exception);
15248                     if (nexus != (Image *) NULL)
15249                       *state|=MontageImageState | NextImageState | ExitState;
15250                     vid_info.x=(short int) windows->image.x;
15251                     vid_info.y=(short int) windows->image.y;
15252                     break;
15253                   }
15254                 /*
15255                   Select a command from the Short Cuts menu.
15256                 */
15257                 entry=XMenuWidget(display,windows,"Short Cuts",ShortCutsMenu,
15258                   command);
15259                 if (entry >= 0)
15260                   nexus=XMagickCommand(display,resource_info,windows,
15261                     ShortCutsCommands[entry],&display_image,exception);
15262                 break;
15263               }
15264               case Button4:
15265               {
15266                 /*
15267                   Wheel up.
15268                 */
15269                 XTranslateImage(display,windows,*image,XK_Up);
15270                 break;
15271               }
15272               case Button5:
15273               {
15274                 /*
15275                   Wheel down.
15276                 */
15277                 XTranslateImage(display,windows,*image,XK_Down);
15278                 break;
15279               }
15280               default:
15281                 break;
15282             }
15283             break;
15284           }
15285         if (event.xbutton.window == windows->magnify.id)
15286           {
15287             int
15288               factor;
15289
15290             static const char
15291               *MagnifyMenu[] =
15292               {
15293                 "2",
15294                 "4",
15295                 "5",
15296                 "6",
15297                 "7",
15298                 "8",
15299                 "9",
15300                 "3",
15301                 (char *) NULL,
15302               };
15303
15304             static KeySym
15305               MagnifyCommands[] =
15306               {
15307                 XK_2,
15308                 XK_4,
15309                 XK_5,
15310                 XK_6,
15311                 XK_7,
15312                 XK_8,
15313                 XK_9,
15314                 XK_3
15315               };
15316
15317             /*
15318               Select a magnify factor from the pop-up menu.
15319             */
15320             factor=XMenuWidget(display,windows,"Magnify",MagnifyMenu,command);
15321             if (factor >= 0)
15322               XMagnifyWindowCommand(display,windows,0,MagnifyCommands[factor]);
15323             break;
15324           }
15325         if (event.xbutton.window == windows->pan.id)
15326           {
15327             switch (event.xbutton.button)
15328             {
15329               case Button4:
15330               {
15331                 /*
15332                   Wheel up.
15333                 */
15334                 XTranslateImage(display,windows,*image,XK_Up);
15335                 break;
15336               }
15337               case Button5:
15338               {
15339                 /*
15340                   Wheel down.
15341                 */
15342                 XTranslateImage(display,windows,*image,XK_Down);
15343                 break;
15344               }
15345               default:
15346               {
15347                 XPanImage(display,windows,&event);
15348                 break;
15349               }
15350             }
15351             break;
15352           }
15353         delay=display_image->delay/MagickMax(display_image->ticks_per_second,
15354           1L);
15355         timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15356         break;
15357       }
15358       case ButtonRelease:
15359       {
15360         if (display_image->debug != MagickFalse)
15361           (void) LogMagickEvent(X11Event,GetMagickModule(),
15362             "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
15363             event.xbutton.button,event.xbutton.x,event.xbutton.y);
15364         break;
15365       }
15366       case ClientMessage:
15367       {
15368         if (display_image->debug != MagickFalse)
15369           (void) LogMagickEvent(X11Event,GetMagickModule(),
15370             "Client Message: 0x%lx 0x%lx %d 0x%lx",event.xclient.window,
15371             event.xclient.message_type,event.xclient.format,(unsigned long)
15372             event.xclient.data.l[0]);
15373         if (event.xclient.message_type == windows->im_protocols)
15374           {
15375             if (*event.xclient.data.l == (long) windows->im_update_widget)
15376               {
15377                 (void) CloneString(&windows->command.name,MagickTitle);
15378                 windows->command.data=MagickMenus;
15379                 (void) XCommandWidget(display,windows,CommandMenu,
15380                   (XEvent *) NULL);
15381                 break;
15382               }
15383             if (*event.xclient.data.l == (long) windows->im_update_colormap)
15384               {
15385                 /*
15386                   Update graphic context and window colormap.
15387                 */
15388                 for (i=0; i < (int) number_windows; i++)
15389                 {
15390                   if (magick_windows[i]->id == windows->icon.id)
15391                     continue;
15392                   context_values.background=pixel->background_color.pixel;
15393                   context_values.foreground=pixel->foreground_color.pixel;
15394                   (void) XChangeGC(display,magick_windows[i]->annotate_context,
15395                     context_mask,&context_values);
15396                   (void) XChangeGC(display,magick_windows[i]->widget_context,
15397                     context_mask,&context_values);
15398                   context_values.background=pixel->foreground_color.pixel;
15399                   context_values.foreground=pixel->background_color.pixel;
15400                   context_values.plane_mask=context_values.background ^
15401                     context_values.foreground;
15402                   (void) XChangeGC(display,magick_windows[i]->highlight_context,
15403                     (size_t) (context_mask | GCPlaneMask),
15404                     &context_values);
15405                   magick_windows[i]->attributes.background_pixel=
15406                     pixel->background_color.pixel;
15407                   magick_windows[i]->attributes.border_pixel=
15408                     pixel->border_color.pixel;
15409                   magick_windows[i]->attributes.colormap=map_info->colormap;
15410                   (void) XChangeWindowAttributes(display,magick_windows[i]->id,
15411                     (unsigned long) magick_windows[i]->mask,
15412                     &magick_windows[i]->attributes);
15413                 }
15414                 if (windows->pan.mapped != MagickFalse)
15415                   {
15416                     (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
15417                       windows->pan.pixmap);
15418                     (void) XClearWindow(display,windows->pan.id);
15419                     XDrawPanRectangle(display,windows);
15420                   }
15421                 if (windows->backdrop.id != (Window) NULL)
15422                   (void) XInstallColormap(display,map_info->colormap);
15423                 break;
15424               }
15425             if (*event.xclient.data.l == (long) windows->im_former_image)
15426               {
15427                 *state|=FormerImageState | ExitState;
15428                 break;
15429               }
15430             if (*event.xclient.data.l == (long) windows->im_next_image)
15431               {
15432                 *state|=NextImageState | ExitState;
15433                 break;
15434               }
15435             if (*event.xclient.data.l == (long) windows->im_retain_colors)
15436               {
15437                 *state|=RetainColorsState;
15438                 break;
15439               }
15440             if (*event.xclient.data.l == (long) windows->im_exit)
15441               {
15442                 *state|=ExitState;
15443                 break;
15444               }
15445             break;
15446           }
15447         if (event.xclient.message_type == windows->dnd_protocols)
15448           {
15449             Atom
15450               selection,
15451               type;
15452
15453             int
15454               format,
15455               status;
15456
15457             unsigned char
15458               *data;
15459
15460             unsigned long
15461               after,
15462               length;
15463
15464             /*
15465               Display image named by the Drag-and-Drop selection.
15466             */
15467             if ((*event.xclient.data.l != 2) && (*event.xclient.data.l != 128))
15468               break;
15469             selection=XInternAtom(display,"DndSelection",MagickFalse);
15470             status=XGetWindowProperty(display,root_window,selection,0L,(long)
15471               MaxTextExtent,MagickFalse,(Atom) AnyPropertyType,&type,&format,
15472               &length,&after,&data);
15473             if ((status != Success) || (length == 0))
15474               break;
15475             if (*event.xclient.data.l == 2)
15476               {
15477                 /*
15478                   Offix DND.
15479                 */
15480                 (void) CopyMagickString(resource_info->image_info->filename,
15481                   (char *) data,MaxTextExtent);
15482               }
15483             else
15484               {
15485                 /*
15486                   XDND.
15487                 */
15488                 if (strncmp((char *) data, "file:", 5) != 0)
15489                   {
15490                     (void) XFree((void *) data);
15491                     break;
15492                   }
15493                 (void) CopyMagickString(resource_info->image_info->filename,
15494                   ((char *) data)+5,MaxTextExtent);
15495               }
15496             nexus=ReadImage(resource_info->image_info,
15497               &display_image->exception);
15498             CatchException(&display_image->exception);
15499             if (nexus != (Image *) NULL)
15500               *state|=NextImageState | ExitState;
15501             (void) XFree((void *) data);
15502             break;
15503           }
15504         /*
15505           If client window delete message, exit.
15506         */
15507         if (event.xclient.message_type != windows->wm_protocols)
15508           break;
15509         if (*event.xclient.data.l != (long) windows->wm_delete_window)
15510           break;
15511         (void) XWithdrawWindow(display,event.xclient.window,
15512           visual_info->screen);
15513         if (event.xclient.window == windows->image.id)
15514           {
15515             *state|=ExitState;
15516             break;
15517           }
15518         if (event.xclient.window == windows->pan.id)
15519           {
15520             /*
15521               Restore original image size when pan window is deleted.
15522             */
15523             windows->image.window_changes.width=windows->image.ximage->width;
15524             windows->image.window_changes.height=windows->image.ximage->height;
15525             (void) XConfigureImage(display,resource_info,windows,
15526               display_image,exception);
15527           }
15528         break;
15529       }
15530       case ConfigureNotify:
15531       {
15532         if (display_image->debug != MagickFalse)
15533           (void) LogMagickEvent(X11Event,GetMagickModule(),
15534             "Configure Notify: 0x%lx %dx%d+%d+%d %d",event.xconfigure.window,
15535             event.xconfigure.width,event.xconfigure.height,event.xconfigure.x,
15536             event.xconfigure.y,event.xconfigure.send_event);
15537         if (event.xconfigure.window == windows->image.id)
15538           {
15539             /*
15540               Image window has a new configuration.
15541             */
15542             if (event.xconfigure.send_event != 0)
15543               {
15544                 XWindowChanges
15545                   window_changes;
15546
15547                 /*
15548                   Position the transient windows relative of the Image window.
15549                 */
15550                 if (windows->command.geometry == (char *) NULL)
15551                   if (windows->command.mapped == MagickFalse)
15552                     {
15553                       windows->command.x=event.xconfigure.x-
15554                         windows->command.width-25;
15555                       windows->command.y=event.xconfigure.y;
15556                       XConstrainWindowPosition(display,&windows->command);
15557                       window_changes.x=windows->command.x;
15558                       window_changes.y=windows->command.y;
15559                       (void) XReconfigureWMWindow(display,windows->command.id,
15560                         windows->command.screen,(unsigned int) (CWX | CWY),
15561                         &window_changes);
15562                     }
15563                 if (windows->widget.geometry == (char *) NULL)
15564                   if (windows->widget.mapped == MagickFalse)
15565                     {
15566                       windows->widget.x=event.xconfigure.x+
15567                         event.xconfigure.width/10;
15568                       windows->widget.y=event.xconfigure.y+
15569                         event.xconfigure.height/10;
15570                       XConstrainWindowPosition(display,&windows->widget);
15571                       window_changes.x=windows->widget.x;
15572                       window_changes.y=windows->widget.y;
15573                       (void) XReconfigureWMWindow(display,windows->widget.id,
15574                         windows->widget.screen,(unsigned int) (CWX | CWY),
15575                         &window_changes);
15576                     }
15577                 if (windows->magnify.geometry == (char *) NULL)
15578                   if (windows->magnify.mapped == MagickFalse)
15579                     {
15580                       windows->magnify.x=event.xconfigure.x+
15581                         event.xconfigure.width+25;
15582                       windows->magnify.y=event.xconfigure.y;
15583                       XConstrainWindowPosition(display,&windows->magnify);
15584                       window_changes.x=windows->magnify.x;
15585                       window_changes.y=windows->magnify.y;
15586                       (void) XReconfigureWMWindow(display,windows->magnify.id,
15587                         windows->magnify.screen,(unsigned int) (CWX | CWY),
15588                         &window_changes);
15589                     }
15590                 if (windows->pan.geometry == (char *) NULL)
15591                   if (windows->pan.mapped == MagickFalse)
15592                     {
15593                       windows->pan.x=event.xconfigure.x+
15594                         event.xconfigure.width+25;
15595                       windows->pan.y=event.xconfigure.y+
15596                         windows->magnify.height+50;
15597                       XConstrainWindowPosition(display,&windows->pan);
15598                       window_changes.x=windows->pan.x;
15599                       window_changes.y=windows->pan.y;
15600                       (void) XReconfigureWMWindow(display,windows->pan.id,
15601                         windows->pan.screen,(unsigned int) (CWX | CWY),
15602                         &window_changes);
15603                     }
15604               }
15605             if ((event.xconfigure.width == (int) windows->image.width) &&
15606                 (event.xconfigure.height == (int) windows->image.height))
15607               break;
15608             windows->image.width=(unsigned int) event.xconfigure.width;
15609             windows->image.height=(unsigned int) event.xconfigure.height;
15610             windows->image.x=0;
15611             windows->image.y=0;
15612             if (display_image->montage != (char *) NULL)
15613               {
15614                 windows->image.x=vid_info.x;
15615                 windows->image.y=vid_info.y;
15616               }
15617             if ((windows->image.mapped != MagickFalse) &&
15618                 (windows->image.stasis != MagickFalse))
15619               {
15620                 /*
15621                   Update image window configuration.
15622                 */
15623                 windows->image.window_changes.width=event.xconfigure.width;
15624                 windows->image.window_changes.height=event.xconfigure.height;
15625                 (void) XConfigureImage(display,resource_info,windows,
15626                   display_image,exception);
15627               }
15628             /*
15629               Update pan window configuration.
15630             */
15631             if ((event.xconfigure.width < windows->image.ximage->width) ||
15632                 (event.xconfigure.height < windows->image.ximage->height))
15633               {
15634                 (void) XMapRaised(display,windows->pan.id);
15635                 XDrawPanRectangle(display,windows);
15636               }
15637             else
15638               if (windows->pan.mapped != MagickFalse)
15639                 (void) XWithdrawWindow(display,windows->pan.id,
15640                   windows->pan.screen);
15641             break;
15642           }
15643         if (event.xconfigure.window == windows->magnify.id)
15644           {
15645             unsigned int
15646               magnify;
15647
15648             /*
15649               Magnify window has a new configuration.
15650             */
15651             windows->magnify.width=(unsigned int) event.xconfigure.width;
15652             windows->magnify.height=(unsigned int) event.xconfigure.height;
15653             if (windows->magnify.mapped == MagickFalse)
15654               break;
15655             magnify=1;
15656             while ((int) magnify <= event.xconfigure.width)
15657               magnify<<=1;
15658             while ((int) magnify <= event.xconfigure.height)
15659               magnify<<=1;
15660             magnify>>=1;
15661             if (((int) magnify != event.xconfigure.width) ||
15662                 ((int) magnify != event.xconfigure.height))
15663               {
15664                 window_changes.width=(int) magnify;
15665                 window_changes.height=(int) magnify;
15666                 (void) XReconfigureWMWindow(display,windows->magnify.id,
15667                   windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
15668                   &window_changes);
15669                 break;
15670               }
15671             if ((windows->magnify.mapped != MagickFalse) &&
15672                 (windows->magnify.stasis != MagickFalse))
15673               {
15674                 status=XMakeImage(display,resource_info,&windows->magnify,
15675                   display_image,windows->magnify.width,windows->magnify.height,
15676                   exception);
15677                 XMakeMagnifyImage(display,windows);
15678               }
15679             break;
15680           }
15681         if ((windows->magnify.mapped != MagickFalse) &&
15682             (event.xconfigure.window == windows->pan.id))
15683           {
15684             /*
15685               Pan icon window has a new configuration.
15686             */
15687             if (event.xconfigure.send_event != 0)
15688               {
15689                 windows->pan.x=event.xconfigure.x;
15690                 windows->pan.y=event.xconfigure.y;
15691               }
15692             windows->pan.width=(unsigned int) event.xconfigure.width;
15693             windows->pan.height=(unsigned int) event.xconfigure.height;
15694             break;
15695           }
15696         if (event.xconfigure.window == windows->icon.id)
15697           {
15698             /*
15699               Icon window has a new configuration.
15700             */
15701             windows->icon.width=(unsigned int) event.xconfigure.width;
15702             windows->icon.height=(unsigned int) event.xconfigure.height;
15703             break;
15704           }
15705         break;
15706       }
15707       case DestroyNotify:
15708       {
15709         /*
15710           Group leader has exited.
15711         */
15712         if (display_image->debug != MagickFalse)
15713           (void) LogMagickEvent(X11Event,GetMagickModule(),
15714             "Destroy Notify: 0x%lx",event.xdestroywindow.window);
15715         if (event.xdestroywindow.window == windows->group_leader.id)
15716           {
15717             *state|=ExitState;
15718             break;
15719           }
15720         break;
15721       }
15722       case EnterNotify:
15723       {
15724         /*
15725           Selectively install colormap.
15726         */
15727         if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15728           if (event.xcrossing.mode != NotifyUngrab)
15729             XInstallColormap(display,map_info->colormap);
15730         break;
15731       }
15732       case Expose:
15733       {
15734         if (display_image->debug != MagickFalse)
15735           (void) LogMagickEvent(X11Event,GetMagickModule(),
15736             "Expose: 0x%lx %dx%d+%d+%d",event.xexpose.window,
15737             event.xexpose.width,event.xexpose.height,event.xexpose.x,
15738             event.xexpose.y);
15739         /*
15740           Refresh windows that are now exposed.
15741         */
15742         if ((event.xexpose.window == windows->image.id) &&
15743             (windows->image.mapped != MagickFalse))
15744           {
15745             XRefreshWindow(display,&windows->image,&event);
15746             delay=display_image->delay/MagickMax(
15747               display_image->ticks_per_second,1L);
15748             timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15749             break;
15750           }
15751         if ((event.xexpose.window == windows->magnify.id) &&
15752             (windows->magnify.mapped != MagickFalse))
15753           {
15754             XMakeMagnifyImage(display,windows);
15755             break;
15756           }
15757         if (event.xexpose.window == windows->pan.id)
15758           {
15759             XDrawPanRectangle(display,windows);
15760             break;
15761           }
15762         if (event.xexpose.window == windows->icon.id)
15763           {
15764             XRefreshWindow(display,&windows->icon,&event);
15765             break;
15766           }
15767         break;
15768       }
15769       case KeyPress:
15770       {
15771         int
15772           length;
15773
15774         /*
15775           Respond to a user key press.
15776         */
15777         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
15778           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15779         *(command+length)='\0';
15780         if (display_image->debug != MagickFalse)
15781           (void) LogMagickEvent(X11Event,GetMagickModule(),
15782             "Key press: %d 0x%lx (%s)",event.xkey.state,(unsigned long)
15783             key_symbol,command);
15784         if (event.xkey.window == windows->image.id)
15785           {
15786             command_type=XImageWindowCommand(display,resource_info,windows,
15787               event.xkey.state,key_symbol,&display_image,exception);
15788             if (command_type != NullCommand)
15789               nexus=XMagickCommand(display,resource_info,windows,command_type,
15790                 &display_image,exception);
15791           }
15792         if (event.xkey.window == windows->magnify.id)
15793           XMagnifyWindowCommand(display,windows,event.xkey.state,key_symbol);
15794         if (event.xkey.window == windows->pan.id)
15795           {
15796             if ((key_symbol == XK_q) || (key_symbol == XK_Escape))
15797               (void) XWithdrawWindow(display,windows->pan.id,
15798                 windows->pan.screen);
15799             else
15800               if ((key_symbol == XK_F1) || (key_symbol == XK_Help))
15801                 XTextViewWidget(display,resource_info,windows,MagickFalse,
15802                   "Help Viewer - Image Pan",ImagePanHelp);
15803               else
15804                 XTranslateImage(display,windows,*image,key_symbol);
15805           }
15806         delay=display_image->delay/MagickMax(
15807           display_image->ticks_per_second,1L);
15808         timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15809         break;
15810       }
15811       case KeyRelease:
15812       {
15813         /*
15814           Respond to a user key release.
15815         */
15816         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
15817           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15818         if (display_image->debug != MagickFalse)
15819           (void) LogMagickEvent(X11Event,GetMagickModule(),
15820             "Key release: 0x%lx (%c)",(unsigned long) key_symbol,*command);
15821         break;
15822       }
15823       case LeaveNotify:
15824       {
15825         /*
15826           Selectively uninstall colormap.
15827         */
15828         if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15829           if (event.xcrossing.mode != NotifyUngrab)
15830             XUninstallColormap(display,map_info->colormap);
15831         break;
15832       }
15833       case MapNotify:
15834       {
15835         if (display_image->debug != MagickFalse)
15836           (void) LogMagickEvent(X11Event,GetMagickModule(),"Map Notify: 0x%lx",
15837             event.xmap.window);
15838         if (event.xmap.window == windows->backdrop.id)
15839           {
15840             (void) XSetInputFocus(display,event.xmap.window,RevertToParent,
15841               CurrentTime);
15842             windows->backdrop.mapped=MagickTrue;
15843             break;
15844           }
15845         if (event.xmap.window == windows->image.id)
15846           {
15847             if (windows->backdrop.id != (Window) NULL)
15848               (void) XInstallColormap(display,map_info->colormap);
15849             if (LocaleCompare(display_image->magick,"LOGO") == 0)
15850               {
15851                 if (LocaleCompare(display_image->filename,"LOGO") == 0)
15852                   nexus=XOpenImage(display,resource_info,windows,MagickFalse);
15853               }
15854             if (((int) windows->image.width < windows->image.ximage->width) ||
15855                 ((int) windows->image.height < windows->image.ximage->height))
15856               (void) XMapRaised(display,windows->pan.id);
15857             windows->image.mapped=MagickTrue;
15858             break;
15859           }
15860         if (event.xmap.window == windows->magnify.id)
15861           {
15862             XMakeMagnifyImage(display,windows);
15863             windows->magnify.mapped=MagickTrue;
15864             (void) XWithdrawWindow(display,windows->info.id,
15865               windows->info.screen);
15866             break;
15867           }
15868         if (event.xmap.window == windows->pan.id)
15869           {
15870             XMakePanImage(display,resource_info,windows,display_image,
15871               exception);
15872             windows->pan.mapped=MagickTrue;
15873             break;
15874           }
15875         if (event.xmap.window == windows->info.id)
15876           {
15877             windows->info.mapped=MagickTrue;
15878             break;
15879           }
15880         if (event.xmap.window == windows->icon.id)
15881           {
15882             MagickBooleanType
15883               taint;
15884
15885             /*
15886               Create an icon image.
15887             */
15888             taint=display_image->taint;
15889             XMakeStandardColormap(display,icon_visual,icon_resources,
15890               display_image,icon_map,icon_pixel);
15891             (void) XMakeImage(display,icon_resources,&windows->icon,
15892               display_image,windows->icon.width,windows->icon.height,
15893               exception);
15894             display_image->taint=taint;
15895             (void) XSetWindowBackgroundPixmap(display,windows->icon.id,
15896               windows->icon.pixmap);
15897             (void) XClearWindow(display,windows->icon.id);
15898             (void) XWithdrawWindow(display,windows->info.id,
15899               windows->info.screen);
15900             windows->icon.mapped=MagickTrue;
15901             break;
15902           }
15903         if (event.xmap.window == windows->command.id)
15904           {
15905             windows->command.mapped=MagickTrue;
15906             break;
15907           }
15908         if (event.xmap.window == windows->popup.id)
15909           {
15910             windows->popup.mapped=MagickTrue;
15911             break;
15912           }
15913         if (event.xmap.window == windows->widget.id)
15914           {
15915             windows->widget.mapped=MagickTrue;
15916             break;
15917           }
15918         break;
15919       }
15920       case MappingNotify:
15921       {
15922         (void) XRefreshKeyboardMapping(&event.xmapping);
15923         break;
15924       }
15925       case NoExpose:
15926         break;
15927       case PropertyNotify:
15928       {
15929         Atom
15930           type;
15931
15932         int
15933           format,
15934           status;
15935
15936         unsigned char
15937           *data;
15938
15939         unsigned long
15940           after,
15941           length;
15942
15943         if (display_image->debug != MagickFalse)
15944           (void) LogMagickEvent(X11Event,GetMagickModule(),
15945             "Property Notify: 0x%lx 0x%lx %d",event.xproperty.window,
15946             event.xproperty.atom,event.xproperty.state);
15947         if (event.xproperty.atom != windows->im_remote_command)
15948           break;
15949         /*
15950           Display image named by the remote command protocol.
15951         */
15952         status=XGetWindowProperty(display,event.xproperty.window,
15953           event.xproperty.atom,0L,(long) MaxTextExtent,MagickFalse,(Atom)
15954           AnyPropertyType,&type,&format,&length,&after,&data);
15955         if ((status != Success) || (length == 0))
15956           break;
15957         if (LocaleCompare((char *) data,"-quit") == 0)
15958           {
15959             XClientMessage(display,windows->image.id,windows->im_protocols,
15960               windows->im_exit,CurrentTime);
15961             (void) XFree((void *) data);
15962             break;
15963           }
15964         (void) CopyMagickString(resource_info->image_info->filename,
15965           (char *) data,MaxTextExtent);
15966         (void) XFree((void *) data);
15967         nexus=ReadImage(resource_info->image_info,&display_image->exception);
15968         CatchException(&display_image->exception);
15969         if (nexus != (Image *) NULL)
15970           *state|=NextImageState | ExitState;
15971         break;
15972       }
15973       case ReparentNotify:
15974       {
15975         if (display_image->debug != MagickFalse)
15976           (void) LogMagickEvent(X11Event,GetMagickModule(),
15977             "Reparent Notify: 0x%lx=>0x%lx",event.xreparent.parent,
15978             event.xreparent.window);
15979         break;
15980       }
15981       case UnmapNotify:
15982       {
15983         if (display_image->debug != MagickFalse)
15984           (void) LogMagickEvent(X11Event,GetMagickModule(),
15985             "Unmap Notify: 0x%lx",event.xunmap.window);
15986         if (event.xunmap.window == windows->backdrop.id)
15987           {
15988             windows->backdrop.mapped=MagickFalse;
15989             break;
15990           }
15991         if (event.xunmap.window == windows->image.id)
15992           {
15993             windows->image.mapped=MagickFalse;
15994             break;
15995           }
15996         if (event.xunmap.window == windows->magnify.id)
15997           {
15998             windows->magnify.mapped=MagickFalse;
15999             break;
16000           }
16001         if (event.xunmap.window == windows->pan.id)
16002           {
16003             windows->pan.mapped=MagickFalse;
16004             break;
16005           }
16006         if (event.xunmap.window == windows->info.id)
16007           {
16008             windows->info.mapped=MagickFalse;
16009             break;
16010           }
16011         if (event.xunmap.window == windows->icon.id)
16012           {
16013             if (map_info->colormap == icon_map->colormap)
16014               XConfigureImageColormap(display,resource_info,windows,
16015                 display_image);
16016             (void) XFreeStandardColormap(display,icon_visual,icon_map,
16017               icon_pixel);
16018             windows->icon.mapped=MagickFalse;
16019             break;
16020           }
16021         if (event.xunmap.window == windows->command.id)
16022           {
16023             windows->command.mapped=MagickFalse;
16024             break;
16025           }
16026         if (event.xunmap.window == windows->popup.id)
16027           {
16028             if (windows->backdrop.id != (Window) NULL)
16029               (void) XSetInputFocus(display,windows->image.id,RevertToParent,
16030                 CurrentTime);
16031             windows->popup.mapped=MagickFalse;
16032             break;
16033           }
16034         if (event.xunmap.window == windows->widget.id)
16035           {
16036             if (windows->backdrop.id != (Window) NULL)
16037               (void) XSetInputFocus(display,windows->image.id,RevertToParent,
16038                 CurrentTime);
16039             windows->widget.mapped=MagickFalse;
16040             break;
16041           }
16042         break;
16043       }
16044       default:
16045       {
16046         if (display_image->debug != MagickFalse)
16047           (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
16048             event.type);
16049         break;
16050       }
16051     }
16052   } while (!(*state & ExitState));
16053   if ((*state & ExitState) == 0)
16054     (void) XMagickCommand(display,resource_info,windows,FreeBuffersCommand,
16055       &display_image,exception);
16056   else
16057     if (resource_info->confirm_edit != MagickFalse)
16058       {
16059         /*
16060           Query user if image has changed.
16061         */
16062         if ((resource_info->immutable == MagickFalse) &&
16063             (display_image->taint != MagickFalse))
16064           {
16065             int
16066               status;
16067
16068             status=XConfirmWidget(display,windows,"Your image changed.",
16069               "Do you want to save it");
16070             if (status == 0)
16071               *state&=(~ExitState);
16072             else
16073               if (status > 0)
16074                 (void) XMagickCommand(display,resource_info,windows,SaveCommand,
16075                   &display_image,exception);
16076           }
16077       }
16078   if ((windows->visual_info->klass == GrayScale) ||
16079       (windows->visual_info->klass == PseudoColor) ||
16080       (windows->visual_info->klass == DirectColor))
16081     {
16082       /*
16083         Withdraw pan and Magnify window.
16084       */
16085       if (windows->info.mapped != MagickFalse)
16086         (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
16087       if (windows->magnify.mapped != MagickFalse)
16088         (void) XWithdrawWindow(display,windows->magnify.id,
16089           windows->magnify.screen);
16090       if (windows->command.mapped != MagickFalse)
16091         (void) XWithdrawWindow(display,windows->command.id,
16092           windows->command.screen);
16093     }
16094   if (windows->pan.mapped != MagickFalse)
16095     (void) XWithdrawWindow(display,windows->pan.id,windows->pan.screen);
16096   if (resource_info->backdrop == MagickFalse)
16097     if (windows->backdrop.mapped)
16098       {
16099         (void) XWithdrawWindow(display,windows->backdrop.id,
16100           windows->backdrop.screen);
16101         (void) XDestroyWindow(display,windows->backdrop.id);
16102         windows->backdrop.id=(Window) NULL;
16103         (void) XWithdrawWindow(display,windows->image.id,
16104           windows->image.screen);
16105         (void) XDestroyWindow(display,windows->image.id);
16106         windows->image.id=(Window) NULL;
16107       }
16108   XSetCursorState(display,windows,MagickTrue);
16109   XCheckRefreshWindows(display,windows);
16110   if (((*state & FormerImageState) != 0) || ((*state & NextImageState) != 0))
16111     *state&=(~ExitState);
16112   if (*state & ExitState)
16113     {
16114       /*
16115         Free Standard Colormap.
16116       */
16117       (void) XFreeStandardColormap(display,icon_visual,icon_map,icon_pixel);
16118       if (resource_info->map_type == (char *) NULL)
16119         (void) XFreeStandardColormap(display,visual_info,map_info,pixel);
16120       /*
16121         Free X resources.
16122       */
16123       if (resource_info->copy_image != (Image *) NULL)
16124         {
16125           resource_info->copy_image=DestroyImage(resource_info->copy_image);
16126           resource_info->copy_image=NewImageList();
16127         }
16128       DestroyXResources();
16129     }
16130   (void) XSync(display,MagickFalse);
16131   /*
16132     Restore our progress monitor and warning handlers.
16133   */
16134   (void) SetErrorHandler(warning_handler);
16135   (void) SetWarningHandler(warning_handler);
16136   /*
16137     Change to home directory.
16138   */
16139   directory=getcwd(working_directory,MaxTextExtent);
16140   (void) directory;
16141   {
16142     int
16143       status;
16144
16145     status=chdir(resource_info->home_directory);
16146     if (status == -1)
16147       (void) ThrowMagickException(&display_image->exception,GetMagickModule(),
16148         FileOpenError,"UnableToOpenFile","%s",resource_info->home_directory);
16149   }
16150   *image=display_image;
16151   return(nexus);
16152 }
16153 #else
16154 \f
16155 /*
16156 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16157 %                                                                             %
16158 %                                                                             %
16159 %                                                                             %
16160 +   D i s p l a y I m a g e s                                                 %
16161 %                                                                             %
16162 %                                                                             %
16163 %                                                                             %
16164 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16165 %
16166 %  DisplayImages() displays an image sequence to any X window screen.  It
16167 %  returns a value other than 0 if successful.  Check the exception member
16168 %  of image to determine the reason for any failure.
16169 %
16170 %  The format of the DisplayImages method is:
16171 %
16172 %      MagickBooleanType DisplayImages(const ImageInfo *image_info,
16173 %        Image *images,ExceptionInfo *exception)
16174 %
16175 %  A description of each parameter follows:
16176 %
16177 %    o image_info: the image info.
16178 %
16179 %    o image: the image.
16180 %
16181 %    o exception: return any errors or warnings in this structure.
16182 %
16183 */
16184 MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
16185   Image *image,ExceptionInfo *exception)
16186 {
16187   assert(image_info != (const ImageInfo *) NULL);
16188   assert(image_info->signature == MagickSignature);
16189   assert(image != (Image *) NULL);
16190   assert(image->signature == MagickSignature);
16191   if (image->debug != MagickFalse)
16192     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
16193   (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16194     "DelegateLibrarySupportNotBuiltIn","`%s' (X11)",image->filename);
16195   return(MagickFalse);
16196 }
16197 \f
16198 /*
16199 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16200 %                                                                             %
16201 %                                                                             %
16202 %                                                                             %
16203 +   R e m o t e D i s p l a y C o m m a n d                                   %
16204 %                                                                             %
16205 %                                                                             %
16206 %                                                                             %
16207 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16208 %
16209 %  RemoteDisplayCommand() encourages a remote display program to display the
16210 %  specified image filename.
16211 %
16212 %  The format of the RemoteDisplayCommand method is:
16213 %
16214 %      MagickBooleanType RemoteDisplayCommand(const ImageInfo *image,
16215 %        const char *window,const char *filename,ExceptionInfo *exception)
16216 %
16217 %  A description of each parameter follows:
16218 %
16219 %    o image_info: the image info.
16220 %
16221 %    o window: Specifies the name or id of an X window.
16222 %
16223 %    o filename: the name of the image filename to display.
16224 %
16225 %    o exception: return any errors or warnings in this structure.
16226 %
16227 */
16228 MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
16229   const char *window,const char *filename,ExceptionInfo *exception)
16230 {
16231   assert(image_info != (const ImageInfo *) NULL);
16232   assert(image_info->signature == MagickSignature);
16233   assert(filename != (char *) NULL);
16234   (void) window;
16235   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
16236   (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16237     "DelegateLibrarySupportNotBuiltIn","`%s' (X11)",image_info->filename);
16238   return(MagickFalse);
16239 }
16240 #endif