]> 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     if (status == 0)
2735       return(MagickFalse);
2736     /*
2737       Free up memory.
2738     */
2739     previous_info=annotate_info->previous;
2740     annotate_info->text=DestroyString(annotate_info->text);
2741     annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2742     annotate_info=previous_info;
2743   }
2744   (void) XSetForeground(display,annotate_context,
2745     windows->pixel_info->foreground_color.pixel);
2746   (void) XSetBackground(display,annotate_context,
2747     windows->pixel_info->background_color.pixel);
2748   (void) XSetFont(display,annotate_context,windows->font_info->fid);
2749   XSetCursorState(display,windows,MagickFalse);
2750   (void) XFreeFont(display,font_info);
2751   /*
2752     Update image configuration.
2753   */
2754   XConfigureImageColormap(display,resource_info,windows,image);
2755   (void) XConfigureImage(display,resource_info,windows,image,exception);
2756   return(MagickTrue);
2757 }
2758 \f
2759 /*
2760 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2761 %                                                                             %
2762 %                                                                             %
2763 %                                                                             %
2764 +   X B a c k g r o u n d I m a g e                                           %
2765 %                                                                             %
2766 %                                                                             %
2767 %                                                                             %
2768 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2769 %
2770 %  XBackgroundImage() displays the image in the background of a window.
2771 %
2772 %  The format of the XBackgroundImage method is:
2773 %
2774 %      MagickBooleanType XBackgroundImage(Display *display,
2775 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
2776 %        ExceptionInfo *exception)
2777 %
2778 %  A description of each parameter follows:
2779 %
2780 %    o display: Specifies a connection to an X server; returned from
2781 %      XOpenDisplay.
2782 %
2783 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2784 %
2785 %    o windows: Specifies a pointer to a XWindows structure.
2786 %
2787 %    o image: the image.
2788 %
2789 %    o exception: return any errors or warnings in this structure.
2790 %
2791 */
2792 static MagickBooleanType XBackgroundImage(Display *display,
2793   XResourceInfo *resource_info,XWindows *windows,Image **image,
2794   ExceptionInfo *exception)
2795 {
2796 #define BackgroundImageTag  "Background/Image"
2797
2798   int
2799     status;
2800
2801   static char
2802     window_id[MaxTextExtent] = "root";
2803
2804   XResourceInfo
2805     background_resources;
2806
2807   /*
2808     Put image in background.
2809   */
2810   status=XDialogWidget(display,windows,"Background",
2811     "Enter window id (id 0x00 selects window with pointer):",window_id);
2812   if (*window_id == '\0')
2813     return(MagickFalse);
2814   (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
2815     exception);
2816   XInfoWidget(display,windows,BackgroundImageTag);
2817   XSetCursorState(display,windows,MagickTrue);
2818   XCheckRefreshWindows(display,windows);
2819   background_resources=(*resource_info);
2820   background_resources.window_id=window_id;
2821   background_resources.backdrop=status != 0 ? MagickTrue : MagickFalse;
2822   status=XDisplayBackgroundImage(display,&background_resources,*image,
2823     exception);
2824   if (status != MagickFalse)
2825     XClientMessage(display,windows->image.id,windows->im_protocols,
2826       windows->im_retain_colors,CurrentTime);
2827   XSetCursorState(display,windows,MagickFalse);
2828   (void) XMagickCommand(display,resource_info,windows,UndoCommand,image,
2829     exception);
2830   return(MagickTrue);
2831 }
2832 \f
2833 /*
2834 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2835 %                                                                             %
2836 %                                                                             %
2837 %                                                                             %
2838 +   X C h o p I m a g e                                                       %
2839 %                                                                             %
2840 %                                                                             %
2841 %                                                                             %
2842 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2843 %
2844 %  XChopImage() chops the X image.
2845 %
2846 %  The format of the XChopImage method is:
2847 %
2848 %    MagickBooleanType XChopImage(Display *display,XResourceInfo *resource_info,
2849 %      XWindows *windows,Image **image,ExceptionInfo *exception)
2850 %
2851 %  A description of each parameter follows:
2852 %
2853 %    o display: Specifies a connection to an X server; returned from
2854 %      XOpenDisplay.
2855 %
2856 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2857 %
2858 %    o windows: Specifies a pointer to a XWindows structure.
2859 %
2860 %    o image: the image.
2861 %
2862 %    o exception: return any errors or warnings in this structure.
2863 %
2864 */
2865 static MagickBooleanType XChopImage(Display *display,
2866   XResourceInfo *resource_info,XWindows *windows,Image **image,
2867   ExceptionInfo *exception)
2868 {
2869   static const char
2870     *ChopMenu[] =
2871     {
2872       "Direction",
2873       "Help",
2874       "Dismiss",
2875       (char *) NULL
2876     };
2877
2878   static ModeType
2879     direction = HorizontalChopCommand;
2880
2881   static const ModeType
2882     ChopCommands[] =
2883     {
2884       ChopDirectionCommand,
2885       ChopHelpCommand,
2886       ChopDismissCommand
2887     },
2888     DirectionCommands[] =
2889     {
2890       HorizontalChopCommand,
2891       VerticalChopCommand
2892     };
2893
2894   char
2895     text[MaxTextExtent];
2896
2897   Image
2898     *chop_image;
2899
2900   int
2901     id,
2902     x,
2903     y;
2904
2905   MagickRealType
2906     scale_factor;
2907
2908   RectangleInfo
2909     chop_info;
2910
2911   unsigned int
2912     distance,
2913     height,
2914     width;
2915
2916   size_t
2917     state;
2918
2919   XEvent
2920     event;
2921
2922   XSegment
2923     segment_info;
2924
2925   /*
2926     Map Command widget.
2927   */
2928   (void) CloneString(&windows->command.name,"Chop");
2929   windows->command.data=1;
2930   (void) XCommandWidget(display,windows,ChopMenu,(XEvent *) NULL);
2931   (void) XMapRaised(display,windows->command.id);
2932   XClientMessage(display,windows->image.id,windows->im_protocols,
2933     windows->im_update_widget,CurrentTime);
2934   /*
2935     Track pointer until button 1 is pressed.
2936   */
2937   XQueryPosition(display,windows->image.id,&x,&y);
2938   (void) XSelectInput(display,windows->image.id,
2939     windows->image.attributes.event_mask | PointerMotionMask);
2940   state=DefaultState;
2941   do
2942   {
2943     if (windows->info.mapped != MagickFalse)
2944       {
2945         /*
2946           Display pointer position.
2947         */
2948         (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
2949           x+windows->image.x,y+windows->image.y);
2950         XInfoWidget(display,windows,text);
2951       }
2952     /*
2953       Wait for next event.
2954     */
2955     XScreenEvent(display,windows,&event);
2956     if (event.xany.window == windows->command.id)
2957       {
2958         /*
2959           Select a command from the Command widget.
2960         */
2961         id=XCommandWidget(display,windows,ChopMenu,&event);
2962         if (id < 0)
2963           continue;
2964         switch (ChopCommands[id])
2965         {
2966           case ChopDirectionCommand:
2967           {
2968             char
2969               command[MaxTextExtent];
2970
2971             static const char
2972               *Directions[] =
2973               {
2974                 "horizontal",
2975                 "vertical",
2976                 (char *) NULL,
2977               };
2978
2979             /*
2980               Select a command from the pop-up menu.
2981             */
2982             id=XMenuWidget(display,windows,ChopMenu[id],Directions,command);
2983             if (id >= 0)
2984               direction=DirectionCommands[id];
2985             break;
2986           }
2987           case ChopHelpCommand:
2988           {
2989             XTextViewWidget(display,resource_info,windows,MagickFalse,
2990               "Help Viewer - Image Chop",ImageChopHelp);
2991             break;
2992           }
2993           case ChopDismissCommand:
2994           {
2995             /*
2996               Prematurely exit.
2997             */
2998             state|=EscapeState;
2999             state|=ExitState;
3000             break;
3001           }
3002           default:
3003             break;
3004         }
3005         continue;
3006       }
3007     switch (event.type)
3008     {
3009       case ButtonPress:
3010       {
3011         if (event.xbutton.button != Button1)
3012           break;
3013         if (event.xbutton.window != windows->image.id)
3014           break;
3015         /*
3016           User has committed to start point of chopping line.
3017         */
3018         segment_info.x1=(short int) event.xbutton.x;
3019         segment_info.x2=(short int) event.xbutton.x;
3020         segment_info.y1=(short int) event.xbutton.y;
3021         segment_info.y2=(short int) event.xbutton.y;
3022         state|=ExitState;
3023         break;
3024       }
3025       case ButtonRelease:
3026         break;
3027       case Expose:
3028         break;
3029       case KeyPress:
3030       {
3031         char
3032           command[MaxTextExtent];
3033
3034         KeySym
3035           key_symbol;
3036
3037         if (event.xkey.window != windows->image.id)
3038           break;
3039         /*
3040           Respond to a user key press.
3041         */
3042         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3043           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3044         switch ((int) key_symbol)
3045         {
3046           case XK_Escape:
3047           case XK_F20:
3048           {
3049             /*
3050               Prematurely exit.
3051             */
3052             state|=EscapeState;
3053             state|=ExitState;
3054             break;
3055           }
3056           case XK_F1:
3057           case XK_Help:
3058           {
3059             (void) XSetFunction(display,windows->image.highlight_context,
3060               GXcopy);
3061             XTextViewWidget(display,resource_info,windows,MagickFalse,
3062               "Help Viewer - Image Chop",ImageChopHelp);
3063             (void) XSetFunction(display,windows->image.highlight_context,
3064               GXinvert);
3065             break;
3066           }
3067           default:
3068           {
3069             (void) XBell(display,0);
3070             break;
3071           }
3072         }
3073         break;
3074       }
3075       case MotionNotify:
3076       {
3077         /*
3078           Map and unmap Info widget as text cursor crosses its boundaries.
3079         */
3080         x=event.xmotion.x;
3081         y=event.xmotion.y;
3082         if (windows->info.mapped != MagickFalse)
3083           {
3084             if ((x < (int) (windows->info.x+windows->info.width)) &&
3085                 (y < (int) (windows->info.y+windows->info.height)))
3086               (void) XWithdrawWindow(display,windows->info.id,
3087                 windows->info.screen);
3088           }
3089         else
3090           if ((x > (int) (windows->info.x+windows->info.width)) ||
3091               (y > (int) (windows->info.y+windows->info.height)))
3092             (void) XMapWindow(display,windows->info.id);
3093       }
3094     }
3095   } while ((state & ExitState) == 0);
3096   (void) XSelectInput(display,windows->image.id,
3097     windows->image.attributes.event_mask);
3098   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3099   if ((state & EscapeState) != 0)
3100     return(MagickTrue);
3101   /*
3102     Draw line as pointer moves until the mouse button is released.
3103   */
3104   chop_info.width=0;
3105   chop_info.height=0;
3106   chop_info.x=0;
3107   chop_info.y=0;
3108   distance=0;
3109   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
3110   state=DefaultState;
3111   do
3112   {
3113     if (distance > 9)
3114       {
3115         /*
3116           Display info and draw chopping line.
3117         */
3118         if (windows->info.mapped == MagickFalse)
3119           (void) XMapWindow(display,windows->info.id);
3120         (void) FormatLocaleString(text,MaxTextExtent,
3121           " %.20gx%.20g%+.20g%+.20g",(double) chop_info.width,(double)
3122           chop_info.height,(double) chop_info.x,(double) chop_info.y);
3123         XInfoWidget(display,windows,text);
3124         XHighlightLine(display,windows->image.id,
3125           windows->image.highlight_context,&segment_info);
3126       }
3127     else
3128       if (windows->info.mapped != MagickFalse)
3129         (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3130     /*
3131       Wait for next event.
3132     */
3133     XScreenEvent(display,windows,&event);
3134     if (distance > 9)
3135       XHighlightLine(display,windows->image.id,
3136         windows->image.highlight_context,&segment_info);
3137     switch (event.type)
3138     {
3139       case ButtonPress:
3140       {
3141         segment_info.x2=(short int) event.xmotion.x;
3142         segment_info.y2=(short int) event.xmotion.y;
3143         break;
3144       }
3145       case ButtonRelease:
3146       {
3147         /*
3148           User has committed to chopping line.
3149         */
3150         segment_info.x2=(short int) event.xbutton.x;
3151         segment_info.y2=(short int) event.xbutton.y;
3152         state|=ExitState;
3153         break;
3154       }
3155       case Expose:
3156         break;
3157       case MotionNotify:
3158       {
3159         segment_info.x2=(short int) event.xmotion.x;
3160         segment_info.y2=(short int) event.xmotion.y;
3161       }
3162       default:
3163         break;
3164     }
3165     /*
3166       Check boundary conditions.
3167     */
3168     if (segment_info.x2 < 0)
3169       segment_info.x2=0;
3170     else
3171       if (segment_info.x2 > windows->image.ximage->width)
3172         segment_info.x2=windows->image.ximage->width;
3173     if (segment_info.y2 < 0)
3174       segment_info.y2=0;
3175     else
3176       if (segment_info.y2 > windows->image.ximage->height)
3177         segment_info.y2=windows->image.ximage->height;
3178     distance=(unsigned int)
3179       (((segment_info.x2-segment_info.x1)*(segment_info.x2-segment_info.x1))+
3180        ((segment_info.y2-segment_info.y1)*(segment_info.y2-segment_info.y1)));
3181     /*
3182       Compute chopping geometry.
3183     */
3184     if (direction == HorizontalChopCommand)
3185       {
3186         chop_info.width=(size_t) (segment_info.x2-segment_info.x1+1);
3187         chop_info.x=(ssize_t) windows->image.x+segment_info.x1;
3188         chop_info.height=0;
3189         chop_info.y=0;
3190         if (segment_info.x1 > (int) segment_info.x2)
3191           {
3192             chop_info.width=(size_t) (segment_info.x1-segment_info.x2+1);
3193             chop_info.x=(ssize_t) windows->image.x+segment_info.x2;
3194           }
3195       }
3196     else
3197       {
3198         chop_info.width=0;
3199         chop_info.height=(size_t) (segment_info.y2-segment_info.y1+1);
3200         chop_info.x=0;
3201         chop_info.y=(ssize_t) windows->image.y+segment_info.y1;
3202         if (segment_info.y1 > segment_info.y2)
3203           {
3204             chop_info.height=(size_t) (segment_info.y1-segment_info.y2+1);
3205             chop_info.y=(ssize_t) windows->image.y+segment_info.y2;
3206           }
3207       }
3208   } while ((state & ExitState) == 0);
3209   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
3210   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3211   if (distance <= 9)
3212     return(MagickTrue);
3213   /*
3214     Image chopping is relative to image configuration.
3215   */
3216   (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
3217     exception);
3218   XSetCursorState(display,windows,MagickTrue);
3219   XCheckRefreshWindows(display,windows);
3220   windows->image.window_changes.width=windows->image.ximage->width-
3221     (unsigned int) chop_info.width;
3222   windows->image.window_changes.height=windows->image.ximage->height-
3223     (unsigned int) chop_info.height;
3224   width=(unsigned int) (*image)->columns;
3225   height=(unsigned int) (*image)->rows;
3226   x=0;
3227   y=0;
3228   if (windows->image.crop_geometry != (char *) NULL)
3229     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
3230   scale_factor=(MagickRealType) width/windows->image.ximage->width;
3231   chop_info.x+=x;
3232   chop_info.x=(ssize_t) (scale_factor*chop_info.x+0.5);
3233   chop_info.width=(unsigned int) (scale_factor*chop_info.width+0.5);
3234   scale_factor=(MagickRealType) height/windows->image.ximage->height;
3235   chop_info.y+=y;
3236   chop_info.y=(ssize_t) (scale_factor*chop_info.y+0.5);
3237   chop_info.height=(unsigned int) (scale_factor*chop_info.height+0.5);
3238   /*
3239     Chop image.
3240   */
3241   chop_image=ChopImage(*image,&chop_info,exception);
3242   XSetCursorState(display,windows,MagickFalse);
3243   if (chop_image == (Image *) NULL)
3244     return(MagickFalse);
3245   *image=DestroyImage(*image);
3246   *image=chop_image;
3247   /*
3248     Update image configuration.
3249   */
3250   XConfigureImageColormap(display,resource_info,windows,*image);
3251   (void) XConfigureImage(display,resource_info,windows,*image,exception);
3252   return(MagickTrue);
3253 }
3254 \f
3255 /*
3256 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3257 %                                                                             %
3258 %                                                                             %
3259 %                                                                             %
3260 +   X C o l o r E d i t I m a g e                                             %
3261 %                                                                             %
3262 %                                                                             %
3263 %                                                                             %
3264 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3265 %
3266 %  XColorEditImage() allows the user to interactively change the color of one
3267 %  pixel for a DirectColor image or one colormap entry for a PseudoClass image.
3268 %
3269 %  The format of the XColorEditImage method is:
3270 %
3271 %      MagickBooleanType XColorEditImage(Display *display,
3272 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
3273 %          ExceptionInfo *exception)
3274 %
3275 %  A description of each parameter follows:
3276 %
3277 %    o display: Specifies a connection to an X server;  returned from
3278 %      XOpenDisplay.
3279 %
3280 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3281 %
3282 %    o windows: Specifies a pointer to a XWindows structure.
3283 %
3284 %    o image: the image; returned from ReadImage.
3285 %
3286 %    o exception: return any errors or warnings in this structure.
3287 %
3288 */
3289 static MagickBooleanType XColorEditImage(Display *display,
3290   XResourceInfo *resource_info,XWindows *windows,Image **image,
3291   ExceptionInfo *exception)
3292 {
3293   static const char
3294     *ColorEditMenu[] =
3295     {
3296       "Method",
3297       "Pixel Color",
3298       "Border Color",
3299       "Fuzz",
3300       "Undo",
3301       "Help",
3302       "Dismiss",
3303       (char *) NULL
3304     };
3305
3306   static const ModeType
3307     ColorEditCommands[] =
3308     {
3309       ColorEditMethodCommand,
3310       ColorEditColorCommand,
3311       ColorEditBorderCommand,
3312       ColorEditFuzzCommand,
3313       ColorEditUndoCommand,
3314       ColorEditHelpCommand,
3315       ColorEditDismissCommand
3316     };
3317
3318   static PaintMethod
3319     method = PointMethod;
3320
3321   static unsigned int
3322     pen_id = 0;
3323
3324   static XColor
3325     border_color = { 0, 0, 0, 0, 0, 0 };
3326
3327   char
3328     command[MaxTextExtent],
3329     text[MaxTextExtent];
3330
3331   Cursor
3332     cursor;
3333
3334   int
3335     entry,
3336     id,
3337     x,
3338     x_offset,
3339     y,
3340     y_offset;
3341
3342   register Quantum
3343     *q;
3344
3345   register ssize_t
3346     i;
3347
3348   unsigned int
3349     height,
3350     width;
3351
3352   size_t
3353     state;
3354
3355   XColor
3356     color;
3357
3358   XEvent
3359     event;
3360
3361   /*
3362     Map Command widget.
3363   */
3364   (void) CloneString(&windows->command.name,"Color Edit");
3365   windows->command.data=4;
3366   (void) XCommandWidget(display,windows,ColorEditMenu,(XEvent *) NULL);
3367   (void) XMapRaised(display,windows->command.id);
3368   XClientMessage(display,windows->image.id,windows->im_protocols,
3369     windows->im_update_widget,CurrentTime);
3370   /*
3371     Make cursor.
3372   */
3373   cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
3374     resource_info->background_color,resource_info->foreground_color);
3375   (void) XCheckDefineCursor(display,windows->image.id,cursor);
3376   /*
3377     Track pointer until button 1 is pressed.
3378   */
3379   XQueryPosition(display,windows->image.id,&x,&y);
3380   (void) XSelectInput(display,windows->image.id,
3381     windows->image.attributes.event_mask | PointerMotionMask);
3382   state=DefaultState;
3383   do
3384   {
3385     if (windows->info.mapped != MagickFalse)
3386       {
3387         /*
3388           Display pointer position.
3389         */
3390         (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
3391           x+windows->image.x,y+windows->image.y);
3392         XInfoWidget(display,windows,text);
3393       }
3394     /*
3395       Wait for next event.
3396     */
3397     XScreenEvent(display,windows,&event);
3398     if (event.xany.window == windows->command.id)
3399       {
3400         /*
3401           Select a command from the Command widget.
3402         */
3403         id=XCommandWidget(display,windows,ColorEditMenu,&event);
3404         if (id < 0)
3405           {
3406             (void) XCheckDefineCursor(display,windows->image.id,cursor);
3407             continue;
3408           }
3409         switch (ColorEditCommands[id])
3410         {
3411           case ColorEditMethodCommand:
3412           {
3413             char
3414               **methods;
3415
3416             /*
3417               Select a method from the pop-up menu.
3418             */
3419             methods=(char **) GetCommandOptions(MagickMethodOptions);
3420             if (methods == (char **) NULL)
3421               break;
3422             entry=XMenuWidget(display,windows,ColorEditMenu[id],
3423               (const char **) methods,command);
3424             if (entry >= 0)
3425               method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
3426                 MagickFalse,methods[entry]);
3427             methods=DestroyStringList(methods);
3428             break;
3429           }
3430           case ColorEditColorCommand:
3431           {
3432             const char
3433               *ColorMenu[MaxNumberPens];
3434
3435             int
3436               pen_number;
3437
3438             /*
3439               Initialize menu selections.
3440             */
3441             for (i=0; i < (int) (MaxNumberPens-2); i++)
3442               ColorMenu[i]=resource_info->pen_colors[i];
3443             ColorMenu[MaxNumberPens-2]="Browser...";
3444             ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3445             /*
3446               Select a pen color from the pop-up menu.
3447             */
3448             pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3449               (const char **) ColorMenu,command);
3450             if (pen_number < 0)
3451               break;
3452             if (pen_number == (MaxNumberPens-2))
3453               {
3454                 static char
3455                   color_name[MaxTextExtent] = "gray";
3456
3457                 /*
3458                   Select a pen color from a dialog.
3459                 */
3460                 resource_info->pen_colors[pen_number]=color_name;
3461                 XColorBrowserWidget(display,windows,"Select",color_name);
3462                 if (*color_name == '\0')
3463                   break;
3464               }
3465             /*
3466               Set pen color.
3467             */
3468             (void) XParseColor(display,windows->map_info->colormap,
3469               resource_info->pen_colors[pen_number],&color);
3470             XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
3471               (unsigned int) MaxColors,&color);
3472             windows->pixel_info->pen_colors[pen_number]=color;
3473             pen_id=(unsigned int) pen_number;
3474             break;
3475           }
3476           case ColorEditBorderCommand:
3477           {
3478             const char
3479               *ColorMenu[MaxNumberPens];
3480
3481             int
3482               pen_number;
3483
3484             /*
3485               Initialize menu selections.
3486             */
3487             for (i=0; i < (int) (MaxNumberPens-2); i++)
3488               ColorMenu[i]=resource_info->pen_colors[i];
3489             ColorMenu[MaxNumberPens-2]="Browser...";
3490             ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3491             /*
3492               Select a pen color from the pop-up menu.
3493             */
3494             pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3495               (const char **) ColorMenu,command);
3496             if (pen_number < 0)
3497               break;
3498             if (pen_number == (MaxNumberPens-2))
3499               {
3500                 static char
3501                   color_name[MaxTextExtent] = "gray";
3502
3503                 /*
3504                   Select a pen color from a dialog.
3505                 */
3506                 resource_info->pen_colors[pen_number]=color_name;
3507                 XColorBrowserWidget(display,windows,"Select",color_name);
3508                 if (*color_name == '\0')
3509                   break;
3510               }
3511             /*
3512               Set border color.
3513             */
3514             (void) XParseColor(display,windows->map_info->colormap,
3515               resource_info->pen_colors[pen_number],&border_color);
3516             break;
3517           }
3518           case ColorEditFuzzCommand:
3519           {
3520             static char
3521               fuzz[MaxTextExtent];
3522
3523             static const char
3524               *FuzzMenu[] =
3525               {
3526                 "0%",
3527                 "2%",
3528                 "5%",
3529                 "10%",
3530                 "15%",
3531                 "Dialog...",
3532                 (char *) NULL,
3533               };
3534
3535             /*
3536               Select a command from the pop-up menu.
3537             */
3538             entry=XMenuWidget(display,windows,ColorEditMenu[id],FuzzMenu,
3539               command);
3540             if (entry < 0)
3541               break;
3542             if (entry != 5)
3543               {
3544                 (*image)->fuzz=SiPrefixToDouble(FuzzMenu[entry],1.0*
3545                   QuantumRange+1.0);
3546                 break;
3547               }
3548             (void) (void) CopyMagickString(fuzz,"20%",MaxTextExtent);
3549             (void) XDialogWidget(display,windows,"Ok",
3550               "Enter fuzz factor (0.0 - 99.9%):",fuzz);
3551             if (*fuzz == '\0')
3552               break;
3553             (void) ConcatenateMagickString(fuzz,"%",MaxTextExtent);
3554             (*image)->fuzz=SiPrefixToDouble(fuzz,1.0*QuantumRange+1.0);
3555             break;
3556           }
3557           case ColorEditUndoCommand:
3558           {
3559             (void) XMagickCommand(display,resource_info,windows,UndoCommand,
3560               image,exception);
3561             break;
3562           }
3563           case ColorEditHelpCommand:
3564           default:
3565           {
3566             XTextViewWidget(display,resource_info,windows,MagickFalse,
3567               "Help Viewer - Image Annotation",ImageColorEditHelp);
3568             break;
3569           }
3570           case ColorEditDismissCommand:
3571           {
3572             /*
3573               Prematurely exit.
3574             */
3575             state|=EscapeState;
3576             state|=ExitState;
3577             break;
3578           }
3579         }
3580         (void) XCheckDefineCursor(display,windows->image.id,cursor);
3581         continue;
3582       }
3583     switch (event.type)
3584     {
3585       case ButtonPress:
3586       {
3587         if (event.xbutton.button != Button1)
3588           break;
3589         if ((event.xbutton.window != windows->image.id) &&
3590             (event.xbutton.window != windows->magnify.id))
3591           break;
3592         /*
3593           exit loop.
3594         */
3595         x=event.xbutton.x;
3596         y=event.xbutton.y;
3597         (void) XMagickCommand(display,resource_info,windows,
3598           SaveToUndoBufferCommand,image,exception);
3599         state|=UpdateConfigurationState;
3600         break;
3601       }
3602       case ButtonRelease:
3603       {
3604         if (event.xbutton.button != Button1)
3605           break;
3606         if ((event.xbutton.window != windows->image.id) &&
3607             (event.xbutton.window != windows->magnify.id))
3608           break;
3609         /*
3610           Update colormap information.
3611         */
3612         x=event.xbutton.x;
3613         y=event.xbutton.y;
3614         XConfigureImageColormap(display,resource_info,windows,*image);
3615         (void) XConfigureImage(display,resource_info,windows,*image,exception);
3616         XInfoWidget(display,windows,text);
3617         (void) XCheckDefineCursor(display,windows->image.id,cursor);
3618         state&=(~UpdateConfigurationState);
3619         break;
3620       }
3621       case Expose:
3622         break;
3623       case KeyPress:
3624       {
3625         KeySym
3626           key_symbol;
3627
3628         if (event.xkey.window == windows->magnify.id)
3629           {
3630             Window
3631               window;
3632
3633             window=windows->magnify.id;
3634             while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
3635           }
3636         if (event.xkey.window != windows->image.id)
3637           break;
3638         /*
3639           Respond to a user key press.
3640         */
3641         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3642           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3643         switch ((int) key_symbol)
3644         {
3645           case XK_Escape:
3646           case XK_F20:
3647           {
3648             /*
3649               Prematurely exit.
3650             */
3651             state|=ExitState;
3652             break;
3653           }
3654           case XK_F1:
3655           case XK_Help:
3656           {
3657             XTextViewWidget(display,resource_info,windows,MagickFalse,
3658               "Help Viewer - Image Annotation",ImageColorEditHelp);
3659             break;
3660           }
3661           default:
3662           {
3663             (void) XBell(display,0);
3664             break;
3665           }
3666         }
3667         break;
3668       }
3669       case MotionNotify:
3670       {
3671         /*
3672           Map and unmap Info widget as cursor crosses its boundaries.
3673         */
3674         x=event.xmotion.x;
3675         y=event.xmotion.y;
3676         if (windows->info.mapped != MagickFalse)
3677           {
3678             if ((x < (int) (windows->info.x+windows->info.width)) &&
3679                 (y < (int) (windows->info.y+windows->info.height)))
3680               (void) XWithdrawWindow(display,windows->info.id,
3681                 windows->info.screen);
3682           }
3683         else
3684           if ((x > (int) (windows->info.x+windows->info.width)) ||
3685               (y > (int) (windows->info.y+windows->info.height)))
3686             (void) XMapWindow(display,windows->info.id);
3687         break;
3688       }
3689       default:
3690         break;
3691     }
3692     if (event.xany.window == windows->magnify.id)
3693       {
3694         x=windows->magnify.x-windows->image.x;
3695         y=windows->magnify.y-windows->image.y;
3696       }
3697     x_offset=x;
3698     y_offset=y;
3699     if ((state & UpdateConfigurationState) != 0)
3700       {
3701         CacheView
3702           *image_view;
3703
3704         int
3705           x,
3706           y;
3707
3708         /*
3709           Pixel edit is relative to image configuration.
3710         */
3711         (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
3712           MagickTrue);
3713         color=windows->pixel_info->pen_colors[pen_id];
3714         XPutPixel(windows->image.ximage,x_offset,y_offset,color.pixel);
3715         width=(unsigned int) (*image)->columns;
3716         height=(unsigned int) (*image)->rows;
3717         x=0;
3718         y=0;
3719         if (windows->image.crop_geometry != (char *) NULL)
3720           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
3721             &width,&height);
3722         x_offset=(int)
3723           (width*(windows->image.x+x_offset)/windows->image.ximage->width+x);
3724         y_offset=(int)
3725           (height*(windows->image.y+y_offset)/windows->image.ximage->height+y);
3726         if ((x_offset < 0) || (y_offset < 0))
3727           continue;
3728         if ((x_offset >= (int) (*image)->columns) ||
3729             (y_offset >= (int) (*image)->rows))
3730           continue;
3731         image_view=AcquireCacheView(*image);
3732         switch (method)
3733         {
3734           case PointMethod:
3735           default:
3736           {
3737             /*
3738               Update color information using point algorithm.
3739             */
3740             if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
3741               return(MagickFalse);
3742             q=GetCacheViewAuthenticPixels(image_view,(ssize_t)x_offset,
3743               (ssize_t) y_offset,1,1,exception);
3744             if (q == (Quantum *) NULL)
3745               break;
3746             SetPixelRed(*image,ScaleShortToQuantum(color.red),q);
3747             SetPixelGreen(*image,ScaleShortToQuantum(color.green),q);
3748             SetPixelBlue(*image,ScaleShortToQuantum(color.blue),q);
3749             (void) SyncCacheViewAuthenticPixels(image_view,exception);
3750             break;
3751           }
3752           case ReplaceMethod:
3753           {
3754             PixelPacket
3755               pixel,
3756               target;
3757
3758             /*
3759               Update color information using replace algorithm.
3760             */
3761             (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t) x_offset,
3762               (ssize_t) y_offset,&target,exception);
3763             if ((*image)->storage_class == DirectClass)
3764               {
3765                 for (y=0; y < (int) (*image)->rows; y++)
3766                 {
3767                   q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3768                     (*image)->columns,1,exception);
3769                   if (q == (Quantum *) NULL)
3770                     break;
3771                   for (x=0; x < (int) (*image)->columns; x++)
3772                   {
3773                     GetPixelPacket(*image,q,&pixel);
3774                     if (IsFuzzyEquivalencePixelPacket(*image,&pixel,&target))
3775                       {
3776                         SetPixelRed(*image,ScaleShortToQuantum(
3777                           color.red),q);
3778                         SetPixelGreen(*image,ScaleShortToQuantum(
3779                           color.green),q);
3780                         SetPixelBlue(*image,ScaleShortToQuantum(
3781                           color.blue),q);
3782                       }
3783                     q+=GetPixelChannels(*image);
3784                   }
3785                   if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3786                     break;
3787                 }
3788               }
3789             else
3790               {
3791                 for (i=0; i < (ssize_t) (*image)->colors; i++)
3792                   if (IsFuzzyEquivalencePixelPacket(*image,(*image)->colormap+i,&target))
3793                     {
3794                       (*image)->colormap[i].red=ScaleShortToQuantum(
3795                         color.red);
3796                       (*image)->colormap[i].green=ScaleShortToQuantum(
3797                         color.green);
3798                       (*image)->colormap[i].blue=ScaleShortToQuantum(
3799                         color.blue);
3800                     }
3801                 (void) SyncImage(*image);
3802               }
3803             break;
3804           }
3805           case FloodfillMethod:
3806           case FillToBorderMethod:
3807           {
3808             DrawInfo
3809               *draw_info;
3810
3811             PixelInfo
3812               target;
3813
3814             /*
3815               Update color information using floodfill algorithm.
3816             */
3817             (void) GetOneVirtualMagickPixel(*image,(ssize_t) x_offset,
3818               (ssize_t) y_offset,&target,exception);
3819             if (method == FillToBorderMethod)
3820               {
3821                 target.red=(MagickRealType)
3822                   ScaleShortToQuantum(border_color.red);
3823                 target.green=(MagickRealType)
3824                   ScaleShortToQuantum(border_color.green);
3825                 target.blue=(MagickRealType)
3826                   ScaleShortToQuantum(border_color.blue);
3827               }
3828             draw_info=CloneDrawInfo(resource_info->image_info,
3829               (DrawInfo *) NULL);
3830             (void) QueryColorDatabase(resource_info->pen_colors[pen_id],
3831               &draw_info->fill,exception);
3832             (void) FloodfillPaintImage(*image,draw_info,&target,(ssize_t)
3833               x_offset,(ssize_t) y_offset,method == FloodfillMethod ?
3834               MagickFalse : MagickTrue,exception);
3835             draw_info=DestroyDrawInfo(draw_info);
3836             break;
3837           }
3838           case ResetMethod:
3839           {
3840             /*
3841               Update color information using reset algorithm.
3842             */
3843             if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
3844               return(MagickFalse);
3845             for (y=0; y < (int) (*image)->rows; y++)
3846             {
3847               q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3848                 (*image)->columns,1,exception);
3849               if (q == (Quantum *) NULL)
3850                 break;
3851               for (x=0; x < (int) (*image)->columns; x++)
3852               {
3853                 SetPixelRed(*image,ScaleShortToQuantum(color.red),q);
3854                 SetPixelGreen(*image,ScaleShortToQuantum(color.green),q);
3855                 SetPixelBlue(*image,ScaleShortToQuantum(color.blue),q);
3856                 q+=GetPixelChannels(*image);
3857               }
3858               if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3859                 break;
3860             }
3861             break;
3862           }
3863         }
3864         image_view=DestroyCacheView(image_view);
3865         state&=(~UpdateConfigurationState);
3866       }
3867   } while ((state & ExitState) == 0);
3868   (void) XSelectInput(display,windows->image.id,
3869     windows->image.attributes.event_mask);
3870   XSetCursorState(display,windows,MagickFalse);
3871   (void) XFreeCursor(display,cursor);
3872   return(MagickTrue);
3873 }
3874 \f
3875 /*
3876 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3877 %                                                                             %
3878 %                                                                             %
3879 %                                                                             %
3880 +   X C o m p o s i t e I m a g e                                             %
3881 %                                                                             %
3882 %                                                                             %
3883 %                                                                             %
3884 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3885 %
3886 %  XCompositeImage() requests an image name from the user, reads the image and
3887 %  composites it with the X window image at a location the user chooses with
3888 %  the pointer.
3889 %
3890 %  The format of the XCompositeImage method is:
3891 %
3892 %      MagickBooleanType XCompositeImage(Display *display,
3893 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
3894 %        ExceptionInfo *exception)
3895 %
3896 %  A description of each parameter follows:
3897 %
3898 %    o display: Specifies a connection to an X server;  returned from
3899 %      XOpenDisplay.
3900 %
3901 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3902 %
3903 %    o windows: Specifies a pointer to a XWindows structure.
3904 %
3905 %    o image: the image; returned from ReadImage.
3906 %
3907 %    o exception: return any errors or warnings in this structure.
3908 %
3909 */
3910 static MagickBooleanType XCompositeImage(Display *display,
3911   XResourceInfo *resource_info,XWindows *windows,Image *image,
3912   ExceptionInfo *exception)
3913 {
3914   static char
3915     displacement_geometry[MaxTextExtent] = "30x30",
3916     filename[MaxTextExtent] = "\0";
3917
3918   static const char
3919     *CompositeMenu[] =
3920     {
3921       "Operators",
3922       "Dissolve",
3923       "Displace",
3924       "Help",
3925       "Dismiss",
3926       (char *) NULL
3927     };
3928
3929   static CompositeOperator
3930     compose = CopyCompositeOp;
3931
3932   static const ModeType
3933     CompositeCommands[] =
3934     {
3935       CompositeOperatorsCommand,
3936       CompositeDissolveCommand,
3937       CompositeDisplaceCommand,
3938       CompositeHelpCommand,
3939       CompositeDismissCommand
3940     };
3941
3942   char
3943     text[MaxTextExtent];
3944
3945   Cursor
3946     cursor;
3947
3948   Image
3949     *composite_image;
3950
3951   int
3952     entry,
3953     id,
3954     x,
3955     y;
3956
3957   MagickRealType
3958     blend,
3959     scale_factor;
3960
3961   RectangleInfo
3962     highlight_info,
3963     composite_info;
3964
3965   unsigned int
3966     height,
3967     width;
3968
3969   size_t
3970     state;
3971
3972   XEvent
3973     event;
3974
3975   /*
3976     Request image file name from user.
3977   */
3978   XFileBrowserWidget(display,windows,"Composite",filename);
3979   if (*filename == '\0')
3980     return(MagickTrue);
3981   /*
3982     Read image.
3983   */
3984   XSetCursorState(display,windows,MagickTrue);
3985   XCheckRefreshWindows(display,windows);
3986   (void) CopyMagickString(resource_info->image_info->filename,filename,
3987     MaxTextExtent);
3988   composite_image=ReadImage(resource_info->image_info,exception);
3989   CatchException(exception);
3990   XSetCursorState(display,windows,MagickFalse);
3991   if (composite_image == (Image *) NULL)
3992     return(MagickFalse);
3993   /*
3994     Map Command widget.
3995   */
3996   (void) CloneString(&windows->command.name,"Composite");
3997   windows->command.data=1;
3998   (void) XCommandWidget(display,windows,CompositeMenu,(XEvent *) NULL);
3999   (void) XMapRaised(display,windows->command.id);
4000   XClientMessage(display,windows->image.id,windows->im_protocols,
4001     windows->im_update_widget,CurrentTime);
4002   /*
4003     Track pointer until button 1 is pressed.
4004   */
4005   XQueryPosition(display,windows->image.id,&x,&y);
4006   (void) XSelectInput(display,windows->image.id,
4007     windows->image.attributes.event_mask | PointerMotionMask);
4008   composite_info.x=(ssize_t) windows->image.x+x;
4009   composite_info.y=(ssize_t) windows->image.y+y;
4010   composite_info.width=0;
4011   composite_info.height=0;
4012   cursor=XCreateFontCursor(display,XC_ul_angle);
4013   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
4014   blend=0.0;
4015   state=DefaultState;
4016   do
4017   {
4018     if (windows->info.mapped != MagickFalse)
4019       {
4020         /*
4021           Display pointer position.
4022         */
4023         (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
4024           (long) composite_info.x,(long) composite_info.y);
4025         XInfoWidget(display,windows,text);
4026       }
4027     highlight_info=composite_info;
4028     highlight_info.x=composite_info.x-windows->image.x;
4029     highlight_info.y=composite_info.y-windows->image.y;
4030     XHighlightRectangle(display,windows->image.id,
4031       windows->image.highlight_context,&highlight_info);
4032     /*
4033       Wait for next event.
4034     */
4035     XScreenEvent(display,windows,&event);
4036     XHighlightRectangle(display,windows->image.id,
4037       windows->image.highlight_context,&highlight_info);
4038     if (event.xany.window == windows->command.id)
4039       {
4040         /*
4041           Select a command from the Command widget.
4042         */
4043         id=XCommandWidget(display,windows,CompositeMenu,&event);
4044         if (id < 0)
4045           continue;
4046         switch (CompositeCommands[id])
4047         {
4048           case CompositeOperatorsCommand:
4049           {
4050             char
4051               command[MaxTextExtent],
4052               **operators;
4053
4054             /*
4055               Select a command from the pop-up menu.
4056             */
4057             operators=GetCommandOptions(MagickComposeOptions);
4058             if (operators == (char **) NULL)
4059               break;
4060             entry=XMenuWidget(display,windows,CompositeMenu[id],
4061               (const char **) operators,command);
4062             if (entry >= 0)
4063               compose=(CompositeOperator) ParseCommandOption(
4064                 MagickComposeOptions,MagickFalse,operators[entry]);
4065             operators=DestroyStringList(operators);
4066             break;
4067           }
4068           case CompositeDissolveCommand:
4069           {
4070             static char
4071               factor[MaxTextExtent] = "20.0";
4072
4073             /*
4074               Dissolve the two images a given percent.
4075             */
4076             (void) XSetFunction(display,windows->image.highlight_context,
4077               GXcopy);
4078             (void) XDialogWidget(display,windows,"Dissolve",
4079               "Enter the blend factor (0.0 - 99.9%):",factor);
4080             (void) XSetFunction(display,windows->image.highlight_context,
4081               GXinvert);
4082             if (*factor == '\0')
4083               break;
4084             blend=InterpretLocaleValue(factor,(char **) NULL);
4085             compose=DissolveCompositeOp;
4086             break;
4087           }
4088           case CompositeDisplaceCommand:
4089           {
4090             /*
4091               Get horizontal and vertical scale displacement geometry.
4092             */
4093             (void) XSetFunction(display,windows->image.highlight_context,
4094               GXcopy);
4095             (void) XDialogWidget(display,windows,"Displace",
4096               "Enter the horizontal and vertical scale:",displacement_geometry);
4097             (void) XSetFunction(display,windows->image.highlight_context,
4098               GXinvert);
4099             if (*displacement_geometry == '\0')
4100               break;
4101             compose=DisplaceCompositeOp;
4102             break;
4103           }
4104           case CompositeHelpCommand:
4105           {
4106             (void) XSetFunction(display,windows->image.highlight_context,
4107               GXcopy);
4108             XTextViewWidget(display,resource_info,windows,MagickFalse,
4109               "Help Viewer - Image Composite",ImageCompositeHelp);
4110             (void) XSetFunction(display,windows->image.highlight_context,
4111               GXinvert);
4112             break;
4113           }
4114           case CompositeDismissCommand:
4115           {
4116             /*
4117               Prematurely exit.
4118             */
4119             state|=EscapeState;
4120             state|=ExitState;
4121             break;
4122           }
4123           default:
4124             break;
4125         }
4126         continue;
4127       }
4128     switch (event.type)
4129     {
4130       case ButtonPress:
4131       {
4132         if (image->debug != MagickFalse)
4133           (void) LogMagickEvent(X11Event,GetMagickModule(),
4134             "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
4135             event.xbutton.button,event.xbutton.x,event.xbutton.y);
4136         if (event.xbutton.button != Button1)
4137           break;
4138         if (event.xbutton.window != windows->image.id)
4139           break;
4140         /*
4141           Change cursor.
4142         */
4143         composite_info.width=composite_image->columns;
4144         composite_info.height=composite_image->rows;
4145         (void) XCheckDefineCursor(display,windows->image.id,cursor);
4146         composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4147         composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4148         break;
4149       }
4150       case ButtonRelease:
4151       {
4152         if (image->debug != MagickFalse)
4153           (void) LogMagickEvent(X11Event,GetMagickModule(),
4154             "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
4155             event.xbutton.button,event.xbutton.x,event.xbutton.y);
4156         if (event.xbutton.button != Button1)
4157           break;
4158         if (event.xbutton.window != windows->image.id)
4159           break;
4160         if ((composite_info.width != 0) && (composite_info.height != 0))
4161           {
4162             /*
4163               User has selected the location of the composite image.
4164             */
4165             composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4166             composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4167             state|=ExitState;
4168           }
4169         break;
4170       }
4171       case Expose:
4172         break;
4173       case KeyPress:
4174       {
4175         char
4176           command[MaxTextExtent];
4177
4178         KeySym
4179           key_symbol;
4180
4181         int
4182           length;
4183
4184         if (event.xkey.window != windows->image.id)
4185           break;
4186         /*
4187           Respond to a user key press.
4188         */
4189         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
4190           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4191         *(command+length)='\0';
4192         if (image->debug != MagickFalse)
4193           (void) LogMagickEvent(X11Event,GetMagickModule(),
4194             "Key press: 0x%lx (%s)",(unsigned long) key_symbol,command);
4195         switch ((int) key_symbol)
4196         {
4197           case XK_Escape:
4198           case XK_F20:
4199           {
4200             /*
4201               Prematurely exit.
4202             */
4203             composite_image=DestroyImage(composite_image);
4204             state|=EscapeState;
4205             state|=ExitState;
4206             break;
4207           }
4208           case XK_F1:
4209           case XK_Help:
4210           {
4211             (void) XSetFunction(display,windows->image.highlight_context,
4212               GXcopy);
4213             XTextViewWidget(display,resource_info,windows,MagickFalse,
4214               "Help Viewer - Image Composite",ImageCompositeHelp);
4215             (void) XSetFunction(display,windows->image.highlight_context,
4216               GXinvert);
4217             break;
4218           }
4219           default:
4220           {
4221             (void) XBell(display,0);
4222             break;
4223           }
4224         }
4225         break;
4226       }
4227       case MotionNotify:
4228       {
4229         /*
4230           Map and unmap Info widget as text cursor crosses its boundaries.
4231         */
4232         x=event.xmotion.x;
4233         y=event.xmotion.y;
4234         if (windows->info.mapped != MagickFalse)
4235           {
4236             if ((x < (int) (windows->info.x+windows->info.width)) &&
4237                 (y < (int) (windows->info.y+windows->info.height)))
4238               (void) XWithdrawWindow(display,windows->info.id,
4239                 windows->info.screen);
4240           }
4241         else
4242           if ((x > (int) (windows->info.x+windows->info.width)) ||
4243               (y > (int) (windows->info.y+windows->info.height)))
4244             (void) XMapWindow(display,windows->info.id);
4245         composite_info.x=(ssize_t) windows->image.x+x;
4246         composite_info.y=(ssize_t) windows->image.y+y;
4247         break;
4248       }
4249       default:
4250       {
4251         if (image->debug != MagickFalse)
4252           (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
4253             event.type);
4254         break;
4255       }
4256     }
4257   } while ((state & ExitState) == 0);
4258   (void) XSelectInput(display,windows->image.id,
4259     windows->image.attributes.event_mask);
4260   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
4261   XSetCursorState(display,windows,MagickFalse);
4262   (void) XFreeCursor(display,cursor);
4263   if ((state & EscapeState) != 0)
4264     return(MagickTrue);
4265   /*
4266     Image compositing is relative to image configuration.
4267   */
4268   XSetCursorState(display,windows,MagickTrue);
4269   XCheckRefreshWindows(display,windows);
4270   width=(unsigned int) image->columns;
4271   height=(unsigned int) image->rows;
4272   x=0;
4273   y=0;
4274   if (windows->image.crop_geometry != (char *) NULL)
4275     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
4276   scale_factor=(MagickRealType) width/windows->image.ximage->width;
4277   composite_info.x+=x;
4278   composite_info.x=(ssize_t) (scale_factor*composite_info.x+0.5);
4279   composite_info.width=(unsigned int) (scale_factor*composite_info.width+0.5);
4280   scale_factor=(MagickRealType) height/windows->image.ximage->height;
4281   composite_info.y+=y;
4282   composite_info.y=(ssize_t) (scale_factor*composite_info.y+0.5);
4283   composite_info.height=(unsigned int) (scale_factor*composite_info.height+0.5);
4284   if ((composite_info.width != composite_image->columns) ||
4285       (composite_info.height != composite_image->rows))
4286     {
4287       Image
4288         *resize_image;
4289
4290       /*
4291         Scale composite image.
4292       */
4293       resize_image=ResizeImage(composite_image,composite_info.width,
4294         composite_info.height,composite_image->filter,composite_image->blur,
4295         exception);
4296       composite_image=DestroyImage(composite_image);
4297       if (resize_image == (Image *) NULL)
4298         {
4299           XSetCursorState(display,windows,MagickFalse);
4300           return(MagickFalse);
4301         }
4302       composite_image=resize_image;
4303     }
4304   if (compose == DisplaceCompositeOp)
4305     (void) SetImageArtifact(composite_image,"compose:args",
4306       displacement_geometry);
4307   if (blend != 0.0)
4308     {
4309       CacheView
4310         *image_view;
4311
4312       int
4313         y;
4314
4315       Quantum
4316         opacity;
4317
4318       register int
4319         x;
4320
4321       register Quantum
4322         *q;
4323
4324       /*
4325         Create mattes for blending.
4326       */
4327       (void) SetImageAlphaChannel(composite_image,OpaqueAlphaChannel,exception);
4328       opacity=(Quantum) (ScaleQuantumToChar((Quantum) QuantumRange)-
4329         ((ssize_t) ScaleQuantumToChar((Quantum) QuantumRange)*blend)/100);
4330       if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
4331         return(MagickFalse);
4332       image->matte=MagickTrue;
4333       image_view=AcquireCacheView(image);
4334       for (y=0; y < (int) image->rows; y++)
4335       {
4336         q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,image->columns,1,
4337           exception);
4338         if (q == (Quantum *) NULL)
4339           break;
4340         for (x=0; x < (int) image->columns; x++)
4341         {
4342           SetPixelAlpha(image,opacity,q);
4343           q+=GetPixelChannels(image);
4344         }
4345         if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4346           break;
4347       }
4348       image_view=DestroyCacheView(image_view);
4349     }
4350   /*
4351     Composite image with X Image window.
4352   */
4353   (void) CompositeImage(image,compose,composite_image,composite_info.x,
4354     composite_info.y);
4355   composite_image=DestroyImage(composite_image);
4356   XSetCursorState(display,windows,MagickFalse);
4357   /*
4358     Update image configuration.
4359   */
4360   XConfigureImageColormap(display,resource_info,windows,image);
4361   (void) XConfigureImage(display,resource_info,windows,image,exception);
4362   return(MagickTrue);
4363 }
4364 \f
4365 /*
4366 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4367 %                                                                             %
4368 %                                                                             %
4369 %                                                                             %
4370 +   X C o n f i g u r e I m a g e                                             %
4371 %                                                                             %
4372 %                                                                             %
4373 %                                                                             %
4374 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4375 %
4376 %  XConfigureImage() creates a new X image.  It also notifies the window
4377 %  manager of the new image size and configures the transient widows.
4378 %
4379 %  The format of the XConfigureImage method is:
4380 %
4381 %      MagickBooleanType XConfigureImage(Display *display,
4382 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
4383 %        ExceptionInfo *exception)
4384 %
4385 %  A description of each parameter follows:
4386 %
4387 %    o display: Specifies a connection to an X server; returned from
4388 %      XOpenDisplay.
4389 %
4390 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4391 %
4392 %    o windows: Specifies a pointer to a XWindows structure.
4393 %
4394 %    o image: the image.
4395 %
4396 %    o exception: return any errors or warnings in this structure.
4397 %
4398 %    o exception: return any errors or warnings in this structure.
4399 %
4400 */
4401 static MagickBooleanType XConfigureImage(Display *display,
4402   XResourceInfo *resource_info,XWindows *windows,Image *image,
4403   ExceptionInfo *exception)
4404 {
4405   char
4406     geometry[MaxTextExtent];
4407
4408   MagickStatusType
4409     status;
4410
4411   size_t
4412     mask,
4413     height,
4414     width;
4415
4416   ssize_t
4417     x,
4418     y;
4419
4420   XSizeHints
4421     *size_hints;
4422
4423   XWindowChanges
4424     window_changes;
4425
4426   /*
4427     Dismiss if window dimensions are zero.
4428   */
4429   width=(unsigned int) windows->image.window_changes.width;
4430   height=(unsigned int) windows->image.window_changes.height;
4431   if (image->debug != MagickFalse)
4432     (void) LogMagickEvent(X11Event,GetMagickModule(),
4433       "Configure Image: %dx%d=>%.20gx%.20g",windows->image.ximage->width,
4434       windows->image.ximage->height,(double) width,(double) height);
4435   if ((width*height) == 0)
4436     return(MagickTrue);
4437   x=0;
4438   y=0;
4439   /*
4440     Resize image to fit Image window dimensions.
4441   */
4442   XSetCursorState(display,windows,MagickTrue);
4443   (void) XFlush(display);
4444   if (((int) width != windows->image.ximage->width) ||
4445       ((int) height != windows->image.ximage->height))
4446     image->taint=MagickTrue;
4447   windows->magnify.x=(int)
4448     width*windows->magnify.x/windows->image.ximage->width;
4449   windows->magnify.y=(int)
4450     height*windows->magnify.y/windows->image.ximage->height;
4451   windows->image.x=(int) (width*windows->image.x/windows->image.ximage->width);
4452   windows->image.y=(int)
4453     (height*windows->image.y/windows->image.ximage->height);
4454   status=XMakeImage(display,resource_info,&windows->image,image,
4455     (unsigned int) width,(unsigned int) height,exception);
4456   if (status == MagickFalse)
4457     XNoticeWidget(display,windows,"Unable to configure X image:",
4458       windows->image.name);
4459   /*
4460     Notify window manager of the new configuration.
4461   */
4462   if (resource_info->image_geometry != (char *) NULL)
4463     (void) FormatLocaleString(geometry,MaxTextExtent,"%s>!",
4464       resource_info->image_geometry);
4465   else
4466     (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>!",
4467       XDisplayWidth(display,windows->image.screen),
4468       XDisplayHeight(display,windows->image.screen));
4469   (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
4470   window_changes.width=(int) width;
4471   if (window_changes.width > XDisplayWidth(display,windows->image.screen))
4472     window_changes.width=XDisplayWidth(display,windows->image.screen);
4473   window_changes.height=(int) height;
4474   if (window_changes.height > XDisplayHeight(display,windows->image.screen))
4475     window_changes.height=XDisplayHeight(display,windows->image.screen);
4476   mask=(size_t) (CWWidth | CWHeight);
4477   if (resource_info->backdrop)
4478     {
4479       mask|=CWX | CWY;
4480       window_changes.x=(int)
4481         ((XDisplayWidth(display,windows->image.screen)/2)-(width/2));
4482       window_changes.y=(int)
4483         ((XDisplayHeight(display,windows->image.screen)/2)-(height/2));
4484     }
4485   (void) XReconfigureWMWindow(display,windows->image.id,windows->image.screen,
4486     (unsigned int) mask,&window_changes);
4487   (void) XClearWindow(display,windows->image.id);
4488   XRefreshWindow(display,&windows->image,(XEvent *) NULL);
4489   /*
4490     Update Magnify window configuration.
4491   */
4492   if (windows->magnify.mapped != MagickFalse)
4493     XMakeMagnifyImage(display,windows);
4494   windows->pan.crop_geometry=windows->image.crop_geometry;
4495   XBestIconSize(display,&windows->pan,image);
4496   while (((windows->pan.width << 1) < MaxIconSize) &&
4497          ((windows->pan.height << 1) < MaxIconSize))
4498   {
4499     windows->pan.width<<=1;
4500     windows->pan.height<<=1;
4501   }
4502   if (windows->pan.geometry != (char *) NULL)
4503     (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
4504       &windows->pan.width,&windows->pan.height);
4505   window_changes.width=(int) windows->pan.width;
4506   window_changes.height=(int) windows->pan.height;
4507   size_hints=XAllocSizeHints();
4508   if (size_hints != (XSizeHints *) NULL)
4509     {
4510       /*
4511         Set new size hints.
4512       */
4513       size_hints->flags=PSize | PMinSize | PMaxSize;
4514       size_hints->width=window_changes.width;
4515       size_hints->height=window_changes.height;
4516       size_hints->min_width=size_hints->width;
4517       size_hints->min_height=size_hints->height;
4518       size_hints->max_width=size_hints->width;
4519       size_hints->max_height=size_hints->height;
4520       (void) XSetNormalHints(display,windows->pan.id,size_hints);
4521       (void) XFree((void *) size_hints);
4522     }
4523   (void) XReconfigureWMWindow(display,windows->pan.id,windows->pan.screen,
4524     (unsigned int) (CWWidth | CWHeight),&window_changes);
4525   /*
4526     Update icon window configuration.
4527   */
4528   windows->icon.crop_geometry=windows->image.crop_geometry;
4529   XBestIconSize(display,&windows->icon,image);
4530   window_changes.width=(int) windows->icon.width;
4531   window_changes.height=(int) windows->icon.height;
4532   (void) XReconfigureWMWindow(display,windows->icon.id,windows->icon.screen,
4533     (unsigned int) (CWWidth | CWHeight),&window_changes);
4534   XSetCursorState(display,windows,MagickFalse);
4535   return(status != 0 ? MagickTrue : MagickFalse);
4536 }
4537 \f
4538 /*
4539 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4540 %                                                                             %
4541 %                                                                             %
4542 %                                                                             %
4543 +   X C r o p I m a g e                                                       %
4544 %                                                                             %
4545 %                                                                             %
4546 %                                                                             %
4547 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4548 %
4549 %  XCropImage() allows the user to select a region of the image and crop, copy,
4550 %  or cut it.  For copy or cut, the image can subsequently be composited onto
4551 %  the image with XPasteImage.
4552 %
4553 %  The format of the XCropImage method is:
4554 %
4555 %      MagickBooleanType XCropImage(Display *display,
4556 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
4557 %        const ClipboardMode mode,ExceptionInfo *exception)
4558 %
4559 %  A description of each parameter follows:
4560 %
4561 %    o display: Specifies a connection to an X server; returned from
4562 %      XOpenDisplay.
4563 %
4564 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4565 %
4566 %    o windows: Specifies a pointer to a XWindows structure.
4567 %
4568 %    o image: the image; returned from ReadImage.
4569 %
4570 %    o mode: This unsigned value specified whether the image should be
4571 %      cropped, copied, or cut.
4572 %
4573 %    o exception: return any errors or warnings in this structure.
4574 %
4575 */
4576 static MagickBooleanType XCropImage(Display *display,
4577   XResourceInfo *resource_info,XWindows *windows,Image *image,
4578   const ClipboardMode mode,ExceptionInfo *exception)
4579 {
4580   static const char
4581     *CropModeMenu[] =
4582     {
4583       "Help",
4584       "Dismiss",
4585       (char *) NULL
4586     },
4587     *RectifyModeMenu[] =
4588     {
4589       "Crop",
4590       "Help",
4591       "Dismiss",
4592       (char *) NULL
4593     };
4594
4595   static const ModeType
4596     CropCommands[] =
4597     {
4598       CropHelpCommand,
4599       CropDismissCommand
4600     },
4601     RectifyCommands[] =
4602     {
4603       RectifyCopyCommand,
4604       RectifyHelpCommand,
4605       RectifyDismissCommand
4606     };
4607
4608   CacheView
4609     *image_view;
4610
4611   char
4612     command[MaxTextExtent],
4613     text[MaxTextExtent];
4614
4615   Cursor
4616     cursor;
4617
4618   int
4619     id,
4620     x,
4621     y;
4622
4623   KeySym
4624     key_symbol;
4625
4626   Image
4627     *crop_image;
4628
4629   MagickRealType
4630     scale_factor;
4631
4632   RectangleInfo
4633     crop_info,
4634     highlight_info;
4635
4636   register Quantum
4637     *q;
4638
4639   unsigned int
4640     height,
4641     width;
4642
4643   size_t
4644     state;
4645
4646   XEvent
4647     event;
4648
4649   /*
4650     Map Command widget.
4651   */
4652   switch (mode)
4653   {
4654     case CopyMode:
4655     {
4656       (void) CloneString(&windows->command.name,"Copy");
4657       break;
4658     }
4659     case CropMode:
4660     {
4661       (void) CloneString(&windows->command.name,"Crop");
4662       break;
4663     }
4664     case CutMode:
4665     {
4666       (void) CloneString(&windows->command.name,"Cut");
4667       break;
4668     }
4669   }
4670   RectifyModeMenu[0]=windows->command.name;
4671   windows->command.data=0;
4672   (void) XCommandWidget(display,windows,CropModeMenu,(XEvent *) NULL);
4673   (void) XMapRaised(display,windows->command.id);
4674   XClientMessage(display,windows->image.id,windows->im_protocols,
4675     windows->im_update_widget,CurrentTime);
4676   /*
4677     Track pointer until button 1 is pressed.
4678   */
4679   XQueryPosition(display,windows->image.id,&x,&y);
4680   (void) XSelectInput(display,windows->image.id,
4681     windows->image.attributes.event_mask | PointerMotionMask);
4682   crop_info.x=(ssize_t) windows->image.x+x;
4683   crop_info.y=(ssize_t) windows->image.y+y;
4684   crop_info.width=0;
4685   crop_info.height=0;
4686   cursor=XCreateFontCursor(display,XC_fleur);
4687   state=DefaultState;
4688   do
4689   {
4690     if (windows->info.mapped != MagickFalse)
4691       {
4692         /*
4693           Display pointer position.
4694         */
4695         (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
4696           (long) crop_info.x,(long) crop_info.y);
4697         XInfoWidget(display,windows,text);
4698       }
4699     /*
4700       Wait for next event.
4701     */
4702     XScreenEvent(display,windows,&event);
4703     if (event.xany.window == windows->command.id)
4704       {
4705         /*
4706           Select a command from the Command widget.
4707         */
4708         id=XCommandWidget(display,windows,CropModeMenu,&event);
4709         if (id < 0)
4710           continue;
4711         switch (CropCommands[id])
4712         {
4713           case CropHelpCommand:
4714           {
4715             switch (mode)
4716             {
4717               case CopyMode:
4718               {
4719                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4720                   "Help Viewer - Image Copy",ImageCopyHelp);
4721                 break;
4722               }
4723               case CropMode:
4724               {
4725                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4726                   "Help Viewer - Image Crop",ImageCropHelp);
4727                 break;
4728               }
4729               case CutMode:
4730               {
4731                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4732                   "Help Viewer - Image Cut",ImageCutHelp);
4733                 break;
4734               }
4735             }
4736             break;
4737           }
4738           case CropDismissCommand:
4739           {
4740             /*
4741               Prematurely exit.
4742             */
4743             state|=EscapeState;
4744             state|=ExitState;
4745             break;
4746           }
4747           default:
4748             break;
4749         }
4750         continue;
4751       }
4752     switch (event.type)
4753     {
4754       case ButtonPress:
4755       {
4756         if (event.xbutton.button != Button1)
4757           break;
4758         if (event.xbutton.window != windows->image.id)
4759           break;
4760         /*
4761           Note first corner of cropping rectangle-- exit loop.
4762         */
4763         (void) XCheckDefineCursor(display,windows->image.id,cursor);
4764         crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4765         crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4766         state|=ExitState;
4767         break;
4768       }
4769       case ButtonRelease:
4770         break;
4771       case Expose:
4772         break;
4773       case KeyPress:
4774       {
4775         if (event.xkey.window != windows->image.id)
4776           break;
4777         /*
4778           Respond to a user key press.
4779         */
4780         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
4781           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4782         switch ((int) key_symbol)
4783         {
4784           case XK_Escape:
4785           case XK_F20:
4786           {
4787             /*
4788               Prematurely exit.
4789             */
4790             state|=EscapeState;
4791             state|=ExitState;
4792             break;
4793           }
4794           case XK_F1:
4795           case XK_Help:
4796           {
4797             switch (mode)
4798             {
4799               case CopyMode:
4800               {
4801                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4802                   "Help Viewer - Image Copy",ImageCopyHelp);
4803                 break;
4804               }
4805               case CropMode:
4806               {
4807                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4808                   "Help Viewer - Image Crop",ImageCropHelp);
4809                 break;
4810               }
4811               case CutMode:
4812               {
4813                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4814                   "Help Viewer - Image Cut",ImageCutHelp);
4815                 break;
4816               }
4817             }
4818             break;
4819           }
4820           default:
4821           {
4822             (void) XBell(display,0);
4823             break;
4824           }
4825         }
4826         break;
4827       }
4828       case MotionNotify:
4829       {
4830         if (event.xmotion.window != windows->image.id)
4831           break;
4832         /*
4833           Map and unmap Info widget as text cursor crosses its boundaries.
4834         */
4835         x=event.xmotion.x;
4836         y=event.xmotion.y;
4837         if (windows->info.mapped != MagickFalse)
4838           {
4839             if ((x < (int) (windows->info.x+windows->info.width)) &&
4840                 (y < (int) (windows->info.y+windows->info.height)))
4841               (void) XWithdrawWindow(display,windows->info.id,
4842                 windows->info.screen);
4843           }
4844         else
4845           if ((x > (int) (windows->info.x+windows->info.width)) ||
4846               (y > (int) (windows->info.y+windows->info.height)))
4847             (void) XMapWindow(display,windows->info.id);
4848         crop_info.x=(ssize_t) windows->image.x+x;
4849         crop_info.y=(ssize_t) windows->image.y+y;
4850         break;
4851       }
4852       default:
4853         break;
4854     }
4855   } while ((state & ExitState) == 0);
4856   (void) XSelectInput(display,windows->image.id,
4857     windows->image.attributes.event_mask);
4858   if ((state & EscapeState) != 0)
4859     {
4860       /*
4861         User want to exit without cropping.
4862       */
4863       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4864       (void) XFreeCursor(display,cursor);
4865       return(MagickTrue);
4866     }
4867   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
4868   do
4869   {
4870     /*
4871       Size rectangle as pointer moves until the mouse button is released.
4872     */
4873     x=(int) crop_info.x;
4874     y=(int) crop_info.y;
4875     crop_info.width=0;
4876     crop_info.height=0;
4877     state=DefaultState;
4878     do
4879     {
4880       highlight_info=crop_info;
4881       highlight_info.x=crop_info.x-windows->image.x;
4882       highlight_info.y=crop_info.y-windows->image.y;
4883       if ((highlight_info.width > 3) && (highlight_info.height > 3))
4884         {
4885           /*
4886             Display info and draw cropping rectangle.
4887           */
4888           if (windows->info.mapped == MagickFalse)
4889             (void) XMapWindow(display,windows->info.id);
4890           (void) FormatLocaleString(text,MaxTextExtent,
4891             " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
4892             crop_info.height,(double) crop_info.x,(double) crop_info.y);
4893           XInfoWidget(display,windows,text);
4894           XHighlightRectangle(display,windows->image.id,
4895             windows->image.highlight_context,&highlight_info);
4896         }
4897       else
4898         if (windows->info.mapped != MagickFalse)
4899           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4900       /*
4901         Wait for next event.
4902       */
4903       XScreenEvent(display,windows,&event);
4904       if ((highlight_info.width > 3) && (highlight_info.height > 3))
4905         XHighlightRectangle(display,windows->image.id,
4906           windows->image.highlight_context,&highlight_info);
4907       switch (event.type)
4908       {
4909         case ButtonPress:
4910         {
4911           crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4912           crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4913           break;
4914         }
4915         case ButtonRelease:
4916         {
4917           /*
4918             User has committed to cropping rectangle.
4919           */
4920           crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4921           crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4922           XSetCursorState(display,windows,MagickFalse);
4923           state|=ExitState;
4924           windows->command.data=0;
4925           (void) XCommandWidget(display,windows,RectifyModeMenu,
4926             (XEvent *) NULL);
4927           break;
4928         }
4929         case Expose:
4930           break;
4931         case MotionNotify:
4932         {
4933           crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
4934           crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
4935         }
4936         default:
4937           break;
4938       }
4939       if ((((int) crop_info.x != x) && ((int) crop_info.y != y)) ||
4940           ((state & ExitState) != 0))
4941         {
4942           /*
4943             Check boundary conditions.
4944           */
4945           if (crop_info.x < 0)
4946             crop_info.x=0;
4947           else
4948             if (crop_info.x > (ssize_t) windows->image.ximage->width)
4949               crop_info.x=(ssize_t) windows->image.ximage->width;
4950           if ((int) crop_info.x < x)
4951             crop_info.width=(unsigned int) (x-crop_info.x);
4952           else
4953             {
4954               crop_info.width=(unsigned int) (crop_info.x-x);
4955               crop_info.x=(ssize_t) x;
4956             }
4957           if (crop_info.y < 0)
4958             crop_info.y=0;
4959           else
4960             if (crop_info.y > (ssize_t) windows->image.ximage->height)
4961               crop_info.y=(ssize_t) windows->image.ximage->height;
4962           if ((int) crop_info.y < y)
4963             crop_info.height=(unsigned int) (y-crop_info.y);
4964           else
4965             {
4966               crop_info.height=(unsigned int) (crop_info.y-y);
4967               crop_info.y=(ssize_t) y;
4968             }
4969         }
4970     } while ((state & ExitState) == 0);
4971     /*
4972       Wait for user to grab a corner of the rectangle or press return.
4973     */
4974     state=DefaultState;
4975     (void) XMapWindow(display,windows->info.id);
4976     do
4977     {
4978       if (windows->info.mapped != MagickFalse)
4979         {
4980           /*
4981             Display pointer position.
4982           */
4983           (void) FormatLocaleString(text,MaxTextExtent,
4984             " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
4985             crop_info.height,(double) crop_info.x,(double) crop_info.y);
4986           XInfoWidget(display,windows,text);
4987         }
4988       highlight_info=crop_info;
4989       highlight_info.x=crop_info.x-windows->image.x;
4990       highlight_info.y=crop_info.y-windows->image.y;
4991       if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
4992         {
4993           state|=EscapeState;
4994           state|=ExitState;
4995           break;
4996         }
4997       XHighlightRectangle(display,windows->image.id,
4998         windows->image.highlight_context,&highlight_info);
4999       XScreenEvent(display,windows,&event);
5000       if (event.xany.window == windows->command.id)
5001         {
5002           /*
5003             Select a command from the Command widget.
5004           */
5005           (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
5006           id=XCommandWidget(display,windows,RectifyModeMenu,&event);
5007           (void) XSetFunction(display,windows->image.highlight_context,
5008             GXinvert);
5009           XHighlightRectangle(display,windows->image.id,
5010             windows->image.highlight_context,&highlight_info);
5011           if (id >= 0)
5012             switch (RectifyCommands[id])
5013             {
5014               case RectifyCopyCommand:
5015               {
5016                 state|=ExitState;
5017                 break;
5018               }
5019               case RectifyHelpCommand:
5020               {
5021                 (void) XSetFunction(display,windows->image.highlight_context,
5022                   GXcopy);
5023                 switch (mode)
5024                 {
5025                   case CopyMode:
5026                   {
5027                     XTextViewWidget(display,resource_info,windows,MagickFalse,
5028                       "Help Viewer - Image Copy",ImageCopyHelp);
5029                     break;
5030                   }
5031                   case CropMode:
5032                   {
5033                     XTextViewWidget(display,resource_info,windows,MagickFalse,
5034                       "Help Viewer - Image Crop",ImageCropHelp);
5035                     break;
5036                   }
5037                   case CutMode:
5038                   {
5039                     XTextViewWidget(display,resource_info,windows,MagickFalse,
5040                       "Help Viewer - Image Cut",ImageCutHelp);
5041                     break;
5042                   }
5043                 }
5044                 (void) XSetFunction(display,windows->image.highlight_context,
5045                   GXinvert);
5046                 break;
5047               }
5048               case RectifyDismissCommand:
5049               {
5050                 /*
5051                   Prematurely exit.
5052                 */
5053                 state|=EscapeState;
5054                 state|=ExitState;
5055                 break;
5056               }
5057               default:
5058                 break;
5059             }
5060           continue;
5061         }
5062       XHighlightRectangle(display,windows->image.id,
5063         windows->image.highlight_context,&highlight_info);
5064       switch (event.type)
5065       {
5066         case ButtonPress:
5067         {
5068           if (event.xbutton.button != Button1)
5069             break;
5070           if (event.xbutton.window != windows->image.id)
5071             break;
5072           x=windows->image.x+event.xbutton.x;
5073           y=windows->image.y+event.xbutton.y;
5074           if ((x < (int) (crop_info.x+RoiDelta)) &&
5075               (x > (int) (crop_info.x-RoiDelta)) &&
5076               (y < (int) (crop_info.y+RoiDelta)) &&
5077               (y > (int) (crop_info.y-RoiDelta)))
5078             {
5079               crop_info.x=(ssize_t) (crop_info.x+crop_info.width);
5080               crop_info.y=(ssize_t) (crop_info.y+crop_info.height);
5081               state|=UpdateConfigurationState;
5082               break;
5083             }
5084           if ((x < (int) (crop_info.x+RoiDelta)) &&
5085               (x > (int) (crop_info.x-RoiDelta)) &&
5086               (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5087               (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5088             {
5089               crop_info.x=(ssize_t) (crop_info.x+crop_info.width);
5090               state|=UpdateConfigurationState;
5091               break;
5092             }
5093           if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5094               (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5095               (y < (int) (crop_info.y+RoiDelta)) &&
5096               (y > (int) (crop_info.y-RoiDelta)))
5097             {
5098               crop_info.y=(ssize_t) (crop_info.y+crop_info.height);
5099               state|=UpdateConfigurationState;
5100               break;
5101             }
5102           if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5103               (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5104               (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5105               (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5106             {
5107               state|=UpdateConfigurationState;
5108               break;
5109             }
5110         }
5111         case ButtonRelease:
5112         {
5113           if (event.xbutton.window == windows->pan.id)
5114             if ((highlight_info.x != crop_info.x-windows->image.x) ||
5115                 (highlight_info.y != crop_info.y-windows->image.y))
5116               XHighlightRectangle(display,windows->image.id,
5117                 windows->image.highlight_context,&highlight_info);
5118           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5119             event.xbutton.time);
5120           break;
5121         }
5122         case Expose:
5123         {
5124           if (event.xexpose.window == windows->image.id)
5125             if (event.xexpose.count == 0)
5126               {
5127                 event.xexpose.x=(int) highlight_info.x;
5128                 event.xexpose.y=(int) highlight_info.y;
5129                 event.xexpose.width=(int) highlight_info.width;
5130                 event.xexpose.height=(int) highlight_info.height;
5131                 XRefreshWindow(display,&windows->image,&event);
5132               }
5133           if (event.xexpose.window == windows->info.id)
5134             if (event.xexpose.count == 0)
5135               XInfoWidget(display,windows,text);
5136           break;
5137         }
5138         case KeyPress:
5139         {
5140           if (event.xkey.window != windows->image.id)
5141             break;
5142           /*
5143             Respond to a user key press.
5144           */
5145           (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5146             sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5147           switch ((int) key_symbol)
5148           {
5149             case XK_Escape:
5150             case XK_F20:
5151               state|=EscapeState;
5152             case XK_Return:
5153             {
5154               state|=ExitState;
5155               break;
5156             }
5157             case XK_Home:
5158             case XK_KP_Home:
5159             {
5160               crop_info.x=(ssize_t) (windows->image.width/2L-
5161                 crop_info.width/2L);
5162               crop_info.y=(ssize_t) (windows->image.height/2L-
5163                 crop_info.height/2L);
5164               break;
5165             }
5166             case XK_Left:
5167             case XK_KP_Left:
5168             {
5169               crop_info.x--;
5170               break;
5171             }
5172             case XK_Up:
5173             case XK_KP_Up:
5174             case XK_Next:
5175             {
5176               crop_info.y--;
5177               break;
5178             }
5179             case XK_Right:
5180             case XK_KP_Right:
5181             {
5182               crop_info.x++;
5183               break;
5184             }
5185             case XK_Prior:
5186             case XK_Down:
5187             case XK_KP_Down:
5188             {
5189               crop_info.y++;
5190               break;
5191             }
5192             case XK_F1:
5193             case XK_Help:
5194             {
5195               (void) XSetFunction(display,windows->image.highlight_context,
5196                 GXcopy);
5197               switch (mode)
5198               {
5199                 case CopyMode:
5200                 {
5201                   XTextViewWidget(display,resource_info,windows,MagickFalse,
5202                     "Help Viewer - Image Copy",ImageCopyHelp);
5203                   break;
5204                 }
5205                 case CropMode:
5206                 {
5207                   XTextViewWidget(display,resource_info,windows,MagickFalse,
5208                     "Help Viewer - Image Cropg",ImageCropHelp);
5209                   break;
5210                 }
5211                 case CutMode:
5212                 {
5213                   XTextViewWidget(display,resource_info,windows,MagickFalse,
5214                     "Help Viewer - Image Cutg",ImageCutHelp);
5215                   break;
5216                 }
5217               }
5218               (void) XSetFunction(display,windows->image.highlight_context,
5219                 GXinvert);
5220               break;
5221             }
5222             default:
5223             {
5224               (void) XBell(display,0);
5225               break;
5226             }
5227           }
5228           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5229             event.xkey.time);
5230           break;
5231         }
5232         case KeyRelease:
5233           break;
5234         case MotionNotify:
5235         {
5236           if (event.xmotion.window != windows->image.id)
5237             break;
5238           /*
5239             Map and unmap Info widget as text cursor crosses its boundaries.
5240           */
5241           x=event.xmotion.x;
5242           y=event.xmotion.y;
5243           if (windows->info.mapped != MagickFalse)
5244             {
5245               if ((x < (int) (windows->info.x+windows->info.width)) &&
5246                   (y < (int) (windows->info.y+windows->info.height)))
5247                 (void) XWithdrawWindow(display,windows->info.id,
5248                   windows->info.screen);
5249             }
5250           else
5251             if ((x > (int) (windows->info.x+windows->info.width)) ||
5252                 (y > (int) (windows->info.y+windows->info.height)))
5253               (void) XMapWindow(display,windows->info.id);
5254           crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
5255           crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
5256           break;
5257         }
5258         case SelectionRequest:
5259         {
5260           XSelectionEvent
5261             notify;
5262
5263           XSelectionRequestEvent
5264             *request;
5265
5266           /*
5267             Set primary selection.
5268           */
5269           (void) FormatLocaleString(text,MaxTextExtent,
5270             "%.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
5271             crop_info.height,(double) crop_info.x,(double) crop_info.y);
5272           request=(&(event.xselectionrequest));
5273           (void) XChangeProperty(request->display,request->requestor,
5274             request->property,request->target,8,PropModeReplace,
5275             (unsigned char *) text,(int) strlen(text));
5276           notify.type=SelectionNotify;
5277           notify.display=request->display;
5278           notify.requestor=request->requestor;
5279           notify.selection=request->selection;
5280           notify.target=request->target;
5281           notify.time=request->time;
5282           if (request->property == None)
5283             notify.property=request->target;
5284           else
5285             notify.property=request->property;
5286           (void) XSendEvent(request->display,request->requestor,False,0,
5287             (XEvent *) &notify);
5288         }
5289         default:
5290           break;
5291       }
5292       if ((state & UpdateConfigurationState) != 0)
5293         {
5294           (void) XPutBackEvent(display,&event);
5295           (void) XCheckDefineCursor(display,windows->image.id,cursor);
5296           break;
5297         }
5298     } while ((state & ExitState) == 0);
5299   } while ((state & ExitState) == 0);
5300   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
5301   XSetCursorState(display,windows,MagickFalse);
5302   if ((state & EscapeState) != 0)
5303     return(MagickTrue);
5304   if (mode == CropMode)
5305     if (((int) crop_info.width != windows->image.ximage->width) ||
5306         ((int) crop_info.height != windows->image.ximage->height))
5307       {
5308         /*
5309           Reconfigure Image window as defined by cropping rectangle.
5310         */
5311         XSetCropGeometry(display,windows,&crop_info,image);
5312         windows->image.window_changes.width=(int) crop_info.width;
5313         windows->image.window_changes.height=(int) crop_info.height;
5314         (void) XConfigureImage(display,resource_info,windows,image,exception);
5315         return(MagickTrue);
5316       }
5317   /*
5318     Copy image before applying image transforms.
5319   */
5320   XSetCursorState(display,windows,MagickTrue);
5321   XCheckRefreshWindows(display,windows);
5322   width=(unsigned int) image->columns;
5323   height=(unsigned int) image->rows;
5324   x=0;
5325   y=0;
5326   if (windows->image.crop_geometry != (char *) NULL)
5327     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
5328   scale_factor=(MagickRealType) width/windows->image.ximage->width;
5329   crop_info.x+=x;
5330   crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
5331   crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
5332   scale_factor=(MagickRealType) height/windows->image.ximage->height;
5333   crop_info.y+=y;
5334   crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
5335   crop_info.height=(unsigned int) (scale_factor*crop_info.height+0.5);
5336   crop_image=CropImage(image,&crop_info,exception);
5337   XSetCursorState(display,windows,MagickFalse);
5338   if (crop_image == (Image *) NULL)
5339     return(MagickFalse);
5340   if (resource_info->copy_image != (Image *) NULL)
5341     resource_info->copy_image=DestroyImage(resource_info->copy_image);
5342   resource_info->copy_image=crop_image;
5343   if (mode == CopyMode)
5344     {
5345       (void) XConfigureImage(display,resource_info,windows,image,exception);
5346       return(MagickTrue);
5347     }
5348   /*
5349     Cut image.
5350   */
5351   if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
5352     return(MagickFalse);
5353   image->matte=MagickTrue;
5354   image_view=AcquireCacheView(image);
5355   for (y=0; y < (int) crop_info.height; y++)
5356   {
5357     q=GetCacheViewAuthenticPixels(image_view,crop_info.x,y+crop_info.y,
5358       crop_info.width,1,exception);
5359     if (q == (Quantum *) NULL)
5360       break;
5361     for (x=0; x < (int) crop_info.width; x++)
5362     {
5363       SetPixelAlpha(image,TransparentAlpha,q);
5364       q+=GetPixelChannels(image);
5365     }
5366     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
5367       break;
5368   }
5369   image_view=DestroyCacheView(image_view);
5370   /*
5371     Update image configuration.
5372   */
5373   XConfigureImageColormap(display,resource_info,windows,image);
5374   (void) XConfigureImage(display,resource_info,windows,image,exception);
5375   return(MagickTrue);
5376 }
5377 \f
5378 /*
5379 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5380 %                                                                             %
5381 %                                                                             %
5382 %                                                                             %
5383 +   X D r a w I m a g e                                                       %
5384 %                                                                             %
5385 %                                                                             %
5386 %                                                                             %
5387 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5388 %
5389 %  XDrawEditImage() draws a graphic element (point, line, rectangle, etc.) on
5390 %  the image.
5391 %
5392 %  The format of the XDrawEditImage method is:
5393 %
5394 %      MagickBooleanType XDrawEditImage(Display *display,
5395 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
5396 %        ExceptionInfo *exception)
5397 %
5398 %  A description of each parameter follows:
5399 %
5400 %    o display: Specifies a connection to an X server; returned from
5401 %      XOpenDisplay.
5402 %
5403 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
5404 %
5405 %    o windows: Specifies a pointer to a XWindows structure.
5406 %
5407 %    o image: the image.
5408 %
5409 %    o exception: return any errors or warnings in this structure.
5410 %
5411 */
5412 static MagickBooleanType XDrawEditImage(Display *display,
5413   XResourceInfo *resource_info,XWindows *windows,Image **image,
5414   ExceptionInfo *exception)
5415 {
5416   static const char
5417     *DrawMenu[] =
5418     {
5419       "Element",
5420       "Color",
5421       "Stipple",
5422       "Width",
5423       "Undo",
5424       "Help",
5425       "Dismiss",
5426       (char *) NULL
5427     };
5428
5429   static ElementType
5430     element = PointElement;
5431
5432   static const ModeType
5433     DrawCommands[] =
5434     {
5435       DrawElementCommand,
5436       DrawColorCommand,
5437       DrawStippleCommand,
5438       DrawWidthCommand,
5439       DrawUndoCommand,
5440       DrawHelpCommand,
5441       DrawDismissCommand
5442     };
5443
5444   static Pixmap
5445     stipple = (Pixmap) NULL;
5446
5447   static unsigned int
5448     pen_id = 0,
5449     line_width = 1;
5450
5451   char
5452     command[MaxTextExtent],
5453     text[MaxTextExtent];
5454
5455   Cursor
5456     cursor;
5457
5458   int
5459     entry,
5460     id,
5461     number_coordinates,
5462     x,
5463     y;
5464
5465   MagickRealType
5466     degrees;
5467
5468   MagickStatusType
5469     status;
5470
5471   RectangleInfo
5472     rectangle_info;
5473
5474   register int
5475     i;
5476
5477   unsigned int
5478     distance,
5479     height,
5480     max_coordinates,
5481     width;
5482
5483   size_t
5484     state;
5485
5486   Window
5487     root_window;
5488
5489   XDrawInfo
5490     draw_info;
5491
5492   XEvent
5493     event;
5494
5495   XPoint
5496     *coordinate_info;
5497
5498   XSegment
5499     line_info;
5500
5501   /*
5502     Allocate polygon info.
5503   */
5504   max_coordinates=2048;
5505   coordinate_info=(XPoint *) AcquireQuantumMemory((size_t) max_coordinates,
5506     sizeof(*coordinate_info));
5507   if (coordinate_info == (XPoint *) NULL)
5508     {
5509       (void) ThrowMagickException(exception,GetMagickModule(),
5510         ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
5511       return(MagickFalse);
5512     }
5513   /*
5514     Map Command widget.
5515   */
5516   (void) CloneString(&windows->command.name,"Draw");
5517   windows->command.data=4;
5518   (void) XCommandWidget(display,windows,DrawMenu,(XEvent *) NULL);
5519   (void) XMapRaised(display,windows->command.id);
5520   XClientMessage(display,windows->image.id,windows->im_protocols,
5521     windows->im_update_widget,CurrentTime);
5522   /*
5523     Wait for first button press.
5524   */
5525   root_window=XRootWindow(display,XDefaultScreen(display));
5526   draw_info.stencil=OpaqueStencil;
5527   status=MagickTrue;
5528   cursor=XCreateFontCursor(display,XC_tcross);
5529   for ( ; ; )
5530   {
5531     XQueryPosition(display,windows->image.id,&x,&y);
5532     (void) XSelectInput(display,windows->image.id,
5533       windows->image.attributes.event_mask | PointerMotionMask);
5534     (void) XCheckDefineCursor(display,windows->image.id,cursor);
5535     state=DefaultState;
5536     do
5537     {
5538       if (windows->info.mapped != MagickFalse)
5539         {
5540           /*
5541             Display pointer position.
5542           */
5543           (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
5544             x+windows->image.x,y+windows->image.y);
5545           XInfoWidget(display,windows,text);
5546         }
5547       /*
5548         Wait for next event.
5549       */
5550       XScreenEvent(display,windows,&event);
5551       if (event.xany.window == windows->command.id)
5552         {
5553           /*
5554             Select a command from the Command widget.
5555           */
5556           id=XCommandWidget(display,windows,DrawMenu,&event);
5557           if (id < 0)
5558             continue;
5559           switch (DrawCommands[id])
5560           {
5561             case DrawElementCommand:
5562             {
5563               static const char
5564                 *Elements[] =
5565                 {
5566                   "point",
5567                   "line",
5568                   "rectangle",
5569                   "fill rectangle",
5570                   "circle",
5571                   "fill circle",
5572                   "ellipse",
5573                   "fill ellipse",
5574                   "polygon",
5575                   "fill polygon",
5576                   (char *) NULL,
5577                 };
5578
5579               /*
5580                 Select a command from the pop-up menu.
5581               */
5582               element=(ElementType) (XMenuWidget(display,windows,
5583                 DrawMenu[id],Elements,command)+1);
5584               break;
5585             }
5586             case DrawColorCommand:
5587             {
5588               const char
5589                 *ColorMenu[MaxNumberPens+1];
5590
5591               int
5592                 pen_number;
5593
5594               MagickBooleanType
5595                 transparent;
5596
5597               XColor
5598                 color;
5599
5600               /*
5601                 Initialize menu selections.
5602               */
5603               for (i=0; i < (int) (MaxNumberPens-2); i++)
5604                 ColorMenu[i]=resource_info->pen_colors[i];
5605               ColorMenu[MaxNumberPens-2]="transparent";
5606               ColorMenu[MaxNumberPens-1]="Browser...";
5607               ColorMenu[MaxNumberPens]=(char *) NULL;
5608               /*
5609                 Select a pen color from the pop-up menu.
5610               */
5611               pen_number=XMenuWidget(display,windows,DrawMenu[id],
5612                 (const char **) ColorMenu,command);
5613               if (pen_number < 0)
5614                 break;
5615               transparent=pen_number == (MaxNumberPens-2) ? MagickTrue :
5616                 MagickFalse;
5617               if (transparent != MagickFalse)
5618                 {
5619                   draw_info.stencil=TransparentStencil;
5620                   break;
5621                 }
5622               if (pen_number == (MaxNumberPens-1))
5623                 {
5624                   static char
5625                     color_name[MaxTextExtent] = "gray";
5626
5627                   /*
5628                     Select a pen color from a dialog.
5629                   */
5630                   resource_info->pen_colors[pen_number]=color_name;
5631                   XColorBrowserWidget(display,windows,"Select",color_name);
5632                   if (*color_name == '\0')
5633                     break;
5634                 }
5635               /*
5636                 Set pen color.
5637               */
5638               (void) XParseColor(display,windows->map_info->colormap,
5639                 resource_info->pen_colors[pen_number],&color);
5640               XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
5641                 (unsigned int) MaxColors,&color);
5642               windows->pixel_info->pen_colors[pen_number]=color;
5643               pen_id=(unsigned int) pen_number;
5644               draw_info.stencil=OpaqueStencil;
5645               break;
5646             }
5647             case DrawStippleCommand:
5648             {
5649               Image
5650                 *stipple_image;
5651
5652               ImageInfo
5653                 *image_info;
5654
5655               int
5656                 status;
5657
5658               static char
5659                 filename[MaxTextExtent] = "\0";
5660
5661               static const char
5662                 *StipplesMenu[] =
5663                 {
5664                   "Brick",
5665                   "Diagonal",
5666                   "Scales",
5667                   "Vertical",
5668                   "Wavy",
5669                   "Translucent",
5670                   "Opaque",
5671                   (char *) NULL,
5672                   (char *) NULL,
5673                 };
5674
5675               /*
5676                 Select a command from the pop-up menu.
5677               */
5678               StipplesMenu[7]="Open...";
5679               entry=XMenuWidget(display,windows,DrawMenu[id],StipplesMenu,
5680                 command);
5681               if (entry < 0)
5682                 break;
5683               if (stipple != (Pixmap) NULL)
5684                 (void) XFreePixmap(display,stipple);
5685               stipple=(Pixmap) NULL;
5686               if (entry != 7)
5687                 {
5688                   switch (entry)
5689                   {
5690                     case 0:
5691                     {
5692                       stipple=XCreateBitmapFromData(display,root_window,
5693                         (char *) BricksBitmap,BricksWidth,BricksHeight);
5694                       break;
5695                     }
5696                     case 1:
5697                     {
5698                       stipple=XCreateBitmapFromData(display,root_window,
5699                         (char *) DiagonalBitmap,DiagonalWidth,DiagonalHeight);
5700                       break;
5701                     }
5702                     case 2:
5703                     {
5704                       stipple=XCreateBitmapFromData(display,root_window,
5705                         (char *) ScalesBitmap,ScalesWidth,ScalesHeight);
5706                       break;
5707                     }
5708                     case 3:
5709                     {
5710                       stipple=XCreateBitmapFromData(display,root_window,
5711                         (char *) VerticalBitmap,VerticalWidth,VerticalHeight);
5712                       break;
5713                     }
5714                     case 4:
5715                     {
5716                       stipple=XCreateBitmapFromData(display,root_window,
5717                         (char *) WavyBitmap,WavyWidth,WavyHeight);
5718                       break;
5719                     }
5720                     case 5:
5721                     {
5722                       stipple=XCreateBitmapFromData(display,root_window,
5723                         (char *) HighlightBitmap,HighlightWidth,
5724                         HighlightHeight);
5725                       break;
5726                     }
5727                     case 6:
5728                     default:
5729                     {
5730                       stipple=XCreateBitmapFromData(display,root_window,
5731                         (char *) OpaqueBitmap,OpaqueWidth,OpaqueHeight);
5732                       break;
5733                     }
5734                   }
5735                   break;
5736                 }
5737               XFileBrowserWidget(display,windows,"Stipple",filename);
5738               if (*filename == '\0')
5739                 break;
5740               /*
5741                 Read image.
5742               */
5743               XSetCursorState(display,windows,MagickTrue);
5744               XCheckRefreshWindows(display,windows);
5745               image_info=AcquireImageInfo();
5746               (void) CopyMagickString(image_info->filename,filename,
5747                 MaxTextExtent);
5748               stipple_image=ReadImage(image_info,exception);
5749               CatchException(exception);
5750               XSetCursorState(display,windows,MagickFalse);
5751               if (stipple_image == (Image *) NULL)
5752                 break;
5753               (void) AcquireUniqueFileResource(filename);
5754               (void) FormatLocaleString(stipple_image->filename,MaxTextExtent,
5755                 "xbm:%s",filename);
5756               (void) WriteImage(image_info,stipple_image,exception);
5757               stipple_image=DestroyImage(stipple_image);
5758               image_info=DestroyImageInfo(image_info);
5759               status=XReadBitmapFile(display,root_window,filename,&width,
5760                 &height,&stipple,&x,&y);
5761               (void) RelinquishUniqueFileResource(filename);
5762               if ((status != BitmapSuccess) != 0)
5763                 XNoticeWidget(display,windows,"Unable to read X bitmap image:",
5764                   filename);
5765               break;
5766             }
5767             case DrawWidthCommand:
5768             {
5769               static char
5770                 width[MaxTextExtent] = "0";
5771
5772               static const char
5773                 *WidthsMenu[] =
5774                 {
5775                   "1",
5776                   "2",
5777                   "4",
5778                   "8",
5779                   "16",
5780                   "Dialog...",
5781                   (char *) NULL,
5782                 };
5783
5784               /*
5785                 Select a command from the pop-up menu.
5786               */
5787               entry=XMenuWidget(display,windows,DrawMenu[id],WidthsMenu,
5788                 command);
5789               if (entry < 0)
5790                 break;
5791               if (entry != 5)
5792                 {
5793                   line_width=(unsigned int) StringToUnsignedLong(
5794                     WidthsMenu[entry]);
5795                   break;
5796                 }
5797               (void) XDialogWidget(display,windows,"Ok","Enter line width:",
5798                 width);
5799               if (*width == '\0')
5800                 break;
5801               line_width=(unsigned int) StringToUnsignedLong(width);
5802               break;
5803             }
5804             case DrawUndoCommand:
5805             {
5806               (void) XMagickCommand(display,resource_info,windows,UndoCommand,
5807                 image,exception);
5808               break;
5809             }
5810             case DrawHelpCommand:
5811             {
5812               XTextViewWidget(display,resource_info,windows,MagickFalse,
5813                 "Help Viewer - Image Rotation",ImageDrawHelp);
5814               (void) XCheckDefineCursor(display,windows->image.id,cursor);
5815               break;
5816             }
5817             case DrawDismissCommand:
5818             {
5819               /*
5820                 Prematurely exit.
5821               */
5822               state|=EscapeState;
5823               state|=ExitState;
5824               break;
5825             }
5826             default:
5827               break;
5828           }
5829           (void) XCheckDefineCursor(display,windows->image.id,cursor);
5830           continue;
5831         }
5832       switch (event.type)
5833       {
5834         case ButtonPress:
5835         {
5836           if (event.xbutton.button != Button1)
5837             break;
5838           if (event.xbutton.window != windows->image.id)
5839             break;
5840           /*
5841             exit loop.
5842           */
5843           x=event.xbutton.x;
5844           y=event.xbutton.y;
5845           state|=ExitState;
5846           break;
5847         }
5848         case ButtonRelease:
5849           break;
5850         case Expose:
5851           break;
5852         case KeyPress:
5853         {
5854           KeySym
5855             key_symbol;
5856
5857           if (event.xkey.window != windows->image.id)
5858             break;
5859           /*
5860             Respond to a user key press.
5861           */
5862           (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5863             sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5864           switch ((int) key_symbol)
5865           {
5866             case XK_Escape:
5867             case XK_F20:
5868             {
5869               /*
5870                 Prematurely exit.
5871               */
5872               state|=EscapeState;
5873               state|=ExitState;
5874               break;
5875             }
5876             case XK_F1:
5877             case XK_Help:
5878             {
5879               XTextViewWidget(display,resource_info,windows,MagickFalse,
5880                 "Help Viewer - Image Rotation",ImageDrawHelp);
5881               break;
5882             }
5883             default:
5884             {
5885               (void) XBell(display,0);
5886               break;
5887             }
5888           }
5889           break;
5890         }
5891         case MotionNotify:
5892         {
5893           /*
5894             Map and unmap Info widget as text cursor crosses its boundaries.
5895           */
5896           x=event.xmotion.x;
5897           y=event.xmotion.y;
5898           if (windows->info.mapped != MagickFalse)
5899             {
5900               if ((x < (int) (windows->info.x+windows->info.width)) &&
5901                   (y < (int) (windows->info.y+windows->info.height)))
5902                 (void) XWithdrawWindow(display,windows->info.id,
5903                   windows->info.screen);
5904             }
5905           else
5906             if ((x > (int) (windows->info.x+windows->info.width)) ||
5907                 (y > (int) (windows->info.y+windows->info.height)))
5908               (void) XMapWindow(display,windows->info.id);
5909           break;
5910         }
5911       }
5912     } while ((state & ExitState) == 0);
5913     (void) XSelectInput(display,windows->image.id,
5914       windows->image.attributes.event_mask);
5915     (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
5916     if ((state & EscapeState) != 0)
5917       break;
5918     /*
5919       Draw element as pointer moves until the button is released.
5920     */
5921     distance=0;
5922     degrees=0.0;
5923     line_info.x1=x;
5924     line_info.y1=y;
5925     line_info.x2=x;
5926     line_info.y2=y;
5927     rectangle_info.x=(ssize_t) x;
5928     rectangle_info.y=(ssize_t) y;
5929     rectangle_info.width=0;
5930     rectangle_info.height=0;
5931     number_coordinates=1;
5932     coordinate_info->x=x;
5933     coordinate_info->y=y;
5934     (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
5935     state=DefaultState;
5936     do
5937     {
5938       switch (element)
5939       {
5940         case PointElement:
5941         default:
5942         {
5943           if (number_coordinates > 1)
5944             {
5945               (void) XDrawLines(display,windows->image.id,
5946                 windows->image.highlight_context,coordinate_info,
5947                 number_coordinates,CoordModeOrigin);
5948               (void) FormatLocaleString(text,MaxTextExtent," %+d%+d",
5949                 coordinate_info[number_coordinates-1].x,
5950                 coordinate_info[number_coordinates-1].y);
5951               XInfoWidget(display,windows,text);
5952             }
5953           break;
5954         }
5955         case LineElement:
5956         {
5957           if (distance > 9)
5958             {
5959               /*
5960                 Display angle of the line.
5961               */
5962               degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
5963                 line_info.y1),(double) (line_info.x2-line_info.x1)));
5964               (void) FormatLocaleString(text,MaxTextExtent," %g",
5965                 (double) degrees);
5966               XInfoWidget(display,windows,text);
5967               XHighlightLine(display,windows->image.id,
5968                 windows->image.highlight_context,&line_info);
5969             }
5970           else
5971             if (windows->info.mapped != MagickFalse)
5972               (void) XWithdrawWindow(display,windows->info.id,
5973                 windows->info.screen);
5974           break;
5975         }
5976         case RectangleElement:
5977         case FillRectangleElement:
5978         {
5979           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
5980             {
5981               /*
5982                 Display info and draw drawing rectangle.
5983               */
5984               (void) FormatLocaleString(text,MaxTextExtent,
5985                 " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
5986                 (double) rectangle_info.height,(double) rectangle_info.x,
5987                 (double) rectangle_info.y);
5988               XInfoWidget(display,windows,text);
5989               XHighlightRectangle(display,windows->image.id,
5990                 windows->image.highlight_context,&rectangle_info);
5991             }
5992           else
5993             if (windows->info.mapped != MagickFalse)
5994               (void) XWithdrawWindow(display,windows->info.id,
5995                 windows->info.screen);
5996           break;
5997         }
5998         case CircleElement:
5999         case FillCircleElement:
6000         case EllipseElement:
6001         case FillEllipseElement:
6002         {
6003           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6004             {
6005               /*
6006                 Display info and draw drawing rectangle.
6007               */
6008               (void) FormatLocaleString(text,MaxTextExtent,
6009                 " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
6010                 (double) rectangle_info.height,(double) rectangle_info.x,
6011                 (double) rectangle_info.y);
6012               XInfoWidget(display,windows,text);
6013               XHighlightEllipse(display,windows->image.id,
6014                 windows->image.highlight_context,&rectangle_info);
6015             }
6016           else
6017             if (windows->info.mapped != MagickFalse)
6018               (void) XWithdrawWindow(display,windows->info.id,
6019                 windows->info.screen);
6020           break;
6021         }
6022         case PolygonElement:
6023         case FillPolygonElement:
6024         {
6025           if (number_coordinates > 1)
6026             (void) XDrawLines(display,windows->image.id,
6027               windows->image.highlight_context,coordinate_info,
6028               number_coordinates,CoordModeOrigin);
6029           if (distance > 9)
6030             {
6031               /*
6032                 Display angle of the line.
6033               */
6034               degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
6035                 line_info.y1),(double) (line_info.x2-line_info.x1)));
6036               (void) FormatLocaleString(text,MaxTextExtent," %g",
6037                 (double) degrees);
6038               XInfoWidget(display,windows,text);
6039               XHighlightLine(display,windows->image.id,
6040                 windows->image.highlight_context,&line_info);
6041             }
6042           else
6043             if (windows->info.mapped != MagickFalse)
6044               (void) XWithdrawWindow(display,windows->info.id,
6045                 windows->info.screen);
6046           break;
6047         }
6048       }
6049       /*
6050         Wait for next event.
6051       */
6052       XScreenEvent(display,windows,&event);
6053       switch (element)
6054       {
6055         case PointElement:
6056         default:
6057         {
6058           if (number_coordinates > 1)
6059             (void) XDrawLines(display,windows->image.id,
6060               windows->image.highlight_context,coordinate_info,
6061               number_coordinates,CoordModeOrigin);
6062           break;
6063         }
6064         case LineElement:
6065         {
6066           if (distance > 9)
6067             XHighlightLine(display,windows->image.id,
6068               windows->image.highlight_context,&line_info);
6069           break;
6070         }
6071         case RectangleElement:
6072         case FillRectangleElement:
6073         {
6074           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6075             XHighlightRectangle(display,windows->image.id,
6076               windows->image.highlight_context,&rectangle_info);
6077           break;
6078         }
6079         case CircleElement:
6080         case FillCircleElement:
6081         case EllipseElement:
6082         case FillEllipseElement:
6083         {
6084           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6085             XHighlightEllipse(display,windows->image.id,
6086               windows->image.highlight_context,&rectangle_info);
6087           break;
6088         }
6089         case PolygonElement:
6090         case FillPolygonElement:
6091         {
6092           if (number_coordinates > 1)
6093             (void) XDrawLines(display,windows->image.id,
6094               windows->image.highlight_context,coordinate_info,
6095               number_coordinates,CoordModeOrigin);
6096           if (distance > 9)
6097             XHighlightLine(display,windows->image.id,
6098               windows->image.highlight_context,&line_info);
6099           break;
6100         }
6101       }
6102       switch (event.type)
6103       {
6104         case ButtonPress:
6105           break;
6106         case ButtonRelease:
6107         {
6108           /*
6109             User has committed to element.
6110           */
6111           line_info.x2=event.xbutton.x;
6112           line_info.y2=event.xbutton.y;
6113           rectangle_info.x=(ssize_t) event.xbutton.x;
6114           rectangle_info.y=(ssize_t) event.xbutton.y;
6115           coordinate_info[number_coordinates].x=event.xbutton.x;
6116           coordinate_info[number_coordinates].y=event.xbutton.y;
6117           if (((element != PolygonElement) &&
6118                (element != FillPolygonElement)) || (distance <= 9))
6119             {
6120               state|=ExitState;
6121               break;
6122             }
6123           number_coordinates++;
6124           if (number_coordinates < (int) max_coordinates)
6125             {
6126               line_info.x1=event.xbutton.x;
6127               line_info.y1=event.xbutton.y;
6128               break;
6129             }
6130           max_coordinates<<=1;
6131           coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6132             max_coordinates,sizeof(*coordinate_info));
6133           if (coordinate_info == (XPoint *) NULL)
6134             (void) ThrowMagickException(exception,GetMagickModule(),
6135               ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6136           break;
6137         }
6138         case Expose:
6139           break;
6140         case MotionNotify:
6141         {
6142           if (event.xmotion.window != windows->image.id)
6143             break;
6144           if (element != PointElement)
6145             {
6146               line_info.x2=event.xmotion.x;
6147               line_info.y2=event.xmotion.y;
6148               rectangle_info.x=(ssize_t) event.xmotion.x;
6149               rectangle_info.y=(ssize_t) event.xmotion.y;
6150               break;
6151             }
6152           coordinate_info[number_coordinates].x=event.xbutton.x;
6153           coordinate_info[number_coordinates].y=event.xbutton.y;
6154           number_coordinates++;
6155           if (number_coordinates < (int) max_coordinates)
6156             break;
6157           max_coordinates<<=1;
6158           coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6159             max_coordinates,sizeof(*coordinate_info));
6160           if (coordinate_info == (XPoint *) NULL)
6161             (void) ThrowMagickException(exception,GetMagickModule(),
6162               ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6163           break;
6164         }
6165         default:
6166           break;
6167       }
6168       /*
6169         Check boundary conditions.
6170       */
6171       if (line_info.x2 < 0)
6172         line_info.x2=0;
6173       else
6174         if (line_info.x2 > (int) windows->image.width)
6175           line_info.x2=(short) windows->image.width;
6176       if (line_info.y2 < 0)
6177         line_info.y2=0;
6178       else
6179         if (line_info.y2 > (int) windows->image.height)
6180           line_info.y2=(short) windows->image.height;
6181       distance=(unsigned int)
6182         (((line_info.x2-line_info.x1+1)*(line_info.x2-line_info.x1+1))+
6183          ((line_info.y2-line_info.y1+1)*(line_info.y2-line_info.y1+1)));
6184       if ((((int) rectangle_info.x != x) && ((int) rectangle_info.y != y)) ||
6185           ((state & ExitState) != 0))
6186         {
6187           if (rectangle_info.x < 0)
6188             rectangle_info.x=0;
6189           else
6190             if (rectangle_info.x > (ssize_t) windows->image.width)
6191               rectangle_info.x=(ssize_t) windows->image.width;
6192           if ((int) rectangle_info.x < x)
6193             rectangle_info.width=(unsigned int) (x-rectangle_info.x);
6194           else
6195             {
6196               rectangle_info.width=(unsigned int) (rectangle_info.x-x);
6197               rectangle_info.x=(ssize_t) x;
6198             }
6199           if (rectangle_info.y < 0)
6200             rectangle_info.y=0;
6201           else
6202             if (rectangle_info.y > (ssize_t) windows->image.height)
6203               rectangle_info.y=(ssize_t) windows->image.height;
6204           if ((int) rectangle_info.y < y)
6205             rectangle_info.height=(unsigned int) (y-rectangle_info.y);
6206           else
6207             {
6208               rectangle_info.height=(unsigned int) (rectangle_info.y-y);
6209               rectangle_info.y=(ssize_t) y;
6210             }
6211         }
6212     } while ((state & ExitState) == 0);
6213     (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
6214     if ((element == PointElement) || (element == PolygonElement) ||
6215         (element == FillPolygonElement))
6216       {
6217         /*
6218           Determine polygon bounding box.
6219         */
6220         rectangle_info.x=(ssize_t) coordinate_info->x;
6221         rectangle_info.y=(ssize_t) coordinate_info->y;
6222         x=coordinate_info->x;
6223         y=coordinate_info->y;
6224         for (i=1; i < number_coordinates; i++)
6225         {
6226           if (coordinate_info[i].x > x)
6227             x=coordinate_info[i].x;
6228           if (coordinate_info[i].y > y)
6229             y=coordinate_info[i].y;
6230           if ((ssize_t) coordinate_info[i].x < rectangle_info.x)
6231             rectangle_info.x=MagickMax((ssize_t) coordinate_info[i].x,0);
6232           if ((ssize_t) coordinate_info[i].y < rectangle_info.y)
6233             rectangle_info.y=MagickMax((ssize_t) coordinate_info[i].y,0);
6234         }
6235         rectangle_info.width=(size_t) (x-rectangle_info.x);
6236         rectangle_info.height=(size_t) (y-rectangle_info.y);
6237         for (i=0; i < number_coordinates; i++)
6238         {
6239           coordinate_info[i].x-=rectangle_info.x;
6240           coordinate_info[i].y-=rectangle_info.y;
6241         }
6242       }
6243     else
6244       if (distance <= 9)
6245         continue;
6246       else
6247         if ((element == RectangleElement) ||
6248             (element == CircleElement) || (element == EllipseElement))
6249           {
6250             rectangle_info.width--;
6251             rectangle_info.height--;
6252           }
6253     /*
6254       Drawing is relative to image configuration.
6255     */
6256     draw_info.x=(int) rectangle_info.x;
6257     draw_info.y=(int) rectangle_info.y;
6258     (void) XMagickCommand(display,resource_info,windows,SaveToUndoBufferCommand,
6259       image,exception);
6260     width=(unsigned int) (*image)->columns;
6261     height=(unsigned int) (*image)->rows;
6262     x=0;
6263     y=0;
6264     if (windows->image.crop_geometry != (char *) NULL)
6265       (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
6266     draw_info.x+=windows->image.x-(line_width/2);
6267     if (draw_info.x < 0)
6268       draw_info.x=0;
6269     draw_info.x=(int) (width*draw_info.x/windows->image.ximage->width);
6270     draw_info.y+=windows->image.y-(line_width/2);
6271     if (draw_info.y < 0)
6272       draw_info.y=0;
6273     draw_info.y=(int) height*draw_info.y/windows->image.ximage->height;
6274     draw_info.width=(unsigned int) rectangle_info.width+(line_width << 1);
6275     if (draw_info.width > (unsigned int) (*image)->columns)
6276       draw_info.width=(unsigned int) (*image)->columns;
6277     draw_info.height=(unsigned int) rectangle_info.height+(line_width << 1);
6278     if (draw_info.height > (unsigned int) (*image)->rows)
6279       draw_info.height=(unsigned int) (*image)->rows;
6280     (void) FormatLocaleString(draw_info.geometry,MaxTextExtent,"%ux%u%+d%+d",
6281       width*draw_info.width/windows->image.ximage->width,
6282       height*draw_info.height/windows->image.ximage->height,
6283       draw_info.x+x,draw_info.y+y);
6284     /*
6285       Initialize drawing attributes.
6286     */
6287     draw_info.degrees=0.0;
6288     draw_info.element=element;
6289     draw_info.stipple=stipple;
6290     draw_info.line_width=line_width;
6291     draw_info.line_info=line_info;
6292     if (line_info.x1 > (int) (line_width/2))
6293       draw_info.line_info.x1=(short) line_width/2;
6294     if (line_info.y1 > (int) (line_width/2))
6295       draw_info.line_info.y1=(short) line_width/2;
6296     draw_info.line_info.x2=(short) (line_info.x2-line_info.x1+(line_width/2));
6297     draw_info.line_info.y2=(short) (line_info.y2-line_info.y1+(line_width/2));
6298     if ((draw_info.line_info.x2 < 0) && (draw_info.line_info.y2 < 0))
6299       {
6300         draw_info.line_info.x2=(-draw_info.line_info.x2);
6301         draw_info.line_info.y2=(-draw_info.line_info.y2);
6302       }
6303     if (draw_info.line_info.x2 < 0)
6304       {
6305         draw_info.line_info.x2=(-draw_info.line_info.x2);
6306         Swap(draw_info.line_info.x1,draw_info.line_info.x2);
6307       }
6308     if (draw_info.line_info.y2 < 0)
6309       {
6310         draw_info.line_info.y2=(-draw_info.line_info.y2);
6311         Swap(draw_info.line_info.y1,draw_info.line_info.y2);
6312       }
6313     draw_info.rectangle_info=rectangle_info;
6314     if (draw_info.rectangle_info.x > (ssize_t) (line_width/2))
6315       draw_info.rectangle_info.x=(ssize_t) line_width/2;
6316     if (draw_info.rectangle_info.y > (ssize_t) (line_width/2))
6317       draw_info.rectangle_info.y=(ssize_t) line_width/2;
6318     draw_info.number_coordinates=(unsigned int) number_coordinates;
6319     draw_info.coordinate_info=coordinate_info;
6320     windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
6321     /*
6322       Draw element on image.
6323     */
6324     XSetCursorState(display,windows,MagickTrue);
6325     XCheckRefreshWindows(display,windows);
6326     status=XDrawImage(display,windows->pixel_info,&draw_info,*image);
6327     XSetCursorState(display,windows,MagickFalse);
6328     /*
6329       Update image colormap and return to image drawing.
6330     */
6331     XConfigureImageColormap(display,resource_info,windows,*image);
6332     (void) XConfigureImage(display,resource_info,windows,*image,exception);
6333   }
6334   XSetCursorState(display,windows,MagickFalse);
6335   coordinate_info=(XPoint *) RelinquishMagickMemory(coordinate_info);
6336   return(status != 0 ? MagickTrue : MagickFalse);
6337 }
6338 \f
6339 /*
6340 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6341 %                                                                             %
6342 %                                                                             %
6343 %                                                                             %
6344 +   X D r a w P a n R e c t a n g l e                                         %
6345 %                                                                             %
6346 %                                                                             %
6347 %                                                                             %
6348 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6349 %
6350 %  XDrawPanRectangle() draws a rectangle in the pan window.  The pan window
6351 %  displays a zoom image and the rectangle shows which portion of the image is
6352 %  displayed in the Image window.
6353 %
6354 %  The format of the XDrawPanRectangle method is:
6355 %
6356 %      XDrawPanRectangle(Display *display,XWindows *windows)
6357 %
6358 %  A description of each parameter follows:
6359 %
6360 %    o display: Specifies a connection to an X server;  returned from
6361 %      XOpenDisplay.
6362 %
6363 %    o windows: Specifies a pointer to a XWindows structure.
6364 %
6365 */
6366 static void XDrawPanRectangle(Display *display,XWindows *windows)
6367 {
6368   MagickRealType
6369     scale_factor;
6370
6371   RectangleInfo
6372     highlight_info;
6373
6374   /*
6375     Determine dimensions of the panning rectangle.
6376   */
6377   scale_factor=(MagickRealType) windows->pan.width/windows->image.ximage->width;
6378   highlight_info.x=(ssize_t) (scale_factor*windows->image.x+0.5);
6379   highlight_info.width=(unsigned int) (scale_factor*windows->image.width+0.5);
6380   scale_factor=(MagickRealType)
6381     windows->pan.height/windows->image.ximage->height;
6382   highlight_info.y=(ssize_t) (scale_factor*windows->image.y+0.5);
6383   highlight_info.height=(unsigned int) (scale_factor*windows->image.height+0.5);
6384   /*
6385     Display the panning rectangle.
6386   */
6387   (void) XClearWindow(display,windows->pan.id);
6388   XHighlightRectangle(display,windows->pan.id,windows->pan.annotate_context,
6389     &highlight_info);
6390 }
6391 \f
6392 /*
6393 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6394 %                                                                             %
6395 %                                                                             %
6396 %                                                                             %
6397 +   X I m a g e C a c h e                                                     %
6398 %                                                                             %
6399 %                                                                             %
6400 %                                                                             %
6401 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6402 %
6403 %  XImageCache() handles the creation, manipulation, and destruction of the
6404 %  image cache (undo and redo buffers).
6405 %
6406 %  The format of the XImageCache method is:
6407 %
6408 %      void XImageCache(Display *display,XResourceInfo *resource_info,
6409 %        XWindows *windows,const CommandType command,Image **image,
6410 %        ExceptionInfo *exception)
6411 %
6412 %  A description of each parameter follows:
6413 %
6414 %    o display: Specifies a connection to an X server; returned from
6415 %      XOpenDisplay.
6416 %
6417 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6418 %
6419 %    o windows: Specifies a pointer to a XWindows structure.
6420 %
6421 %    o command: Specifies a command to perform.
6422 %
6423 %    o image: the image;  XImageCache may transform the image and return a new
6424 %      image pointer.
6425 %
6426 %    o exception: return any errors or warnings in this structure.
6427 %
6428 */
6429 static void XImageCache(Display *display,XResourceInfo *resource_info,
6430   XWindows *windows,const CommandType command,Image **image,
6431   ExceptionInfo *exception)
6432 {
6433   Image
6434     *cache_image;
6435
6436   static Image
6437     *redo_image = (Image *) NULL,
6438     *undo_image = (Image *) NULL;
6439
6440   switch (command)
6441   {
6442     case FreeBuffersCommand:
6443     {
6444       /*
6445         Free memory from the undo and redo cache.
6446       */
6447       while (undo_image != (Image *) NULL)
6448       {
6449         cache_image=undo_image;
6450         undo_image=GetPreviousImageInList(undo_image);
6451         cache_image->list=DestroyImage(cache_image->list);
6452         cache_image=DestroyImage(cache_image);
6453       }
6454       undo_image=NewImageList();
6455       if (redo_image != (Image *) NULL)
6456         redo_image=DestroyImage(redo_image);
6457       redo_image=NewImageList();
6458       return;
6459     }
6460     case UndoCommand:
6461     {
6462       char
6463         image_geometry[MaxTextExtent];
6464
6465       /*
6466         Undo the last image transformation.
6467       */
6468       if (undo_image == (Image *) NULL)
6469         {
6470           (void) XBell(display,0);
6471           return;
6472         }
6473       cache_image=undo_image;
6474       undo_image=GetPreviousImageInList(undo_image);
6475       windows->image.window_changes.width=(int) cache_image->columns;
6476       windows->image.window_changes.height=(int) cache_image->rows;
6477       (void) FormatLocaleString(image_geometry,MaxTextExtent,"%dx%d!",
6478         windows->image.ximage->width,windows->image.ximage->height);
6479       (void) TransformImage(image,windows->image.crop_geometry,image_geometry);
6480       if (windows->image.crop_geometry != (char *) NULL)
6481         windows->image.crop_geometry=(char *)
6482           RelinquishMagickMemory(windows->image.crop_geometry);
6483       windows->image.crop_geometry=cache_image->geometry;
6484       if (redo_image != (Image *) NULL)
6485         redo_image=DestroyImage(redo_image);
6486       redo_image=(*image);
6487       *image=cache_image->list;
6488       cache_image=DestroyImage(cache_image);
6489       if (windows->image.orphan != MagickFalse)
6490         return;
6491       XConfigureImageColormap(display,resource_info,windows,*image);
6492       (void) XConfigureImage(display,resource_info,windows,*image,exception);
6493       return;
6494     }
6495     case CutCommand:
6496     case PasteCommand:
6497     case ApplyCommand:
6498     case HalfSizeCommand:
6499     case OriginalSizeCommand:
6500     case DoubleSizeCommand:
6501     case ResizeCommand:
6502     case TrimCommand:
6503     case CropCommand:
6504     case ChopCommand:
6505     case FlipCommand:
6506     case FlopCommand:
6507     case RotateRightCommand:
6508     case RotateLeftCommand:
6509     case RotateCommand:
6510     case ShearCommand:
6511     case RollCommand:
6512     case NegateCommand:
6513     case ContrastStretchCommand:
6514     case SigmoidalContrastCommand:
6515     case NormalizeCommand:
6516     case EqualizeCommand:
6517     case HueCommand:
6518     case SaturationCommand:
6519     case BrightnessCommand:
6520     case GammaCommand:
6521     case SpiffCommand:
6522     case DullCommand:
6523     case GrayscaleCommand:
6524     case MapCommand:
6525     case QuantizeCommand:
6526     case DespeckleCommand:
6527     case EmbossCommand:
6528     case ReduceNoiseCommand:
6529     case AddNoiseCommand:
6530     case SharpenCommand:
6531     case BlurCommand:
6532     case ThresholdCommand:
6533     case EdgeDetectCommand:
6534     case SpreadCommand:
6535     case ShadeCommand:
6536     case RaiseCommand:
6537     case SegmentCommand:
6538     case SolarizeCommand:
6539     case SepiaToneCommand:
6540     case SwirlCommand:
6541     case ImplodeCommand:
6542     case VignetteCommand:
6543     case WaveCommand:
6544     case OilPaintCommand:
6545     case CharcoalDrawCommand:
6546     case AnnotateCommand:
6547     case AddBorderCommand:
6548     case AddFrameCommand:
6549     case CompositeCommand:
6550     case CommentCommand:
6551     case LaunchCommand:
6552     case RegionofInterestCommand:
6553     case SaveToUndoBufferCommand:
6554     case RedoCommand:
6555     {
6556       Image
6557         *previous_image;
6558
6559       ssize_t
6560         bytes;
6561
6562       bytes=(ssize_t) ((*image)->columns*(*image)->rows*sizeof(PixelPacket));
6563       if (undo_image != (Image *) NULL)
6564         {
6565           /*
6566             Ensure the undo cache has enough memory available.
6567           */
6568           previous_image=undo_image;
6569           while (previous_image != (Image *) NULL)
6570           {
6571             bytes+=previous_image->list->columns*previous_image->list->rows*
6572               sizeof(PixelPacket);
6573             if (bytes <= (ssize_t) (resource_info->undo_cache << 20))
6574               {
6575                 previous_image=GetPreviousImageInList(previous_image);
6576                 continue;
6577               }
6578             bytes-=previous_image->list->columns*previous_image->list->rows*
6579               sizeof(PixelPacket);
6580             if (previous_image == undo_image)
6581               undo_image=NewImageList();
6582             else
6583               previous_image->next->previous=NewImageList();
6584             break;
6585           }
6586           while (previous_image != (Image *) NULL)
6587           {
6588             /*
6589               Delete any excess memory from undo cache.
6590             */
6591             cache_image=previous_image;
6592             previous_image=GetPreviousImageInList(previous_image);
6593             cache_image->list=DestroyImage(cache_image->list);
6594             cache_image=DestroyImage(cache_image);
6595           }
6596         }
6597       if (bytes > (ssize_t) (resource_info->undo_cache << 20))
6598         break;
6599       /*
6600         Save image before transformations are applied.
6601       */
6602       cache_image=AcquireImage((ImageInfo *) NULL);
6603       if (cache_image == (Image *) NULL)
6604         break;
6605       XSetCursorState(display,windows,MagickTrue);
6606       XCheckRefreshWindows(display,windows);
6607       cache_image->list=CloneImage(*image,0,0,MagickTrue,exception);
6608       XSetCursorState(display,windows,MagickFalse);
6609       if (cache_image->list == (Image *) NULL)
6610         {
6611           cache_image=DestroyImage(cache_image);
6612           break;
6613         }
6614       cache_image->columns=(size_t) windows->image.ximage->width;
6615       cache_image->rows=(size_t) windows->image.ximage->height;
6616       cache_image->geometry=windows->image.crop_geometry;
6617       if (windows->image.crop_geometry != (char *) NULL)
6618         {
6619           cache_image->geometry=AcquireString((char *) NULL);
6620           (void) CopyMagickString(cache_image->geometry,
6621             windows->image.crop_geometry,MaxTextExtent);
6622         }
6623       if (undo_image == (Image *) NULL)
6624         {
6625           undo_image=cache_image;
6626           break;
6627         }
6628       undo_image->next=cache_image;
6629       undo_image->next->previous=undo_image;
6630       undo_image=undo_image->next;
6631       break;
6632     }
6633     default:
6634       break;
6635   }
6636   if (command == RedoCommand)
6637     {
6638       /*
6639         Redo the last image transformation.
6640       */
6641       if (redo_image == (Image *) NULL)
6642         {
6643           (void) XBell(display,0);
6644           return;
6645         }
6646       windows->image.window_changes.width=(int) redo_image->columns;
6647       windows->image.window_changes.height=(int) redo_image->rows;
6648       if (windows->image.crop_geometry != (char *) NULL)
6649         windows->image.crop_geometry=(char *)
6650           RelinquishMagickMemory(windows->image.crop_geometry);
6651       windows->image.crop_geometry=redo_image->geometry;
6652       *image=DestroyImage(*image);
6653       *image=redo_image;
6654       redo_image=NewImageList();
6655       if (windows->image.orphan != MagickFalse)
6656         return;
6657       XConfigureImageColormap(display,resource_info,windows,*image);
6658       (void) XConfigureImage(display,resource_info,windows,*image,exception);
6659       return;
6660     }
6661   if (command != InfoCommand)
6662     return;
6663   /*
6664     Display image info.
6665   */
6666   XSetCursorState(display,windows,MagickTrue);
6667   XCheckRefreshWindows(display,windows);
6668   XDisplayImageInfo(display,resource_info,windows,undo_image,*image);
6669   XSetCursorState(display,windows,MagickFalse);
6670 }
6671 \f
6672 /*
6673 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6674 %                                                                             %
6675 %                                                                             %
6676 %                                                                             %
6677 +   X I m a g e W i n d o w C o m m a n d                                     %
6678 %                                                                             %
6679 %                                                                             %
6680 %                                                                             %
6681 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6682 %
6683 %  XImageWindowCommand() makes a transform to the image or Image window as
6684 %  specified by a user menu button or keyboard command.
6685 %
6686 %  The format of the XImageWindowCommand method is:
6687 %
6688 %      CommandType XImageWindowCommand(Display *display,
6689 %        XResourceInfo *resource_info,XWindows *windows,
6690 %        const MagickStatusType state,KeySym key_symbol,Image **image,
6691 %        ExceptionInfo *exception)
6692 %
6693 %  A description of each parameter follows:
6694 %
6695 %    o nexus:  Method XImageWindowCommand returns an image when the
6696 %      user chooses 'Open Image' from the command menu.  Otherwise a null
6697 %      image is returned.
6698 %
6699 %    o display: Specifies a connection to an X server; returned from
6700 %      XOpenDisplay.
6701 %
6702 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6703 %
6704 %    o windows: Specifies a pointer to a XWindows structure.
6705 %
6706 %    o state: key mask.
6707 %
6708 %    o key_symbol: Specifies a command to perform.
6709 %
6710 %    o image: the image;  XImageWIndowCommand may transform the image and
6711 %      return a new image pointer.
6712 %
6713 %    o exception: return any errors or warnings in this structure.
6714 %
6715 */
6716 static CommandType XImageWindowCommand(Display *display,
6717   XResourceInfo *resource_info,XWindows *windows,const MagickStatusType state,
6718   KeySym key_symbol,Image **image,ExceptionInfo *exception)
6719 {
6720   static char
6721     delta[MaxTextExtent] = "";
6722
6723   static const char
6724     Digits[] = "01234567890";
6725
6726   static KeySym
6727     last_symbol = XK_0;
6728
6729   if ((key_symbol >= XK_0) && (key_symbol <= XK_9))
6730     {
6731       if (((last_symbol < XK_0) || (last_symbol > XK_9)))
6732         {
6733           *delta='\0';
6734           resource_info->quantum=1;
6735         }
6736       last_symbol=key_symbol;
6737       delta[strlen(delta)+1]='\0';
6738       delta[strlen(delta)]=Digits[key_symbol-XK_0];
6739       resource_info->quantum=StringToLong(delta);
6740       return(NullCommand);
6741     }
6742   last_symbol=key_symbol;
6743   if (resource_info->immutable)
6744     {
6745       /*
6746         Virtual image window has a restricted command set.
6747       */
6748       switch (key_symbol)
6749       {
6750         case XK_question:
6751           return(InfoCommand);
6752         case XK_p:
6753         case XK_Print:
6754           return(PrintCommand);
6755         case XK_space:
6756           return(NextCommand);
6757         case XK_q:
6758         case XK_Escape:
6759           return(QuitCommand);
6760         default:
6761           break;
6762       }
6763       return(NullCommand);
6764     }
6765   switch ((int) key_symbol)
6766   {
6767     case XK_o:
6768     {
6769       if ((state & ControlMask) == 0)
6770         break;
6771       return(OpenCommand);
6772     }
6773     case XK_space:
6774       return(NextCommand);
6775     case XK_BackSpace:
6776       return(FormerCommand);
6777     case XK_s:
6778     {
6779       if ((state & Mod1Mask) != 0)
6780         return(SwirlCommand);
6781       if ((state & ControlMask) == 0)
6782         return(ShearCommand);
6783       return(SaveCommand);
6784     }
6785     case XK_p:
6786     case XK_Print:
6787     {
6788       if ((state & Mod1Mask) != 0)
6789         return(OilPaintCommand);
6790       if ((state & Mod4Mask) != 0)
6791         return(ColorCommand);
6792       if ((state & ControlMask) == 0)
6793         return(NullCommand);
6794       return(PrintCommand);
6795     }
6796     case XK_d:
6797     {
6798       if ((state & Mod4Mask) != 0)
6799         return(DrawCommand);
6800       if ((state & ControlMask) == 0)
6801         return(NullCommand);
6802       return(DeleteCommand);
6803     }
6804     case XK_Select:
6805     {
6806       if ((state & ControlMask) == 0)
6807         return(NullCommand);
6808       return(SelectCommand);
6809     }
6810     case XK_n:
6811     {
6812       if ((state & ControlMask) == 0)
6813         return(NullCommand);
6814       return(NewCommand);
6815     }
6816     case XK_q:
6817     case XK_Escape:
6818       return(QuitCommand);
6819     case XK_z:
6820     case XK_Undo:
6821     {
6822       if ((state & ControlMask) == 0)
6823         return(NullCommand);
6824       return(UndoCommand);
6825     }
6826     case XK_r:
6827     case XK_Redo:
6828     {
6829       if ((state & ControlMask) == 0)
6830         return(RollCommand);
6831       return(RedoCommand);
6832     }
6833     case XK_x:
6834     {
6835       if ((state & ControlMask) == 0)
6836         return(NullCommand);
6837       return(CutCommand);
6838     }
6839     case XK_c:
6840     {
6841       if ((state & Mod1Mask) != 0)
6842         return(CharcoalDrawCommand);
6843       if ((state & ControlMask) == 0)
6844         return(CropCommand);
6845       return(CopyCommand);
6846     }
6847     case XK_v:
6848     case XK_Insert:
6849     {
6850       if ((state & Mod4Mask) != 0)
6851         return(CompositeCommand);
6852       if ((state & ControlMask) == 0)
6853         return(FlipCommand);
6854       return(PasteCommand);
6855     }
6856     case XK_less:
6857       return(HalfSizeCommand);
6858     case XK_minus:
6859       return(OriginalSizeCommand);
6860     case XK_greater:
6861       return(DoubleSizeCommand);
6862     case XK_percent:
6863       return(ResizeCommand);
6864     case XK_at:
6865       return(RefreshCommand);
6866     case XK_bracketleft:
6867       return(ChopCommand);
6868     case XK_h:
6869       return(FlopCommand);
6870     case XK_slash:
6871       return(RotateRightCommand);
6872     case XK_backslash:
6873       return(RotateLeftCommand);
6874     case XK_asterisk:
6875       return(RotateCommand);
6876     case XK_t:
6877       return(TrimCommand);
6878     case XK_H:
6879       return(HueCommand);
6880     case XK_S:
6881       return(SaturationCommand);
6882     case XK_L:
6883       return(BrightnessCommand);
6884     case XK_G:
6885       return(GammaCommand);
6886     case XK_C:
6887       return(SpiffCommand);
6888     case XK_Z:
6889       return(DullCommand);
6890     case XK_N:
6891       return(NormalizeCommand);
6892     case XK_equal:
6893       return(EqualizeCommand);
6894     case XK_asciitilde:
6895       return(NegateCommand);
6896     case XK_period:
6897       return(GrayscaleCommand);
6898     case XK_numbersign:
6899       return(QuantizeCommand);
6900     case XK_F2:
6901       return(DespeckleCommand);
6902     case XK_F3:
6903       return(EmbossCommand);
6904     case XK_F4:
6905       return(ReduceNoiseCommand);
6906     case XK_F5:
6907       return(AddNoiseCommand);
6908     case XK_F6:
6909       return(SharpenCommand);
6910     case XK_F7:
6911       return(BlurCommand);
6912     case XK_F8:
6913       return(ThresholdCommand);
6914     case XK_F9:
6915       return(EdgeDetectCommand);
6916     case XK_F10:
6917       return(SpreadCommand);
6918     case XK_F11:
6919       return(ShadeCommand);
6920     case XK_F12:
6921       return(RaiseCommand);
6922     case XK_F13:
6923       return(SegmentCommand);
6924     case XK_i:
6925     {
6926       if ((state & Mod1Mask) == 0)
6927         return(NullCommand);
6928       return(ImplodeCommand);
6929     }
6930     case XK_w:
6931     {
6932       if ((state & Mod1Mask) == 0)
6933         return(NullCommand);
6934       return(WaveCommand);
6935     }
6936     case XK_m:
6937     {
6938       if ((state & Mod4Mask) == 0)
6939         return(NullCommand);
6940       return(MatteCommand);
6941     }
6942     case XK_b:
6943     {
6944       if ((state & Mod4Mask) == 0)
6945         return(NullCommand);
6946       return(AddBorderCommand);
6947     }
6948     case XK_f:
6949     {
6950       if ((state & Mod4Mask) == 0)
6951         return(NullCommand);
6952       return(AddFrameCommand);
6953     }
6954     case XK_exclam:
6955     {
6956       if ((state & Mod4Mask) == 0)
6957         return(NullCommand);
6958       return(CommentCommand);
6959     }
6960     case XK_a:
6961     {
6962       if ((state & Mod1Mask) != 0)
6963         return(ApplyCommand);
6964       if ((state & Mod4Mask) != 0)
6965         return(AnnotateCommand);
6966       if ((state & ControlMask) == 0)
6967         return(NullCommand);
6968       return(RegionofInterestCommand);
6969     }
6970     case XK_question:
6971       return(InfoCommand);
6972     case XK_plus:
6973       return(ZoomCommand);
6974     case XK_P:
6975     {
6976       if ((state & ShiftMask) == 0)
6977         return(NullCommand);
6978       return(ShowPreviewCommand);
6979     }
6980     case XK_Execute:
6981       return(LaunchCommand);
6982     case XK_F1:
6983       return(HelpCommand);
6984     case XK_Find:
6985       return(BrowseDocumentationCommand);
6986     case XK_Menu:
6987     {
6988       (void) XMapRaised(display,windows->command.id);
6989       return(NullCommand);
6990     }
6991     case XK_Next:
6992     case XK_Prior:
6993     case XK_Home:
6994     case XK_KP_Home:
6995     {
6996       XTranslateImage(display,windows,*image,key_symbol);
6997       return(NullCommand);
6998     }
6999     case XK_Up:
7000     case XK_KP_Up:
7001     case XK_Down:
7002     case XK_KP_Down:
7003     case XK_Left:
7004     case XK_KP_Left:
7005     case XK_Right:
7006     case XK_KP_Right:
7007     {
7008       if ((state & Mod1Mask) != 0)
7009         {
7010           RectangleInfo
7011             crop_info;
7012
7013           /*
7014             Trim one pixel from edge of image.
7015           */
7016           crop_info.x=0;
7017           crop_info.y=0;
7018           crop_info.width=(size_t) windows->image.ximage->width;
7019           crop_info.height=(size_t) windows->image.ximage->height;
7020           if ((key_symbol == XK_Up) || (key_symbol == XK_KP_Up))
7021             {
7022               if (resource_info->quantum >= (int) crop_info.height)
7023                 resource_info->quantum=(int) crop_info.height-1;
7024               crop_info.height-=resource_info->quantum;
7025             }
7026           if ((key_symbol == XK_Down) || (key_symbol == XK_KP_Down))
7027             {
7028               if (resource_info->quantum >= (int) (crop_info.height-crop_info.y))
7029                 resource_info->quantum=(int) (crop_info.height-crop_info.y-1);
7030               crop_info.y+=resource_info->quantum;
7031               crop_info.height-=resource_info->quantum;
7032             }
7033           if ((key_symbol == XK_Left) || (key_symbol == XK_KP_Left))
7034             {
7035               if (resource_info->quantum >= (int) crop_info.width)
7036                 resource_info->quantum=(int) crop_info.width-1;
7037               crop_info.width-=resource_info->quantum;
7038             }
7039           if ((key_symbol == XK_Right) || (key_symbol == XK_KP_Right))
7040             {
7041               if (resource_info->quantum >= (int) (crop_info.width-crop_info.x))
7042                 resource_info->quantum=(int) (crop_info.width-crop_info.x-1);
7043               crop_info.x+=resource_info->quantum;
7044               crop_info.width-=resource_info->quantum;
7045             }
7046           if ((int) (windows->image.x+windows->image.width) >
7047               (int) crop_info.width)
7048             windows->image.x=(int) (crop_info.width-windows->image.width);
7049           if ((int) (windows->image.y+windows->image.height) >
7050               (int) crop_info.height)
7051             windows->image.y=(int) (crop_info.height-windows->image.height);
7052           XSetCropGeometry(display,windows,&crop_info,*image);
7053           windows->image.window_changes.width=(int) crop_info.width;
7054           windows->image.window_changes.height=(int) crop_info.height;
7055           (void) XSetWindowBackgroundPixmap(display,windows->image.id,None);
7056           (void) XConfigureImage(display,resource_info,windows,*image,
7057             exception);
7058           return(NullCommand);
7059         }
7060       XTranslateImage(display,windows,*image,key_symbol);
7061       return(NullCommand);
7062     }
7063     default:
7064       return(NullCommand);
7065   }
7066   return(NullCommand);
7067 }
7068 \f
7069 /*
7070 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7071 %                                                                             %
7072 %                                                                             %
7073 %                                                                             %
7074 +   X M a g i c k C o m m a n d                                               %
7075 %                                                                             %
7076 %                                                                             %
7077 %                                                                             %
7078 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7079 %
7080 %  XMagickCommand() makes a transform to the image or Image window as
7081 %  specified by a user menu button or keyboard command.
7082 %
7083 %  The format of the XMagickCommand method is:
7084 %
7085 %      Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7086 %        XWindows *windows,const CommandType command,Image **image,
7087 %        ExceptionInfo *exception)
7088 %
7089 %  A description of each parameter follows:
7090 %
7091 %    o display: Specifies a connection to an X server; returned from
7092 %      XOpenDisplay.
7093 %
7094 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
7095 %
7096 %    o windows: Specifies a pointer to a XWindows structure.
7097 %
7098 %    o command: Specifies a command to perform.
7099 %
7100 %    o image: the image;  XMagickCommand may transform the image and return a
7101 %      new image pointer.
7102 %
7103 %    o exception: return any errors or warnings in this structure.
7104 %
7105 */
7106 static Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7107   XWindows *windows,const CommandType command,Image **image,
7108   ExceptionInfo *exception)
7109 {
7110   char
7111     filename[MaxTextExtent],
7112     geometry[MaxTextExtent],
7113     modulate_factors[MaxTextExtent];
7114
7115   GeometryInfo
7116     geometry_info;
7117
7118   Image
7119     *nexus;
7120
7121   ImageInfo
7122     *image_info;
7123
7124   int
7125     x,
7126     y;
7127
7128   MagickStatusType
7129     flags,
7130     status;
7131
7132   QuantizeInfo
7133     quantize_info;
7134
7135   RectangleInfo
7136     page_geometry;
7137
7138   register int
7139     i;
7140
7141   static char
7142     color[MaxTextExtent] = "gray";
7143
7144   unsigned int
7145     height,
7146     width;
7147
7148   /*
7149     Process user command.
7150   */
7151   XCheckRefreshWindows(display,windows);
7152   XImageCache(display,resource_info,windows,command,image,exception);
7153   nexus=NewImageList();
7154   windows->image.window_changes.width=windows->image.ximage->width;
7155   windows->image.window_changes.height=windows->image.ximage->height;
7156   image_info=CloneImageInfo(resource_info->image_info);
7157   SetGeometryInfo(&geometry_info);
7158   GetQuantizeInfo(&quantize_info);
7159   switch (command)
7160   {
7161     case OpenCommand:
7162     {
7163       /*
7164         Load image.
7165       */
7166       nexus=XOpenImage(display,resource_info,windows,MagickFalse);
7167       break;
7168     }
7169     case NextCommand:
7170     {
7171       /*
7172         Display next image.
7173       */
7174       for (i=0; i < resource_info->quantum; i++)
7175         XClientMessage(display,windows->image.id,windows->im_protocols,
7176           windows->im_next_image,CurrentTime);
7177       break;
7178     }
7179     case FormerCommand:
7180     {
7181       /*
7182         Display former image.
7183       */
7184       for (i=0; i < resource_info->quantum; i++)
7185         XClientMessage(display,windows->image.id,windows->im_protocols,
7186           windows->im_former_image,CurrentTime);
7187       break;
7188     }
7189     case SelectCommand:
7190     {
7191       int
7192         status;
7193
7194       /*
7195         Select image.
7196       */
7197       status=chdir(resource_info->home_directory);
7198       if (status == -1)
7199         (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
7200           "UnableToOpenFile","%s",resource_info->home_directory);
7201       nexus=XOpenImage(display,resource_info,windows,MagickTrue);
7202       break;
7203     }
7204     case SaveCommand:
7205     {
7206       /*
7207         Save image.
7208       */
7209       status=XSaveImage(display,resource_info,windows,*image,exception);
7210       if (status == MagickFalse)
7211         {
7212           XNoticeWidget(display,windows,"Unable to write X image:",
7213             (*image)->filename);
7214           break;
7215         }
7216       break;
7217     }
7218     case PrintCommand:
7219     {
7220       /*
7221         Print image.
7222       */
7223       status=XPrintImage(display,resource_info,windows,*image,exception);
7224       if (status == MagickFalse)
7225         {
7226           XNoticeWidget(display,windows,"Unable to print X image:",
7227             (*image)->filename);
7228           break;
7229         }
7230       break;
7231     }
7232     case DeleteCommand:
7233     {
7234       static char
7235         filename[MaxTextExtent] = "\0";
7236
7237       /*
7238         Delete image file.
7239       */
7240       XFileBrowserWidget(display,windows,"Delete",filename);
7241       if (*filename == '\0')
7242         break;
7243       status=remove(filename) != 0 ? MagickTrue : MagickFalse;
7244       if (status != MagickFalse)
7245         XNoticeWidget(display,windows,"Unable to delete image file:",filename);
7246       break;
7247     }
7248     case NewCommand:
7249     {
7250       int
7251         status;
7252
7253       static char
7254         color[MaxTextExtent] = "gray",
7255         geometry[MaxTextExtent] = "640x480";
7256
7257       static const char
7258         *format = "gradient";
7259
7260       /*
7261         Query user for canvas geometry.
7262       */
7263       status=XDialogWidget(display,windows,"New","Enter image geometry:",
7264         geometry);
7265       if (*geometry == '\0')
7266         break;
7267       if (status == 0)
7268         format="xc";
7269       XColorBrowserWidget(display,windows,"Select",color);
7270       if (*color == '\0')
7271         break;
7272       /*
7273         Create canvas.
7274       */
7275       (void) FormatLocaleString(image_info->filename,MaxTextExtent,
7276         "%s:%s",format,color);
7277       (void) CloneString(&image_info->size,geometry);
7278       nexus=ReadImage(image_info,exception);
7279       CatchException(exception);
7280       XClientMessage(display,windows->image.id,windows->im_protocols,
7281         windows->im_next_image,CurrentTime);
7282       break;
7283     }
7284     case VisualDirectoryCommand:
7285     {
7286       /*
7287         Visual Image directory.
7288       */
7289       nexus=XVisualDirectoryImage(display,resource_info,windows,exception);
7290       break;
7291     }
7292     case QuitCommand:
7293     {
7294       /*
7295         exit program.
7296       */
7297       if (resource_info->confirm_exit == MagickFalse)
7298         XClientMessage(display,windows->image.id,windows->im_protocols,
7299           windows->im_exit,CurrentTime);
7300       else
7301         {
7302           int
7303             status;
7304
7305           /*
7306             Confirm program exit.
7307           */
7308           status=XConfirmWidget(display,windows,"Do you really want to exit",
7309             resource_info->client_name);
7310           if (status > 0)
7311             XClientMessage(display,windows->image.id,windows->im_protocols,
7312               windows->im_exit,CurrentTime);
7313         }
7314       break;
7315     }
7316     case CutCommand:
7317     {
7318       /*
7319         Cut image.
7320       */
7321       (void) XCropImage(display,resource_info,windows,*image,CutMode,exception);
7322       break;
7323     }
7324     case CopyCommand:
7325     {
7326       /*
7327         Copy image.
7328       */
7329       (void) XCropImage(display,resource_info,windows,*image,CopyMode,
7330         exception);
7331       break;
7332     }
7333     case PasteCommand:
7334     {
7335       /*
7336         Paste image.
7337       */
7338       status=XPasteImage(display,resource_info,windows,*image,exception);
7339       if (status == MagickFalse)
7340         {
7341           XNoticeWidget(display,windows,"Unable to paste X image",
7342             (*image)->filename);
7343           break;
7344         }
7345       break;
7346     }
7347     case HalfSizeCommand:
7348     {
7349       /*
7350         Half image size.
7351       */
7352       windows->image.window_changes.width=windows->image.ximage->width/2;
7353       windows->image.window_changes.height=windows->image.ximage->height/2;
7354       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7355       break;
7356     }
7357     case OriginalSizeCommand:
7358     {
7359       /*
7360         Original image size.
7361       */
7362       windows->image.window_changes.width=(int) (*image)->columns;
7363       windows->image.window_changes.height=(int) (*image)->rows;
7364       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7365       break;
7366     }
7367     case DoubleSizeCommand:
7368     {
7369       /*
7370         Double the image size.
7371       */
7372       windows->image.window_changes.width=windows->image.ximage->width << 1;
7373       windows->image.window_changes.height=windows->image.ximage->height << 1;
7374       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7375       break;
7376     }
7377     case ResizeCommand:
7378     {
7379       int
7380         status;
7381
7382       size_t
7383         height,
7384         width;
7385
7386       ssize_t
7387         x,
7388         y;
7389
7390       /*
7391         Resize image.
7392       */
7393       width=(size_t) windows->image.ximage->width;
7394       height=(size_t) windows->image.ximage->height;
7395       x=0;
7396       y=0;
7397       (void) FormatLocaleString(geometry,MaxTextExtent,"%.20gx%.20g+0+0",
7398         (double) width,(double) height);
7399       status=XDialogWidget(display,windows,"Resize",
7400         "Enter resize geometry (e.g. 640x480, 200%):",geometry);
7401       if (*geometry == '\0')
7402         break;
7403       if (status == 0)
7404         (void) ConcatenateMagickString(geometry,"!",MaxTextExtent);
7405       (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
7406       windows->image.window_changes.width=(int) width;
7407       windows->image.window_changes.height=(int) height;
7408       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7409       break;
7410     }
7411     case ApplyCommand:
7412     {
7413       char
7414         image_geometry[MaxTextExtent];
7415
7416       if ((windows->image.crop_geometry == (char *) NULL) &&
7417           ((int) (*image)->columns == windows->image.ximage->width) &&
7418           ((int) (*image)->rows == windows->image.ximage->height))
7419         break;
7420       /*
7421         Apply size transforms to image.
7422       */
7423       XSetCursorState(display,windows,MagickTrue);
7424       XCheckRefreshWindows(display,windows);
7425       /*
7426         Crop and/or scale displayed image.
7427       */
7428       (void) FormatLocaleString(image_geometry,MaxTextExtent,"%dx%d!",
7429         windows->image.ximage->width,windows->image.ximage->height);
7430       (void) TransformImage(image,windows->image.crop_geometry,image_geometry);
7431       if (windows->image.crop_geometry != (char *) NULL)
7432         windows->image.crop_geometry=(char *)
7433           RelinquishMagickMemory(windows->image.crop_geometry);
7434       windows->image.x=0;
7435       windows->image.y=0;
7436       XConfigureImageColormap(display,resource_info,windows,*image);
7437       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7438       break;
7439     }
7440     case RefreshCommand:
7441     {
7442       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7443       break;
7444     }
7445     case RestoreCommand:
7446     {
7447       /*
7448         Restore Image window to its original size.
7449       */
7450       if ((windows->image.width == (unsigned int) (*image)->columns) &&
7451           (windows->image.height == (unsigned int) (*image)->rows) &&
7452           (windows->image.crop_geometry == (char *) NULL))
7453         {
7454           (void) XBell(display,0);
7455           break;
7456         }
7457       windows->image.window_changes.width=(int) (*image)->columns;
7458       windows->image.window_changes.height=(int) (*image)->rows;
7459       if (windows->image.crop_geometry != (char *) NULL)
7460         {
7461           windows->image.crop_geometry=(char *)
7462             RelinquishMagickMemory(windows->image.crop_geometry);
7463           windows->image.crop_geometry=(char *) NULL;
7464           windows->image.x=0;
7465           windows->image.y=0;
7466         }
7467       XConfigureImageColormap(display,resource_info,windows,*image);
7468       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7469       break;
7470     }
7471     case CropCommand:
7472     {
7473       /*
7474         Crop image.
7475       */
7476       (void) XCropImage(display,resource_info,windows,*image,CropMode,
7477         exception);
7478       break;
7479     }
7480     case ChopCommand:
7481     {
7482       /*
7483         Chop image.
7484       */
7485       status=XChopImage(display,resource_info,windows,image,exception);
7486       if (status == MagickFalse)
7487         {
7488           XNoticeWidget(display,windows,"Unable to cut X image",
7489             (*image)->filename);
7490           break;
7491         }
7492       break;
7493     }
7494     case FlopCommand:
7495     {
7496       Image
7497         *flop_image;
7498
7499       /*
7500         Flop image scanlines.
7501       */
7502       XSetCursorState(display,windows,MagickTrue);
7503       XCheckRefreshWindows(display,windows);
7504       flop_image=FlopImage(*image,exception);
7505       if (flop_image != (Image *) NULL)
7506         {
7507           *image=DestroyImage(*image);
7508           *image=flop_image;
7509         }
7510       CatchException(exception);
7511       XSetCursorState(display,windows,MagickFalse);
7512       if (windows->image.crop_geometry != (char *) NULL)
7513         {
7514           /*
7515             Flop crop geometry.
7516           */
7517           width=(unsigned int) (*image)->columns;
7518           height=(unsigned int) (*image)->rows;
7519           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7520             &width,&height);
7521           (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
7522             "%ux%u%+d%+d",width,height,(int) (*image)->columns-(int) width-x,y);
7523         }
7524       if (windows->image.orphan != MagickFalse)
7525         break;
7526       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7527       break;
7528     }
7529     case FlipCommand:
7530     {
7531       Image
7532         *flip_image;
7533
7534       /*
7535         Flip image scanlines.
7536       */
7537       XSetCursorState(display,windows,MagickTrue);
7538       XCheckRefreshWindows(display,windows);
7539       flip_image=FlipImage(*image,exception);
7540       if (flip_image != (Image *) NULL)
7541         {
7542           *image=DestroyImage(*image);
7543           *image=flip_image;
7544         }
7545       CatchException(exception);
7546       XSetCursorState(display,windows,MagickFalse);
7547       if (windows->image.crop_geometry != (char *) NULL)
7548         {
7549           /*
7550             Flip crop geometry.
7551           */
7552           width=(unsigned int) (*image)->columns;
7553           height=(unsigned int) (*image)->rows;
7554           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7555             &width,&height);
7556           (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
7557             "%ux%u%+d%+d",width,height,x,(int) (*image)->rows-(int) height-y);
7558         }
7559       if (windows->image.orphan != MagickFalse)
7560         break;
7561       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7562       break;
7563     }
7564     case RotateRightCommand:
7565     {
7566       /*
7567         Rotate image 90 degrees clockwise.
7568       */
7569       status=XRotateImage(display,resource_info,windows,90.0,image,exception);
7570       if (status == MagickFalse)
7571         {
7572           XNoticeWidget(display,windows,"Unable to rotate X image",
7573             (*image)->filename);
7574           break;
7575         }
7576       break;
7577     }
7578     case RotateLeftCommand:
7579     {
7580       /*
7581         Rotate image 90 degrees counter-clockwise.
7582       */
7583       status=XRotateImage(display,resource_info,windows,-90.0,image,exception);
7584       if (status == MagickFalse)
7585         {
7586           XNoticeWidget(display,windows,"Unable to rotate X image",
7587             (*image)->filename);
7588           break;
7589         }
7590       break;
7591     }
7592     case RotateCommand:
7593     {
7594       /*
7595         Rotate image.
7596       */
7597       status=XRotateImage(display,resource_info,windows,0.0,image,exception);
7598       if (status == MagickFalse)
7599         {
7600           XNoticeWidget(display,windows,"Unable to rotate X image",
7601             (*image)->filename);
7602           break;
7603         }
7604       break;
7605     }
7606     case ShearCommand:
7607     {
7608       Image
7609         *shear_image;
7610
7611       static char
7612         geometry[MaxTextExtent] = "45.0x45.0";
7613
7614       /*
7615         Query user for shear color and geometry.
7616       */
7617       XColorBrowserWidget(display,windows,"Select",color);
7618       if (*color == '\0')
7619         break;
7620       (void) XDialogWidget(display,windows,"Shear","Enter shear geometry:",
7621         geometry);
7622       if (*geometry == '\0')
7623         break;
7624       /*
7625         Shear image.
7626       */
7627       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
7628         exception);
7629       XSetCursorState(display,windows,MagickTrue);
7630       XCheckRefreshWindows(display,windows);
7631       (void) QueryColorDatabase(color,&(*image)->background_color,
7632         exception);
7633       flags=ParseGeometry(geometry,&geometry_info);
7634       if ((flags & SigmaValue) == 0)
7635         geometry_info.sigma=geometry_info.rho;
7636       shear_image=ShearImage(*image,geometry_info.rho,geometry_info.sigma,
7637         exception);
7638       if (shear_image != (Image *) NULL)
7639         {
7640           *image=DestroyImage(*image);
7641           *image=shear_image;
7642         }
7643       CatchException(exception);
7644       XSetCursorState(display,windows,MagickFalse);
7645       if (windows->image.orphan != MagickFalse)
7646         break;
7647       windows->image.window_changes.width=(int) (*image)->columns;
7648       windows->image.window_changes.height=(int) (*image)->rows;
7649       XConfigureImageColormap(display,resource_info,windows,*image);
7650       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7651       break;
7652     }
7653     case RollCommand:
7654     {
7655       Image
7656         *roll_image;
7657
7658       static char
7659         geometry[MaxTextExtent] = "+2+2";
7660
7661       /*
7662         Query user for the roll geometry.
7663       */
7664       (void) XDialogWidget(display,windows,"Roll","Enter roll geometry:",
7665         geometry);
7666       if (*geometry == '\0')
7667         break;
7668       /*
7669         Roll image.
7670       */
7671       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
7672         exception);
7673       XSetCursorState(display,windows,MagickTrue);
7674       XCheckRefreshWindows(display,windows);
7675       (void) ParsePageGeometry(*image,geometry,&page_geometry,
7676         exception);
7677       roll_image=RollImage(*image,page_geometry.x,page_geometry.y,
7678         exception);
7679       if (roll_image != (Image *) NULL)
7680         {
7681           *image=DestroyImage(*image);
7682           *image=roll_image;
7683         }
7684       CatchException(exception);
7685       XSetCursorState(display,windows,MagickFalse);
7686       if (windows->image.orphan != MagickFalse)
7687         break;
7688       windows->image.window_changes.width=(int) (*image)->columns;
7689       windows->image.window_changes.height=(int) (*image)->rows;
7690       XConfigureImageColormap(display,resource_info,windows,*image);
7691       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7692       break;
7693     }
7694     case TrimCommand:
7695     {
7696       static char
7697         fuzz[MaxTextExtent];
7698
7699       /*
7700         Query user for the fuzz factor.
7701       */
7702       (void) FormatLocaleString(fuzz,MaxTextExtent,"%g%%",100.0*
7703         (*image)->fuzz/(QuantumRange+1.0));
7704       (void) XDialogWidget(display,windows,"Trim","Enter fuzz factor:",fuzz);
7705       if (*fuzz == '\0')
7706         break;
7707       (*image)->fuzz=SiPrefixToDouble(fuzz,(double) QuantumRange+1.0);
7708       /*
7709         Trim image.
7710       */
7711       status=XTrimImage(display,resource_info,windows,*image,exception);
7712       if (status == MagickFalse)
7713         {
7714           XNoticeWidget(display,windows,"Unable to trim X image",
7715             (*image)->filename);
7716           break;
7717         }
7718       break;
7719     }
7720     case HueCommand:
7721     {
7722       static char
7723         hue_percent[MaxTextExtent] = "110";
7724
7725       /*
7726         Query user for percent hue change.
7727       */
7728       (void) XDialogWidget(display,windows,"Apply",
7729         "Enter percent change in image hue (0-200):",hue_percent);
7730       if (*hue_percent == '\0')
7731         break;
7732       /*
7733         Vary the image hue.
7734       */
7735       XSetCursorState(display,windows,MagickTrue);
7736       XCheckRefreshWindows(display,windows);
7737       (void) CopyMagickString(modulate_factors,"100.0/100.0/",MaxTextExtent);
7738       (void) ConcatenateMagickString(modulate_factors,hue_percent,
7739         MaxTextExtent);
7740       (void) ModulateImage(*image,modulate_factors,exception);
7741       XSetCursorState(display,windows,MagickFalse);
7742       if (windows->image.orphan != MagickFalse)
7743         break;
7744       XConfigureImageColormap(display,resource_info,windows,*image);
7745       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7746       break;
7747     }
7748     case SaturationCommand:
7749     {
7750       static char
7751         saturation_percent[MaxTextExtent] = "110";
7752
7753       /*
7754         Query user for percent saturation change.
7755       */
7756       (void) XDialogWidget(display,windows,"Apply",
7757         "Enter percent change in color saturation (0-200):",saturation_percent);
7758       if (*saturation_percent == '\0')
7759         break;
7760       /*
7761         Vary color saturation.
7762       */
7763       XSetCursorState(display,windows,MagickTrue);
7764       XCheckRefreshWindows(display,windows);
7765       (void) CopyMagickString(modulate_factors,"100.0/",MaxTextExtent);
7766       (void) ConcatenateMagickString(modulate_factors,saturation_percent,
7767         MaxTextExtent);
7768       (void) ModulateImage(*image,modulate_factors,exception);
7769       XSetCursorState(display,windows,MagickFalse);
7770       if (windows->image.orphan != MagickFalse)
7771         break;
7772       XConfigureImageColormap(display,resource_info,windows,*image);
7773       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7774       break;
7775     }
7776     case BrightnessCommand:
7777     {
7778       static char
7779         brightness_percent[MaxTextExtent] = "110";
7780
7781       /*
7782         Query user for percent brightness change.
7783       */
7784       (void) XDialogWidget(display,windows,"Apply",
7785         "Enter percent change in color brightness (0-200):",brightness_percent);
7786       if (*brightness_percent == '\0')
7787         break;
7788       /*
7789         Vary the color brightness.
7790       */
7791       XSetCursorState(display,windows,MagickTrue);
7792       XCheckRefreshWindows(display,windows);
7793       (void) CopyMagickString(modulate_factors,brightness_percent,
7794         MaxTextExtent);
7795       (void) ModulateImage(*image,modulate_factors,exception);
7796       XSetCursorState(display,windows,MagickFalse);
7797       if (windows->image.orphan != MagickFalse)
7798         break;
7799       XConfigureImageColormap(display,resource_info,windows,*image);
7800       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7801       break;
7802     }
7803     case GammaCommand:
7804     {
7805       static char
7806         factor[MaxTextExtent] = "1.6";
7807
7808       /*
7809         Query user for gamma value.
7810       */
7811       (void) XDialogWidget(display,windows,"Gamma",
7812         "Enter gamma value (e.g. 1.2):",factor);
7813       if (*factor == '\0')
7814         break;
7815       /*
7816         Gamma correct image.
7817       */
7818       XSetCursorState(display,windows,MagickTrue);
7819       XCheckRefreshWindows(display,windows);
7820       (void) GammaImage(*image,atof(factor),exception);
7821       XSetCursorState(display,windows,MagickFalse);
7822       if (windows->image.orphan != MagickFalse)
7823         break;
7824       XConfigureImageColormap(display,resource_info,windows,*image);
7825       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7826       break;
7827     }
7828     case SpiffCommand:
7829     {
7830       /*
7831         Sharpen the image contrast.
7832       */
7833       XSetCursorState(display,windows,MagickTrue);
7834       XCheckRefreshWindows(display,windows);
7835       (void) ContrastImage(*image,MagickTrue,exception);
7836       XSetCursorState(display,windows,MagickFalse);
7837       if (windows->image.orphan != MagickFalse)
7838         break;
7839       XConfigureImageColormap(display,resource_info,windows,*image);
7840       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7841       break;
7842     }
7843     case DullCommand:
7844     {
7845       /*
7846         Dull the image contrast.
7847       */
7848       XSetCursorState(display,windows,MagickTrue);
7849       XCheckRefreshWindows(display,windows);
7850       (void) ContrastImage(*image,MagickFalse,exception);
7851       XSetCursorState(display,windows,MagickFalse);
7852       if (windows->image.orphan != MagickFalse)
7853         break;
7854       XConfigureImageColormap(display,resource_info,windows,*image);
7855       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7856       break;
7857     }
7858     case ContrastStretchCommand:
7859     {
7860       double
7861         black_point,
7862         white_point;
7863
7864       static char
7865         levels[MaxTextExtent] = "1%";
7866
7867       /*
7868         Query user for gamma value.
7869       */
7870       (void) XDialogWidget(display,windows,"Contrast Stretch",
7871         "Enter black and white points:",levels);
7872       if (*levels == '\0')
7873         break;
7874       /*
7875         Contrast stretch image.
7876       */
7877       XSetCursorState(display,windows,MagickTrue);
7878       XCheckRefreshWindows(display,windows);
7879       flags=ParseGeometry(levels,&geometry_info);
7880       black_point=geometry_info.rho;
7881       white_point=(flags & SigmaValue) != 0 ? geometry_info.sigma : black_point;
7882       if ((flags & PercentValue) != 0)
7883         {
7884           black_point*=(double) (*image)->columns*(*image)->rows/100.0;
7885           white_point*=(double) (*image)->columns*(*image)->rows/100.0;
7886         }
7887       white_point=(MagickRealType) (*image)->columns*(*image)->rows-white_point;
7888       (void) ContrastStretchImage(*image,black_point,white_point,
7889         exception);
7890       XSetCursorState(display,windows,MagickFalse);
7891       if (windows->image.orphan != MagickFalse)
7892         break;
7893       XConfigureImageColormap(display,resource_info,windows,*image);
7894       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7895       break;
7896     }
7897     case SigmoidalContrastCommand:
7898     {
7899       GeometryInfo
7900         geometry_info;
7901
7902       MagickStatusType
7903         flags;
7904
7905       static char
7906         levels[MaxTextExtent] = "3x50%";
7907
7908       /*
7909         Query user for gamma value.
7910       */
7911       (void) XDialogWidget(display,windows,"Sigmoidal Contrast",
7912         "Enter contrast and midpoint:",levels);
7913       if (*levels == '\0')
7914         break;
7915       /*
7916         Contrast stretch image.
7917       */
7918       XSetCursorState(display,windows,MagickTrue);
7919       XCheckRefreshWindows(display,windows);
7920       flags=ParseGeometry(levels,&geometry_info);
7921       if ((flags & SigmaValue) == 0)
7922         geometry_info.sigma=1.0*QuantumRange/2.0;
7923       if ((flags & PercentValue) != 0)
7924         geometry_info.sigma=1.0*QuantumRange*geometry_info.sigma/100.0;
7925       (void) SigmoidalContrastImage(*image,MagickTrue,geometry_info.rho,
7926         geometry_info.sigma,exception);
7927       XSetCursorState(display,windows,MagickFalse);
7928       if (windows->image.orphan != MagickFalse)
7929         break;
7930       XConfigureImageColormap(display,resource_info,windows,*image);
7931       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7932       break;
7933     }
7934     case NormalizeCommand:
7935     {
7936       /*
7937         Perform histogram normalization on the image.
7938       */
7939       XSetCursorState(display,windows,MagickTrue);
7940       XCheckRefreshWindows(display,windows);
7941       (void) NormalizeImage(*image,exception);
7942       XSetCursorState(display,windows,MagickFalse);
7943       if (windows->image.orphan != MagickFalse)
7944         break;
7945       XConfigureImageColormap(display,resource_info,windows,*image);
7946       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7947       break;
7948     }
7949     case EqualizeCommand:
7950     {
7951       /*
7952         Perform histogram equalization on the image.
7953       */
7954       XSetCursorState(display,windows,MagickTrue);
7955       XCheckRefreshWindows(display,windows);
7956       (void) EqualizeImage(*image,exception);
7957       XSetCursorState(display,windows,MagickFalse);
7958       if (windows->image.orphan != MagickFalse)
7959         break;
7960       XConfigureImageColormap(display,resource_info,windows,*image);
7961       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7962       break;
7963     }
7964     case NegateCommand:
7965     {
7966       /*
7967         Negate colors in image.
7968       */
7969       XSetCursorState(display,windows,MagickTrue);
7970       XCheckRefreshWindows(display,windows);
7971       (void) NegateImage(*image,MagickFalse,exception);
7972       XSetCursorState(display,windows,MagickFalse);
7973       if (windows->image.orphan != MagickFalse)
7974         break;
7975       XConfigureImageColormap(display,resource_info,windows,*image);
7976       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7977       break;
7978     }
7979     case GrayscaleCommand:
7980     {
7981       /*
7982         Convert image to grayscale.
7983       */
7984       XSetCursorState(display,windows,MagickTrue);
7985       XCheckRefreshWindows(display,windows);
7986       (void) SetImageType(*image,(*image)->matte == MagickFalse ?
7987         GrayscaleType : GrayscaleMatteType);
7988       XSetCursorState(display,windows,MagickFalse);
7989       if (windows->image.orphan != MagickFalse)
7990         break;
7991       XConfigureImageColormap(display,resource_info,windows,*image);
7992       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7993       break;
7994     }
7995     case MapCommand:
7996     {
7997       Image
7998         *affinity_image;
7999
8000       static char
8001         filename[MaxTextExtent] = "\0";
8002
8003       /*
8004         Request image file name from user.
8005       */
8006       XFileBrowserWidget(display,windows,"Map",filename);
8007       if (*filename == '\0')
8008         break;
8009       /*
8010         Map image.
8011       */
8012       XSetCursorState(display,windows,MagickTrue);
8013       XCheckRefreshWindows(display,windows);
8014       (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
8015       affinity_image=ReadImage(image_info,exception);
8016       if (affinity_image != (Image *) NULL)
8017         {
8018           (void) RemapImage(&quantize_info,*image,affinity_image);
8019           affinity_image=DestroyImage(affinity_image);
8020         }
8021       CatchException(exception);
8022       XSetCursorState(display,windows,MagickFalse);
8023       if (windows->image.orphan != MagickFalse)
8024         break;
8025       XConfigureImageColormap(display,resource_info,windows,*image);
8026       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8027       break;
8028     }
8029     case QuantizeCommand:
8030     {
8031       int
8032         status;
8033
8034       static char
8035         colors[MaxTextExtent] = "256";
8036
8037       /*
8038         Query user for maximum number of colors.
8039       */
8040       status=XDialogWidget(display,windows,"Quantize",
8041         "Maximum number of colors:",colors);
8042       if (*colors == '\0')
8043         break;
8044       /*
8045         Color reduce the image.
8046       */
8047       XSetCursorState(display,windows,MagickTrue);
8048       XCheckRefreshWindows(display,windows);
8049       quantize_info.number_colors=StringToUnsignedLong(colors);
8050       quantize_info.dither=status != 0 ? MagickTrue : MagickFalse;
8051       (void) QuantizeImage(&quantize_info,*image);
8052       XSetCursorState(display,windows,MagickFalse);
8053       if (windows->image.orphan != MagickFalse)
8054         break;
8055       XConfigureImageColormap(display,resource_info,windows,*image);
8056       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8057       break;
8058     }
8059     case DespeckleCommand:
8060     {
8061       Image
8062         *despeckle_image;
8063
8064       /*
8065         Despeckle image.
8066       */
8067       XSetCursorState(display,windows,MagickTrue);
8068       XCheckRefreshWindows(display,windows);
8069       despeckle_image=DespeckleImage(*image,exception);
8070       if (despeckle_image != (Image *) NULL)
8071         {
8072           *image=DestroyImage(*image);
8073           *image=despeckle_image;
8074         }
8075       CatchException(exception);
8076       XSetCursorState(display,windows,MagickFalse);
8077       if (windows->image.orphan != MagickFalse)
8078         break;
8079       XConfigureImageColormap(display,resource_info,windows,*image);
8080       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8081       break;
8082     }
8083     case EmbossCommand:
8084     {
8085       Image
8086         *emboss_image;
8087
8088       static char
8089         radius[MaxTextExtent] = "0.0x1.0";
8090
8091       /*
8092         Query user for emboss radius.
8093       */
8094       (void) XDialogWidget(display,windows,"Emboss",
8095         "Enter the emboss radius and standard deviation:",radius);
8096       if (*radius == '\0')
8097         break;
8098       /*
8099         Reduce noise in the image.
8100       */
8101       XSetCursorState(display,windows,MagickTrue);
8102       XCheckRefreshWindows(display,windows);
8103       flags=ParseGeometry(radius,&geometry_info);
8104       if ((flags & SigmaValue) == 0)
8105         geometry_info.sigma=1.0;
8106       emboss_image=EmbossImage(*image,geometry_info.rho,geometry_info.sigma,
8107         exception);
8108       if (emboss_image != (Image *) NULL)
8109         {
8110           *image=DestroyImage(*image);
8111           *image=emboss_image;
8112         }
8113       CatchException(exception);
8114       XSetCursorState(display,windows,MagickFalse);
8115       if (windows->image.orphan != MagickFalse)
8116         break;
8117       XConfigureImageColormap(display,resource_info,windows,*image);
8118       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8119       break;
8120     }
8121     case ReduceNoiseCommand:
8122     {
8123       Image
8124         *noise_image;
8125
8126       static char
8127         radius[MaxTextExtent] = "0";
8128
8129       /*
8130         Query user for noise radius.
8131       */
8132       (void) XDialogWidget(display,windows,"Reduce Noise",
8133         "Enter the noise radius:",radius);
8134       if (*radius == '\0')
8135         break;
8136       /*
8137         Reduce noise in the image.
8138       */
8139       XSetCursorState(display,windows,MagickTrue);
8140       XCheckRefreshWindows(display,windows);
8141       flags=ParseGeometry(radius,&geometry_info);
8142       noise_image=StatisticImage(*image,NonpeakStatistic,(size_t)
8143         geometry_info.rho,(size_t) geometry_info.rho,exception);
8144       if (noise_image != (Image *) NULL)
8145         {
8146           *image=DestroyImage(*image);
8147           *image=noise_image;
8148         }
8149       CatchException(exception);
8150       XSetCursorState(display,windows,MagickFalse);
8151       if (windows->image.orphan != MagickFalse)
8152         break;
8153       XConfigureImageColormap(display,resource_info,windows,*image);
8154       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8155       break;
8156     }
8157     case AddNoiseCommand:
8158     {
8159       char
8160         **noises;
8161
8162       Image
8163         *noise_image;
8164
8165       static char
8166         noise_type[MaxTextExtent] = "Gaussian";
8167
8168       /*
8169         Add noise to the image.
8170       */
8171       noises=GetCommandOptions(MagickNoiseOptions);
8172       if (noises == (char **) NULL)
8173         break;
8174       XListBrowserWidget(display,windows,&windows->widget,
8175         (const char **) noises,"Add Noise",
8176         "Select a type of noise to add to your image:",noise_type);
8177       noises=DestroyStringList(noises);
8178       if (*noise_type == '\0')
8179         break;
8180       XSetCursorState(display,windows,MagickTrue);
8181       XCheckRefreshWindows(display,windows);
8182       noise_image=AddNoiseImage(*image,(NoiseType) ParseCommandOption(
8183         MagickNoiseOptions,MagickFalse,noise_type),exception);
8184       if (noise_image != (Image *) NULL)
8185         {
8186           *image=DestroyImage(*image);
8187           *image=noise_image;
8188         }
8189       CatchException(exception);
8190       XSetCursorState(display,windows,MagickFalse);
8191       if (windows->image.orphan != MagickFalse)
8192         break;
8193       XConfigureImageColormap(display,resource_info,windows,*image);
8194       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8195       break;
8196     }
8197     case SharpenCommand:
8198     {
8199       Image
8200         *sharp_image;
8201
8202       static char
8203         radius[MaxTextExtent] = "0.0x1.0";
8204
8205       /*
8206         Query user for sharpen radius.
8207       */
8208       (void) XDialogWidget(display,windows,"Sharpen",
8209         "Enter the sharpen radius and standard deviation:",radius);
8210       if (*radius == '\0')
8211         break;
8212       /*
8213         Sharpen image scanlines.
8214       */
8215       XSetCursorState(display,windows,MagickTrue);
8216       XCheckRefreshWindows(display,windows);
8217       flags=ParseGeometry(radius,&geometry_info);
8218       sharp_image=SharpenImage(*image,geometry_info.rho,geometry_info.sigma,
8219         exception);
8220       if (sharp_image != (Image *) NULL)
8221         {
8222           *image=DestroyImage(*image);
8223           *image=sharp_image;
8224         }
8225       CatchException(exception);
8226       XSetCursorState(display,windows,MagickFalse);
8227       if (windows->image.orphan != MagickFalse)
8228         break;
8229       XConfigureImageColormap(display,resource_info,windows,*image);
8230       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8231       break;
8232     }
8233     case BlurCommand:
8234     {
8235       Image
8236         *blur_image;
8237
8238       static char
8239         radius[MaxTextExtent] = "0.0x1.0";
8240
8241       /*
8242         Query user for blur radius.
8243       */
8244       (void) XDialogWidget(display,windows,"Blur",
8245         "Enter the blur radius and standard deviation:",radius);
8246       if (*radius == '\0')
8247         break;
8248       /*
8249         Blur an image.
8250       */
8251       XSetCursorState(display,windows,MagickTrue);
8252       XCheckRefreshWindows(display,windows);
8253       flags=ParseGeometry(radius,&geometry_info);
8254       blur_image=BlurImage(*image,geometry_info.rho,geometry_info.sigma,
8255         exception);
8256       if (blur_image != (Image *) NULL)
8257         {
8258           *image=DestroyImage(*image);
8259           *image=blur_image;
8260         }
8261       CatchException(exception);
8262       XSetCursorState(display,windows,MagickFalse);
8263       if (windows->image.orphan != MagickFalse)
8264         break;
8265       XConfigureImageColormap(display,resource_info,windows,*image);
8266       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8267       break;
8268     }
8269     case ThresholdCommand:
8270     {
8271       double
8272         threshold;
8273
8274       static char
8275         factor[MaxTextExtent] = "128";
8276
8277       /*
8278         Query user for threshold value.
8279       */
8280       (void) XDialogWidget(display,windows,"Threshold",
8281         "Enter threshold value:",factor);
8282       if (*factor == '\0')
8283         break;
8284       /*
8285         Gamma correct image.
8286       */
8287       XSetCursorState(display,windows,MagickTrue);
8288       XCheckRefreshWindows(display,windows);
8289       threshold=SiPrefixToDouble(factor,QuantumRange);
8290       (void) BilevelImage(*image,threshold);
8291       XSetCursorState(display,windows,MagickFalse);
8292       if (windows->image.orphan != MagickFalse)
8293         break;
8294       XConfigureImageColormap(display,resource_info,windows,*image);
8295       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8296       break;
8297     }
8298     case EdgeDetectCommand:
8299     {
8300       Image
8301         *edge_image;
8302
8303       static char
8304         radius[MaxTextExtent] = "0";
8305
8306       /*
8307         Query user for edge factor.
8308       */
8309       (void) XDialogWidget(display,windows,"Detect Edges",
8310         "Enter the edge detect radius:",radius);
8311       if (*radius == '\0')
8312         break;
8313       /*
8314         Detect edge in image.
8315       */
8316       XSetCursorState(display,windows,MagickTrue);
8317       XCheckRefreshWindows(display,windows);
8318       flags=ParseGeometry(radius,&geometry_info);
8319       edge_image=EdgeImage(*image,geometry_info.rho,exception);
8320       if (edge_image != (Image *) NULL)
8321         {
8322           *image=DestroyImage(*image);
8323           *image=edge_image;
8324         }
8325       CatchException(exception);
8326       XSetCursorState(display,windows,MagickFalse);
8327       if (windows->image.orphan != MagickFalse)
8328         break;
8329       XConfigureImageColormap(display,resource_info,windows,*image);
8330       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8331       break;
8332     }
8333     case SpreadCommand:
8334     {
8335       Image
8336         *spread_image;
8337
8338       static char
8339         amount[MaxTextExtent] = "2";
8340
8341       /*
8342         Query user for spread amount.
8343       */
8344       (void) XDialogWidget(display,windows,"Spread",
8345         "Enter the displacement amount:",amount);
8346       if (*amount == '\0')
8347         break;
8348       /*
8349         Displace image pixels by a random amount.
8350       */
8351       XSetCursorState(display,windows,MagickTrue);
8352       XCheckRefreshWindows(display,windows);
8353       flags=ParseGeometry(amount,&geometry_info);
8354       spread_image=EdgeImage(*image,geometry_info.rho,exception);
8355       if (spread_image != (Image *) NULL)
8356         {
8357           *image=DestroyImage(*image);
8358           *image=spread_image;
8359         }
8360       CatchException(exception);
8361       XSetCursorState(display,windows,MagickFalse);
8362       if (windows->image.orphan != MagickFalse)
8363         break;
8364       XConfigureImageColormap(display,resource_info,windows,*image);
8365       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8366       break;
8367     }
8368     case ShadeCommand:
8369     {
8370       Image
8371         *shade_image;
8372
8373       int
8374         status;
8375
8376       static char
8377         geometry[MaxTextExtent] = "30x30";
8378
8379       /*
8380         Query user for the shade geometry.
8381       */
8382       status=XDialogWidget(display,windows,"Shade",
8383         "Enter the azimuth and elevation of the light source:",geometry);
8384       if (*geometry == '\0')
8385         break;
8386       /*
8387         Shade image pixels.
8388       */
8389       XSetCursorState(display,windows,MagickTrue);
8390       XCheckRefreshWindows(display,windows);
8391       flags=ParseGeometry(geometry,&geometry_info);
8392       if ((flags & SigmaValue) == 0)
8393         geometry_info.sigma=1.0;
8394       shade_image=ShadeImage(*image,status != 0 ? MagickFalse : MagickTrue,
8395         geometry_info.rho,geometry_info.sigma,exception);
8396       if (shade_image != (Image *) NULL)
8397         {
8398           *image=DestroyImage(*image);
8399           *image=shade_image;
8400         }
8401       CatchException(exception);
8402       XSetCursorState(display,windows,MagickFalse);
8403       if (windows->image.orphan != MagickFalse)
8404         break;
8405       XConfigureImageColormap(display,resource_info,windows,*image);
8406       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8407       break;
8408     }
8409     case RaiseCommand:
8410     {
8411       static char
8412         bevel_width[MaxTextExtent] = "10";
8413
8414       /*
8415         Query user for bevel width.
8416       */
8417       (void) XDialogWidget(display,windows,"Raise","Bevel width:",bevel_width);
8418       if (*bevel_width == '\0')
8419         break;
8420       /*
8421         Raise an image.
8422       */
8423       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8424         exception);
8425       XSetCursorState(display,windows,MagickTrue);
8426       XCheckRefreshWindows(display,windows);
8427       (void) ParsePageGeometry(*image,bevel_width,&page_geometry,
8428         exception);
8429       (void) RaiseImage(*image,&page_geometry,MagickTrue,exception);
8430       XSetCursorState(display,windows,MagickFalse);
8431       if (windows->image.orphan != MagickFalse)
8432         break;
8433       XConfigureImageColormap(display,resource_info,windows,*image);
8434       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8435       break;
8436     }
8437     case SegmentCommand:
8438     {
8439       static char
8440         threshold[MaxTextExtent] = "1.0x1.5";
8441
8442       /*
8443         Query user for smoothing threshold.
8444       */
8445       (void) XDialogWidget(display,windows,"Segment","Smooth threshold:",
8446         threshold);
8447       if (*threshold == '\0')
8448         break;
8449       /*
8450         Segment an image.
8451       */
8452       XSetCursorState(display,windows,MagickTrue);
8453       XCheckRefreshWindows(display,windows);
8454       flags=ParseGeometry(threshold,&geometry_info);
8455       if ((flags & SigmaValue) == 0)
8456         geometry_info.sigma=1.0;
8457       (void) SegmentImage(*image,RGBColorspace,MagickFalse,geometry_info.rho,
8458         geometry_info.sigma);
8459       XSetCursorState(display,windows,MagickFalse);
8460       if (windows->image.orphan != MagickFalse)
8461         break;
8462       XConfigureImageColormap(display,resource_info,windows,*image);
8463       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8464       break;
8465     }
8466     case SepiaToneCommand:
8467     {
8468       double
8469         threshold;
8470
8471       Image
8472         *sepia_image;
8473
8474       static char
8475         factor[MaxTextExtent] = "80%";
8476
8477       /*
8478         Query user for sepia-tone factor.
8479       */
8480       (void) XDialogWidget(display,windows,"Sepia Tone",
8481         "Enter the sepia tone factor (0 - 99.9%):",factor);
8482       if (*factor == '\0')
8483         break;
8484       /*
8485         Sepia tone image pixels.
8486       */
8487       XSetCursorState(display,windows,MagickTrue);
8488       XCheckRefreshWindows(display,windows);
8489       threshold=SiPrefixToDouble(factor,QuantumRange);
8490       sepia_image=SepiaToneImage(*image,threshold,exception);
8491       if (sepia_image != (Image *) NULL)
8492         {
8493           *image=DestroyImage(*image);
8494           *image=sepia_image;
8495         }
8496       CatchException(exception);
8497       XSetCursorState(display,windows,MagickFalse);
8498       if (windows->image.orphan != MagickFalse)
8499         break;
8500       XConfigureImageColormap(display,resource_info,windows,*image);
8501       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8502       break;
8503     }
8504     case SolarizeCommand:
8505     {
8506       double
8507         threshold;
8508
8509       static char
8510         factor[MaxTextExtent] = "60%";
8511
8512       /*
8513         Query user for solarize factor.
8514       */
8515       (void) XDialogWidget(display,windows,"Solarize",
8516         "Enter the solarize factor (0 - 99.9%):",factor);
8517       if (*factor == '\0')
8518         break;
8519       /*
8520         Solarize image pixels.
8521       */
8522       XSetCursorState(display,windows,MagickTrue);
8523       XCheckRefreshWindows(display,windows);
8524       threshold=SiPrefixToDouble(factor,QuantumRange);
8525       (void) SolarizeImage(*image,threshold,exception);
8526       XSetCursorState(display,windows,MagickFalse);
8527       if (windows->image.orphan != MagickFalse)
8528         break;
8529       XConfigureImageColormap(display,resource_info,windows,*image);
8530       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8531       break;
8532     }
8533     case SwirlCommand:
8534     {
8535       Image
8536         *swirl_image;
8537
8538       static char
8539         degrees[MaxTextExtent] = "60";
8540
8541       /*
8542         Query user for swirl angle.
8543       */
8544       (void) XDialogWidget(display,windows,"Swirl","Enter the swirl angle:",
8545         degrees);
8546       if (*degrees == '\0')
8547         break;
8548       /*
8549         Swirl image pixels about the center.
8550       */
8551       XSetCursorState(display,windows,MagickTrue);
8552       XCheckRefreshWindows(display,windows);
8553       flags=ParseGeometry(degrees,&geometry_info);
8554       swirl_image=SwirlImage(*image,geometry_info.rho,exception);
8555       if (swirl_image != (Image *) NULL)
8556         {
8557           *image=DestroyImage(*image);
8558           *image=swirl_image;
8559         }
8560       CatchException(exception);
8561       XSetCursorState(display,windows,MagickFalse);
8562       if (windows->image.orphan != MagickFalse)
8563         break;
8564       XConfigureImageColormap(display,resource_info,windows,*image);
8565       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8566       break;
8567     }
8568     case ImplodeCommand:
8569     {
8570       Image
8571         *implode_image;
8572
8573       static char
8574         factor[MaxTextExtent] = "0.3";
8575
8576       /*
8577         Query user for implode factor.
8578       */
8579       (void) XDialogWidget(display,windows,"Implode",
8580         "Enter the implosion/explosion factor (-1.0 - 1.0):",factor);
8581       if (*factor == '\0')
8582         break;
8583       /*
8584         Implode image pixels about the center.
8585       */
8586       XSetCursorState(display,windows,MagickTrue);
8587       XCheckRefreshWindows(display,windows);
8588       flags=ParseGeometry(factor,&geometry_info);
8589       implode_image=ImplodeImage(*image,geometry_info.rho,exception);
8590       if (implode_image != (Image *) NULL)
8591         {
8592           *image=DestroyImage(*image);
8593           *image=implode_image;
8594         }
8595       CatchException(exception);
8596       XSetCursorState(display,windows,MagickFalse);
8597       if (windows->image.orphan != MagickFalse)
8598         break;
8599       XConfigureImageColormap(display,resource_info,windows,*image);
8600       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8601       break;
8602     }
8603     case VignetteCommand:
8604     {
8605       Image
8606         *vignette_image;
8607
8608       static char
8609         geometry[MaxTextExtent] = "0x20";
8610
8611       /*
8612         Query user for the vignette geometry.
8613       */
8614       (void) XDialogWidget(display,windows,"Vignette",
8615         "Enter the radius, sigma, and x and y offsets:",geometry);
8616       if (*geometry == '\0')
8617         break;
8618       /*
8619         Soften the edges of the image in vignette style
8620       */
8621       XSetCursorState(display,windows,MagickTrue);
8622       XCheckRefreshWindows(display,windows);
8623       flags=ParseGeometry(geometry,&geometry_info);
8624       if ((flags & SigmaValue) == 0)
8625         geometry_info.sigma=1.0;
8626       if ((flags & XiValue) == 0)
8627         geometry_info.xi=0.1*(*image)->columns;
8628       if ((flags & PsiValue) == 0)
8629         geometry_info.psi=0.1*(*image)->rows;
8630       vignette_image=VignetteImage(*image,geometry_info.rho,geometry_info.sigma,
8631         (ssize_t) ceil(geometry_info.xi-0.5),(ssize_t) ceil(geometry_info.psi-
8632         0.5),exception);
8633       if (vignette_image != (Image *) NULL)
8634         {
8635           *image=DestroyImage(*image);
8636           *image=vignette_image;
8637         }
8638       CatchException(exception);
8639       XSetCursorState(display,windows,MagickFalse);
8640       if (windows->image.orphan != MagickFalse)
8641         break;
8642       XConfigureImageColormap(display,resource_info,windows,*image);
8643       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8644       break;
8645     }
8646     case WaveCommand:
8647     {
8648       Image
8649         *wave_image;
8650
8651       static char
8652         geometry[MaxTextExtent] = "25x150";
8653
8654       /*
8655         Query user for the wave geometry.
8656       */
8657       (void) XDialogWidget(display,windows,"Wave",
8658         "Enter the amplitude and length of the wave:",geometry);
8659       if (*geometry == '\0')
8660         break;
8661       /*
8662         Alter an image along a sine wave.
8663       */
8664       XSetCursorState(display,windows,MagickTrue);
8665       XCheckRefreshWindows(display,windows);
8666       flags=ParseGeometry(geometry,&geometry_info);
8667       if ((flags & SigmaValue) == 0)
8668         geometry_info.sigma=1.0;
8669       wave_image=WaveImage(*image,geometry_info.rho,geometry_info.sigma,
8670         exception);
8671       if (wave_image != (Image *) NULL)
8672         {
8673           *image=DestroyImage(*image);
8674           *image=wave_image;
8675         }
8676       CatchException(exception);
8677       XSetCursorState(display,windows,MagickFalse);
8678       if (windows->image.orphan != MagickFalse)
8679         break;
8680       XConfigureImageColormap(display,resource_info,windows,*image);
8681       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8682       break;
8683     }
8684     case OilPaintCommand:
8685     {
8686       Image
8687         *paint_image;
8688
8689       static char
8690         radius[MaxTextExtent] = "0";
8691
8692       /*
8693         Query user for circular neighborhood radius.
8694       */
8695       (void) XDialogWidget(display,windows,"Oil Paint",
8696         "Enter the mask radius:",radius);
8697       if (*radius == '\0')
8698         break;
8699       /*
8700         OilPaint image scanlines.
8701       */
8702       XSetCursorState(display,windows,MagickTrue);
8703       XCheckRefreshWindows(display,windows);
8704       flags=ParseGeometry(radius,&geometry_info);
8705       paint_image=OilPaintImage(*image,geometry_info.rho,geometry_info.sigma,
8706         exception);
8707       if (paint_image != (Image *) NULL)
8708         {
8709           *image=DestroyImage(*image);
8710           *image=paint_image;
8711         }
8712       CatchException(exception);
8713       XSetCursorState(display,windows,MagickFalse);
8714       if (windows->image.orphan != MagickFalse)
8715         break;
8716       XConfigureImageColormap(display,resource_info,windows,*image);
8717       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8718       break;
8719     }
8720     case CharcoalDrawCommand:
8721     {
8722       Image
8723         *charcoal_image;
8724
8725       static char
8726         radius[MaxTextExtent] = "0x1";
8727
8728       /*
8729         Query user for charcoal radius.
8730       */
8731       (void) XDialogWidget(display,windows,"Charcoal Draw",
8732         "Enter the charcoal radius and sigma:",radius);
8733       if (*radius == '\0')
8734         break;
8735       /*
8736         Charcoal the image.
8737       */
8738       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8739         exception);
8740       XSetCursorState(display,windows,MagickTrue);
8741       XCheckRefreshWindows(display,windows);
8742       flags=ParseGeometry(radius,&geometry_info);
8743       if ((flags & SigmaValue) == 0)
8744         geometry_info.sigma=geometry_info.rho;
8745       charcoal_image=CharcoalImage(*image,geometry_info.rho,geometry_info.sigma,
8746         exception);
8747       if (charcoal_image != (Image *) NULL)
8748         {
8749           *image=DestroyImage(*image);
8750           *image=charcoal_image;
8751         }
8752       CatchException(exception);
8753       XSetCursorState(display,windows,MagickFalse);
8754       if (windows->image.orphan != MagickFalse)
8755         break;
8756       XConfigureImageColormap(display,resource_info,windows,*image);
8757       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8758       break;
8759     }
8760     case AnnotateCommand:
8761     {
8762       /*
8763         Annotate the image with text.
8764       */
8765       status=XAnnotateEditImage(display,resource_info,windows,*image,exception);
8766       if (status == MagickFalse)
8767         {
8768           XNoticeWidget(display,windows,"Unable to annotate X image",
8769             (*image)->filename);
8770           break;
8771         }
8772       break;
8773     }
8774     case DrawCommand:
8775     {
8776       /*
8777         Draw image.
8778       */
8779       status=XDrawEditImage(display,resource_info,windows,image,exception);
8780       if (status == MagickFalse)
8781         {
8782           XNoticeWidget(display,windows,"Unable to draw on the X image",
8783             (*image)->filename);
8784           break;
8785         }
8786       break;
8787     }
8788     case ColorCommand:
8789     {
8790       /*
8791         Color edit.
8792       */
8793       status=XColorEditImage(display,resource_info,windows,image,exception);
8794       if (status == MagickFalse)
8795         {
8796           XNoticeWidget(display,windows,"Unable to pixel edit X image",
8797             (*image)->filename);
8798           break;
8799         }
8800       break;
8801     }
8802     case MatteCommand:
8803     {
8804       /*
8805         Matte edit.
8806       */
8807       status=XMatteEditImage(display,resource_info,windows,image,exception);
8808       if (status == MagickFalse)
8809         {
8810           XNoticeWidget(display,windows,"Unable to matte edit X image",
8811             (*image)->filename);
8812           break;
8813         }
8814       break;
8815     }
8816     case CompositeCommand:
8817     {
8818       /*
8819         Composite image.
8820       */
8821       status=XCompositeImage(display,resource_info,windows,*image,
8822         exception);
8823       if (status == MagickFalse)
8824         {
8825           XNoticeWidget(display,windows,"Unable to composite X image",
8826             (*image)->filename);
8827           break;
8828         }
8829       break;
8830     }
8831     case AddBorderCommand:
8832     {
8833       Image
8834         *border_image;
8835
8836       static char
8837         geometry[MaxTextExtent] = "6x6";
8838
8839       /*
8840         Query user for border color and geometry.
8841       */
8842       XColorBrowserWidget(display,windows,"Select",color);
8843       if (*color == '\0')
8844         break;
8845       (void) XDialogWidget(display,windows,"Add Border",
8846         "Enter border geometry:",geometry);
8847       if (*geometry == '\0')
8848         break;
8849       /*
8850         Add a border to the image.
8851       */
8852       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8853         exception);
8854       XSetCursorState(display,windows,MagickTrue);
8855       XCheckRefreshWindows(display,windows);
8856       (void) QueryColorDatabase(color,&(*image)->border_color,
8857         exception);
8858       (void) ParsePageGeometry(*image,geometry,&page_geometry,
8859         exception);
8860       border_image=BorderImage(*image,&page_geometry,exception);
8861       if (border_image != (Image *) NULL)
8862         {
8863           *image=DestroyImage(*image);
8864           *image=border_image;
8865         }
8866       CatchException(exception);
8867       XSetCursorState(display,windows,MagickFalse);
8868       if (windows->image.orphan != MagickFalse)
8869         break;
8870       windows->image.window_changes.width=(int) (*image)->columns;
8871       windows->image.window_changes.height=(int) (*image)->rows;
8872       XConfigureImageColormap(display,resource_info,windows,*image);
8873       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8874       break;
8875     }
8876     case AddFrameCommand:
8877     {
8878       FrameInfo
8879         frame_info;
8880
8881       Image
8882         *frame_image;
8883
8884       static char
8885         geometry[MaxTextExtent] = "6x6";
8886
8887       /*
8888         Query user for frame color and geometry.
8889       */
8890       XColorBrowserWidget(display,windows,"Select",color);
8891       if (*color == '\0')
8892         break;
8893       (void) XDialogWidget(display,windows,"Add Frame","Enter frame geometry:",
8894         geometry);
8895       if (*geometry == '\0')
8896         break;
8897       /*
8898         Surround image with an ornamental border.
8899       */
8900       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8901         exception);
8902       XSetCursorState(display,windows,MagickTrue);
8903       XCheckRefreshWindows(display,windows);
8904       (void) QueryColorDatabase(color,&(*image)->matte_color,
8905         exception);
8906       (void) ParsePageGeometry(*image,geometry,&page_geometry,
8907         exception);
8908       frame_info.width=page_geometry.width;
8909       frame_info.height=page_geometry.height;
8910       frame_info.outer_bevel=page_geometry.x;
8911       frame_info.inner_bevel=page_geometry.y;
8912       frame_info.x=(ssize_t) frame_info.width;
8913       frame_info.y=(ssize_t) frame_info.height;
8914       frame_info.width=(*image)->columns+2*frame_info.width;
8915       frame_info.height=(*image)->rows+2*frame_info.height;
8916       frame_image=FrameImage(*image,&frame_info,exception);
8917       if (frame_image != (Image *) NULL)
8918         {
8919           *image=DestroyImage(*image);
8920           *image=frame_image;
8921         }
8922       CatchException(exception);
8923       XSetCursorState(display,windows,MagickFalse);
8924       if (windows->image.orphan != MagickFalse)
8925         break;
8926       windows->image.window_changes.width=(int) (*image)->columns;
8927       windows->image.window_changes.height=(int) (*image)->rows;
8928       XConfigureImageColormap(display,resource_info,windows,*image);
8929       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8930       break;
8931     }
8932     case CommentCommand:
8933     {
8934       const char
8935         *value;
8936
8937       FILE
8938         *file;
8939
8940       int
8941         unique_file;
8942
8943       /*
8944         Edit image comment.
8945       */
8946       unique_file=AcquireUniqueFileResource(image_info->filename);
8947       if (unique_file == -1)
8948         XNoticeWidget(display,windows,"Unable to edit image comment",
8949           image_info->filename);
8950       value=GetImageProperty(*image,"comment");
8951       if (value == (char *) NULL)
8952         unique_file=close(unique_file)-1;
8953       else
8954         {
8955           register const char
8956             *p;
8957
8958           file=fdopen(unique_file,"w");
8959           if (file == (FILE *) NULL)
8960             {
8961               XNoticeWidget(display,windows,"Unable to edit image comment",
8962                 image_info->filename);
8963               break;
8964             }
8965           for (p=value; *p != '\0'; p++)
8966             (void) fputc((int) *p,file);
8967           (void) fputc('\n',file);
8968           (void) fclose(file);
8969         }
8970       XSetCursorState(display,windows,MagickTrue);
8971       XCheckRefreshWindows(display,windows);
8972       status=InvokeDelegate(image_info,*image,"edit",(char *) NULL,
8973         exception);
8974       if (status == MagickFalse)
8975         XNoticeWidget(display,windows,"Unable to edit image comment",
8976           (char *) NULL);
8977       else
8978         {
8979           char
8980             *comment;
8981
8982           comment=FileToString(image_info->filename,~0UL,exception);
8983           if (comment != (char *) NULL)
8984             {
8985               (void) SetImageProperty(*image,"comment",comment);
8986               (*image)->taint=MagickTrue;
8987             }
8988         }
8989       (void) RelinquishUniqueFileResource(image_info->filename);
8990       XSetCursorState(display,windows,MagickFalse);
8991       break;
8992     }
8993     case LaunchCommand:
8994     {
8995       /*
8996         Launch program.
8997       */
8998       XSetCursorState(display,windows,MagickTrue);
8999       XCheckRefreshWindows(display,windows);
9000       (void) AcquireUniqueFilename(filename);
9001       (void) FormatLocaleString((*image)->filename,MaxTextExtent,"launch:%s",
9002         filename);
9003       status=WriteImage(image_info,*image,exception);
9004       if (status == MagickFalse)
9005         XNoticeWidget(display,windows,"Unable to launch image editor",
9006           (char *) NULL);
9007       else
9008         {
9009           nexus=ReadImage(resource_info->image_info,exception);
9010           CatchException(exception);
9011           XClientMessage(display,windows->image.id,windows->im_protocols,
9012             windows->im_next_image,CurrentTime);
9013         }
9014       (void) RelinquishUniqueFileResource(filename);
9015       XSetCursorState(display,windows,MagickFalse);
9016       break;
9017     }
9018     case RegionofInterestCommand:
9019     {
9020       /*
9021         Apply an image processing technique to a region of interest.
9022       */
9023       (void) XROIImage(display,resource_info,windows,image,exception);
9024       break;
9025     }
9026     case InfoCommand:
9027       break;
9028     case ZoomCommand:
9029     {
9030       /*
9031         Zoom image.
9032       */
9033       if (windows->magnify.mapped != MagickFalse)
9034         (void) XRaiseWindow(display,windows->magnify.id);
9035       else
9036         {
9037           /*
9038             Make magnify image.
9039           */
9040           XSetCursorState(display,windows,MagickTrue);
9041           (void) XMapRaised(display,windows->magnify.id);
9042           XSetCursorState(display,windows,MagickFalse);
9043         }
9044       break;
9045     }
9046     case ShowPreviewCommand:
9047     {
9048       char
9049         **previews;
9050
9051       Image
9052         *preview_image;
9053
9054       static char
9055         preview_type[MaxTextExtent] = "Gamma";
9056
9057       /*
9058         Select preview type from menu.
9059       */
9060       previews=GetCommandOptions(MagickPreviewOptions);
9061       if (previews == (char **) NULL)
9062         break;
9063       XListBrowserWidget(display,windows,&windows->widget,
9064         (const char **) previews,"Preview",
9065         "Select an enhancement, effect, or F/X:",preview_type);
9066       previews=DestroyStringList(previews);
9067       if (*preview_type == '\0')
9068         break;
9069       /*
9070         Show image preview.
9071       */
9072       XSetCursorState(display,windows,MagickTrue);
9073       XCheckRefreshWindows(display,windows);
9074       image_info->preview_type=(PreviewType)
9075         ParseCommandOption(MagickPreviewOptions,MagickFalse,preview_type);
9076       image_info->group=(ssize_t) windows->image.id;
9077       (void) DeleteImageProperty(*image,"label");
9078       (void) SetImageProperty(*image,"label","Preview");
9079       (void) AcquireUniqueFilename(filename);
9080       (void) FormatLocaleString((*image)->filename,MaxTextExtent,"preview:%s",
9081         filename);
9082       status=WriteImage(image_info,*image,exception);
9083       (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9084       preview_image=ReadImage(image_info,exception);
9085       (void) RelinquishUniqueFileResource(filename);
9086       if (preview_image == (Image *) NULL)
9087         break;
9088       (void) FormatLocaleString(preview_image->filename,MaxTextExtent,"show:%s",
9089         filename);
9090       status=WriteImage(image_info,preview_image,exception);
9091       preview_image=DestroyImage(preview_image);
9092       if (status == MagickFalse)
9093         XNoticeWidget(display,windows,"Unable to show image preview",
9094           (*image)->filename);
9095       XDelay(display,1500);
9096       XSetCursorState(display,windows,MagickFalse);
9097       break;
9098     }
9099     case ShowHistogramCommand:
9100     {
9101       Image
9102         *histogram_image;
9103
9104       /*
9105         Show image histogram.
9106       */
9107       XSetCursorState(display,windows,MagickTrue);
9108       XCheckRefreshWindows(display,windows);
9109       image_info->group=(ssize_t) windows->image.id;
9110       (void) DeleteImageProperty(*image,"label");
9111       (void) SetImageProperty(*image,"label","Histogram");
9112       (void) AcquireUniqueFilename(filename);
9113       (void) FormatLocaleString((*image)->filename,MaxTextExtent,"histogram:%s",
9114         filename);
9115       status=WriteImage(image_info,*image,exception);
9116       (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9117       histogram_image=ReadImage(image_info,exception);
9118       (void) RelinquishUniqueFileResource(filename);
9119       if (histogram_image == (Image *) NULL)
9120         break;
9121       (void) FormatLocaleString(histogram_image->filename,MaxTextExtent,
9122         "show:%s",filename);
9123       status=WriteImage(image_info,histogram_image,exception);
9124       histogram_image=DestroyImage(histogram_image);
9125       if (status == MagickFalse)
9126         XNoticeWidget(display,windows,"Unable to show histogram",
9127           (*image)->filename);
9128       XDelay(display,1500);
9129       XSetCursorState(display,windows,MagickFalse);
9130       break;
9131     }
9132     case ShowMatteCommand:
9133     {
9134       Image
9135         *matte_image;
9136
9137       if ((*image)->matte == MagickFalse)
9138         {
9139           XNoticeWidget(display,windows,
9140             "Image does not have any matte information",(*image)->filename);
9141           break;
9142         }
9143       /*
9144         Show image matte.
9145       */
9146       XSetCursorState(display,windows,MagickTrue);
9147       XCheckRefreshWindows(display,windows);
9148       image_info->group=(ssize_t) windows->image.id;
9149       (void) DeleteImageProperty(*image,"label");
9150       (void) SetImageProperty(*image,"label","Matte");
9151       (void) AcquireUniqueFilename(filename);
9152       (void) FormatLocaleString((*image)->filename,MaxTextExtent,"matte:%s",
9153         filename);
9154       status=WriteImage(image_info,*image,exception);
9155       (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9156       matte_image=ReadImage(image_info,exception);
9157       (void) RelinquishUniqueFileResource(filename);
9158       if (matte_image == (Image *) NULL)
9159         break;
9160       (void) FormatLocaleString(matte_image->filename,MaxTextExtent,"show:%s",
9161         filename);
9162       status=WriteImage(image_info,matte_image,exception);
9163       matte_image=DestroyImage(matte_image);
9164       if (status == MagickFalse)
9165         XNoticeWidget(display,windows,"Unable to show matte",
9166           (*image)->filename);
9167       XDelay(display,1500);
9168       XSetCursorState(display,windows,MagickFalse);
9169       break;
9170     }
9171     case BackgroundCommand:
9172     {
9173       /*
9174         Background image.
9175       */
9176       status=XBackgroundImage(display,resource_info,windows,image,exception);
9177       if (status == MagickFalse)
9178         break;
9179       nexus=CloneImage(*image,0,0,MagickTrue,exception);
9180       if (nexus != (Image *) NULL)
9181         XClientMessage(display,windows->image.id,windows->im_protocols,
9182           windows->im_next_image,CurrentTime);
9183       break;
9184     }
9185     case SlideShowCommand:
9186     {
9187       static char
9188         delay[MaxTextExtent] = "5";
9189
9190       /*
9191         Display next image after pausing.
9192       */
9193       (void) XDialogWidget(display,windows,"Slide Show",
9194         "Pause how many 1/100ths of a second between images:",delay);
9195       if (*delay == '\0')
9196         break;
9197       resource_info->delay=StringToUnsignedLong(delay);
9198       XClientMessage(display,windows->image.id,windows->im_protocols,
9199         windows->im_next_image,CurrentTime);
9200       break;
9201     }
9202     case PreferencesCommand:
9203     {
9204       /*
9205         Set user preferences.
9206       */
9207       status=XPreferencesWidget(display,resource_info,windows);
9208       if (status == MagickFalse)
9209         break;
9210       nexus=CloneImage(*image,0,0,MagickTrue,exception);
9211       if (nexus != (Image *) NULL)
9212         XClientMessage(display,windows->image.id,windows->im_protocols,
9213           windows->im_next_image,CurrentTime);
9214       break;
9215     }
9216     case HelpCommand:
9217     {
9218       /*
9219         User requested help.
9220       */
9221       XTextViewWidget(display,resource_info,windows,MagickFalse,
9222         "Help Viewer - Display",DisplayHelp);
9223       break;
9224     }
9225     case BrowseDocumentationCommand:
9226     {
9227       Atom
9228         mozilla_atom;
9229
9230       Window
9231         mozilla_window,
9232         root_window;
9233
9234       /*
9235         Browse the ImageMagick documentation.
9236       */
9237       root_window=XRootWindow(display,XDefaultScreen(display));
9238       mozilla_atom=XInternAtom(display,"_MOZILLA_VERSION",MagickFalse);
9239       mozilla_window=XWindowByProperty(display,root_window,mozilla_atom);
9240       if (mozilla_window != (Window) NULL)
9241         {
9242           char
9243             command[MaxTextExtent],
9244             *url;
9245
9246           /*
9247             Display documentation using Netscape remote control.
9248           */
9249           url=GetMagickHomeURL();
9250           (void) FormatLocaleString(command,MaxTextExtent,
9251             "openurl(%s,new-tab)",url);
9252           url=DestroyString(url);
9253           mozilla_atom=XInternAtom(display,"_MOZILLA_COMMAND",MagickFalse);
9254           (void) XChangeProperty(display,mozilla_window,mozilla_atom,XA_STRING,
9255             8,PropModeReplace,(unsigned char *) command,(int) strlen(command));
9256           XSetCursorState(display,windows,MagickFalse);
9257           break;
9258         }
9259       XSetCursorState(display,windows,MagickTrue);
9260       XCheckRefreshWindows(display,windows);
9261       status=InvokeDelegate(image_info,*image,"browse",(char *) NULL,
9262         exception);
9263       if (status == MagickFalse)
9264         XNoticeWidget(display,windows,"Unable to browse documentation",
9265           (char *) NULL);
9266       XDelay(display,1500);
9267       XSetCursorState(display,windows,MagickFalse);
9268       break;
9269     }
9270     case VersionCommand:
9271     {
9272       XNoticeWidget(display,windows,GetMagickVersion((size_t *) NULL),
9273         GetMagickCopyright());
9274       break;
9275     }
9276     case SaveToUndoBufferCommand:
9277       break;
9278     default:
9279     {
9280       (void) XBell(display,0);
9281       break;
9282     }
9283   }
9284   image_info=DestroyImageInfo(image_info);
9285   return(nexus);
9286 }
9287 \f
9288 /*
9289 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9290 %                                                                             %
9291 %                                                                             %
9292 %                                                                             %
9293 +   X M a g n i f y I m a g e                                                 %
9294 %                                                                             %
9295 %                                                                             %
9296 %                                                                             %
9297 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9298 %
9299 %  XMagnifyImage() magnifies portions of the image as indicated by the pointer.
9300 %  The magnified portion is displayed in a separate window.
9301 %
9302 %  The format of the XMagnifyImage method is:
9303 %
9304 %      void XMagnifyImage(Display *display,XWindows *windows,XEvent *event)
9305 %
9306 %  A description of each parameter follows:
9307 %
9308 %    o display: Specifies a connection to an X server;  returned from
9309 %      XOpenDisplay.
9310 %
9311 %    o windows: Specifies a pointer to a XWindows structure.
9312 %
9313 %    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
9314 %      the entire image is refreshed.
9315 %
9316 */
9317 static void XMagnifyImage(Display *display,XWindows *windows,XEvent *event)
9318 {
9319   char
9320     text[MaxTextExtent];
9321
9322   register int
9323     x,
9324     y;
9325
9326   size_t
9327     state;
9328
9329   /*
9330     Update magnified image until the mouse button is released.
9331   */
9332   (void) XCheckDefineCursor(display,windows->image.id,windows->magnify.cursor);
9333   state=DefaultState;
9334   x=event->xbutton.x;
9335   y=event->xbutton.y;
9336   windows->magnify.x=(int) windows->image.x+x;
9337   windows->magnify.y=(int) windows->image.y+y;
9338   do
9339   {
9340     /*
9341       Map and unmap Info widget as text cursor crosses its boundaries.
9342     */
9343     if (windows->info.mapped != MagickFalse)
9344       {
9345         if ((x < (int) (windows->info.x+windows->info.width)) &&
9346             (y < (int) (windows->info.y+windows->info.height)))
9347           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
9348       }
9349     else
9350       if ((x > (int) (windows->info.x+windows->info.width)) ||
9351           (y > (int) (windows->info.y+windows->info.height)))
9352         (void) XMapWindow(display,windows->info.id);
9353     if (windows->info.mapped != MagickFalse)
9354       {
9355         /*
9356           Display pointer position.
9357         */
9358         (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
9359           windows->magnify.x,windows->magnify.y);
9360         XInfoWidget(display,windows,text);
9361       }
9362     /*
9363       Wait for next event.
9364     */
9365     XScreenEvent(display,windows,event);
9366     switch (event->type)
9367     {
9368       case ButtonPress:
9369         break;
9370       case ButtonRelease:
9371       {
9372         /*
9373           User has finished magnifying image.
9374         */
9375         x=event->xbutton.x;
9376         y=event->xbutton.y;
9377         state|=ExitState;
9378         break;
9379       }
9380       case Expose:
9381         break;
9382       case MotionNotify:
9383       {
9384         x=event->xmotion.x;
9385         y=event->xmotion.y;
9386         break;
9387       }
9388       default:
9389         break;
9390     }
9391     /*
9392       Check boundary conditions.
9393     */
9394     if (x < 0)
9395       x=0;
9396     else
9397       if (x >= (int) windows->image.width)
9398         x=(int) windows->image.width-1;
9399     if (y < 0)
9400       y=0;
9401     else
9402      if (y >= (int) windows->image.height)
9403        y=(int) windows->image.height-1;
9404   } while ((state & ExitState) == 0);
9405   /*
9406     Display magnified image.
9407   */
9408   XSetCursorState(display,windows,MagickFalse);
9409 }
9410 \f
9411 /*
9412 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9413 %                                                                             %
9414 %                                                                             %
9415 %                                                                             %
9416 +   X M a g n i f y W i n d o w C o m m a n d                                 %
9417 %                                                                             %
9418 %                                                                             %
9419 %                                                                             %
9420 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9421 %
9422 %  XMagnifyWindowCommand() moves the image within an Magnify window by one
9423 %  pixel as specified by the key symbol.
9424 %
9425 %  The format of the XMagnifyWindowCommand method is:
9426 %
9427 %      void XMagnifyWindowCommand(Display *display,XWindows *windows,
9428 %        const MagickStatusType state,const KeySym key_symbol)
9429 %
9430 %  A description of each parameter follows:
9431 %
9432 %    o display: Specifies a connection to an X server; returned from
9433 %      XOpenDisplay.
9434 %
9435 %    o windows: Specifies a pointer to a XWindows structure.
9436 %
9437 %    o state: key mask.
9438 %
9439 %    o key_symbol: Specifies a KeySym which indicates which side of the image
9440 %      to trim.
9441 %
9442 */
9443 static void XMagnifyWindowCommand(Display *display,XWindows *windows,
9444   const MagickStatusType state,const KeySym key_symbol)
9445 {
9446   unsigned int
9447     quantum;
9448
9449   /*
9450     User specified a magnify factor or position.
9451   */
9452   quantum=1;
9453   if ((state & Mod1Mask) != 0)
9454     quantum=10;
9455   switch ((int) key_symbol)
9456   {
9457     case QuitCommand:
9458     {
9459       (void) XWithdrawWindow(display,windows->magnify.id,
9460         windows->magnify.screen);
9461       break;
9462     }
9463     case XK_Home:
9464     case XK_KP_Home:
9465     {
9466       windows->magnify.x=(int) windows->image.width/2;
9467       windows->magnify.y=(int) windows->image.height/2;
9468       break;
9469     }
9470     case XK_Left:
9471     case XK_KP_Left:
9472     {
9473       if (windows->magnify.x > 0)
9474         windows->magnify.x-=quantum;
9475       break;
9476     }
9477     case XK_Up:
9478     case XK_KP_Up:
9479     {
9480       if (windows->magnify.y > 0)
9481         windows->magnify.y-=quantum;
9482       break;
9483     }
9484     case XK_Right:
9485     case XK_KP_Right:
9486     {
9487       if (windows->magnify.x < (int) (windows->image.ximage->width-1))
9488         windows->magnify.x+=quantum;
9489       break;
9490     }
9491     case XK_Down:
9492     case XK_KP_Down:
9493     {
9494       if (windows->magnify.y < (int) (windows->image.ximage->height-1))
9495         windows->magnify.y+=quantum;
9496       break;
9497     }
9498     case XK_0:
9499     case XK_1:
9500     case XK_2:
9501     case XK_3:
9502     case XK_4:
9503     case XK_5:
9504     case XK_6:
9505     case XK_7:
9506     case XK_8:
9507     case XK_9:
9508     {
9509       windows->magnify.data=(key_symbol-XK_0);
9510       break;
9511     }
9512     case XK_KP_0:
9513     case XK_KP_1:
9514     case XK_KP_2:
9515     case XK_KP_3:
9516     case XK_KP_4:
9517     case XK_KP_5:
9518     case XK_KP_6:
9519     case XK_KP_7:
9520     case XK_KP_8:
9521     case XK_KP_9:
9522     {
9523       windows->magnify.data=(key_symbol-XK_KP_0);
9524       break;
9525     }
9526     default:
9527       break;
9528   }
9529   XMakeMagnifyImage(display,windows);
9530 }
9531 \f
9532 /*
9533 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9534 %                                                                             %
9535 %                                                                             %
9536 %                                                                             %
9537 +   X M a k e P a n I m a g e                                                 %
9538 %                                                                             %
9539 %                                                                             %
9540 %                                                                             %
9541 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9542 %
9543 %  XMakePanImage() creates a thumbnail of the image and displays it in the Pan
9544 %  icon window.
9545 %
9546 %  The format of the XMakePanImage method is:
9547 %
9548 %        void XMakePanImage(Display *display,XResourceInfo *resource_info,
9549 %          XWindows *windows,Image *image,ExceptionInfo *exception)
9550 %
9551 %  A description of each parameter follows:
9552 %
9553 %    o display: Specifies a connection to an X server;  returned from
9554 %      XOpenDisplay.
9555 %
9556 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9557 %
9558 %    o windows: Specifies a pointer to a XWindows structure.
9559 %
9560 %    o image: the image.
9561 %
9562 %    o exception: return any errors or warnings in this structure.
9563 %
9564 */
9565 static void XMakePanImage(Display *display,XResourceInfo *resource_info,
9566   XWindows *windows,Image *image,ExceptionInfo *exception)
9567 {
9568   MagickStatusType
9569     status;
9570
9571   /*
9572     Create and display image for panning icon.
9573   */
9574   XSetCursorState(display,windows,MagickTrue);
9575   XCheckRefreshWindows(display,windows);
9576   windows->pan.x=(int) windows->image.x;
9577   windows->pan.y=(int) windows->image.y;
9578   status=XMakeImage(display,resource_info,&windows->pan,image,
9579     windows->pan.width,windows->pan.height,exception);
9580   if (status == MagickFalse)
9581     ThrowXWindowFatalException(ResourceLimitError,
9582      "MemoryAllocationFailed",image->filename);
9583   (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
9584     windows->pan.pixmap);
9585   (void) XClearWindow(display,windows->pan.id);
9586   XDrawPanRectangle(display,windows);
9587   XSetCursorState(display,windows,MagickFalse);
9588 }
9589 \f
9590 /*
9591 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9592 %                                                                             %
9593 %                                                                             %
9594 %                                                                             %
9595 +   X M a t t a E d i t I m a g e                                             %
9596 %                                                                             %
9597 %                                                                             %
9598 %                                                                             %
9599 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9600 %
9601 %  XMatteEditImage() allows the user to interactively change the Matte channel
9602 %  of an image.  If the image is PseudoClass it is promoted to DirectClass
9603 %  before the matte information is stored.
9604 %
9605 %  The format of the XMatteEditImage method is:
9606 %
9607 %      MagickBooleanType XMatteEditImage(Display *display,
9608 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
9609 %        ExceptionInfo *exception)
9610 %
9611 %  A description of each parameter follows:
9612 %
9613 %    o display: Specifies a connection to an X server;  returned from
9614 %      XOpenDisplay.
9615 %
9616 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9617 %
9618 %    o windows: Specifies a pointer to a XWindows structure.
9619 %
9620 %    o image: the image; returned from ReadImage.
9621 %
9622 %    o exception: return any errors or warnings in this structure.
9623 %
9624 */
9625 static MagickBooleanType XMatteEditImage(Display *display,
9626   XResourceInfo *resource_info,XWindows *windows,Image **image,
9627   ExceptionInfo *exception)
9628 {
9629   static char
9630     matte[MaxTextExtent] = "0";
9631
9632   static const char
9633     *MatteEditMenu[] =
9634     {
9635       "Method",
9636       "Border Color",
9637       "Fuzz",
9638       "Matte Value",
9639       "Undo",
9640       "Help",
9641       "Dismiss",
9642       (char *) NULL
9643     };
9644
9645   static const ModeType
9646     MatteEditCommands[] =
9647     {
9648       MatteEditMethod,
9649       MatteEditBorderCommand,
9650       MatteEditFuzzCommand,
9651       MatteEditValueCommand,
9652       MatteEditUndoCommand,
9653       MatteEditHelpCommand,
9654       MatteEditDismissCommand
9655     };
9656
9657   static PaintMethod
9658     method = PointMethod;
9659
9660   static XColor
9661     border_color = { 0, 0, 0, 0, 0, 0 };
9662
9663   char
9664     command[MaxTextExtent],
9665     text[MaxTextExtent];
9666
9667   Cursor
9668     cursor;
9669
9670   int
9671     entry,
9672     id,
9673     x,
9674     x_offset,
9675     y,
9676     y_offset;
9677
9678   register int
9679     i;
9680
9681   register Quantum
9682     *q;
9683
9684   unsigned int
9685     height,
9686     width;
9687
9688   size_t
9689     state;
9690
9691   XEvent
9692     event;
9693
9694   /*
9695     Map Command widget.
9696   */
9697   (void) CloneString(&windows->command.name,"Matte Edit");
9698   windows->command.data=4;
9699   (void) XCommandWidget(display,windows,MatteEditMenu,(XEvent *) NULL);
9700   (void) XMapRaised(display,windows->command.id);
9701   XClientMessage(display,windows->image.id,windows->im_protocols,
9702     windows->im_update_widget,CurrentTime);
9703   /*
9704     Make cursor.
9705   */
9706   cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
9707     resource_info->background_color,resource_info->foreground_color);
9708   (void) XCheckDefineCursor(display,windows->image.id,cursor);
9709   /*
9710     Track pointer until button 1 is pressed.
9711   */
9712   XQueryPosition(display,windows->image.id,&x,&y);
9713   (void) XSelectInput(display,windows->image.id,
9714     windows->image.attributes.event_mask | PointerMotionMask);
9715   state=DefaultState;
9716   do
9717   {
9718     if (windows->info.mapped != MagickFalse)
9719       {
9720         /*
9721           Display pointer position.
9722         */
9723         (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
9724           x+windows->image.x,y+windows->image.y);
9725         XInfoWidget(display,windows,text);
9726       }
9727     /*
9728       Wait for next event.
9729     */
9730     XScreenEvent(display,windows,&event);
9731     if (event.xany.window == windows->command.id)
9732       {
9733         /*
9734           Select a command from the Command widget.
9735         */
9736         id=XCommandWidget(display,windows,MatteEditMenu,&event);
9737         if (id < 0)
9738           {
9739             (void) XCheckDefineCursor(display,windows->image.id,cursor);
9740             continue;
9741           }
9742         switch (MatteEditCommands[id])
9743         {
9744           case MatteEditMethod:
9745           {
9746             char
9747               **methods;
9748
9749             /*
9750               Select a method from the pop-up menu.
9751             */
9752             methods=GetCommandOptions(MagickMethodOptions);
9753             if (methods == (char **) NULL)
9754               break;
9755             entry=XMenuWidget(display,windows,MatteEditMenu[id],
9756               (const char **) methods,command);
9757             if (entry >= 0)
9758               method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
9759                 MagickFalse,methods[entry]);
9760             methods=DestroyStringList(methods);
9761             break;
9762           }
9763           case MatteEditBorderCommand:
9764           {
9765             const char
9766               *ColorMenu[MaxNumberPens];
9767
9768             int
9769               pen_number;
9770
9771             /*
9772               Initialize menu selections.
9773             */
9774             for (i=0; i < (int) (MaxNumberPens-2); i++)
9775               ColorMenu[i]=resource_info->pen_colors[i];
9776             ColorMenu[MaxNumberPens-2]="Browser...";
9777             ColorMenu[MaxNumberPens-1]=(const char *) NULL;
9778             /*
9779               Select a pen color from the pop-up menu.
9780             */
9781             pen_number=XMenuWidget(display,windows,MatteEditMenu[id],
9782               (const char **) ColorMenu,command);
9783             if (pen_number < 0)
9784               break;
9785             if (pen_number == (MaxNumberPens-2))
9786               {
9787                 static char
9788                   color_name[MaxTextExtent] = "gray";
9789
9790                 /*
9791                   Select a pen color from a dialog.
9792                 */
9793                 resource_info->pen_colors[pen_number]=color_name;
9794                 XColorBrowserWidget(display,windows,"Select",color_name);
9795                 if (*color_name == '\0')
9796                   break;
9797               }
9798             /*
9799               Set border color.
9800             */
9801             (void) XParseColor(display,windows->map_info->colormap,
9802               resource_info->pen_colors[pen_number],&border_color);
9803             break;
9804           }
9805           case MatteEditFuzzCommand:
9806           {
9807             static char
9808               fuzz[MaxTextExtent];
9809
9810             static const char
9811               *FuzzMenu[] =
9812               {
9813                 "0%",
9814                 "2%",
9815                 "5%",
9816                 "10%",
9817                 "15%",
9818                 "Dialog...",
9819                 (char *) NULL,
9820               };
9821
9822             /*
9823               Select a command from the pop-up menu.
9824             */
9825             entry=XMenuWidget(display,windows,MatteEditMenu[id],FuzzMenu,
9826               command);
9827             if (entry < 0)
9828               break;
9829             if (entry != 5)
9830               {
9831                 (*image)->fuzz=SiPrefixToDouble(FuzzMenu[entry],1.0*
9832                   QuantumRange+1.0);
9833                 break;
9834               }
9835             (void) CopyMagickString(fuzz,"20%",MaxTextExtent);
9836             (void) XDialogWidget(display,windows,"Ok",
9837               "Enter fuzz factor (0.0 - 99.9%):",fuzz);
9838             if (*fuzz == '\0')
9839               break;
9840             (void) ConcatenateMagickString(fuzz,"%",MaxTextExtent);
9841             (*image)->fuzz=SiPrefixToDouble(fuzz,1.0*QuantumRange+1.0);
9842             break;
9843           }
9844           case MatteEditValueCommand:
9845           {
9846             static char
9847               message[MaxTextExtent];
9848
9849             static const char
9850               *MatteMenu[] =
9851               {
9852                 "Opaque",
9853                 "Transparent",
9854                 "Dialog...",
9855                 (char *) NULL,
9856               };
9857
9858             /*
9859               Select a command from the pop-up menu.
9860             */
9861             entry=XMenuWidget(display,windows,MatteEditMenu[id],MatteMenu,
9862               command);
9863             if (entry < 0)
9864               break;
9865             if (entry != 2)
9866               {
9867                 (void) FormatLocaleString(matte,MaxTextExtent,QuantumFormat,
9868                   OpaqueAlpha);
9869                 if (LocaleCompare(MatteMenu[entry],"Transparent") == 0)
9870                   (void) FormatLocaleString(matte,MaxTextExtent,QuantumFormat,
9871                     (Quantum) TransparentAlpha);
9872                 break;
9873               }
9874             (void) FormatLocaleString(message,MaxTextExtent,
9875               "Enter matte value (0 - " QuantumFormat "):",(Quantum)
9876               QuantumRange);
9877             (void) XDialogWidget(display,windows,"Matte",message,matte);
9878             if (*matte == '\0')
9879               break;
9880             break;
9881           }
9882           case MatteEditUndoCommand:
9883           {
9884             (void) XMagickCommand(display,resource_info,windows,UndoCommand,
9885               image,exception);
9886             break;
9887           }
9888           case MatteEditHelpCommand:
9889           {
9890             XTextViewWidget(display,resource_info,windows,MagickFalse,
9891               "Help Viewer - Matte Edit",ImageMatteEditHelp);
9892             break;
9893           }
9894           case MatteEditDismissCommand:
9895           {
9896             /*
9897               Prematurely exit.
9898             */
9899             state|=EscapeState;
9900             state|=ExitState;
9901             break;
9902           }
9903           default:
9904             break;
9905         }
9906         (void) XCheckDefineCursor(display,windows->image.id,cursor);
9907         continue;
9908       }
9909     switch (event.type)
9910     {
9911       case ButtonPress:
9912       {
9913         if (event.xbutton.button != Button1)
9914           break;
9915         if ((event.xbutton.window != windows->image.id) &&
9916             (event.xbutton.window != windows->magnify.id))
9917           break;
9918         /*
9919           Update matte data.
9920         */
9921         x=event.xbutton.x;
9922         y=event.xbutton.y;
9923         (void) XMagickCommand(display,resource_info,windows,
9924           SaveToUndoBufferCommand,image,exception);
9925         state|=UpdateConfigurationState;
9926         break;
9927       }
9928       case ButtonRelease:
9929       {
9930         if (event.xbutton.button != Button1)
9931           break;
9932         if ((event.xbutton.window != windows->image.id) &&
9933             (event.xbutton.window != windows->magnify.id))
9934           break;
9935         /*
9936           Update colormap information.
9937         */
9938         x=event.xbutton.x;
9939         y=event.xbutton.y;
9940         XConfigureImageColormap(display,resource_info,windows,*image);
9941         (void) XConfigureImage(display,resource_info,windows,*image,exception);
9942         XInfoWidget(display,windows,text);
9943         (void) XCheckDefineCursor(display,windows->image.id,cursor);
9944         state&=(~UpdateConfigurationState);
9945         break;
9946       }
9947       case Expose:
9948         break;
9949       case KeyPress:
9950       {
9951         char
9952           command[MaxTextExtent];
9953
9954         KeySym
9955           key_symbol;
9956
9957         if (event.xkey.window == windows->magnify.id)
9958           {
9959             Window
9960               window;
9961
9962             window=windows->magnify.id;
9963             while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
9964           }
9965         if (event.xkey.window != windows->image.id)
9966           break;
9967         /*
9968           Respond to a user key press.
9969         */
9970         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
9971           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
9972         switch ((int) key_symbol)
9973         {
9974           case XK_Escape:
9975           case XK_F20:
9976           {
9977             /*
9978               Prematurely exit.
9979             */
9980             state|=ExitState;
9981             break;
9982           }
9983           case XK_F1:
9984           case XK_Help:
9985           {
9986             XTextViewWidget(display,resource_info,windows,MagickFalse,
9987               "Help Viewer - Matte Edit",ImageMatteEditHelp);
9988             break;
9989           }
9990           default:
9991           {
9992             (void) XBell(display,0);
9993             break;
9994           }
9995         }
9996         break;
9997       }
9998       case MotionNotify:
9999       {
10000         /*
10001           Map and unmap Info widget as cursor crosses its boundaries.
10002         */
10003         x=event.xmotion.x;
10004         y=event.xmotion.y;
10005         if (windows->info.mapped != MagickFalse)
10006           {
10007             if ((x < (int) (windows->info.x+windows->info.width)) &&
10008                 (y < (int) (windows->info.y+windows->info.height)))
10009               (void) XWithdrawWindow(display,windows->info.id,
10010                 windows->info.screen);
10011           }
10012         else
10013           if ((x > (int) (windows->info.x+windows->info.width)) ||
10014               (y > (int) (windows->info.y+windows->info.height)))
10015             (void) XMapWindow(display,windows->info.id);
10016         break;
10017       }
10018       default:
10019         break;
10020     }
10021     if (event.xany.window == windows->magnify.id)
10022       {
10023         x=windows->magnify.x-windows->image.x;
10024         y=windows->magnify.y-windows->image.y;
10025       }
10026     x_offset=x;
10027     y_offset=y;
10028     if ((state & UpdateConfigurationState) != 0)
10029       {
10030         CacheView
10031           *image_view;
10032
10033         int
10034           x,
10035           y;
10036
10037         /*
10038           Matte edit is relative to image configuration.
10039         */
10040         (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
10041           MagickTrue);
10042         XPutPixel(windows->image.ximage,x_offset,y_offset,
10043           windows->pixel_info->background_color.pixel);
10044         width=(unsigned int) (*image)->columns;
10045         height=(unsigned int) (*image)->rows;
10046         x=0;
10047         y=0;
10048         if (windows->image.crop_geometry != (char *) NULL)
10049           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,
10050             &height);
10051         x_offset=(int) (width*(windows->image.x+x_offset)/
10052           windows->image.ximage->width+x);
10053         y_offset=(int) (height*(windows->image.y+y_offset)/
10054           windows->image.ximage->height+y);
10055         if ((x_offset < 0) || (y_offset < 0))
10056           continue;
10057         if ((x_offset >= (int) (*image)->columns) ||
10058             (y_offset >= (int) (*image)->rows))
10059           continue;
10060         if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
10061           return(MagickFalse);
10062         (*image)->matte=MagickTrue;
10063         image_view=AcquireCacheView(*image);
10064         switch (method)
10065         {
10066           case PointMethod:
10067           default:
10068           {
10069             /*
10070               Update matte information using point algorithm.
10071             */
10072             q=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,
10073               (ssize_t) y_offset,1,1,exception);
10074             if (q == (Quantum *) NULL)
10075               break;
10076             SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10077             (void) SyncCacheViewAuthenticPixels(image_view,exception);
10078             break;
10079           }
10080           case ReplaceMethod:
10081           {
10082             PixelPacket
10083               pixel,
10084               target;
10085
10086             /*
10087               Update matte information using replace algorithm.
10088             */
10089             (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t) x_offset,
10090               (ssize_t) y_offset,&target,exception);
10091             for (y=0; y < (int) (*image)->rows; y++)
10092             {
10093               q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10094                 (*image)->columns,1,exception);
10095               if (q == (Quantum *) NULL)
10096                 break;
10097               for (x=0; x < (int) (*image)->columns; x++)
10098               {
10099                 GetPixelPacket(*image,q,&pixel);
10100                 if (IsFuzzyEquivalencePixelPacket(*image,&pixel,&target))
10101                   SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10102                 q+=GetPixelChannels(*image);
10103               }
10104               if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
10105                 break;
10106             }
10107             break;
10108           }
10109           case FloodfillMethod:
10110           case FillToBorderMethod:
10111           {
10112             ChannelType
10113               channel_mask;
10114
10115             DrawInfo
10116               *draw_info;
10117
10118             PixelInfo
10119               target;
10120
10121             /*
10122               Update matte information using floodfill algorithm.
10123             */
10124             (void) GetOneVirtualMagickPixel(*image,(ssize_t) x_offset,
10125               (ssize_t) y_offset,&target,exception);
10126             if (method == FillToBorderMethod)
10127               {
10128                 target.red=(MagickRealType) ScaleShortToQuantum(
10129                   border_color.red);
10130                 target.green=(MagickRealType) ScaleShortToQuantum(
10131                   border_color.green);
10132                 target.blue=(MagickRealType) ScaleShortToQuantum(
10133                   border_color.blue);
10134               }
10135             draw_info=CloneDrawInfo(resource_info->image_info,
10136               (DrawInfo *) NULL);
10137             draw_info->fill.alpha=ClampToQuantum(InterpretLocaleValue(matte,
10138               (char **) NULL));
10139             channel_mask=SetPixelChannelMask(*image,AlphaChannel); 
10140             (void) FloodfillPaintImage(*image,draw_info,&target,(ssize_t)
10141               x_offset,(ssize_t) y_offset,method == FloodfillMethod ?
10142               MagickFalse : MagickTrue,exception);
10143             (void) SetPixelChannelMap(*image,channel_mask);
10144             draw_info=DestroyDrawInfo(draw_info);
10145             break;
10146           }
10147           case ResetMethod:
10148           {
10149             /*
10150               Update matte information using reset algorithm.
10151             */
10152             if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
10153               return(MagickFalse);
10154             for (y=0; y < (int) (*image)->rows; y++)
10155             {
10156               q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10157                 (*image)->columns,1,exception);
10158               if (q == (Quantum *) NULL)
10159                 break;
10160               for (x=0; x < (int) (*image)->columns; x++)
10161               {
10162                 SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10163                 q+=GetPixelChannels(*image);
10164               }
10165               if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
10166                 break;
10167             }
10168             if (StringToLong(matte) == (long) OpaqueAlpha)
10169               (*image)->matte=MagickFalse;
10170             break;
10171           }
10172         }
10173         image_view=DestroyCacheView(image_view);
10174         state&=(~UpdateConfigurationState);
10175       }
10176   } while ((state & ExitState) == 0);
10177   (void) XSelectInput(display,windows->image.id,
10178     windows->image.attributes.event_mask);
10179   XSetCursorState(display,windows,MagickFalse);
10180   (void) XFreeCursor(display,cursor);
10181   return(MagickTrue);
10182 }
10183 \f
10184 /*
10185 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10186 %                                                                             %
10187 %                                                                             %
10188 %                                                                             %
10189 +   X O p e n I m a g e                                                       %
10190 %                                                                             %
10191 %                                                                             %
10192 %                                                                             %
10193 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10194 %
10195 %  XOpenImage() loads an image from a file.
10196 %
10197 %  The format of the XOpenImage method is:
10198 %
10199 %     Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10200 %       XWindows *windows,const unsigned int command)
10201 %
10202 %  A description of each parameter follows:
10203 %
10204 %    o display: Specifies a connection to an X server; returned from
10205 %      XOpenDisplay.
10206 %
10207 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10208 %
10209 %    o windows: Specifies a pointer to a XWindows structure.
10210 %
10211 %    o command: A value other than zero indicates that the file is selected
10212 %      from the command line argument list.
10213 %
10214 */
10215 static Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10216   XWindows *windows,const MagickBooleanType command)
10217 {
10218   const MagickInfo
10219     *magick_info;
10220
10221   ExceptionInfo
10222     *exception;
10223
10224   Image
10225     *nexus;
10226
10227   ImageInfo
10228     *image_info;
10229
10230   static char
10231     filename[MaxTextExtent] = "\0";
10232
10233   /*
10234     Request file name from user.
10235   */
10236   if (command == MagickFalse)
10237     XFileBrowserWidget(display,windows,"Open",filename);
10238   else
10239     {
10240       char
10241         **filelist,
10242         **files;
10243
10244       int
10245         count,
10246         status;
10247
10248       register int
10249         i,
10250         j;
10251
10252       /*
10253         Select next image from the command line.
10254       */
10255       status=XGetCommand(display,windows->image.id,&files,&count);
10256       if (status == 0)
10257         {
10258           ThrowXWindowFatalException(XServerError,"UnableToGetProperty","...");
10259           return((Image *) NULL);
10260         }
10261       filelist=(char **) AcquireQuantumMemory((size_t) count,sizeof(*filelist));
10262       if (filelist == (char **) NULL)
10263         {
10264           ThrowXWindowFatalException(ResourceLimitError,
10265             "MemoryAllocationFailed","...");
10266           (void) XFreeStringList(files);
10267           return((Image *) NULL);
10268         }
10269       j=0;
10270       for (i=1; i < count; i++)
10271         if (*files[i] != '-')
10272           filelist[j++]=files[i];
10273       filelist[j]=(char *) NULL;
10274       XListBrowserWidget(display,windows,&windows->widget,
10275         (const char **) filelist,"Load","Select Image to Load:",filename);
10276       filelist=(char **) RelinquishMagickMemory(filelist);
10277       (void) XFreeStringList(files);
10278     }
10279   if (*filename == '\0')
10280     return((Image *) NULL);
10281   image_info=CloneImageInfo(resource_info->image_info);
10282   (void) SetImageInfoProgressMonitor(image_info,(MagickProgressMonitor) NULL,
10283     (void *) NULL);
10284   (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
10285   exception=AcquireExceptionInfo();
10286   (void) SetImageInfo(image_info,0,exception);
10287   if (LocaleCompare(image_info->magick,"X") == 0)
10288     {
10289       char
10290         seconds[MaxTextExtent];
10291
10292       /*
10293         User may want to delay the X server screen grab.
10294       */
10295       (void) CopyMagickString(seconds,"0",MaxTextExtent);
10296       (void) XDialogWidget(display,windows,"Grab","Enter any delay in seconds:",
10297         seconds);
10298       if (*seconds == '\0')
10299         return((Image *) NULL);
10300       XDelay(display,(size_t) (1000*StringToLong(seconds)));
10301     }
10302   magick_info=GetMagickInfo(image_info->magick,exception);
10303   if ((magick_info != (const MagickInfo *) NULL) &&
10304       (magick_info->raw != MagickFalse))
10305     {
10306       char
10307         geometry[MaxTextExtent];
10308
10309       /*
10310         Request image size from the user.
10311       */
10312       (void) CopyMagickString(geometry,"512x512",MaxTextExtent);
10313       if (image_info->size != (char *) NULL)
10314         (void) CopyMagickString(geometry,image_info->size,MaxTextExtent);
10315       (void) XDialogWidget(display,windows,"Load","Enter the image geometry:",
10316         geometry);
10317       (void) CloneString(&image_info->size,geometry);
10318     }
10319   /*
10320     Load the image.
10321   */
10322   XSetCursorState(display,windows,MagickTrue);
10323   XCheckRefreshWindows(display,windows);
10324   (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
10325   nexus=ReadImage(image_info,exception);
10326   CatchException(exception);
10327   XSetCursorState(display,windows,MagickFalse);
10328   if (nexus != (Image *) NULL)
10329     XClientMessage(display,windows->image.id,windows->im_protocols,
10330       windows->im_next_image,CurrentTime);
10331   else
10332     {
10333       char
10334         *text,
10335         **textlist;
10336
10337       /*
10338         Unknown image format.
10339       */
10340       text=FileToString(filename,~0,exception);
10341       if (text == (char *) NULL)
10342         return((Image *) NULL);
10343       textlist=StringToList(text);
10344       if (textlist != (char **) NULL)
10345         {
10346           char
10347             title[MaxTextExtent];
10348
10349           register int
10350             i;
10351
10352           (void) FormatLocaleString(title,MaxTextExtent,
10353             "Unknown format: %s",filename);
10354           XTextViewWidget(display,resource_info,windows,MagickTrue,title,
10355             (const char **) textlist);
10356           for (i=0; textlist[i] != (char *) NULL; i++)
10357             textlist[i]=DestroyString(textlist[i]);
10358           textlist=(char **) RelinquishMagickMemory(textlist);
10359         }
10360       text=DestroyString(text);
10361     }
10362   exception=DestroyExceptionInfo(exception);
10363   image_info=DestroyImageInfo(image_info);
10364   return(nexus);
10365 }
10366 \f
10367 /*
10368 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10369 %                                                                             %
10370 %                                                                             %
10371 %                                                                             %
10372 +   X P a n I m a g e                                                         %
10373 %                                                                             %
10374 %                                                                             %
10375 %                                                                             %
10376 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10377 %
10378 %  XPanImage() pans the image until the mouse button is released.
10379 %
10380 %  The format of the XPanImage method is:
10381 %
10382 %      void XPanImage(Display *display,XWindows *windows,XEvent *event)
10383 %
10384 %  A description of each parameter follows:
10385 %
10386 %    o display: Specifies a connection to an X server;  returned from
10387 %      XOpenDisplay.
10388 %
10389 %    o windows: Specifies a pointer to a XWindows structure.
10390 %
10391 %    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
10392 %      the entire image is refreshed.
10393 %
10394 */
10395 static void XPanImage(Display *display,XWindows *windows,XEvent *event)
10396 {
10397   char
10398     text[MaxTextExtent];
10399
10400   Cursor
10401     cursor;
10402
10403   MagickRealType
10404     x_factor,
10405     y_factor;
10406
10407   RectangleInfo
10408     pan_info;
10409
10410   size_t
10411     state;
10412
10413   /*
10414     Define cursor.
10415   */
10416   if ((windows->image.ximage->width > (int) windows->image.width) &&
10417       (windows->image.ximage->height > (int) windows->image.height))
10418     cursor=XCreateFontCursor(display,XC_fleur);
10419   else
10420     if (windows->image.ximage->width > (int) windows->image.width)
10421       cursor=XCreateFontCursor(display,XC_sb_h_double_arrow);
10422     else
10423       if (windows->image.ximage->height > (int) windows->image.height)
10424         cursor=XCreateFontCursor(display,XC_sb_v_double_arrow);
10425       else
10426         cursor=XCreateFontCursor(display,XC_arrow);
10427   (void) XCheckDefineCursor(display,windows->pan.id,cursor);
10428   /*
10429     Pan image as pointer moves until the mouse button is released.
10430   */
10431   x_factor=(MagickRealType) windows->image.ximage->width/windows->pan.width;
10432   y_factor=(MagickRealType) windows->image.ximage->height/windows->pan.height;
10433   pan_info.width=windows->pan.width*windows->image.width/
10434     windows->image.ximage->width;
10435   pan_info.height=windows->pan.height*windows->image.height/
10436     windows->image.ximage->height;
10437   pan_info.x=0;
10438   pan_info.y=0;
10439   state=UpdateConfigurationState;
10440   do
10441   {
10442     switch (event->type)
10443     {
10444       case ButtonPress:
10445       {
10446         /*
10447           User choose an initial pan location.
10448         */
10449         pan_info.x=(ssize_t) event->xbutton.x;
10450         pan_info.y=(ssize_t) event->xbutton.y;
10451         state|=UpdateConfigurationState;
10452         break;
10453       }
10454       case ButtonRelease:
10455       {
10456         /*
10457           User has finished panning the image.
10458         */
10459         pan_info.x=(ssize_t) event->xbutton.x;
10460         pan_info.y=(ssize_t) event->xbutton.y;
10461         state|=UpdateConfigurationState | ExitState;
10462         break;
10463       }
10464       case MotionNotify:
10465       {
10466         pan_info.x=(ssize_t) event->xmotion.x;
10467         pan_info.y=(ssize_t) event->xmotion.y;
10468         state|=UpdateConfigurationState;
10469       }
10470       default:
10471         break;
10472     }
10473     if ((state & UpdateConfigurationState) != 0)
10474       {
10475         /*
10476           Check boundary conditions.
10477         */
10478         if (pan_info.x < (ssize_t) (pan_info.width/2))
10479           pan_info.x=0;
10480         else
10481           pan_info.x=(ssize_t) (x_factor*(pan_info.x-(pan_info.width/2)));
10482         if (pan_info.x < 0)
10483           pan_info.x=0;
10484         else
10485           if ((int) (pan_info.x+windows->image.width) >
10486               windows->image.ximage->width)
10487             pan_info.x=(ssize_t)
10488               (windows->image.ximage->width-windows->image.width);
10489         if (pan_info.y < (ssize_t) (pan_info.height/2))
10490           pan_info.y=0;
10491         else
10492           pan_info.y=(ssize_t) (y_factor*(pan_info.y-(pan_info.height/2)));
10493         if (pan_info.y < 0)
10494           pan_info.y=0;
10495         else
10496           if ((int) (pan_info.y+windows->image.height) >
10497               windows->image.ximage->height)
10498             pan_info.y=(ssize_t)
10499               (windows->image.ximage->height-windows->image.height);
10500         if ((windows->image.x != (int) pan_info.x) ||
10501             (windows->image.y != (int) pan_info.y))
10502           {
10503             /*
10504               Display image pan offset.
10505             */
10506             windows->image.x=(int) pan_info.x;
10507             windows->image.y=(int) pan_info.y;
10508             (void) FormatLocaleString(text,MaxTextExtent," %ux%u%+d%+d ",
10509               windows->image.width,windows->image.height,windows->image.x,
10510               windows->image.y);
10511             XInfoWidget(display,windows,text);
10512             /*
10513               Refresh Image window.
10514             */
10515             XDrawPanRectangle(display,windows);
10516             XRefreshWindow(display,&windows->image,(XEvent *) NULL);
10517           }
10518         state&=(~UpdateConfigurationState);
10519       }
10520     /*
10521       Wait for next event.
10522     */
10523     if ((state & ExitState) == 0)
10524       XScreenEvent(display,windows,event);
10525   } while ((state & ExitState) == 0);
10526   /*
10527     Restore cursor.
10528   */
10529   (void) XCheckDefineCursor(display,windows->pan.id,windows->pan.cursor);
10530   (void) XFreeCursor(display,cursor);
10531   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
10532 }
10533 \f
10534 /*
10535 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10536 %                                                                             %
10537 %                                                                             %
10538 %                                                                             %
10539 +   X P a s t e I m a g e                                                     %
10540 %                                                                             %
10541 %                                                                             %
10542 %                                                                             %
10543 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10544 %
10545 %  XPasteImage() pastes an image previously saved with XCropImage in the X
10546 %  window image at a location the user chooses with the pointer.
10547 %
10548 %  The format of the XPasteImage method is:
10549 %
10550 %      MagickBooleanType XPasteImage(Display *display,
10551 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
10552 %        ExceptionInfo *exception)
10553 %
10554 %  A description of each parameter follows:
10555 %
10556 %    o display: Specifies a connection to an X server;  returned from
10557 %      XOpenDisplay.
10558 %
10559 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10560 %
10561 %    o windows: Specifies a pointer to a XWindows structure.
10562 %
10563 %    o image: the image; returned from ReadImage.
10564 %
10565 %    o exception: return any errors or warnings in this structure.
10566 %
10567 */
10568 static MagickBooleanType XPasteImage(Display *display,
10569   XResourceInfo *resource_info,XWindows *windows,Image *image,
10570   ExceptionInfo *exception)
10571 {
10572   static const char
10573     *PasteMenu[] =
10574     {
10575       "Operator",
10576       "Help",
10577       "Dismiss",
10578       (char *) NULL
10579     };
10580
10581   static const ModeType
10582     PasteCommands[] =
10583     {
10584       PasteOperatorsCommand,
10585       PasteHelpCommand,
10586       PasteDismissCommand
10587     };
10588
10589   static CompositeOperator
10590     compose = CopyCompositeOp;
10591
10592   char
10593     text[MaxTextExtent];
10594
10595   Cursor
10596     cursor;
10597
10598   Image
10599     *paste_image;
10600
10601   int
10602     entry,
10603     id,
10604     x,
10605     y;
10606
10607   MagickRealType
10608     scale_factor;
10609
10610   RectangleInfo
10611     highlight_info,
10612     paste_info;
10613
10614   unsigned int
10615     height,
10616     width;
10617
10618   size_t
10619     state;
10620
10621   XEvent
10622     event;
10623
10624   /*
10625     Copy image.
10626   */
10627   if (resource_info->copy_image == (Image *) NULL)
10628     return(MagickFalse);
10629   paste_image=CloneImage(resource_info->copy_image,0,0,MagickTrue,exception);
10630   /*
10631     Map Command widget.
10632   */
10633   (void) CloneString(&windows->command.name,"Paste");
10634   windows->command.data=1;
10635   (void) XCommandWidget(display,windows,PasteMenu,(XEvent *) NULL);
10636   (void) XMapRaised(display,windows->command.id);
10637   XClientMessage(display,windows->image.id,windows->im_protocols,
10638     windows->im_update_widget,CurrentTime);
10639   /*
10640     Track pointer until button 1 is pressed.
10641   */
10642   XSetCursorState(display,windows,MagickFalse);
10643   XQueryPosition(display,windows->image.id,&x,&y);
10644   (void) XSelectInput(display,windows->image.id,
10645     windows->image.attributes.event_mask | PointerMotionMask);
10646   paste_info.x=(ssize_t) windows->image.x+x;
10647   paste_info.y=(ssize_t) windows->image.y+y;
10648   paste_info.width=0;
10649   paste_info.height=0;
10650   cursor=XCreateFontCursor(display,XC_ul_angle);
10651   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
10652   state=DefaultState;
10653   do
10654   {
10655     if (windows->info.mapped != MagickFalse)
10656       {
10657         /*
10658           Display pointer position.
10659         */
10660         (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
10661           (long) paste_info.x,(long) paste_info.y);
10662         XInfoWidget(display,windows,text);
10663       }
10664     highlight_info=paste_info;
10665     highlight_info.x=paste_info.x-windows->image.x;
10666     highlight_info.y=paste_info.y-windows->image.y;
10667     XHighlightRectangle(display,windows->image.id,
10668       windows->image.highlight_context,&highlight_info);
10669     /*
10670       Wait for next event.
10671     */
10672     XScreenEvent(display,windows,&event);
10673     XHighlightRectangle(display,windows->image.id,
10674       windows->image.highlight_context,&highlight_info);
10675     if (event.xany.window == windows->command.id)
10676       {
10677         /*
10678           Select a command from the Command widget.
10679         */
10680         id=XCommandWidget(display,windows,PasteMenu,&event);
10681         if (id < 0)
10682           continue;
10683         switch (PasteCommands[id])
10684         {
10685           case PasteOperatorsCommand:
10686           {
10687             char
10688               command[MaxTextExtent],
10689               **operators;
10690
10691             /*
10692               Select a command from the pop-up menu.
10693             */
10694             operators=GetCommandOptions(MagickComposeOptions);
10695             if (operators == (char **) NULL)
10696               break;
10697             entry=XMenuWidget(display,windows,PasteMenu[id],
10698               (const char **) operators,command);
10699             if (entry >= 0)
10700               compose=(CompositeOperator) ParseCommandOption(
10701                 MagickComposeOptions,MagickFalse,operators[entry]);
10702             operators=DestroyStringList(operators);
10703             break;
10704           }
10705           case PasteHelpCommand:
10706           {
10707             XTextViewWidget(display,resource_info,windows,MagickFalse,
10708               "Help Viewer - Image Composite",ImagePasteHelp);
10709             break;
10710           }
10711           case PasteDismissCommand:
10712           {
10713             /*
10714               Prematurely exit.
10715             */
10716             state|=EscapeState;
10717             state|=ExitState;
10718             break;
10719           }
10720           default:
10721             break;
10722         }
10723         continue;
10724       }
10725     switch (event.type)
10726     {
10727       case ButtonPress:
10728       {
10729         if (image->debug != MagickFalse)
10730           (void) LogMagickEvent(X11Event,GetMagickModule(),
10731             "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
10732             event.xbutton.button,event.xbutton.x,event.xbutton.y);
10733         if (event.xbutton.button != Button1)
10734           break;
10735         if (event.xbutton.window != windows->image.id)
10736           break;
10737         /*
10738           Paste rectangle is relative to image configuration.
10739         */
10740         width=(unsigned int) image->columns;
10741         height=(unsigned int) image->rows;
10742         x=0;
10743         y=0;
10744         if (windows->image.crop_geometry != (char *) NULL)
10745           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
10746             &width,&height);
10747         scale_factor=(MagickRealType) windows->image.ximage->width/width;
10748         paste_info.width=(unsigned int) (scale_factor*paste_image->columns+0.5);
10749         scale_factor=(MagickRealType) windows->image.ximage->height/height;
10750         paste_info.height=(unsigned int) (scale_factor*paste_image->rows+0.5);
10751         (void) XCheckDefineCursor(display,windows->image.id,cursor);
10752         paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10753         paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10754         break;
10755       }
10756       case ButtonRelease:
10757       {
10758         if (image->debug != MagickFalse)
10759           (void) LogMagickEvent(X11Event,GetMagickModule(),
10760             "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
10761             event.xbutton.button,event.xbutton.x,event.xbutton.y);
10762         if (event.xbutton.button != Button1)
10763           break;
10764         if (event.xbutton.window != windows->image.id)
10765           break;
10766         if ((paste_info.width != 0) && (paste_info.height != 0))
10767           {
10768             /*
10769               User has selected the location of the paste image.
10770             */
10771             paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10772             paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10773             state|=ExitState;
10774           }
10775         break;
10776       }
10777       case Expose:
10778         break;
10779       case KeyPress:
10780       {
10781         char
10782           command[MaxTextExtent];
10783
10784         KeySym
10785           key_symbol;
10786
10787         int
10788           length;
10789
10790         if (event.xkey.window != windows->image.id)
10791           break;
10792         /*
10793           Respond to a user key press.
10794         */
10795         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
10796           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
10797         *(command+length)='\0';
10798         if (image->debug != MagickFalse)
10799           (void) LogMagickEvent(X11Event,GetMagickModule(),
10800             "Key press: 0x%lx (%s)",(long) key_symbol,command);
10801         switch ((int) key_symbol)
10802         {
10803           case XK_Escape:
10804           case XK_F20:
10805           {
10806             /*
10807               Prematurely exit.
10808             */
10809             paste_image=DestroyImage(paste_image);
10810             state|=EscapeState;
10811             state|=ExitState;
10812             break;
10813           }
10814           case XK_F1:
10815           case XK_Help:
10816           {
10817             (void) XSetFunction(display,windows->image.highlight_context,
10818               GXcopy);
10819             XTextViewWidget(display,resource_info,windows,MagickFalse,
10820               "Help Viewer - Image Composite",ImagePasteHelp);
10821             (void) XSetFunction(display,windows->image.highlight_context,
10822               GXinvert);
10823             break;
10824           }
10825           default:
10826           {
10827             (void) XBell(display,0);
10828             break;
10829           }
10830         }
10831         break;
10832       }
10833       case MotionNotify:
10834       {
10835         /*
10836           Map and unmap Info widget as text cursor crosses its boundaries.
10837         */
10838         x=event.xmotion.x;
10839         y=event.xmotion.y;
10840         if (windows->info.mapped != MagickFalse)
10841           {
10842             if ((x < (int) (windows->info.x+windows->info.width)) &&
10843                 (y < (int) (windows->info.y+windows->info.height)))
10844               (void) XWithdrawWindow(display,windows->info.id,
10845                 windows->info.screen);
10846           }
10847         else
10848           if ((x > (int) (windows->info.x+windows->info.width)) ||
10849               (y > (int) (windows->info.y+windows->info.height)))
10850             (void) XMapWindow(display,windows->info.id);
10851         paste_info.x=(ssize_t) windows->image.x+x;
10852         paste_info.y=(ssize_t) windows->image.y+y;
10853         break;
10854       }
10855       default:
10856       {
10857         if (image->debug != MagickFalse)
10858           (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
10859             event.type);
10860         break;
10861       }
10862     }
10863   } while ((state & ExitState) == 0);
10864   (void) XSelectInput(display,windows->image.id,
10865     windows->image.attributes.event_mask);
10866   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
10867   XSetCursorState(display,windows,MagickFalse);
10868   (void) XFreeCursor(display,cursor);
10869   if ((state & EscapeState) != 0)
10870     return(MagickTrue);
10871   /*
10872     Image pasting is relative to image configuration.
10873   */
10874   XSetCursorState(display,windows,MagickTrue);
10875   XCheckRefreshWindows(display,windows);
10876   width=(unsigned int) image->columns;
10877   height=(unsigned int) image->rows;
10878   x=0;
10879   y=0;
10880   if (windows->image.crop_geometry != (char *) NULL)
10881     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
10882   scale_factor=(MagickRealType) width/windows->image.ximage->width;
10883   paste_info.x+=x;
10884   paste_info.x=(ssize_t) (scale_factor*paste_info.x+0.5);
10885   paste_info.width=(unsigned int) (scale_factor*paste_info.width+0.5);
10886   scale_factor=(MagickRealType) height/windows->image.ximage->height;
10887   paste_info.y+=y;
10888   paste_info.y=(ssize_t) (scale_factor*paste_info.y*scale_factor+0.5);
10889   paste_info.height=(unsigned int) (scale_factor*paste_info.height+0.5);
10890   /*
10891     Paste image with X Image window.
10892   */
10893   (void) CompositeImage(image,compose,paste_image,paste_info.x,paste_info.y);
10894   paste_image=DestroyImage(paste_image);
10895   XSetCursorState(display,windows,MagickFalse);
10896   /*
10897     Update image colormap.
10898   */
10899   XConfigureImageColormap(display,resource_info,windows,image);
10900   (void) XConfigureImage(display,resource_info,windows,image,exception);
10901   return(MagickTrue);
10902 }
10903 \f
10904 /*
10905 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10906 %                                                                             %
10907 %                                                                             %
10908 %                                                                             %
10909 +   X P r i n t I m a g e                                                     %
10910 %                                                                             %
10911 %                                                                             %
10912 %                                                                             %
10913 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10914 %
10915 %  XPrintImage() prints an image to a Postscript printer.
10916 %
10917 %  The format of the XPrintImage method is:
10918 %
10919 %      MagickBooleanType XPrintImage(Display *display,
10920 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
10921 %        ExceptionInfo *exception)
10922 %
10923 %  A description of each parameter follows:
10924 %
10925 %    o display: Specifies a connection to an X server; returned from
10926 %      XOpenDisplay.
10927 %
10928 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10929 %
10930 %    o windows: Specifies a pointer to a XWindows structure.
10931 %
10932 %    o image: the image.
10933 %
10934 %    o exception: return any errors or warnings in this structure.
10935 %
10936 */
10937 static MagickBooleanType XPrintImage(Display *display,
10938   XResourceInfo *resource_info,XWindows *windows,Image *image,
10939   ExceptionInfo *exception)
10940 {
10941   char
10942     filename[MaxTextExtent],
10943     geometry[MaxTextExtent];
10944
10945   Image
10946     *print_image;
10947
10948   ImageInfo
10949     *image_info;
10950
10951   MagickStatusType
10952     status;
10953
10954   /*
10955     Request Postscript page geometry from user.
10956   */
10957   image_info=CloneImageInfo(resource_info->image_info);
10958   (void) FormatLocaleString(geometry,MaxTextExtent,"Letter");
10959   if (image_info->page != (char *) NULL)
10960     (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
10961   XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
10962     "Select Postscript Page Geometry:",geometry);
10963   if (*geometry == '\0')
10964     return(MagickTrue);
10965   image_info->page=GetPageGeometry(geometry);
10966   /*
10967     Apply image transforms.
10968   */
10969   XSetCursorState(display,windows,MagickTrue);
10970   XCheckRefreshWindows(display,windows);
10971   print_image=CloneImage(image,0,0,MagickTrue,exception);
10972   if (print_image == (Image *) NULL)
10973     return(MagickFalse);
10974   (void) FormatLocaleString(geometry,MaxTextExtent,"%dx%d!",
10975     windows->image.ximage->width,windows->image.ximage->height);
10976   (void) TransformImage(&print_image,windows->image.crop_geometry,geometry);
10977   /*
10978     Print image.
10979   */
10980   (void) AcquireUniqueFilename(filename);
10981   (void) FormatLocaleString(print_image->filename,MaxTextExtent,"print:%s",
10982     filename);
10983   status=WriteImage(image_info,print_image,exception);
10984   (void) RelinquishUniqueFileResource(filename);
10985   print_image=DestroyImage(print_image);
10986   image_info=DestroyImageInfo(image_info);
10987   XSetCursorState(display,windows,MagickFalse);
10988   return(status != 0 ? MagickTrue : MagickFalse);
10989 }
10990 \f
10991 /*
10992 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10993 %                                                                             %
10994 %                                                                             %
10995 %                                                                             %
10996 +   X R O I I m a g e                                                         %
10997 %                                                                             %
10998 %                                                                             %
10999 %                                                                             %
11000 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11001 %
11002 %  XROIImage() applies an image processing technique to a region of interest.
11003 %
11004 %  The format of the XROIImage method is:
11005 %
11006 %      MagickBooleanType XROIImage(Display *display,
11007 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
11008 %        ExceptionInfo *exception)
11009 %
11010 %  A description of each parameter follows:
11011 %
11012 %    o display: Specifies a connection to an X server; returned from
11013 %      XOpenDisplay.
11014 %
11015 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
11016 %
11017 %    o windows: Specifies a pointer to a XWindows structure.
11018 %
11019 %    o image: the image; returned from ReadImage.
11020 %
11021 %    o exception: return any errors or warnings in this structure.
11022 %
11023 */
11024 static MagickBooleanType XROIImage(Display *display,
11025   XResourceInfo *resource_info,XWindows *windows,Image **image,
11026   ExceptionInfo *exception)
11027 {
11028 #define ApplyMenus  7
11029
11030   static const char
11031     *ROIMenu[] =
11032     {
11033       "Help",
11034       "Dismiss",
11035       (char *) NULL
11036     },
11037     *ApplyMenu[] =
11038     {
11039       "File",
11040       "Edit",
11041       "Transform",
11042       "Enhance",
11043       "Effects",
11044       "F/X",
11045       "Miscellany",
11046       "Help",
11047       "Dismiss",
11048       (char *) NULL
11049     },
11050     *FileMenu[] =
11051     {
11052       "Save...",
11053       "Print...",
11054       (char *) NULL
11055     },
11056     *EditMenu[] =
11057     {
11058       "Undo",
11059       "Redo",
11060       (char *) NULL
11061     },
11062     *TransformMenu[] =
11063     {
11064       "Flop",
11065       "Flip",
11066       "Rotate Right",
11067       "Rotate Left",
11068       (char *) NULL
11069     },
11070     *EnhanceMenu[] =
11071     {
11072       "Hue...",
11073       "Saturation...",
11074       "Brightness...",
11075       "Gamma...",
11076       "Spiff",
11077       "Dull",
11078       "Contrast Stretch...",
11079       "Sigmoidal Contrast...",
11080       "Normalize",
11081       "Equalize",
11082       "Negate",
11083       "Grayscale",
11084       "Map...",
11085       "Quantize...",
11086       (char *) NULL
11087     },
11088     *EffectsMenu[] =
11089     {
11090       "Despeckle",
11091       "Emboss",
11092       "Reduce Noise",
11093       "Add Noise",
11094       "Sharpen...",
11095       "Blur...",
11096       "Threshold...",
11097       "Edge Detect...",
11098       "Spread...",
11099       "Shade...",
11100       "Raise...",
11101       "Segment...",
11102       (char *) NULL
11103     },
11104     *FXMenu[] =
11105     {
11106       "Solarize...",
11107       "Sepia Tone...",
11108       "Swirl...",
11109       "Implode...",
11110       "Vignette...",
11111       "Wave...",
11112       "Oil Paint...",
11113       "Charcoal Draw...",
11114       (char *) NULL
11115     },
11116     *MiscellanyMenu[] =
11117     {
11118       "Image Info",
11119       "Zoom Image",
11120       "Show Preview...",
11121       "Show Histogram",
11122       "Show Matte",
11123       (char *) NULL
11124     };
11125
11126   static const char
11127     **Menus[ApplyMenus] =
11128     {
11129       FileMenu,
11130       EditMenu,
11131       TransformMenu,
11132       EnhanceMenu,
11133       EffectsMenu,
11134       FXMenu,
11135       MiscellanyMenu
11136     };
11137
11138   static const CommandType
11139     ApplyCommands[] =
11140     {
11141       NullCommand,
11142       NullCommand,
11143       NullCommand,
11144       NullCommand,
11145       NullCommand,
11146       NullCommand,
11147       NullCommand,
11148       HelpCommand,
11149       QuitCommand
11150     },
11151     FileCommands[] =
11152     {
11153       SaveCommand,
11154       PrintCommand
11155     },
11156     EditCommands[] =
11157     {
11158       UndoCommand,
11159       RedoCommand
11160     },
11161     TransformCommands[] =
11162     {
11163       FlopCommand,
11164       FlipCommand,
11165       RotateRightCommand,
11166       RotateLeftCommand
11167     },
11168     EnhanceCommands[] =
11169     {
11170       HueCommand,
11171       SaturationCommand,
11172       BrightnessCommand,
11173       GammaCommand,
11174       SpiffCommand,
11175       DullCommand,
11176       ContrastStretchCommand,
11177       SigmoidalContrastCommand,
11178       NormalizeCommand,
11179       EqualizeCommand,
11180       NegateCommand,
11181       GrayscaleCommand,
11182       MapCommand,
11183       QuantizeCommand
11184     },
11185     EffectsCommands[] =
11186     {
11187       DespeckleCommand,
11188       EmbossCommand,
11189       ReduceNoiseCommand,
11190       AddNoiseCommand,
11191       SharpenCommand,
11192       BlurCommand,
11193       EdgeDetectCommand,
11194       SpreadCommand,
11195       ShadeCommand,
11196       RaiseCommand,
11197       SegmentCommand
11198     },
11199     FXCommands[] =
11200     {
11201       SolarizeCommand,
11202       SepiaToneCommand,
11203       SwirlCommand,
11204       ImplodeCommand,
11205       VignetteCommand,
11206       WaveCommand,
11207       OilPaintCommand,
11208       CharcoalDrawCommand
11209     },
11210     MiscellanyCommands[] =
11211     {
11212       InfoCommand,
11213       ZoomCommand,
11214       ShowPreviewCommand,
11215       ShowHistogramCommand,
11216       ShowMatteCommand
11217     },
11218     ROICommands[] =
11219     {
11220       ROIHelpCommand,
11221       ROIDismissCommand
11222     };
11223
11224   static const CommandType
11225     *Commands[ApplyMenus] =
11226     {
11227       FileCommands,
11228       EditCommands,
11229       TransformCommands,
11230       EnhanceCommands,
11231       EffectsCommands,
11232       FXCommands,
11233       MiscellanyCommands
11234     };
11235
11236   char
11237     command[MaxTextExtent],
11238     text[MaxTextExtent];
11239
11240   CommandType
11241     command_type;
11242
11243   Cursor
11244     cursor;
11245
11246   Image
11247     *roi_image;
11248
11249   int
11250     entry,
11251     id,
11252     x,
11253     y;
11254
11255   MagickRealType
11256     scale_factor;
11257
11258   MagickProgressMonitor
11259     progress_monitor;
11260
11261   RectangleInfo
11262     crop_info,
11263     highlight_info,
11264     roi_info;
11265
11266   unsigned int
11267     height,
11268     width;
11269
11270   size_t
11271     state;
11272
11273   XEvent
11274     event;
11275
11276   /*
11277     Map Command widget.
11278   */
11279   (void) CloneString(&windows->command.name,"ROI");
11280   windows->command.data=0;
11281   (void) XCommandWidget(display,windows,ROIMenu,(XEvent *) NULL);
11282   (void) XMapRaised(display,windows->command.id);
11283   XClientMessage(display,windows->image.id,windows->im_protocols,
11284     windows->im_update_widget,CurrentTime);
11285   /*
11286     Track pointer until button 1 is pressed.
11287   */
11288   XQueryPosition(display,windows->image.id,&x,&y);
11289   (void) XSelectInput(display,windows->image.id,
11290     windows->image.attributes.event_mask | PointerMotionMask);
11291   roi_info.x=(ssize_t) windows->image.x+x;
11292   roi_info.y=(ssize_t) windows->image.y+y;
11293   roi_info.width=0;
11294   roi_info.height=0;
11295   cursor=XCreateFontCursor(display,XC_fleur);
11296   state=DefaultState;
11297   do
11298   {
11299     if (windows->info.mapped != MagickFalse)
11300       {
11301         /*
11302           Display pointer position.
11303         */
11304         (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
11305           (long) roi_info.x,(long) roi_info.y);
11306         XInfoWidget(display,windows,text);
11307       }
11308     /*
11309       Wait for next event.
11310     */
11311     XScreenEvent(display,windows,&event);
11312     if (event.xany.window == windows->command.id)
11313       {
11314         /*
11315           Select a command from the Command widget.
11316         */
11317         id=XCommandWidget(display,windows,ROIMenu,&event);
11318         if (id < 0)
11319           continue;
11320         switch (ROICommands[id])
11321         {
11322           case ROIHelpCommand:
11323           {
11324             XTextViewWidget(display,resource_info,windows,MagickFalse,
11325               "Help Viewer - Region of Interest",ImageROIHelp);
11326             break;
11327           }
11328           case ROIDismissCommand:
11329           {
11330             /*
11331               Prematurely exit.
11332             */
11333             state|=EscapeState;
11334             state|=ExitState;
11335             break;
11336           }
11337           default:
11338             break;
11339         }
11340         continue;
11341       }
11342     switch (event.type)
11343     {
11344       case ButtonPress:
11345       {
11346         if (event.xbutton.button != Button1)
11347           break;
11348         if (event.xbutton.window != windows->image.id)
11349           break;
11350         /*
11351           Note first corner of region of interest rectangle-- exit loop.
11352         */
11353         (void) XCheckDefineCursor(display,windows->image.id,cursor);
11354         roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11355         roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11356         state|=ExitState;
11357         break;
11358       }
11359       case ButtonRelease:
11360         break;
11361       case Expose:
11362         break;
11363       case KeyPress:
11364       {
11365         KeySym
11366           key_symbol;
11367
11368         if (event.xkey.window != windows->image.id)
11369           break;
11370         /*
11371           Respond to a user key press.
11372         */
11373         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11374           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11375         switch ((int) key_symbol)
11376         {
11377           case XK_Escape:
11378           case XK_F20:
11379           {
11380             /*
11381               Prematurely exit.
11382             */
11383             state|=EscapeState;
11384             state|=ExitState;
11385             break;
11386           }
11387           case XK_F1:
11388           case XK_Help:
11389           {
11390             XTextViewWidget(display,resource_info,windows,MagickFalse,
11391               "Help Viewer - Region of Interest",ImageROIHelp);
11392             break;
11393           }
11394           default:
11395           {
11396             (void) XBell(display,0);
11397             break;
11398           }
11399         }
11400         break;
11401       }
11402       case MotionNotify:
11403       {
11404         /*
11405           Map and unmap Info widget as text cursor crosses its boundaries.
11406         */
11407         x=event.xmotion.x;
11408         y=event.xmotion.y;
11409         if (windows->info.mapped != MagickFalse)
11410           {
11411             if ((x < (int) (windows->info.x+windows->info.width)) &&
11412                 (y < (int) (windows->info.y+windows->info.height)))
11413               (void) XWithdrawWindow(display,windows->info.id,
11414                 windows->info.screen);
11415           }
11416         else
11417           if ((x > (int) (windows->info.x+windows->info.width)) ||
11418               (y > (int) (windows->info.y+windows->info.height)))
11419             (void) XMapWindow(display,windows->info.id);
11420         roi_info.x=(ssize_t) windows->image.x+x;
11421         roi_info.y=(ssize_t) windows->image.y+y;
11422         break;
11423       }
11424       default:
11425         break;
11426     }
11427   } while ((state & ExitState) == 0);
11428   (void) XSelectInput(display,windows->image.id,
11429     windows->image.attributes.event_mask);
11430   if ((state & EscapeState) != 0)
11431     {
11432       /*
11433         User want to exit without region of interest.
11434       */
11435       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11436       (void) XFreeCursor(display,cursor);
11437       return(MagickTrue);
11438     }
11439   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
11440   do
11441   {
11442     /*
11443       Size rectangle as pointer moves until the mouse button is released.
11444     */
11445     x=(int) roi_info.x;
11446     y=(int) roi_info.y;
11447     roi_info.width=0;
11448     roi_info.height=0;
11449     state=DefaultState;
11450     do
11451     {
11452       highlight_info=roi_info;
11453       highlight_info.x=roi_info.x-windows->image.x;
11454       highlight_info.y=roi_info.y-windows->image.y;
11455       if ((highlight_info.width > 3) && (highlight_info.height > 3))
11456         {
11457           /*
11458             Display info and draw region of interest rectangle.
11459           */
11460           if (windows->info.mapped == MagickFalse)
11461             (void) XMapWindow(display,windows->info.id);
11462           (void) FormatLocaleString(text,MaxTextExtent,
11463             " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11464             roi_info.height,(double) roi_info.x,(double) roi_info.y);
11465           XInfoWidget(display,windows,text);
11466           XHighlightRectangle(display,windows->image.id,
11467             windows->image.highlight_context,&highlight_info);
11468         }
11469       else
11470         if (windows->info.mapped != MagickFalse)
11471           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11472       /*
11473         Wait for next event.
11474       */
11475       XScreenEvent(display,windows,&event);
11476       if ((highlight_info.width > 3) && (highlight_info.height > 3))
11477         XHighlightRectangle(display,windows->image.id,
11478           windows->image.highlight_context,&highlight_info);
11479       switch (event.type)
11480       {
11481         case ButtonPress:
11482         {
11483           roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11484           roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11485           break;
11486         }
11487         case ButtonRelease:
11488         {
11489           /*
11490             User has committed to region of interest rectangle.
11491           */
11492           roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11493           roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11494           XSetCursorState(display,windows,MagickFalse);
11495           state|=ExitState;
11496           if (LocaleCompare(windows->command.name,"Apply") == 0)
11497             break;
11498           (void) CloneString(&windows->command.name,"Apply");
11499           windows->command.data=ApplyMenus;
11500           (void) XCommandWidget(display,windows,ApplyMenu,(XEvent *) NULL);
11501           break;
11502         }
11503         case Expose:
11504           break;
11505         case MotionNotify:
11506         {
11507           roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11508           roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11509         }
11510         default:
11511           break;
11512       }
11513       if ((((int) roi_info.x != x) && ((int) roi_info.y != y)) ||
11514           ((state & ExitState) != 0))
11515         {
11516           /*
11517             Check boundary conditions.
11518           */
11519           if (roi_info.x < 0)
11520             roi_info.x=0;
11521           else
11522             if (roi_info.x > (ssize_t) windows->image.ximage->width)
11523               roi_info.x=(ssize_t) windows->image.ximage->width;
11524           if ((int) roi_info.x < x)
11525             roi_info.width=(unsigned int) (x-roi_info.x);
11526           else
11527             {
11528               roi_info.width=(unsigned int) (roi_info.x-x);
11529               roi_info.x=(ssize_t) x;
11530             }
11531           if (roi_info.y < 0)
11532             roi_info.y=0;
11533           else
11534             if (roi_info.y > (ssize_t) windows->image.ximage->height)
11535               roi_info.y=(ssize_t) windows->image.ximage->height;
11536           if ((int) roi_info.y < y)
11537             roi_info.height=(unsigned int) (y-roi_info.y);
11538           else
11539             {
11540               roi_info.height=(unsigned int) (roi_info.y-y);
11541               roi_info.y=(ssize_t) y;
11542             }
11543         }
11544     } while ((state & ExitState) == 0);
11545     /*
11546       Wait for user to grab a corner of the rectangle or press return.
11547     */
11548     state=DefaultState;
11549     command_type=NullCommand;
11550     (void) XMapWindow(display,windows->info.id);
11551     do
11552     {
11553       if (windows->info.mapped != MagickFalse)
11554         {
11555           /*
11556             Display pointer position.
11557           */
11558           (void) FormatLocaleString(text,MaxTextExtent,
11559             " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11560             roi_info.height,(double) roi_info.x,(double) roi_info.y);
11561           XInfoWidget(display,windows,text);
11562         }
11563       highlight_info=roi_info;
11564       highlight_info.x=roi_info.x-windows->image.x;
11565       highlight_info.y=roi_info.y-windows->image.y;
11566       if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
11567         {
11568           state|=EscapeState;
11569           state|=ExitState;
11570           break;
11571         }
11572       if ((state & UpdateRegionState) != 0)
11573         {
11574           (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11575           switch (command_type)
11576           {
11577             case UndoCommand:
11578             case RedoCommand:
11579             {
11580               (void) XMagickCommand(display,resource_info,windows,command_type,
11581                 image,exception);
11582               break;
11583             }
11584             default:
11585             {
11586               /*
11587                 Region of interest is relative to image configuration.
11588               */
11589               progress_monitor=SetImageProgressMonitor(*image,
11590                 (MagickProgressMonitor) NULL,(*image)->client_data);
11591               crop_info=roi_info;
11592               width=(unsigned int) (*image)->columns;
11593               height=(unsigned int) (*image)->rows;
11594               x=0;
11595               y=0;
11596               if (windows->image.crop_geometry != (char *) NULL)
11597                 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
11598                   &width,&height);
11599               scale_factor=(MagickRealType) width/windows->image.ximage->width;
11600               crop_info.x+=x;
11601               crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
11602               crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
11603               scale_factor=(MagickRealType)
11604                 height/windows->image.ximage->height;
11605               crop_info.y+=y;
11606               crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
11607               crop_info.height=(unsigned int)
11608                 (scale_factor*crop_info.height+0.5);
11609               roi_image=CropImage(*image,&crop_info,exception);
11610               (void) SetImageProgressMonitor(*image,progress_monitor,
11611                 (*image)->client_data);
11612               if (roi_image == (Image *) NULL)
11613                 continue;
11614               /*
11615                 Apply image processing technique to the region of interest.
11616               */
11617               windows->image.orphan=MagickTrue;
11618               (void) XMagickCommand(display,resource_info,windows,command_type,
11619                 &roi_image,exception);
11620               progress_monitor=SetImageProgressMonitor(*image,
11621                 (MagickProgressMonitor) NULL,(*image)->client_data);
11622               (void) XMagickCommand(display,resource_info,windows,
11623                 SaveToUndoBufferCommand,image,exception);
11624               windows->image.orphan=MagickFalse;
11625               (void) CompositeImage(*image,CopyCompositeOp,roi_image,
11626                 crop_info.x,crop_info.y);
11627               roi_image=DestroyImage(roi_image);
11628               (void) SetImageProgressMonitor(*image,progress_monitor,
11629                 (*image)->client_data);
11630               break;
11631             }
11632           }
11633           if (command_type != InfoCommand)
11634             {
11635               XConfigureImageColormap(display,resource_info,windows,*image);
11636               (void) XConfigureImage(display,resource_info,windows,*image,exception);
11637             }
11638           XCheckRefreshWindows(display,windows);
11639           XInfoWidget(display,windows,text);
11640           (void) XSetFunction(display,windows->image.highlight_context,
11641             GXinvert);
11642           state&=(~UpdateRegionState);
11643         }
11644       XHighlightRectangle(display,windows->image.id,
11645         windows->image.highlight_context,&highlight_info);
11646       XScreenEvent(display,windows,&event);
11647       if (event.xany.window == windows->command.id)
11648         {
11649           /*
11650             Select a command from the Command widget.
11651           */
11652           (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11653           command_type=NullCommand;
11654           id=XCommandWidget(display,windows,ApplyMenu,&event);
11655           if (id >= 0)
11656             {
11657               (void) CopyMagickString(command,ApplyMenu[id],MaxTextExtent);
11658               command_type=ApplyCommands[id];
11659               if (id < ApplyMenus)
11660                 {
11661                   /*
11662                     Select a command from a pop-up menu.
11663                   */
11664                   entry=XMenuWidget(display,windows,ApplyMenu[id],
11665                     (const char **) Menus[id],command);
11666                   if (entry >= 0)
11667                     {
11668                       (void) CopyMagickString(command,Menus[id][entry],
11669                         MaxTextExtent);
11670                       command_type=Commands[id][entry];
11671                     }
11672                 }
11673             }
11674           (void) XSetFunction(display,windows->image.highlight_context,
11675             GXinvert);
11676           XHighlightRectangle(display,windows->image.id,
11677             windows->image.highlight_context,&highlight_info);
11678           if (command_type == HelpCommand)
11679             {
11680               (void) XSetFunction(display,windows->image.highlight_context,
11681                 GXcopy);
11682               XTextViewWidget(display,resource_info,windows,MagickFalse,
11683                 "Help Viewer - Region of Interest",ImageROIHelp);
11684               (void) XSetFunction(display,windows->image.highlight_context,
11685                 GXinvert);
11686               continue;
11687             }
11688           if (command_type == QuitCommand)
11689             {
11690               /*
11691                 exit.
11692               */
11693               state|=EscapeState;
11694               state|=ExitState;
11695               continue;
11696             }
11697           if (command_type != NullCommand)
11698             state|=UpdateRegionState;
11699           continue;
11700         }
11701       XHighlightRectangle(display,windows->image.id,
11702         windows->image.highlight_context,&highlight_info);
11703       switch (event.type)
11704       {
11705         case ButtonPress:
11706         {
11707           x=windows->image.x;
11708           y=windows->image.y;
11709           if (event.xbutton.button != Button1)
11710             break;
11711           if (event.xbutton.window != windows->image.id)
11712             break;
11713           x=windows->image.x+event.xbutton.x;
11714           y=windows->image.y+event.xbutton.y;
11715           if ((x < (int) (roi_info.x+RoiDelta)) &&
11716               (x > (int) (roi_info.x-RoiDelta)) &&
11717               (y < (int) (roi_info.y+RoiDelta)) &&
11718               (y > (int) (roi_info.y-RoiDelta)))
11719             {
11720               roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11721               roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11722               state|=UpdateConfigurationState;
11723               break;
11724             }
11725           if ((x < (int) (roi_info.x+RoiDelta)) &&
11726               (x > (int) (roi_info.x-RoiDelta)) &&
11727               (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11728               (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11729             {
11730               roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11731               state|=UpdateConfigurationState;
11732               break;
11733             }
11734           if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11735               (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11736               (y < (int) (roi_info.y+RoiDelta)) &&
11737               (y > (int) (roi_info.y-RoiDelta)))
11738             {
11739               roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11740               state|=UpdateConfigurationState;
11741               break;
11742             }
11743           if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11744               (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11745               (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11746               (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11747             {
11748               state|=UpdateConfigurationState;
11749               break;
11750             }
11751         }
11752         case ButtonRelease:
11753         {
11754           if (event.xbutton.window == windows->pan.id)
11755             if ((highlight_info.x != crop_info.x-windows->image.x) ||
11756                 (highlight_info.y != crop_info.y-windows->image.y))
11757               XHighlightRectangle(display,windows->image.id,
11758                 windows->image.highlight_context,&highlight_info);
11759           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11760             event.xbutton.time);
11761           break;
11762         }
11763         case Expose:
11764         {
11765           if (event.xexpose.window == windows->image.id)
11766             if (event.xexpose.count == 0)
11767               {
11768                 event.xexpose.x=(int) highlight_info.x;
11769                 event.xexpose.y=(int) highlight_info.y;
11770                 event.xexpose.width=(int) highlight_info.width;
11771                 event.xexpose.height=(int) highlight_info.height;
11772                 XRefreshWindow(display,&windows->image,&event);
11773               }
11774           if (event.xexpose.window == windows->info.id)
11775             if (event.xexpose.count == 0)
11776               XInfoWidget(display,windows,text);
11777           break;
11778         }
11779         case KeyPress:
11780         {
11781           KeySym
11782             key_symbol;
11783
11784           if (event.xkey.window != windows->image.id)
11785             break;
11786           /*
11787             Respond to a user key press.
11788           */
11789           (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11790             sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11791           switch ((int) key_symbol)
11792           {
11793             case XK_Shift_L:
11794             case XK_Shift_R:
11795               break;
11796             case XK_Escape:
11797             case XK_F20:
11798               state|=EscapeState;
11799             case XK_Return:
11800             {
11801               state|=ExitState;
11802               break;
11803             }
11804             case XK_Home:
11805             case XK_KP_Home:
11806             {
11807               roi_info.x=(ssize_t) (windows->image.width/2L-roi_info.width/2L);
11808               roi_info.y=(ssize_t) (windows->image.height/2L-
11809                 roi_info.height/2L);
11810               break;
11811             }
11812             case XK_Left:
11813             case XK_KP_Left:
11814             {
11815               roi_info.x--;
11816               break;
11817             }
11818             case XK_Up:
11819             case XK_KP_Up:
11820             case XK_Next:
11821             {
11822               roi_info.y--;
11823               break;
11824             }
11825             case XK_Right:
11826             case XK_KP_Right:
11827             {
11828               roi_info.x++;
11829               break;
11830             }
11831             case XK_Prior:
11832             case XK_Down:
11833             case XK_KP_Down:
11834             {
11835               roi_info.y++;
11836               break;
11837             }
11838             case XK_F1:
11839             case XK_Help:
11840             {
11841               (void) XSetFunction(display,windows->image.highlight_context,
11842                 GXcopy);
11843               XTextViewWidget(display,resource_info,windows,MagickFalse,
11844                 "Help Viewer - Region of Interest",ImageROIHelp);
11845               (void) XSetFunction(display,windows->image.highlight_context,
11846                 GXinvert);
11847               break;
11848             }
11849             default:
11850             {
11851               command_type=XImageWindowCommand(display,resource_info,windows,
11852                 event.xkey.state,key_symbol,image,exception);
11853               if (command_type != NullCommand)
11854                 state|=UpdateRegionState;
11855               break;
11856             }
11857           }
11858           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11859             event.xkey.time);
11860           break;
11861         }
11862         case KeyRelease:
11863           break;
11864         case MotionNotify:
11865         {
11866           if (event.xbutton.window != windows->image.id)
11867             break;
11868           /*
11869             Map and unmap Info widget as text cursor crosses its boundaries.
11870           */
11871           x=event.xmotion.x;
11872           y=event.xmotion.y;
11873           if (windows->info.mapped != MagickFalse)
11874             {
11875               if ((x < (int) (windows->info.x+windows->info.width)) &&
11876                   (y < (int) (windows->info.y+windows->info.height)))
11877                 (void) XWithdrawWindow(display,windows->info.id,
11878                   windows->info.screen);
11879             }
11880           else
11881             if ((x > (int) (windows->info.x+windows->info.width)) ||
11882                 (y > (int) (windows->info.y+windows->info.height)))
11883               (void) XMapWindow(display,windows->info.id);
11884           roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11885           roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11886           break;
11887         }
11888         case SelectionRequest:
11889         {
11890           XSelectionEvent
11891             notify;
11892
11893           XSelectionRequestEvent
11894             *request;
11895
11896           /*
11897             Set primary selection.
11898           */
11899           (void) FormatLocaleString(text,MaxTextExtent,
11900             "%.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11901             roi_info.height,(double) roi_info.x,(double) roi_info.y);
11902           request=(&(event.xselectionrequest));
11903           (void) XChangeProperty(request->display,request->requestor,
11904             request->property,request->target,8,PropModeReplace,
11905             (unsigned char *) text,(int) strlen(text));
11906           notify.type=SelectionNotify;
11907           notify.display=request->display;
11908           notify.requestor=request->requestor;
11909           notify.selection=request->selection;
11910           notify.target=request->target;
11911           notify.time=request->time;
11912           if (request->property == None)
11913             notify.property=request->target;
11914           else
11915             notify.property=request->property;
11916           (void) XSendEvent(request->display,request->requestor,False,0,
11917             (XEvent *) &notify);
11918         }
11919         default:
11920           break;
11921       }
11922       if ((state & UpdateConfigurationState) != 0)
11923         {
11924           (void) XPutBackEvent(display,&event);
11925           (void) XCheckDefineCursor(display,windows->image.id,cursor);
11926           break;
11927         }
11928     } while ((state & ExitState) == 0);
11929   } while ((state & ExitState) == 0);
11930   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11931   XSetCursorState(display,windows,MagickFalse);
11932   if ((state & EscapeState) != 0)
11933     return(MagickTrue);
11934   return(MagickTrue);
11935 }
11936 \f
11937 /*
11938 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11939 %                                                                             %
11940 %                                                                             %
11941 %                                                                             %
11942 +   X R o t a t e I m a g e                                                   %
11943 %                                                                             %
11944 %                                                                             %
11945 %                                                                             %
11946 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11947 %
11948 %  XRotateImage() rotates the X image.  If the degrees parameter if zero, the
11949 %  rotation angle is computed from the slope of a line drawn by the user.
11950 %
11951 %  The format of the XRotateImage method is:
11952 %
11953 %      MagickBooleanType XRotateImage(Display *display,
11954 %        XResourceInfo *resource_info,XWindows *windows,double degrees,
11955 %        Image **image,ExceptionInfo *exception)
11956 %
11957 %  A description of each parameter follows:
11958 %
11959 %    o display: Specifies a connection to an X server; returned from
11960 %      XOpenDisplay.
11961 %
11962 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
11963 %
11964 %    o windows: Specifies a pointer to a XWindows structure.
11965 %
11966 %    o degrees: Specifies the number of degrees to rotate the image.
11967 %
11968 %    o image: the image.
11969 %
11970 %    o exception: return any errors or warnings in this structure.
11971 %
11972 */
11973 static MagickBooleanType XRotateImage(Display *display,
11974   XResourceInfo *resource_info,XWindows *windows,double degrees,Image **image,
11975   ExceptionInfo *exception)
11976 {
11977   static const char
11978     *RotateMenu[] =
11979     {
11980       "Pixel Color",
11981       "Direction",
11982       "Help",
11983       "Dismiss",
11984       (char *) NULL
11985     };
11986
11987   static ModeType
11988     direction = HorizontalRotateCommand;
11989
11990   static const ModeType
11991     DirectionCommands[] =
11992     {
11993       HorizontalRotateCommand,
11994       VerticalRotateCommand
11995     },
11996     RotateCommands[] =
11997     {
11998       RotateColorCommand,
11999       RotateDirectionCommand,
12000       RotateHelpCommand,
12001       RotateDismissCommand
12002     };
12003
12004   static unsigned int
12005     pen_id = 0;
12006
12007   char
12008     command[MaxTextExtent],
12009     text[MaxTextExtent];
12010
12011   Image
12012     *rotate_image;
12013
12014   int
12015     id,
12016     x,
12017     y;
12018
12019   MagickRealType
12020     normalized_degrees;
12021
12022   register int
12023     i;
12024
12025   unsigned int
12026     height,
12027     rotations,
12028     width;
12029
12030   if (degrees == 0.0)
12031     {
12032       unsigned int
12033         distance;
12034
12035       size_t
12036         state;
12037
12038       XEvent
12039         event;
12040
12041       XSegment
12042         rotate_info;
12043
12044       /*
12045         Map Command widget.
12046       */
12047       (void) CloneString(&windows->command.name,"Rotate");
12048       windows->command.data=2;
12049       (void) XCommandWidget(display,windows,RotateMenu,(XEvent *) NULL);
12050       (void) XMapRaised(display,windows->command.id);
12051       XClientMessage(display,windows->image.id,windows->im_protocols,
12052         windows->im_update_widget,CurrentTime);
12053       /*
12054         Wait for first button press.
12055       */
12056       (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12057       XQueryPosition(display,windows->image.id,&x,&y);
12058       rotate_info.x1=x;
12059       rotate_info.y1=y;
12060       rotate_info.x2=x;
12061       rotate_info.y2=y;
12062       state=DefaultState;
12063       do
12064       {
12065         XHighlightLine(display,windows->image.id,
12066           windows->image.highlight_context,&rotate_info);
12067         /*
12068           Wait for next event.
12069         */
12070         XScreenEvent(display,windows,&event);
12071         XHighlightLine(display,windows->image.id,
12072           windows->image.highlight_context,&rotate_info);
12073         if (event.xany.window == windows->command.id)
12074           {
12075             /*
12076               Select a command from the Command widget.
12077             */
12078             id=XCommandWidget(display,windows,RotateMenu,&event);
12079             if (id < 0)
12080               continue;
12081             (void) XSetFunction(display,windows->image.highlight_context,
12082               GXcopy);
12083             switch (RotateCommands[id])
12084             {
12085               case RotateColorCommand:
12086               {
12087                 const char
12088                   *ColorMenu[MaxNumberPens];
12089
12090                 int
12091                   pen_number;
12092
12093                 XColor
12094                   color;
12095
12096                 /*
12097                   Initialize menu selections.
12098                 */
12099                 for (i=0; i < (int) (MaxNumberPens-2); i++)
12100                   ColorMenu[i]=resource_info->pen_colors[i];
12101                 ColorMenu[MaxNumberPens-2]="Browser...";
12102                 ColorMenu[MaxNumberPens-1]=(const char *) NULL;
12103                 /*
12104                   Select a pen color from the pop-up menu.
12105                 */
12106                 pen_number=XMenuWidget(display,windows,RotateMenu[id],
12107                   (const char **) ColorMenu,command);
12108                 if (pen_number < 0)
12109                   break;
12110                 if (pen_number == (MaxNumberPens-2))
12111                   {
12112                     static char
12113                       color_name[MaxTextExtent] = "gray";
12114
12115                     /*
12116                       Select a pen color from a dialog.
12117                     */
12118                     resource_info->pen_colors[pen_number]=color_name;
12119                     XColorBrowserWidget(display,windows,"Select",color_name);
12120                     if (*color_name == '\0')
12121                       break;
12122                   }
12123                 /*
12124                   Set pen color.
12125                 */
12126                 (void) XParseColor(display,windows->map_info->colormap,
12127                   resource_info->pen_colors[pen_number],&color);
12128                 XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
12129                   (unsigned int) MaxColors,&color);
12130                 windows->pixel_info->pen_colors[pen_number]=color;
12131                 pen_id=(unsigned int) pen_number;
12132                 break;
12133               }
12134               case RotateDirectionCommand:
12135               {
12136                 static const char
12137                   *Directions[] =
12138                   {
12139                     "horizontal",
12140                     "vertical",
12141                     (char *) NULL,
12142                   };
12143
12144                 /*
12145                   Select a command from the pop-up menu.
12146                 */
12147                 id=XMenuWidget(display,windows,RotateMenu[id],
12148                   Directions,command);
12149                 if (id >= 0)
12150                   direction=DirectionCommands[id];
12151                 break;
12152               }
12153               case RotateHelpCommand:
12154               {
12155                 XTextViewWidget(display,resource_info,windows,MagickFalse,
12156                   "Help Viewer - Image Rotation",ImageRotateHelp);
12157                 break;
12158               }
12159               case RotateDismissCommand:
12160               {
12161                 /*
12162                   Prematurely exit.
12163                 */
12164                 state|=EscapeState;
12165                 state|=ExitState;
12166                 break;
12167               }
12168               default:
12169                 break;
12170             }
12171             (void) XSetFunction(display,windows->image.highlight_context,
12172               GXinvert);
12173             continue;
12174           }
12175         switch (event.type)
12176         {
12177           case ButtonPress:
12178           {
12179             if (event.xbutton.button != Button1)
12180               break;
12181             if (event.xbutton.window != windows->image.id)
12182               break;
12183             /*
12184               exit loop.
12185             */
12186             (void) XSetFunction(display,windows->image.highlight_context,
12187               GXcopy);
12188             rotate_info.x1=event.xbutton.x;
12189             rotate_info.y1=event.xbutton.y;
12190             state|=ExitState;
12191             break;
12192           }
12193           case ButtonRelease:
12194             break;
12195           case Expose:
12196             break;
12197           case KeyPress:
12198           {
12199             char
12200               command[MaxTextExtent];
12201
12202             KeySym
12203               key_symbol;
12204
12205             if (event.xkey.window != windows->image.id)
12206               break;
12207             /*
12208               Respond to a user key press.
12209             */
12210             (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
12211               sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12212             switch ((int) key_symbol)
12213             {
12214               case XK_Escape:
12215               case XK_F20:
12216               {
12217                 /*
12218                   Prematurely exit.
12219                 */
12220                 state|=EscapeState;
12221                 state|=ExitState;
12222                 break;
12223               }
12224               case XK_F1:
12225               case XK_Help:
12226               {
12227                 (void) XSetFunction(display,windows->image.highlight_context,
12228                   GXcopy);
12229                 XTextViewWidget(display,resource_info,windows,MagickFalse,
12230                   "Help Viewer - Image Rotation",ImageRotateHelp);
12231                 (void) XSetFunction(display,windows->image.highlight_context,
12232                   GXinvert);
12233                 break;
12234               }
12235               default:
12236               {
12237                 (void) XBell(display,0);
12238                 break;
12239               }
12240             }
12241             break;
12242           }
12243           case MotionNotify:
12244           {
12245             rotate_info.x1=event.xmotion.x;
12246             rotate_info.y1=event.xmotion.y;
12247           }
12248         }
12249         rotate_info.x2=rotate_info.x1;
12250         rotate_info.y2=rotate_info.y1;
12251         if (direction == HorizontalRotateCommand)
12252           rotate_info.x2+=32;
12253         else
12254           rotate_info.y2-=32;
12255       } while ((state & ExitState) == 0);
12256       (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12257       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12258       if ((state & EscapeState) != 0)
12259         return(MagickTrue);
12260       /*
12261         Draw line as pointer moves until the mouse button is released.
12262       */
12263       distance=0;
12264       (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12265       state=DefaultState;
12266       do
12267       {
12268         if (distance > 9)
12269           {
12270             /*
12271               Display info and draw rotation line.
12272             */
12273             if (windows->info.mapped == MagickFalse)
12274               (void) XMapWindow(display,windows->info.id);
12275             (void) FormatLocaleString(text,MaxTextExtent," %g",
12276               direction == VerticalRotateCommand ? degrees-90.0 : degrees);
12277             XInfoWidget(display,windows,text);
12278             XHighlightLine(display,windows->image.id,
12279               windows->image.highlight_context,&rotate_info);
12280           }
12281         else
12282           if (windows->info.mapped != MagickFalse)
12283             (void) XWithdrawWindow(display,windows->info.id,
12284               windows->info.screen);
12285         /*
12286           Wait for next event.
12287         */
12288         XScreenEvent(display,windows,&event);
12289         if (distance > 9)
12290           XHighlightLine(display,windows->image.id,
12291             windows->image.highlight_context,&rotate_info);
12292         switch (event.type)
12293         {
12294           case ButtonPress:
12295             break;
12296           case ButtonRelease:
12297           {
12298             /*
12299               User has committed to rotation line.
12300             */
12301             rotate_info.x2=event.xbutton.x;
12302             rotate_info.y2=event.xbutton.y;
12303             state|=ExitState;
12304             break;
12305           }
12306           case Expose:
12307             break;
12308           case MotionNotify:
12309           {
12310             rotate_info.x2=event.xmotion.x;
12311             rotate_info.y2=event.xmotion.y;
12312           }
12313           default:
12314             break;
12315         }
12316         /*
12317           Check boundary conditions.
12318         */
12319         if (rotate_info.x2 < 0)
12320           rotate_info.x2=0;
12321         else
12322           if (rotate_info.x2 > (int) windows->image.width)
12323             rotate_info.x2=(short) windows->image.width;
12324         if (rotate_info.y2 < 0)
12325           rotate_info.y2=0;
12326         else
12327           if (rotate_info.y2 > (int) windows->image.height)
12328             rotate_info.y2=(short) windows->image.height;
12329         /*
12330           Compute rotation angle from the slope of the line.
12331         */
12332         degrees=0.0;
12333         distance=(unsigned int)
12334           ((rotate_info.x2-rotate_info.x1+1)*(rotate_info.x2-rotate_info.x1+1))+
12335           ((rotate_info.y2-rotate_info.y1+1)*(rotate_info.y2-rotate_info.y1+1));
12336         if (distance > 9)
12337           degrees=RadiansToDegrees(-atan2((double) (rotate_info.y2-
12338             rotate_info.y1),(double) (rotate_info.x2-rotate_info.x1)));
12339       } while ((state & ExitState) == 0);
12340       (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12341       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12342       if (distance <= 9)
12343         return(MagickTrue);
12344     }
12345   if (direction == VerticalRotateCommand)
12346     degrees-=90.0;
12347   if (degrees == 0.0)
12348     return(MagickTrue);
12349   /*
12350     Rotate image.
12351   */
12352   normalized_degrees=degrees;
12353   while (normalized_degrees < -45.0)
12354     normalized_degrees+=360.0;
12355   for (rotations=0; normalized_degrees > 45.0; rotations++)
12356     normalized_degrees-=90.0;
12357   if (normalized_degrees != 0.0)
12358     (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
12359       exception);
12360   XSetCursorState(display,windows,MagickTrue);
12361   XCheckRefreshWindows(display,windows);
12362   (*image)->background_color.red=ScaleShortToQuantum(
12363     windows->pixel_info->pen_colors[pen_id].red);
12364   (*image)->background_color.green=ScaleShortToQuantum(
12365     windows->pixel_info->pen_colors[pen_id].green);
12366   (*image)->background_color.blue=ScaleShortToQuantum(
12367     windows->pixel_info->pen_colors[pen_id].blue);
12368   rotate_image=RotateImage(*image,degrees,exception);
12369   XSetCursorState(display,windows,MagickFalse);
12370   if (rotate_image == (Image *) NULL)
12371     return(MagickFalse);
12372   *image=DestroyImage(*image);
12373   *image=rotate_image;
12374   if (windows->image.crop_geometry != (char *) NULL)
12375     {
12376       /*
12377         Rotate crop geometry.
12378       */
12379       width=(unsigned int) (*image)->columns;
12380       height=(unsigned int) (*image)->rows;
12381       (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12382       switch (rotations % 4)
12383       {
12384         default:
12385         case 0:
12386           break;
12387         case 1:
12388         {
12389           /*
12390             Rotate 90 degrees.
12391           */
12392           (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12393             "%ux%u%+d%+d",height,width,(int) (*image)->columns-
12394             (int) height-y,x);
12395           break;
12396         }
12397         case 2:
12398         {
12399           /*
12400             Rotate 180 degrees.
12401           */
12402           (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12403             "%ux%u%+d%+d",width,height,(int) width-x,(int) height-y);
12404           break;
12405         }
12406         case 3:
12407         {
12408           /*
12409             Rotate 270 degrees.
12410           */
12411           (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12412             "%ux%u%+d%+d",height,width,y,(int) (*image)->rows-(int) width-x);
12413           break;
12414         }
12415       }
12416     }
12417   if (windows->image.orphan != MagickFalse)
12418     return(MagickTrue);
12419   if (normalized_degrees != 0.0)
12420     {
12421       /*
12422         Update image colormap.
12423       */
12424       windows->image.window_changes.width=(int) (*image)->columns;
12425       windows->image.window_changes.height=(int) (*image)->rows;
12426       if (windows->image.crop_geometry != (char *) NULL)
12427         {
12428           /*
12429             Obtain dimensions of image from crop geometry.
12430           */
12431           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
12432             &width,&height);
12433           windows->image.window_changes.width=(int) width;
12434           windows->image.window_changes.height=(int) height;
12435         }
12436       XConfigureImageColormap(display,resource_info,windows,*image);
12437     }
12438   else
12439     if (((rotations % 4) == 1) || ((rotations % 4) == 3))
12440       {
12441         windows->image.window_changes.width=windows->image.ximage->height;
12442         windows->image.window_changes.height=windows->image.ximage->width;
12443       }
12444   /*
12445     Update image configuration.
12446   */
12447   (void) XConfigureImage(display,resource_info,windows,*image,exception);
12448   return(MagickTrue);
12449 }
12450 \f
12451 /*
12452 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12453 %                                                                             %
12454 %                                                                             %
12455 %                                                                             %
12456 +   X S a v e I m a g e                                                       %
12457 %                                                                             %
12458 %                                                                             %
12459 %                                                                             %
12460 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12461 %
12462 %  XSaveImage() saves an image to a file.
12463 %
12464 %  The format of the XSaveImage method is:
12465 %
12466 %      MagickBooleanType XSaveImage(Display *display,
12467 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
12468 %        ExceptionInfo *exception)
12469 %
12470 %  A description of each parameter follows:
12471 %
12472 %    o display: Specifies a connection to an X server; returned from
12473 %      XOpenDisplay.
12474 %
12475 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12476 %
12477 %    o windows: Specifies a pointer to a XWindows structure.
12478 %
12479 %    o image: the image.
12480 %
12481 %    o exception: return any errors or warnings in this structure.
12482 %
12483 */
12484 static MagickBooleanType XSaveImage(Display *display,
12485   XResourceInfo *resource_info,XWindows *windows,Image *image,
12486   ExceptionInfo *exception)
12487 {
12488   char
12489     filename[MaxTextExtent],
12490     geometry[MaxTextExtent];
12491
12492   Image
12493     *save_image;
12494
12495   ImageInfo
12496     *image_info;
12497
12498   MagickStatusType
12499     status;
12500
12501   /*
12502     Request file name from user.
12503   */
12504   if (resource_info->write_filename != (char *) NULL)
12505     (void) CopyMagickString(filename,resource_info->write_filename,
12506       MaxTextExtent);
12507   else
12508     {
12509       char
12510         path[MaxTextExtent];
12511
12512       int
12513         status;
12514
12515       GetPathComponent(image->filename,HeadPath,path);
12516       GetPathComponent(image->filename,TailPath,filename);
12517       if (*path != '\0')
12518         {
12519           status=chdir(path);
12520           if (status == -1)
12521             (void) ThrowMagickException(exception,GetMagickModule(),
12522               FileOpenError,"UnableToOpenFile","%s",path);
12523         }
12524     }
12525   XFileBrowserWidget(display,windows,"Save",filename);
12526   if (*filename == '\0')
12527     return(MagickTrue);
12528   if (IsPathAccessible(filename) != MagickFalse)
12529     {
12530       int
12531         status;
12532
12533       /*
12534         File exists-- seek user's permission before overwriting.
12535       */
12536       status=XConfirmWidget(display,windows,"Overwrite",filename);
12537       if (status <= 0)
12538         return(MagickTrue);
12539     }
12540   image_info=CloneImageInfo(resource_info->image_info);
12541   (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
12542   (void) SetImageInfo(image_info,1,exception);
12543   if ((LocaleCompare(image_info->magick,"JPEG") == 0) ||
12544       (LocaleCompare(image_info->magick,"JPG") == 0))
12545     {
12546       char
12547         quality[MaxTextExtent];
12548
12549       int
12550         status;
12551
12552       /*
12553         Request JPEG quality from user.
12554       */
12555       (void) FormatLocaleString(quality,MaxTextExtent,"%.20g",(double)
12556         image->quality);
12557       status=XDialogWidget(display,windows,"Save","Enter JPEG quality:",
12558         quality);
12559       if (*quality == '\0')
12560         return(MagickTrue);
12561       image->quality=StringToUnsignedLong(quality);
12562       image_info->interlace=status != 0 ? NoInterlace : PlaneInterlace;
12563     }
12564   if ((LocaleCompare(image_info->magick,"EPS") == 0) ||
12565       (LocaleCompare(image_info->magick,"PDF") == 0) ||
12566       (LocaleCompare(image_info->magick,"PS") == 0) ||
12567       (LocaleCompare(image_info->magick,"PS2") == 0))
12568     {
12569       char
12570         geometry[MaxTextExtent];
12571
12572       /*
12573         Request page geometry from user.
12574       */
12575       (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
12576       if (LocaleCompare(image_info->magick,"PDF") == 0)
12577         (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
12578       if (image_info->page != (char *) NULL)
12579         (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
12580       XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
12581         "Select page geometry:",geometry);
12582       if (*geometry != '\0')
12583         image_info->page=GetPageGeometry(geometry);
12584     }
12585   /*
12586     Apply image transforms.
12587   */
12588   XSetCursorState(display,windows,MagickTrue);
12589   XCheckRefreshWindows(display,windows);
12590   save_image=CloneImage(image,0,0,MagickTrue,exception);
12591   if (save_image == (Image *) NULL)
12592     return(MagickFalse);
12593   (void) FormatLocaleString(geometry,MaxTextExtent,"%dx%d!",
12594     windows->image.ximage->width,windows->image.ximage->height);
12595   (void) TransformImage(&save_image,windows->image.crop_geometry,geometry);
12596   /*
12597     Write image.
12598   */
12599   (void) CopyMagickString(save_image->filename,filename,MaxTextExtent);
12600   status=WriteImage(image_info,save_image,exception);
12601   if (status != MagickFalse)
12602     image->taint=MagickFalse;
12603   save_image=DestroyImage(save_image);
12604   image_info=DestroyImageInfo(image_info);
12605   XSetCursorState(display,windows,MagickFalse);
12606   return(status != 0 ? MagickTrue : MagickFalse);
12607 }
12608 \f
12609 /*
12610 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12611 %                                                                             %
12612 %                                                                             %
12613 %                                                                             %
12614 +   X S c r e e n E v e n t                                                   %
12615 %                                                                             %
12616 %                                                                             %
12617 %                                                                             %
12618 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12619 %
12620 %  XScreenEvent() handles global events associated with the Pan and Magnify
12621 %  windows.
12622 %
12623 %  The format of the XScreenEvent function is:
12624 %
12625 %      void XScreenEvent(Display *display,XWindows *windows,XEvent *event)
12626 %
12627 %  A description of each parameter follows:
12628 %
12629 %    o display: Specifies a pointer to the Display structure;  returned from
12630 %      XOpenDisplay.
12631 %
12632 %    o windows: Specifies a pointer to a XWindows structure.
12633 %
12634 %    o event: Specifies a pointer to a X11 XEvent structure.
12635 %
12636 %
12637 */
12638
12639 #if defined(__cplusplus) || defined(c_plusplus)
12640 extern "C" {
12641 #endif
12642
12643 static int XPredicate(Display *magick_unused(display),XEvent *event,char *data)
12644 {
12645   register XWindows
12646     *windows;
12647
12648   windows=(XWindows *) data;
12649   if ((event->type == ClientMessage) &&
12650       (event->xclient.window == windows->image.id))
12651     return(MagickFalse);
12652   return(MagickTrue);
12653 }
12654
12655 #if defined(__cplusplus) || defined(c_plusplus)
12656 }
12657 #endif
12658
12659 static void XScreenEvent(Display *display,XWindows *windows,XEvent *event)
12660 {
12661   register int
12662     x,
12663     y;
12664
12665   (void) XIfEvent(display,event,XPredicate,(char *) windows);
12666   if (event->xany.window == windows->command.id)
12667     return;
12668   switch (event->type)
12669   {
12670     case ButtonPress:
12671     case ButtonRelease:
12672     {
12673       if ((event->xbutton.button == Button3) &&
12674           (event->xbutton.state & Mod1Mask))
12675         {
12676           /*
12677             Convert Alt-Button3 to Button2.
12678           */
12679           event->xbutton.button=Button2;
12680           event->xbutton.state&=(~Mod1Mask);
12681         }
12682       if (event->xbutton.window == windows->backdrop.id)
12683         {
12684           (void) XSetInputFocus(display,event->xbutton.window,RevertToParent,
12685             event->xbutton.time);
12686           break;
12687         }
12688       if (event->xbutton.window == windows->pan.id)
12689         {
12690           XPanImage(display,windows,event);
12691           break;
12692         }
12693       if (event->xbutton.window == windows->image.id)
12694         if (event->xbutton.button == Button2)
12695           {
12696             /*
12697               Update magnified image.
12698             */
12699             x=event->xbutton.x;
12700             y=event->xbutton.y;
12701             if (x < 0)
12702               x=0;
12703             else
12704               if (x >= (int) windows->image.width)
12705                 x=(int) (windows->image.width-1);
12706             windows->magnify.x=(int) windows->image.x+x;
12707             if (y < 0)
12708               y=0;
12709             else
12710              if (y >= (int) windows->image.height)
12711                y=(int) (windows->image.height-1);
12712             windows->magnify.y=windows->image.y+y;
12713             if (windows->magnify.mapped == MagickFalse)
12714               (void) XMapRaised(display,windows->magnify.id);
12715             XMakeMagnifyImage(display,windows);
12716             if (event->type == ButtonRelease)
12717               (void) XWithdrawWindow(display,windows->info.id,
12718                 windows->info.screen);
12719             break;
12720           }
12721       break;
12722     }
12723     case ClientMessage:
12724     {
12725       /*
12726         If client window delete message, exit.
12727       */
12728       if (event->xclient.message_type != windows->wm_protocols)
12729         break;
12730       if (*event->xclient.data.l != (long) windows->wm_delete_window)
12731         break;
12732       if (event->xclient.window == windows->magnify.id)
12733         {
12734           (void) XWithdrawWindow(display,windows->magnify.id,
12735             windows->magnify.screen);
12736           break;
12737         }
12738       break;
12739     }
12740     case ConfigureNotify:
12741     {
12742       if (event->xconfigure.window == windows->magnify.id)
12743         {
12744           unsigned int
12745             magnify;
12746
12747           /*
12748             Magnify window has a new configuration.
12749           */
12750           windows->magnify.width=(unsigned int) event->xconfigure.width;
12751           windows->magnify.height=(unsigned int) event->xconfigure.height;
12752           if (windows->magnify.mapped == MagickFalse)
12753             break;
12754           magnify=1;
12755           while ((int) magnify <= event->xconfigure.width)
12756             magnify<<=1;
12757           while ((int) magnify <= event->xconfigure.height)
12758             magnify<<=1;
12759           magnify>>=1;
12760           if (((int) magnify != event->xconfigure.width) ||
12761               ((int) magnify != event->xconfigure.height))
12762             {
12763               XWindowChanges
12764                 window_changes;
12765
12766               window_changes.width=(int) magnify;
12767               window_changes.height=(int) magnify;
12768               (void) XReconfigureWMWindow(display,windows->magnify.id,
12769                 windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
12770                 &window_changes);
12771               break;
12772             }
12773           XMakeMagnifyImage(display,windows);
12774           break;
12775         }
12776       break;
12777     }
12778     case Expose:
12779     {
12780       if (event->xexpose.window == windows->image.id)
12781         {
12782           XRefreshWindow(display,&windows->image,event);
12783           break;
12784         }
12785       if (event->xexpose.window == windows->pan.id)
12786         if (event->xexpose.count == 0)
12787           {
12788             XDrawPanRectangle(display,windows);
12789             break;
12790           }
12791       if (event->xexpose.window == windows->magnify.id)
12792         if (event->xexpose.count == 0)
12793           {
12794             XMakeMagnifyImage(display,windows);
12795             break;
12796           }
12797       break;
12798     }
12799     case KeyPress:
12800     {
12801       char
12802         command[MaxTextExtent];
12803
12804       KeySym
12805         key_symbol;
12806
12807       if (event->xkey.window != windows->magnify.id)
12808         break;
12809       /*
12810         Respond to a user key press.
12811       */
12812       (void) XLookupString((XKeyEvent *) &event->xkey,command,(int)
12813         sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12814       XMagnifyWindowCommand(display,windows,event->xkey.state,key_symbol);
12815       break;
12816     }
12817     case MapNotify:
12818     {
12819       if (event->xmap.window == windows->magnify.id)
12820         {
12821           windows->magnify.mapped=MagickTrue;
12822           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12823           break;
12824         }
12825       if (event->xmap.window == windows->info.id)
12826         {
12827           windows->info.mapped=MagickTrue;
12828           break;
12829         }
12830       break;
12831     }
12832     case MotionNotify:
12833     {
12834       while (XCheckMaskEvent(display,ButtonMotionMask,event)) ;
12835       if (event->xmotion.window == windows->image.id)
12836         if (windows->magnify.mapped != MagickFalse)
12837           {
12838             /*
12839               Update magnified image.
12840             */
12841             x=event->xmotion.x;
12842             y=event->xmotion.y;
12843             if (x < 0)
12844               x=0;
12845             else
12846               if (x >= (int) windows->image.width)
12847                 x=(int) (windows->image.width-1);
12848             windows->magnify.x=(int) windows->image.x+x;
12849             if (y < 0)
12850               y=0;
12851             else
12852              if (y >= (int) windows->image.height)
12853                y=(int) (windows->image.height-1);
12854             windows->magnify.y=windows->image.y+y;
12855             XMakeMagnifyImage(display,windows);
12856           }
12857       break;
12858     }
12859     case UnmapNotify:
12860     {
12861       if (event->xunmap.window == windows->magnify.id)
12862         {
12863           windows->magnify.mapped=MagickFalse;
12864           break;
12865         }
12866       if (event->xunmap.window == windows->info.id)
12867         {
12868           windows->info.mapped=MagickFalse;
12869           break;
12870         }
12871       break;
12872     }
12873     default:
12874       break;
12875   }
12876 }
12877 \f
12878 /*
12879 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12880 %                                                                             %
12881 %                                                                             %
12882 %                                                                             %
12883 +   X S e t C r o p G e o m e t r y                                           %
12884 %                                                                             %
12885 %                                                                             %
12886 %                                                                             %
12887 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12888 %
12889 %  XSetCropGeometry() accepts a cropping geometry relative to the Image window
12890 %  and translates it to a cropping geometry relative to the image.
12891 %
12892 %  The format of the XSetCropGeometry method is:
12893 %
12894 %      void XSetCropGeometry(Display *display,XWindows *windows,
12895 %        RectangleInfo *crop_info,Image *image)
12896 %
12897 %  A description of each parameter follows:
12898 %
12899 %    o display: Specifies a connection to an X server; returned from
12900 %      XOpenDisplay.
12901 %
12902 %    o windows: Specifies a pointer to a XWindows structure.
12903 %
12904 %    o crop_info:  A pointer to a RectangleInfo that defines a region of the
12905 %      Image window to crop.
12906 %
12907 %    o image: the image.
12908 %
12909 */
12910 static void XSetCropGeometry(Display *display,XWindows *windows,
12911   RectangleInfo *crop_info,Image *image)
12912 {
12913   char
12914     text[MaxTextExtent];
12915
12916   int
12917     x,
12918     y;
12919
12920   MagickRealType
12921     scale_factor;
12922
12923   unsigned int
12924     height,
12925     width;
12926
12927   if (windows->info.mapped != MagickFalse)
12928     {
12929       /*
12930         Display info on cropping rectangle.
12931       */
12932       (void) FormatLocaleString(text,MaxTextExtent," %.20gx%.20g%+.20g%+.20g",
12933         (double) crop_info->width,(double) crop_info->height,(double)
12934         crop_info->x,(double) crop_info->y);
12935       XInfoWidget(display,windows,text);
12936     }
12937   /*
12938     Cropping geometry is relative to any previous crop geometry.
12939   */
12940   x=0;
12941   y=0;
12942   width=(unsigned int) image->columns;
12943   height=(unsigned int) image->rows;
12944   if (windows->image.crop_geometry != (char *) NULL)
12945     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12946   else
12947     windows->image.crop_geometry=AcquireString((char *) NULL);
12948   /*
12949     Define the crop geometry string from the cropping rectangle.
12950   */
12951   scale_factor=(MagickRealType) width/windows->image.ximage->width;
12952   if (crop_info->x > 0)
12953     x+=(int) (scale_factor*crop_info->x+0.5);
12954   width=(unsigned int) (scale_factor*crop_info->width+0.5);
12955   if (width == 0)
12956     width=1;
12957   scale_factor=(MagickRealType) height/windows->image.ximage->height;
12958   if (crop_info->y > 0)
12959     y+=(int) (scale_factor*crop_info->y+0.5);
12960   height=(unsigned int) (scale_factor*crop_info->height+0.5);
12961   if (height == 0)
12962     height=1;
12963   (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12964     "%ux%u%+d%+d",width,height,x,y);
12965 }
12966 \f
12967 /*
12968 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12969 %                                                                             %
12970 %                                                                             %
12971 %                                                                             %
12972 +   X T i l e I m a g e                                                       %
12973 %                                                                             %
12974 %                                                                             %
12975 %                                                                             %
12976 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12977 %
12978 %  XTileImage() loads or deletes a selected tile from a visual image directory.
12979 %  The load or delete command is chosen from a menu.
12980 %
12981 %  The format of the XTileImage method is:
12982 %
12983 %      Image *XTileImage(Display *display,XResourceInfo *resource_info,
12984 %        XWindows *windows,Image *image,XEvent *event,ExceptionInfo *exception)
12985 %
12986 %  A description of each parameter follows:
12987 %
12988 %    o tile_image:  XTileImage reads or deletes the tile image
12989 %      and returns it.  A null image is returned if an error occurs.
12990 %
12991 %    o display: Specifies a connection to an X server;  returned from
12992 %      XOpenDisplay.
12993 %
12994 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12995 %
12996 %    o windows: Specifies a pointer to a XWindows structure.
12997 %
12998 %    o image: the image; returned from ReadImage.
12999 %
13000 %    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
13001 %      the entire image is refreshed.
13002 %
13003 %    o exception: return any errors or warnings in this structure.
13004 %
13005 */
13006 static Image *XTileImage(Display *display,XResourceInfo *resource_info,
13007   XWindows *windows,Image *image,XEvent *event,ExceptionInfo *exception)
13008 {
13009   static const char
13010     *VerbMenu[] =
13011     {
13012       "Load",
13013       "Next",
13014       "Former",
13015       "Delete",
13016       "Update",
13017       (char *) NULL,
13018     };
13019
13020   static const ModeType
13021     TileCommands[] =
13022     {
13023       TileLoadCommand,
13024       TileNextCommand,
13025       TileFormerCommand,
13026       TileDeleteCommand,
13027       TileUpdateCommand
13028     };
13029
13030   char
13031     command[MaxTextExtent],
13032     filename[MaxTextExtent];
13033
13034   Image
13035     *tile_image;
13036
13037   int
13038     id,
13039     status,
13040     tile,
13041     x,
13042     y;
13043
13044   MagickRealType
13045     scale_factor;
13046
13047   register char
13048     *p,
13049     *q;
13050
13051   register int
13052     i;
13053
13054   unsigned int
13055     height,
13056     width;
13057
13058   /*
13059     Tile image is relative to montage image configuration.
13060   */
13061   x=0;
13062   y=0;
13063   width=(unsigned int) image->columns;
13064   height=(unsigned int) image->rows;
13065   if (windows->image.crop_geometry != (char *) NULL)
13066     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
13067   scale_factor=(MagickRealType) width/windows->image.ximage->width;
13068   event->xbutton.x+=windows->image.x;
13069   event->xbutton.x=(int) (scale_factor*event->xbutton.x+x+0.5);
13070   scale_factor=(MagickRealType) height/windows->image.ximage->height;
13071   event->xbutton.y+=windows->image.y;
13072   event->xbutton.y=(int) (scale_factor*event->xbutton.y+y+0.5);
13073   /*
13074     Determine size and location of each tile in the visual image directory.
13075   */
13076   width=(unsigned int) image->columns;
13077   height=(unsigned int) image->rows;
13078   x=0;
13079   y=0;
13080   (void) XParseGeometry(image->montage,&x,&y,&width,&height);
13081   tile=((event->xbutton.y-y)/height)*(((int) image->columns-x)/width)+
13082     (event->xbutton.x-x)/width;
13083   if (tile < 0)
13084     {
13085       /*
13086         Button press is outside any tile.
13087       */
13088       (void) XBell(display,0);
13089       return((Image *) NULL);
13090     }
13091   /*
13092     Determine file name from the tile directory.
13093   */
13094   p=image->directory;
13095   for (i=tile; (i != 0) && (*p != '\0'); )
13096   {
13097     if (*p == '\n')
13098       i--;
13099     p++;
13100   }
13101   if (*p == '\0')
13102     {
13103       /*
13104         Button press is outside any tile.
13105       */
13106       (void) XBell(display,0);
13107       return((Image *) NULL);
13108     }
13109   /*
13110     Select a command from the pop-up menu.
13111   */
13112   id=XMenuWidget(display,windows,"Tile Verb",VerbMenu,command);
13113   if (id < 0)
13114     return((Image *) NULL);
13115   q=p;
13116   while ((*q != '\n') && (*q != '\0'))
13117     q++;
13118   (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13119   /*
13120     Perform command for the selected tile.
13121   */
13122   XSetCursorState(display,windows,MagickTrue);
13123   XCheckRefreshWindows(display,windows);
13124   tile_image=NewImageList();
13125   switch (TileCommands[id])
13126   {
13127     case TileLoadCommand:
13128     {
13129       /*
13130         Load tile image.
13131       */
13132       XCheckRefreshWindows(display,windows);
13133       (void) CopyMagickString(resource_info->image_info->magick,"MIFF",
13134         MaxTextExtent);
13135       (void) CopyMagickString(resource_info->image_info->filename,filename,
13136         MaxTextExtent);
13137       tile_image=ReadImage(resource_info->image_info,exception);
13138       CatchException(exception);
13139       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13140       break;
13141     }
13142     case TileNextCommand:
13143     {
13144       /*
13145         Display next image.
13146       */
13147       XClientMessage(display,windows->image.id,windows->im_protocols,
13148         windows->im_next_image,CurrentTime);
13149       break;
13150     }
13151     case TileFormerCommand:
13152     {
13153       /*
13154         Display former image.
13155       */
13156       XClientMessage(display,windows->image.id,windows->im_protocols,
13157         windows->im_former_image,CurrentTime);
13158       break;
13159     }
13160     case TileDeleteCommand:
13161     {
13162       /*
13163         Delete tile image.
13164       */
13165       if (IsPathAccessible(filename) == MagickFalse)
13166         {
13167           XNoticeWidget(display,windows,"Image file does not exist:",filename);
13168           break;
13169         }
13170       status=XConfirmWidget(display,windows,"Really delete tile",filename);
13171       if (status <= 0)
13172         break;
13173       status=remove(filename) != 0 ? MagickTrue : MagickFalse;
13174       if (status != MagickFalse)
13175         {
13176           XNoticeWidget(display,windows,"Unable to delete image file:",
13177             filename);
13178           break;
13179         }
13180     }
13181     case TileUpdateCommand:
13182     {
13183       int
13184         x_offset,
13185         y_offset;
13186
13187       PixelPacket
13188         pixel;
13189
13190       register int
13191         j;
13192
13193       register Quantum
13194         *s;
13195
13196       /*
13197         Ensure all the images exist.
13198       */
13199       tile=0;
13200       for (p=image->directory; *p != '\0'; p++)
13201       {
13202         CacheView
13203           *image_view;
13204
13205         q=p;
13206         while ((*q != '\n') && (*q != '\0'))
13207           q++;
13208         (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13209         p=q;
13210         if (IsPathAccessible(filename) != MagickFalse)
13211           {
13212             tile++;
13213             continue;
13214           }
13215         /*
13216           Overwrite tile with background color.
13217         */
13218         x_offset=(int) (width*(tile % (((int) image->columns-x)/width))+x);
13219         y_offset=(int) (height*(tile/(((int) image->columns-x)/width))+y);
13220         image_view=AcquireCacheView(image);
13221         (void) GetOneCacheViewVirtualPixel(image_view,0,0,&pixel,exception);
13222         for (i=0; i < (int) height; i++)
13223         {
13224           s=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,(ssize_t)
13225             y_offset+i,width,1,exception);
13226           if (s == (Quantum *) NULL)
13227             break;
13228           for (j=0; j < (int) width; j++)
13229           {
13230             SetPixelPacket(image,&pixel,s);
13231             s+=GetPixelChannels(image);
13232           }
13233           if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
13234             break;
13235         }
13236         image_view=DestroyCacheView(image_view);
13237         tile++;
13238       }
13239       windows->image.window_changes.width=(int) image->columns;
13240       windows->image.window_changes.height=(int) image->rows;
13241       XConfigureImageColormap(display,resource_info,windows,image);
13242       (void) XConfigureImage(display,resource_info,windows,image,exception);
13243       break;
13244     }
13245     default:
13246       break;
13247   }
13248   XSetCursorState(display,windows,MagickFalse);
13249   return(tile_image);
13250 }
13251 \f
13252 /*
13253 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13254 %                                                                             %
13255 %                                                                             %
13256 %                                                                             %
13257 +   X T r a n s l a t e I m a g e                                             %
13258 %                                                                             %
13259 %                                                                             %
13260 %                                                                             %
13261 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13262 %
13263 %  XTranslateImage() translates the image within an Image window by one pixel
13264 %  as specified by the key symbol.  If the image has a `montage string the
13265 %  translation is respect to the width and height contained within the string.
13266 %
13267 %  The format of the XTranslateImage method is:
13268 %
13269 %      void XTranslateImage(Display *display,XWindows *windows,
13270 %        Image *image,const KeySym key_symbol)
13271 %
13272 %  A description of each parameter follows:
13273 %
13274 %    o display: Specifies a connection to an X server; returned from
13275 %      XOpenDisplay.
13276 %
13277 %    o windows: Specifies a pointer to a XWindows structure.
13278 %
13279 %    o image: the image.
13280 %
13281 %    o key_symbol: Specifies a KeySym which indicates which side of the image
13282 %      to trim.
13283 %
13284 */
13285 static void XTranslateImage(Display *display,XWindows *windows,
13286   Image *image,const KeySym key_symbol)
13287 {
13288   char
13289     text[MaxTextExtent];
13290
13291   int
13292     x,
13293     y;
13294
13295   unsigned int
13296     x_offset,
13297     y_offset;
13298
13299   /*
13300     User specified a pan position offset.
13301   */
13302   x_offset=windows->image.width;
13303   y_offset=windows->image.height;
13304   if (image->montage != (char *) NULL)
13305     (void) XParseGeometry(image->montage,&x,&y,&x_offset,&y_offset);
13306   switch ((int) key_symbol)
13307   {
13308     case XK_Home:
13309     case XK_KP_Home:
13310     {
13311       windows->image.x=(int) windows->image.width/2;
13312       windows->image.y=(int) windows->image.height/2;
13313       break;
13314     }
13315     case XK_Left:
13316     case XK_KP_Left:
13317     {
13318       windows->image.x-=x_offset;
13319       break;
13320     }
13321     case XK_Next:
13322     case XK_Up:
13323     case XK_KP_Up:
13324     {
13325       windows->image.y-=y_offset;
13326       break;
13327     }
13328     case XK_Right:
13329     case XK_KP_Right:
13330     {
13331       windows->image.x+=x_offset;
13332       break;
13333     }
13334     case XK_Prior:
13335     case XK_Down:
13336     case XK_KP_Down:
13337     {
13338       windows->image.y+=y_offset;
13339       break;
13340     }
13341     default:
13342       return;
13343   }
13344   /*
13345     Check boundary conditions.
13346   */
13347   if (windows->image.x < 0)
13348     windows->image.x=0;
13349   else
13350     if ((int) (windows->image.x+windows->image.width) >
13351         windows->image.ximage->width)
13352       windows->image.x=(int) windows->image.ximage->width-windows->image.width;
13353   if (windows->image.y < 0)
13354     windows->image.y=0;
13355   else
13356     if ((int) (windows->image.y+windows->image.height) >
13357         windows->image.ximage->height)
13358       windows->image.y=(int) windows->image.ximage->height-windows->image.height;
13359   /*
13360     Refresh Image window.
13361   */
13362   (void) FormatLocaleString(text,MaxTextExtent," %ux%u%+d%+d ",
13363     windows->image.width,windows->image.height,windows->image.x,
13364     windows->image.y);
13365   XInfoWidget(display,windows,text);
13366   XCheckRefreshWindows(display,windows);
13367   XDrawPanRectangle(display,windows);
13368   XRefreshWindow(display,&windows->image,(XEvent *) NULL);
13369   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13370 }
13371 \f
13372 /*
13373 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13374 %                                                                             %
13375 %                                                                             %
13376 %                                                                             %
13377 +   X T r i m I m a g e                                                       %
13378 %                                                                             %
13379 %                                                                             %
13380 %                                                                             %
13381 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13382 %
13383 %  XTrimImage() trims the edges from the Image window.
13384 %
13385 %  The format of the XTrimImage method is:
13386 %
13387 %      MagickBooleanType XTrimImage(Display *display,
13388 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
13389 %        ExceptionInfo *exception)
13390 %
13391 %  A description of each parameter follows:
13392 %
13393 %    o display: Specifies a connection to an X server; returned from
13394 %      XOpenDisplay.
13395 %
13396 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13397 %
13398 %    o windows: Specifies a pointer to a XWindows structure.
13399 %
13400 %    o image: the image.
13401 %
13402 %    o exception: return any errors or warnings in this structure.
13403 %
13404 */
13405 static MagickBooleanType XTrimImage(Display *display,
13406   XResourceInfo *resource_info,XWindows *windows,Image *image,
13407   ExceptionInfo *exception)
13408 {
13409   RectangleInfo
13410     trim_info;
13411
13412   register int
13413     x,
13414     y;
13415
13416   size_t
13417     background,
13418     pixel;
13419
13420   /*
13421     Trim edges from image.
13422   */
13423   XSetCursorState(display,windows,MagickTrue);
13424   XCheckRefreshWindows(display,windows);
13425   /*
13426     Crop the left edge.
13427   */
13428   background=XGetPixel(windows->image.ximage,0,0);
13429   trim_info.width=(size_t) windows->image.ximage->width;
13430   for (x=0; x < windows->image.ximage->width; x++)
13431   {
13432     for (y=0; y < windows->image.ximage->height; y++)
13433     {
13434       pixel=XGetPixel(windows->image.ximage,x,y);
13435       if (pixel != background)
13436         break;
13437     }
13438     if (y < windows->image.ximage->height)
13439       break;
13440   }
13441   trim_info.x=(ssize_t) x;
13442   if (trim_info.x == (ssize_t) windows->image.ximage->width)
13443     {
13444       XSetCursorState(display,windows,MagickFalse);
13445       return(MagickFalse);
13446     }
13447   /*
13448     Crop the right edge.
13449   */
13450   background=XGetPixel(windows->image.ximage,windows->image.ximage->width-1,0);
13451   for (x=windows->image.ximage->width-1; x != 0; x--)
13452   {
13453     for (y=0; y < windows->image.ximage->height; y++)
13454     {
13455       pixel=XGetPixel(windows->image.ximage,x,y);
13456       if (pixel != background)
13457         break;
13458     }
13459     if (y < windows->image.ximage->height)
13460       break;
13461   }
13462   trim_info.width=(size_t) (x-trim_info.x+1);
13463   /*
13464     Crop the top edge.
13465   */
13466   background=XGetPixel(windows->image.ximage,0,0);
13467   trim_info.height=(size_t) windows->image.ximage->height;
13468   for (y=0; y < windows->image.ximage->height; y++)
13469   {
13470     for (x=0; x < windows->image.ximage->width; x++)
13471     {
13472       pixel=XGetPixel(windows->image.ximage,x,y);
13473       if (pixel != background)
13474         break;
13475     }
13476     if (x < windows->image.ximage->width)
13477       break;
13478   }
13479   trim_info.y=(ssize_t) y;
13480   /*
13481     Crop the bottom edge.
13482   */
13483   background=XGetPixel(windows->image.ximage,0,windows->image.ximage->height-1);
13484   for (y=windows->image.ximage->height-1; y != 0; y--)
13485   {
13486     for (x=0; x < windows->image.ximage->width; x++)
13487     {
13488       pixel=XGetPixel(windows->image.ximage,x,y);
13489       if (pixel != background)
13490         break;
13491     }
13492     if (x < windows->image.ximage->width)
13493       break;
13494   }
13495   trim_info.height=(size_t) y-trim_info.y+1;
13496   if (((unsigned int) trim_info.width != windows->image.width) ||
13497       ((unsigned int) trim_info.height != windows->image.height))
13498     {
13499       /*
13500         Reconfigure Image window as defined by the trimming rectangle.
13501       */
13502       XSetCropGeometry(display,windows,&trim_info,image);
13503       windows->image.window_changes.width=(int) trim_info.width;
13504       windows->image.window_changes.height=(int) trim_info.height;
13505       (void) XConfigureImage(display,resource_info,windows,image,exception);
13506     }
13507   XSetCursorState(display,windows,MagickFalse);
13508   return(MagickTrue);
13509 }
13510 \f
13511 /*
13512 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13513 %                                                                             %
13514 %                                                                             %
13515 %                                                                             %
13516 +   X V i s u a l D i r e c t o r y I m a g e                                 %
13517 %                                                                             %
13518 %                                                                             %
13519 %                                                                             %
13520 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13521 %
13522 %  XVisualDirectoryImage() creates a Visual Image Directory.
13523 %
13524 %  The format of the XVisualDirectoryImage method is:
13525 %
13526 %      Image *XVisualDirectoryImage(Display *display,
13527 %        XResourceInfo *resource_info,XWindows *windows,
13528 %        ExceptionInfo *exception)
13529 %
13530 %  A description of each parameter follows:
13531 %
13532 %    o display: Specifies a connection to an X server; returned from
13533 %      XOpenDisplay.
13534 %
13535 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13536 %
13537 %    o windows: Specifies a pointer to a XWindows structure.
13538 %
13539 %    o exception: return any errors or warnings in this structure.
13540 %
13541 */
13542 static Image *XVisualDirectoryImage(Display *display,
13543   XResourceInfo *resource_info,XWindows *windows,ExceptionInfo *exception)
13544 {
13545 #define TileImageTag  "Scale/Image"
13546 #define XClientName  "montage"
13547
13548   char
13549     **filelist;
13550
13551   Image
13552     *images,
13553     *montage_image,
13554     *next_image,
13555     *thumbnail_image;
13556
13557   ImageInfo
13558     *read_info;
13559
13560   int
13561     number_files;
13562
13563   MagickBooleanType
13564     backdrop;
13565
13566   MagickStatusType
13567     status;
13568
13569   MontageInfo
13570     *montage_info;
13571
13572   RectangleInfo
13573     geometry;
13574
13575   register int
13576     i;
13577
13578   static char
13579     filename[MaxTextExtent] = "\0",
13580     filenames[MaxTextExtent] = "*";
13581
13582   XResourceInfo
13583     background_resources;
13584
13585   /*
13586     Request file name from user.
13587   */
13588   XFileBrowserWidget(display,windows,"Directory",filenames);
13589   if (*filenames == '\0')
13590     return((Image *) NULL);
13591   /*
13592     Expand the filenames.
13593   */
13594   filelist=(char **) AcquireMagickMemory(sizeof(*filelist));
13595   if (filelist == (char **) NULL)
13596     {
13597       ThrowXWindowFatalException(ResourceLimitError,"MemoryAllocationFailed",
13598         filenames);
13599       return((Image *) NULL);
13600     }
13601   number_files=1;
13602   filelist[0]=filenames;
13603   status=ExpandFilenames(&number_files,&filelist);
13604   if ((status == MagickFalse) || (number_files == 0))
13605     {
13606       if (number_files == 0)
13607         ThrowXWindowFatalException(ImageError,"NoImagesWereFound",filenames)
13608       else
13609         ThrowXWindowFatalException(ResourceLimitError,"MemoryAllocationFailed",
13610           filenames);
13611       return((Image *) NULL);
13612     }
13613   /*
13614     Set image background resources.
13615   */
13616   background_resources=(*resource_info);
13617   background_resources.window_id=AcquireString("");
13618   (void) FormatLocaleString(background_resources.window_id,MaxTextExtent,
13619     "0x%lx",windows->image.id);
13620   background_resources.backdrop=MagickTrue;
13621   /*
13622     Read each image and convert them to a tile.
13623   */
13624   backdrop=(windows->visual_info->klass == TrueColor) ||
13625     (windows->visual_info->klass == DirectColor) ? MagickTrue : MagickFalse;
13626   read_info=CloneImageInfo(resource_info->image_info);
13627   (void) SetImageOption(read_info,"jpeg:size","120x120");
13628   (void) CloneString(&read_info->size,DefaultTileGeometry);
13629   (void) SetImageInfoProgressMonitor(read_info,(MagickProgressMonitor) NULL,
13630     (void *) NULL);
13631   images=NewImageList();
13632   XSetCursorState(display,windows,MagickTrue);
13633   XCheckRefreshWindows(display,windows);
13634   for (i=0; i < (int) number_files; i++)
13635   {
13636     (void) CopyMagickString(read_info->filename,filelist[i],MaxTextExtent);
13637     filelist[i]=DestroyString(filelist[i]);
13638     *read_info->magick='\0';
13639     next_image=ReadImage(read_info,exception);
13640     CatchException(exception);
13641     if (next_image != (Image *) NULL)
13642       {
13643         (void) DeleteImageProperty(next_image,"label");
13644         (void) SetImageProperty(next_image,"label",InterpretImageProperties(
13645           read_info,next_image,DefaultTileLabel));
13646         (void) ParseRegionGeometry(next_image,read_info->size,&geometry,
13647           exception);
13648         thumbnail_image=ThumbnailImage(next_image,geometry.width,
13649           geometry.height,exception);
13650         if (thumbnail_image != (Image *) NULL)
13651           {
13652             next_image=DestroyImage(next_image);
13653             next_image=thumbnail_image;
13654           }
13655         if (backdrop)
13656           {
13657             (void) XDisplayBackgroundImage(display,&background_resources,
13658               next_image,exception);
13659             XSetCursorState(display,windows,MagickTrue);
13660           }
13661         AppendImageToList(&images,next_image);
13662         if (images->progress_monitor != (MagickProgressMonitor) NULL)
13663           {
13664             MagickBooleanType
13665               proceed;
13666
13667             proceed=SetImageProgress(images,LoadImageTag,(MagickOffsetType) i,
13668               (MagickSizeType) number_files);
13669             if (proceed == MagickFalse)
13670               break;
13671           }
13672       }
13673   }
13674   filelist=(char **) RelinquishMagickMemory(filelist);
13675   if (images == (Image *) NULL)
13676     {
13677       read_info=DestroyImageInfo(read_info);
13678       XSetCursorState(display,windows,MagickFalse);
13679       ThrowXWindowFatalException(ImageError,"NoImagesWereLoaded",filenames);
13680       return((Image *) NULL);
13681     }
13682   /*
13683     Create the Visual Image Directory.
13684   */
13685   montage_info=CloneMontageInfo(read_info,(MontageInfo *) NULL);
13686   montage_info->pointsize=10;
13687   if (resource_info->font != (char *) NULL)
13688     (void) CloneString(&montage_info->font,resource_info->font);
13689   (void) CopyMagickString(montage_info->filename,filename,MaxTextExtent);
13690   montage_image=MontageImageList(read_info,montage_info,GetFirstImageInList(
13691     images),exception);
13692   images=DestroyImageList(images);
13693   montage_info=DestroyMontageInfo(montage_info);
13694   read_info=DestroyImageInfo(read_info);
13695   XSetCursorState(display,windows,MagickFalse);
13696   if (montage_image == (Image *) NULL)
13697     return(montage_image);
13698   XClientMessage(display,windows->image.id,windows->im_protocols,
13699     windows->im_next_image,CurrentTime);
13700   return(montage_image);
13701 }
13702 \f
13703 /*
13704 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13705 %                                                                             %
13706 %                                                                             %
13707 %                                                                             %
13708 %   X D i s p l a y B a c k g r o u n d I m a g e                             %
13709 %                                                                             %
13710 %                                                                             %
13711 %                                                                             %
13712 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13713 %
13714 %  XDisplayBackgroundImage() displays an image in the background of a window.
13715 %
13716 %  The format of the XDisplayBackgroundImage method is:
13717 %
13718 %      MagickBooleanType XDisplayBackgroundImage(Display *display,
13719 %        XResourceInfo *resource_info,Image *image,ExceptionInfo *exception)
13720 %
13721 %  A description of each parameter follows:
13722 %
13723 %    o display: Specifies a connection to an X server;  returned from
13724 %      XOpenDisplay.
13725 %
13726 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13727 %
13728 %    o image: the image.
13729 %
13730 %    o exception: return any errors or warnings in this structure.
13731 %
13732 */
13733 MagickExport MagickBooleanType XDisplayBackgroundImage(Display *display,
13734   XResourceInfo *resource_info,Image *image,ExceptionInfo *exception)
13735 {
13736   char
13737     geometry[MaxTextExtent],
13738     visual_type[MaxTextExtent];
13739
13740   int
13741     height,
13742     status,
13743     width;
13744
13745   RectangleInfo
13746     geometry_info;
13747
13748   static XPixelInfo
13749     pixel;
13750
13751   static XStandardColormap
13752     *map_info;
13753
13754   static XVisualInfo
13755     *visual_info = (XVisualInfo *) NULL;
13756
13757   static XWindowInfo
13758     window_info;
13759
13760   size_t
13761     delay;
13762
13763   Window
13764     root_window;
13765
13766   XGCValues
13767     context_values;
13768
13769   XResourceInfo
13770     resources;
13771
13772   XWindowAttributes
13773     window_attributes;
13774
13775   /*
13776     Determine target window.
13777   */
13778   assert(image != (Image *) NULL);
13779   assert(image->signature == MagickSignature);
13780   if (image->debug != MagickFalse)
13781     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
13782   resources=(*resource_info);
13783   window_info.id=(Window) NULL;
13784   root_window=XRootWindow(display,XDefaultScreen(display));
13785   if (LocaleCompare(resources.window_id,"root") == 0)
13786     window_info.id=root_window;
13787   else
13788     {
13789       if (isdigit((unsigned char) *resources.window_id) != 0)
13790         window_info.id=XWindowByID(display,root_window,
13791           (Window) strtol((char *) resources.window_id,(char **) NULL,0));
13792       if (window_info.id == (Window) NULL)
13793         window_info.id=XWindowByName(display,root_window,resources.window_id);
13794     }
13795   if (window_info.id == (Window) NULL)
13796     {
13797       ThrowXWindowFatalException(XServerError,"NoWindowWithSpecifiedIDExists",
13798         resources.window_id);
13799       return(MagickFalse);
13800     }
13801   /*
13802     Determine window visual id.
13803   */
13804   window_attributes.width=XDisplayWidth(display,XDefaultScreen(display));
13805   window_attributes.height=XDisplayHeight(display,XDefaultScreen(display));
13806   (void) CopyMagickString(visual_type,"default",MaxTextExtent);
13807   status=XGetWindowAttributes(display,window_info.id,&window_attributes);
13808   if (status != 0)
13809     (void) FormatLocaleString(visual_type,MaxTextExtent,"0x%lx",
13810       XVisualIDFromVisual(window_attributes.visual));
13811   if (visual_info == (XVisualInfo *) NULL)
13812     {
13813       /*
13814         Allocate standard colormap.
13815       */
13816       map_info=XAllocStandardColormap();
13817       if (map_info == (XStandardColormap *) NULL)
13818         ThrowXWindowFatalException(XServerFatalError,"MemoryAllocationFailed",
13819           image->filename);
13820       map_info->colormap=(Colormap) NULL;
13821       pixel.pixels=(unsigned long *) NULL;
13822       /*
13823         Initialize visual info.
13824       */
13825       resources.map_type=(char *) NULL;
13826       resources.visual_type=visual_type;
13827       visual_info=XBestVisualInfo(display,map_info,&resources);
13828       if (visual_info == (XVisualInfo *) NULL)
13829         ThrowXWindowFatalException(XServerFatalError,"UnableToGetVisual",
13830           resources.visual_type);
13831       /*
13832         Initialize window info.
13833       */
13834       window_info.ximage=(XImage *) NULL;
13835       window_info.matte_image=(XImage *) NULL;
13836       window_info.pixmap=(Pixmap) NULL;
13837       window_info.matte_pixmap=(Pixmap) NULL;
13838     }
13839   /*
13840     Free previous root colors.
13841   */
13842   if (window_info.id == root_window)
13843     (void) XDestroyWindowColors(display,root_window);
13844   /*
13845     Initialize Standard Colormap.
13846   */
13847   resources.colormap=SharedColormap;
13848   XMakeStandardColormap(display,visual_info,&resources,image,map_info,&pixel);
13849   /*
13850     Graphic context superclass.
13851   */
13852   context_values.background=pixel.background_color.pixel;
13853   context_values.foreground=pixel.foreground_color.pixel;
13854   pixel.annotate_context=XCreateGC(display,window_info.id,
13855     (size_t) (GCBackground | GCForeground),&context_values);
13856   if (pixel.annotate_context == (GC) NULL)
13857     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
13858       image->filename);
13859   /*
13860     Initialize Image window attributes.
13861   */
13862   window_info.name=AcquireString("\0");
13863   window_info.icon_name=AcquireString("\0");
13864   XGetWindowInfo(display,visual_info,map_info,&pixel,(XFontStruct *) NULL,
13865     &resources,&window_info);
13866   /*
13867     Create the X image.
13868   */
13869   window_info.width=(unsigned int) image->columns;
13870   window_info.height=(unsigned int) image->rows;
13871   if ((image->columns != window_info.width) ||
13872       (image->rows != window_info.height))
13873     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13874       image->filename);
13875   (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>",
13876     window_attributes.width,window_attributes.height);
13877   geometry_info.width=window_info.width;
13878   geometry_info.height=window_info.height;
13879   geometry_info.x=(ssize_t) window_info.x;
13880   geometry_info.y=(ssize_t) window_info.y;
13881   (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
13882     &geometry_info.width,&geometry_info.height);
13883   window_info.width=(unsigned int) geometry_info.width;
13884   window_info.height=(unsigned int) geometry_info.height;
13885   window_info.x=(int) geometry_info.x;
13886   window_info.y=(int) geometry_info.y;
13887   status=XMakeImage(display,&resources,&window_info,image,window_info.width,
13888     window_info.height,exception);
13889   if (status == MagickFalse)
13890     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13891       image->filename);
13892   window_info.x=0;
13893   window_info.y=0;
13894   if (image->debug != MagickFalse)
13895     {
13896       (void) LogMagickEvent(X11Event,GetMagickModule(),
13897         "Image: %s[%.20g] %.20gx%.20g ",image->filename,(double) image->scene,
13898         (double) image->columns,(double) image->rows);
13899       if (image->colors != 0)
13900         (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
13901           image->colors);
13902       (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",image->magick);
13903     }
13904   /*
13905     Adjust image dimensions as specified by backdrop or geometry options.
13906   */
13907   width=(int) window_info.width;
13908   height=(int) window_info.height;
13909   if (resources.backdrop != MagickFalse)
13910     {
13911       /*
13912         Center image on window.
13913       */
13914       window_info.x=(window_attributes.width/2)-
13915         (window_info.ximage->width/2);
13916       window_info.y=(window_attributes.height/2)-
13917         (window_info.ximage->height/2);
13918       width=window_attributes.width;
13919       height=window_attributes.height;
13920     }
13921   if ((resources.image_geometry != (char *) NULL) &&
13922       (*resources.image_geometry != '\0'))
13923     {
13924       char
13925         default_geometry[MaxTextExtent];
13926
13927       int
13928         flags,
13929         gravity;
13930
13931       XSizeHints
13932         *size_hints;
13933
13934       /*
13935         User specified geometry.
13936       */
13937       size_hints=XAllocSizeHints();
13938       if (size_hints == (XSizeHints *) NULL)
13939         ThrowXWindowFatalException(ResourceLimitFatalError,
13940           "MemoryAllocationFailed",image->filename);
13941       size_hints->flags=0L;
13942       (void) FormatLocaleString(default_geometry,MaxTextExtent,"%dx%d",
13943         width,height);
13944       flags=XWMGeometry(display,visual_info->screen,resources.image_geometry,
13945         default_geometry,window_info.border_width,size_hints,&window_info.x,
13946         &window_info.y,&width,&height,&gravity);
13947       if (flags & (XValue | YValue))
13948         {
13949           width=window_attributes.width;
13950           height=window_attributes.height;
13951         }
13952       (void) XFree((void *) size_hints);
13953     }
13954   /*
13955     Create the X pixmap.
13956   */
13957   window_info.pixmap=XCreatePixmap(display,window_info.id,(unsigned int) width,
13958     (unsigned int) height,window_info.depth);
13959   if (window_info.pixmap == (Pixmap) NULL)
13960     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXPixmap",
13961       image->filename);
13962   /*
13963     Display pixmap on the window.
13964   */
13965   if (((unsigned int) width > window_info.width) ||
13966       ((unsigned int) height > window_info.height))
13967     (void) XFillRectangle(display,window_info.pixmap,
13968       window_info.annotate_context,0,0,(unsigned int) width,
13969       (unsigned int) height);
13970   (void) XPutImage(display,window_info.pixmap,window_info.annotate_context,
13971     window_info.ximage,0,0,window_info.x,window_info.y,(unsigned int)
13972     window_info.width,(unsigned int) window_info.height);
13973   (void) XSetWindowBackgroundPixmap(display,window_info.id,window_info.pixmap);
13974   (void) XClearWindow(display,window_info.id);
13975   delay=1000*image->delay/MagickMax(image->ticks_per_second,1L);
13976   XDelay(display,delay == 0UL ? 10UL : delay);
13977   (void) XSync(display,MagickFalse);
13978   return(window_info.id == root_window ? MagickTrue : MagickFalse);
13979 }
13980 \f
13981 /*
13982 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13983 %                                                                             %
13984 %                                                                             %
13985 %                                                                             %
13986 +   X D i s p l a y I m a g e                                                 %
13987 %                                                                             %
13988 %                                                                             %
13989 %                                                                             %
13990 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13991 %
13992 %  XDisplayImage() displays an image via X11.  A new image is created and
13993 %  returned if the user interactively transforms the displayed image.
13994 %
13995 %  The format of the XDisplayImage method is:
13996 %
13997 %      Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
13998 %        char **argv,int argc,Image **image,size_t *state,
13999 %        ExceptionInfo *exception)
14000 %
14001 %  A description of each parameter follows:
14002 %
14003 %    o nexus:  Method XDisplayImage returns an image when the
14004 %      user chooses 'Open Image' from the command menu or picks a tile
14005 %      from the image directory.  Otherwise a null image is returned.
14006 %
14007 %    o display: Specifies a connection to an X server;  returned from
14008 %      XOpenDisplay.
14009 %
14010 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
14011 %
14012 %    o argv: Specifies the application's argument list.
14013 %
14014 %    o argc: Specifies the number of arguments.
14015 %
14016 %    o image: Specifies an address to an address of an Image structure;
14017 %
14018 %    o exception: return any errors or warnings in this structure.
14019 %
14020 */
14021 MagickExport Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
14022   char **argv,int argc,Image **image,size_t *state,ExceptionInfo *exception)
14023 {
14024 #define MagnifySize  256  /* must be a power of 2 */
14025 #define MagickMenus  10
14026 #define MagickTitle  "Commands"
14027
14028   static const char
14029     *CommandMenu[] =
14030     {
14031       "File",
14032       "Edit",
14033       "View",
14034       "Transform",
14035       "Enhance",
14036       "Effects",
14037       "F/X",
14038       "Image Edit",
14039       "Miscellany",
14040       "Help",
14041       (char *) NULL
14042     },
14043     *FileMenu[] =
14044     {
14045       "Open...",
14046       "Next",
14047       "Former",
14048       "Select...",
14049       "Save...",
14050       "Print...",
14051       "Delete...",
14052       "New...",
14053       "Visual Directory...",
14054       "Quit",
14055       (char *) NULL
14056     },
14057     *EditMenu[] =
14058     {
14059       "Undo",
14060       "Redo",
14061       "Cut",
14062       "Copy",
14063       "Paste",
14064       (char *) NULL
14065     },
14066     *ViewMenu[] =
14067     {
14068       "Half Size",
14069       "Original Size",
14070       "Double Size",
14071       "Resize...",
14072       "Apply",
14073       "Refresh",
14074       "Restore",
14075       (char *) NULL
14076     },
14077     *TransformMenu[] =
14078     {
14079       "Crop",
14080       "Chop",
14081       "Flop",
14082       "Flip",
14083       "Rotate Right",
14084       "Rotate Left",
14085       "Rotate...",
14086       "Shear...",
14087       "Roll...",
14088       "Trim Edges",
14089       (char *) NULL
14090     },
14091     *EnhanceMenu[] =
14092     {
14093       "Hue...",
14094       "Saturation...",
14095       "Brightness...",
14096       "Gamma...",
14097       "Spiff",
14098       "Dull",
14099       "Contrast Stretch...",
14100       "Sigmoidal Contrast...",
14101       "Normalize",
14102       "Equalize",
14103       "Negate",
14104       "Grayscale",
14105       "Map...",
14106       "Quantize...",
14107       (char *) NULL
14108     },
14109     *EffectsMenu[] =
14110     {
14111       "Despeckle",
14112       "Emboss",
14113       "Reduce Noise",
14114       "Add Noise...",
14115       "Sharpen...",
14116       "Blur...",
14117       "Threshold...",
14118       "Edge Detect...",
14119       "Spread...",
14120       "Shade...",
14121       "Raise...",
14122       "Segment...",
14123       (char *) NULL
14124     },
14125     *FXMenu[] =
14126     {
14127       "Solarize...",
14128       "Sepia Tone...",
14129       "Swirl...",
14130       "Implode...",
14131       "Vignette...",
14132       "Wave...",
14133       "Oil Paint...",
14134       "Charcoal Draw...",
14135       (char *) NULL
14136     },
14137     *ImageEditMenu[] =
14138     {
14139       "Annotate...",
14140       "Draw...",
14141       "Color...",
14142       "Matte...",
14143       "Composite...",
14144       "Add Border...",
14145       "Add Frame...",
14146       "Comment...",
14147       "Launch...",
14148       "Region of Interest...",
14149       (char *) NULL
14150     },
14151     *MiscellanyMenu[] =
14152     {
14153       "Image Info",
14154       "Zoom Image",
14155       "Show Preview...",
14156       "Show Histogram",
14157       "Show Matte",
14158       "Background...",
14159       "Slide Show...",
14160       "Preferences...",
14161       (char *) NULL
14162     },
14163     *HelpMenu[] =
14164     {
14165       "Overview",
14166       "Browse Documentation",
14167       "About Display",
14168       (char *) NULL
14169     },
14170     *ShortCutsMenu[] =
14171     {
14172       "Next",
14173       "Former",
14174       "Open...",
14175       "Save...",
14176       "Print...",
14177       "Undo",
14178       "Restore",
14179       "Image Info",
14180       "Quit",
14181       (char *) NULL
14182     },
14183     *VirtualMenu[] =
14184     {
14185       "Image Info",
14186       "Print",
14187       "Next",
14188       "Quit",
14189       (char *) NULL
14190     };
14191
14192   static const char
14193     **Menus[MagickMenus] =
14194     {
14195       FileMenu,
14196       EditMenu,
14197       ViewMenu,
14198       TransformMenu,
14199       EnhanceMenu,
14200       EffectsMenu,
14201       FXMenu,
14202       ImageEditMenu,
14203       MiscellanyMenu,
14204       HelpMenu
14205     };
14206
14207   static CommandType
14208     CommandMenus[] =
14209     {
14210       NullCommand,
14211       NullCommand,
14212       NullCommand,
14213       NullCommand,
14214       NullCommand,
14215       NullCommand,
14216       NullCommand,
14217       NullCommand,
14218       NullCommand,
14219       NullCommand,
14220     },
14221     FileCommands[] =
14222     {
14223       OpenCommand,
14224       NextCommand,
14225       FormerCommand,
14226       SelectCommand,
14227       SaveCommand,
14228       PrintCommand,
14229       DeleteCommand,
14230       NewCommand,
14231       VisualDirectoryCommand,
14232       QuitCommand
14233     },
14234     EditCommands[] =
14235     {
14236       UndoCommand,
14237       RedoCommand,
14238       CutCommand,
14239       CopyCommand,
14240       PasteCommand
14241     },
14242     ViewCommands[] =
14243     {
14244       HalfSizeCommand,
14245       OriginalSizeCommand,
14246       DoubleSizeCommand,
14247       ResizeCommand,
14248       ApplyCommand,
14249       RefreshCommand,
14250       RestoreCommand
14251     },
14252     TransformCommands[] =
14253     {
14254       CropCommand,
14255       ChopCommand,
14256       FlopCommand,
14257       FlipCommand,
14258       RotateRightCommand,
14259       RotateLeftCommand,
14260       RotateCommand,
14261       ShearCommand,
14262       RollCommand,
14263       TrimCommand
14264     },
14265     EnhanceCommands[] =
14266     {
14267       HueCommand,
14268       SaturationCommand,
14269       BrightnessCommand,
14270       GammaCommand,
14271       SpiffCommand,
14272       DullCommand,
14273       ContrastStretchCommand,
14274       SigmoidalContrastCommand,
14275       NormalizeCommand,
14276       EqualizeCommand,
14277       NegateCommand,
14278       GrayscaleCommand,
14279       MapCommand,
14280       QuantizeCommand
14281     },
14282     EffectsCommands[] =
14283     {
14284       DespeckleCommand,
14285       EmbossCommand,
14286       ReduceNoiseCommand,
14287       AddNoiseCommand,
14288       SharpenCommand,
14289       BlurCommand,
14290       ThresholdCommand,
14291       EdgeDetectCommand,
14292       SpreadCommand,
14293       ShadeCommand,
14294       RaiseCommand,
14295       SegmentCommand
14296     },
14297     FXCommands[] =
14298     {
14299       SolarizeCommand,
14300       SepiaToneCommand,
14301       SwirlCommand,
14302       ImplodeCommand,
14303       VignetteCommand,
14304       WaveCommand,
14305       OilPaintCommand,
14306       CharcoalDrawCommand
14307     },
14308     ImageEditCommands[] =
14309     {
14310       AnnotateCommand,
14311       DrawCommand,
14312       ColorCommand,
14313       MatteCommand,
14314       CompositeCommand,
14315       AddBorderCommand,
14316       AddFrameCommand,
14317       CommentCommand,
14318       LaunchCommand,
14319       RegionofInterestCommand
14320     },
14321     MiscellanyCommands[] =
14322     {
14323       InfoCommand,
14324       ZoomCommand,
14325       ShowPreviewCommand,
14326       ShowHistogramCommand,
14327       ShowMatteCommand,
14328       BackgroundCommand,
14329       SlideShowCommand,
14330       PreferencesCommand
14331     },
14332     HelpCommands[] =
14333     {
14334       HelpCommand,
14335       BrowseDocumentationCommand,
14336       VersionCommand
14337     },
14338     ShortCutsCommands[] =
14339     {
14340       NextCommand,
14341       FormerCommand,
14342       OpenCommand,
14343       SaveCommand,
14344       PrintCommand,
14345       UndoCommand,
14346       RestoreCommand,
14347       InfoCommand,
14348       QuitCommand
14349     },
14350     VirtualCommands[] =
14351     {
14352       InfoCommand,
14353       PrintCommand,
14354       NextCommand,
14355       QuitCommand
14356     };
14357
14358   static CommandType
14359     *Commands[MagickMenus] =
14360     {
14361       FileCommands,
14362       EditCommands,
14363       ViewCommands,
14364       TransformCommands,
14365       EnhanceCommands,
14366       EffectsCommands,
14367       FXCommands,
14368       ImageEditCommands,
14369       MiscellanyCommands,
14370       HelpCommands
14371     };
14372
14373   char
14374     command[MaxTextExtent],
14375     *directory,
14376     geometry[MaxTextExtent],
14377     resource_name[MaxTextExtent];
14378
14379   CommandType
14380     command_type;
14381
14382   Image
14383     *display_image,
14384     *nexus;
14385
14386   int
14387     entry,
14388     id;
14389
14390   KeySym
14391     key_symbol;
14392
14393   MagickStatusType
14394     context_mask,
14395     status;
14396
14397   RectangleInfo
14398     geometry_info;
14399
14400   register int
14401     i;
14402
14403   static char
14404     working_directory[MaxTextExtent];
14405
14406   static XPoint
14407     vid_info;
14408
14409   static XWindowInfo
14410     *magick_windows[MaxXWindows];
14411
14412   static unsigned int
14413     number_windows;
14414
14415   struct stat
14416     attributes;
14417
14418   time_t
14419     timer,
14420     timestamp,
14421     update_time;
14422
14423   unsigned int
14424     height,
14425     width;
14426
14427   size_t
14428     delay;
14429
14430   WarningHandler
14431     warning_handler;
14432
14433   Window
14434     root_window;
14435
14436   XClassHint
14437     *class_hints;
14438
14439   XEvent
14440     event;
14441
14442   XFontStruct
14443     *font_info;
14444
14445   XGCValues
14446     context_values;
14447
14448   XPixelInfo
14449     *icon_pixel,
14450     *pixel;
14451
14452   XResourceInfo
14453     *icon_resources;
14454
14455   XStandardColormap
14456     *icon_map,
14457     *map_info;
14458
14459   XVisualInfo
14460     *icon_visual,
14461     *visual_info;
14462
14463   XWindowChanges
14464     window_changes;
14465
14466   XWindows
14467     *windows;
14468
14469   XWMHints
14470     *manager_hints;
14471
14472   assert(image != (Image **) NULL);
14473   assert((*image)->signature == MagickSignature);
14474   if ((*image)->debug != MagickFalse)
14475     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*image)->filename);
14476   display_image=(*image);
14477   warning_handler=(WarningHandler) NULL;
14478   windows=XSetWindows((XWindows *) ~0);
14479   if (windows != (XWindows *) NULL)
14480     {
14481       int
14482         status;
14483
14484       status=chdir(working_directory);
14485       if (status == -1)
14486         (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
14487           "UnableToOpenFile","%s",working_directory);
14488       warning_handler=resource_info->display_warnings ?
14489         SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
14490       warning_handler=resource_info->display_warnings ?
14491         SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
14492     }
14493   else
14494     {
14495       /*
14496         Allocate windows structure.
14497       */
14498       resource_info->colors=display_image->colors;
14499       windows=XSetWindows(XInitializeWindows(display,resource_info));
14500       if (windows == (XWindows *) NULL)
14501         ThrowXWindowFatalException(XServerFatalError,"UnableToCreateWindow",
14502           (*image)->filename);
14503       /*
14504         Initialize window id's.
14505       */
14506       number_windows=0;
14507       magick_windows[number_windows++]=(&windows->icon);
14508       magick_windows[number_windows++]=(&windows->backdrop);
14509       magick_windows[number_windows++]=(&windows->image);
14510       magick_windows[number_windows++]=(&windows->info);
14511       magick_windows[number_windows++]=(&windows->command);
14512       magick_windows[number_windows++]=(&windows->widget);
14513       magick_windows[number_windows++]=(&windows->popup);
14514       magick_windows[number_windows++]=(&windows->magnify);
14515       magick_windows[number_windows++]=(&windows->pan);
14516       for (i=0; i < (int) number_windows; i++)
14517         magick_windows[i]->id=(Window) NULL;
14518       vid_info.x=0;
14519       vid_info.y=0;
14520     }
14521   /*
14522     Initialize font info.
14523   */
14524   if (windows->font_info != (XFontStruct *) NULL)
14525     (void) XFreeFont(display,windows->font_info);
14526   windows->font_info=XBestFont(display,resource_info,MagickFalse);
14527   if (windows->font_info == (XFontStruct *) NULL)
14528     ThrowXWindowFatalException(XServerFatalError,"UnableToLoadFont",
14529       resource_info->font);
14530   /*
14531     Initialize Standard Colormap.
14532   */
14533   map_info=windows->map_info;
14534   icon_map=windows->icon_map;
14535   visual_info=windows->visual_info;
14536   icon_visual=windows->icon_visual;
14537   pixel=windows->pixel_info;
14538   icon_pixel=windows->icon_pixel;
14539   font_info=windows->font_info;
14540   icon_resources=windows->icon_resources;
14541   class_hints=windows->class_hints;
14542   manager_hints=windows->manager_hints;
14543   root_window=XRootWindow(display,visual_info->screen);
14544   nexus=NewImageList();
14545   if (display_image->debug != MagickFalse)
14546     {
14547       (void) LogMagickEvent(X11Event,GetMagickModule(),
14548         "Image: %s[%.20g] %.20gx%.20g ",display_image->filename,
14549         (double) display_image->scene,(double) display_image->columns,
14550         (double) display_image->rows);
14551       if (display_image->colors != 0)
14552         (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
14553           display_image->colors);
14554       (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",
14555         display_image->magick);
14556     }
14557   XMakeStandardColormap(display,visual_info,resource_info,display_image,
14558     map_info,pixel);
14559   display_image->taint=MagickFalse;
14560   /*
14561     Initialize graphic context.
14562   */
14563   windows->context.id=(Window) NULL;
14564   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14565     resource_info,&windows->context);
14566   (void) CloneString(&class_hints->res_name,resource_info->client_name);
14567   (void) CloneString(&class_hints->res_class,resource_info->client_name);
14568   class_hints->res_class[0]=(char) toupper((int) class_hints->res_class[0]);
14569   manager_hints->flags=InputHint | StateHint;
14570   manager_hints->input=MagickFalse;
14571   manager_hints->initial_state=WithdrawnState;
14572   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14573     &windows->context);
14574   if (display_image->debug != MagickFalse)
14575     (void) LogMagickEvent(X11Event,GetMagickModule(),
14576       "Window id: 0x%lx (context)",windows->context.id);
14577   context_values.background=pixel->background_color.pixel;
14578   context_values.font=font_info->fid;
14579   context_values.foreground=pixel->foreground_color.pixel;
14580   context_values.graphics_exposures=MagickFalse;
14581   context_mask=(MagickStatusType)
14582     (GCBackground | GCFont | GCForeground | GCGraphicsExposures);
14583   if (pixel->annotate_context != (GC) NULL)
14584     (void) XFreeGC(display,pixel->annotate_context);
14585   pixel->annotate_context=XCreateGC(display,windows->context.id,
14586     context_mask,&context_values);
14587   if (pixel->annotate_context == (GC) NULL)
14588     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14589       display_image->filename);
14590   context_values.background=pixel->depth_color.pixel;
14591   if (pixel->widget_context != (GC) NULL)
14592     (void) XFreeGC(display,pixel->widget_context);
14593   pixel->widget_context=XCreateGC(display,windows->context.id,context_mask,
14594     &context_values);
14595   if (pixel->widget_context == (GC) NULL)
14596     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14597       display_image->filename);
14598   context_values.background=pixel->foreground_color.pixel;
14599   context_values.foreground=pixel->background_color.pixel;
14600   context_values.plane_mask=context_values.background ^
14601     context_values.foreground;
14602   if (pixel->highlight_context != (GC) NULL)
14603     (void) XFreeGC(display,pixel->highlight_context);
14604   pixel->highlight_context=XCreateGC(display,windows->context.id,
14605     (size_t) (context_mask | GCPlaneMask),&context_values);
14606   if (pixel->highlight_context == (GC) NULL)
14607     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14608       display_image->filename);
14609   (void) XDestroyWindow(display,windows->context.id);
14610   /*
14611     Initialize icon window.
14612   */
14613   XGetWindowInfo(display,icon_visual,icon_map,icon_pixel,(XFontStruct *) NULL,
14614     icon_resources,&windows->icon);
14615   windows->icon.geometry=resource_info->icon_geometry;
14616   XBestIconSize(display,&windows->icon,display_image);
14617   windows->icon.attributes.colormap=XDefaultColormap(display,
14618     icon_visual->screen);
14619   windows->icon.attributes.event_mask=ExposureMask | StructureNotifyMask;
14620   manager_hints->flags=InputHint | StateHint;
14621   manager_hints->input=MagickFalse;
14622   manager_hints->initial_state=IconicState;
14623   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14624     &windows->icon);
14625   if (display_image->debug != MagickFalse)
14626     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (icon)",
14627       windows->icon.id);
14628   /*
14629     Initialize graphic context for icon window.
14630   */
14631   if (icon_pixel->annotate_context != (GC) NULL)
14632     (void) XFreeGC(display,icon_pixel->annotate_context);
14633   context_values.background=icon_pixel->background_color.pixel;
14634   context_values.foreground=icon_pixel->foreground_color.pixel;
14635   icon_pixel->annotate_context=XCreateGC(display,windows->icon.id,
14636     (size_t) (GCBackground | GCForeground),&context_values);
14637   if (icon_pixel->annotate_context == (GC) NULL)
14638     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14639       display_image->filename);
14640   windows->icon.annotate_context=icon_pixel->annotate_context;
14641   /*
14642     Initialize Image window.
14643   */
14644   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14645     &windows->image);
14646   windows->image.shape=MagickTrue;  /* non-rectangular shape hint */
14647   if (resource_info->use_shared_memory == MagickFalse)
14648     windows->image.shared_memory=MagickFalse;
14649   if ((resource_info->title != (char *) NULL) && !(*state & MontageImageState))
14650     {
14651       char
14652         *title;
14653
14654       title=InterpretImageProperties(resource_info->image_info,display_image,
14655         resource_info->title);
14656       (void) CopyMagickString(windows->image.name,title,MaxTextExtent);
14657       (void) CopyMagickString(windows->image.icon_name,title,MaxTextExtent);
14658       title=DestroyString(title);
14659     }
14660   else
14661     {
14662       char
14663         filename[MaxTextExtent];
14664
14665       /*
14666         Window name is the base of the filename.
14667       */
14668       GetPathComponent(display_image->magick_filename,TailPath,filename);
14669       if (GetImageListLength(display_image) == 1)
14670         (void) FormatLocaleString(windows->image.name,MaxTextExtent,
14671           "%s: %s",MagickPackageName,filename);
14672       else
14673         (void) FormatLocaleString(windows->image.name,MaxTextExtent,
14674           "%s: %s[scene: %.20g frames: %.20g]",MagickPackageName,filename,
14675           (double) display_image->scene,(double) GetImageListLength(
14676           display_image));
14677       (void) CopyMagickString(windows->image.icon_name,filename,MaxTextExtent);
14678     }
14679   if (resource_info->immutable)
14680     windows->image.immutable=MagickTrue;
14681   windows->image.use_pixmap=resource_info->use_pixmap;
14682   windows->image.geometry=resource_info->image_geometry;
14683   (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>!",
14684     XDisplayWidth(display,visual_info->screen),
14685     XDisplayHeight(display,visual_info->screen));
14686   geometry_info.width=display_image->columns;
14687   geometry_info.height=display_image->rows;
14688   geometry_info.x=0;
14689   geometry_info.y=0;
14690   (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
14691     &geometry_info.width,&geometry_info.height);
14692   windows->image.width=(unsigned int) geometry_info.width;
14693   windows->image.height=(unsigned int) geometry_info.height;
14694   windows->image.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14695     ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14696     KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14697     PropertyChangeMask | StructureNotifyMask | SubstructureNotifyMask;
14698   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14699     resource_info,&windows->backdrop);
14700   if ((resource_info->backdrop) || (windows->backdrop.id != (Window) NULL))
14701     {
14702       /*
14703         Initialize backdrop window.
14704       */
14705       windows->backdrop.x=0;
14706       windows->backdrop.y=0;
14707       (void) CloneString(&windows->backdrop.name,"Backdrop");
14708       windows->backdrop.flags=(size_t) (USSize | USPosition);
14709       windows->backdrop.width=(unsigned int)
14710         XDisplayWidth(display,visual_info->screen);
14711       windows->backdrop.height=(unsigned int)
14712         XDisplayHeight(display,visual_info->screen);
14713       windows->backdrop.border_width=0;
14714       windows->backdrop.immutable=MagickTrue;
14715       windows->backdrop.attributes.do_not_propagate_mask=ButtonPressMask |
14716         ButtonReleaseMask;
14717       windows->backdrop.attributes.event_mask=ButtonPressMask | KeyPressMask |
14718         StructureNotifyMask;
14719       manager_hints->flags=IconWindowHint | InputHint | StateHint;
14720       manager_hints->icon_window=windows->icon.id;
14721       manager_hints->input=MagickTrue;
14722       manager_hints->initial_state=resource_info->iconic ? IconicState :
14723         NormalState;
14724       XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14725         &windows->backdrop);
14726       if (display_image->debug != MagickFalse)
14727         (void) LogMagickEvent(X11Event,GetMagickModule(),
14728           "Window id: 0x%lx (backdrop)",windows->backdrop.id);
14729       (void) XMapWindow(display,windows->backdrop.id);
14730       (void) XClearWindow(display,windows->backdrop.id);
14731       if (windows->image.id != (Window) NULL)
14732         {
14733           (void) XDestroyWindow(display,windows->image.id);
14734           windows->image.id=(Window) NULL;
14735         }
14736       /*
14737         Position image in the center the backdrop.
14738       */
14739       windows->image.flags|=USPosition;
14740       windows->image.x=(XDisplayWidth(display,visual_info->screen)/2)-
14741         (windows->image.width/2);
14742       windows->image.y=(XDisplayHeight(display,visual_info->screen)/2)-
14743         (windows->image.height/2);
14744     }
14745   manager_hints->flags=IconWindowHint | InputHint | StateHint;
14746   manager_hints->icon_window=windows->icon.id;
14747   manager_hints->input=MagickTrue;
14748   manager_hints->initial_state=resource_info->iconic ? IconicState :
14749     NormalState;
14750   if (windows->group_leader.id != (Window) NULL)
14751     {
14752       /*
14753         Follow the leader.
14754       */
14755       manager_hints->flags|=WindowGroupHint;
14756       manager_hints->window_group=windows->group_leader.id;
14757       (void) XSelectInput(display,windows->group_leader.id,StructureNotifyMask);
14758       if (display_image->debug != MagickFalse)
14759         (void) LogMagickEvent(X11Event,GetMagickModule(),
14760           "Window id: 0x%lx (group leader)",windows->group_leader.id);
14761     }
14762   XMakeWindow(display,
14763     (Window) (resource_info->backdrop ? windows->backdrop.id : root_window),
14764     argv,argc,class_hints,manager_hints,&windows->image);
14765   (void) XChangeProperty(display,windows->image.id,windows->im_protocols,
14766     XA_STRING,8,PropModeReplace,(unsigned char *) NULL,0);
14767   if (windows->group_leader.id != (Window) NULL)
14768     (void) XSetTransientForHint(display,windows->image.id,
14769       windows->group_leader.id);
14770   if (display_image->debug != MagickFalse)
14771     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (image)",
14772       windows->image.id);
14773   /*
14774     Initialize Info widget.
14775   */
14776   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14777     &windows->info);
14778   (void) CloneString(&windows->info.name,"Info");
14779   (void) CloneString(&windows->info.icon_name,"Info");
14780   windows->info.border_width=1;
14781   windows->info.x=2;
14782   windows->info.y=2;
14783   windows->info.flags|=PPosition;
14784   windows->info.attributes.win_gravity=UnmapGravity;
14785   windows->info.attributes.event_mask=ButtonPressMask | ExposureMask |
14786     StructureNotifyMask;
14787   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14788   manager_hints->input=MagickFalse;
14789   manager_hints->initial_state=NormalState;
14790   manager_hints->window_group=windows->image.id;
14791   XMakeWindow(display,windows->image.id,argv,argc,class_hints,manager_hints,
14792     &windows->info);
14793   windows->info.highlight_stipple=XCreateBitmapFromData(display,
14794     windows->info.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14795   windows->info.shadow_stipple=XCreateBitmapFromData(display,
14796     windows->info.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14797   (void) XSetTransientForHint(display,windows->info.id,windows->image.id);
14798   if (windows->image.mapped != MagickFalse)
14799     (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14800   if (display_image->debug != MagickFalse)
14801     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (info)",
14802       windows->info.id);
14803   /*
14804     Initialize Command widget.
14805   */
14806   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14807     resource_info,&windows->command);
14808   windows->command.data=MagickMenus;
14809   (void) XCommandWidget(display,windows,CommandMenu,(XEvent *) NULL);
14810   (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.command",
14811     resource_info->client_name);
14812   windows->command.geometry=XGetResourceClass(resource_info->resource_database,
14813     resource_name,"geometry",(char *) NULL);
14814   (void) CloneString(&windows->command.name,MagickTitle);
14815   windows->command.border_width=0;
14816   windows->command.flags|=PPosition;
14817   windows->command.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14818     ButtonReleaseMask | EnterWindowMask | ExposureMask | LeaveWindowMask |
14819     OwnerGrabButtonMask | StructureNotifyMask;
14820   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14821   manager_hints->input=MagickTrue;
14822   manager_hints->initial_state=NormalState;
14823   manager_hints->window_group=windows->image.id;
14824   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14825     &windows->command);
14826   windows->command.highlight_stipple=XCreateBitmapFromData(display,
14827     windows->command.id,(char *) HighlightBitmap,HighlightWidth,
14828     HighlightHeight);
14829   windows->command.shadow_stipple=XCreateBitmapFromData(display,
14830     windows->command.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14831   (void) XSetTransientForHint(display,windows->command.id,windows->image.id);
14832   if (windows->command.mapped != MagickFalse)
14833     (void) XMapRaised(display,windows->command.id);
14834   if (display_image->debug != MagickFalse)
14835     (void) LogMagickEvent(X11Event,GetMagickModule(),
14836       "Window id: 0x%lx (command)",windows->command.id);
14837   /*
14838     Initialize Widget window.
14839   */
14840   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14841     resource_info,&windows->widget);
14842   (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.widget",
14843     resource_info->client_name);
14844   windows->widget.geometry=XGetResourceClass(resource_info->resource_database,
14845     resource_name,"geometry",(char *) NULL);
14846   windows->widget.border_width=0;
14847   windows->widget.flags|=PPosition;
14848   windows->widget.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14849     ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14850     KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14851     StructureNotifyMask;
14852   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14853   manager_hints->input=MagickTrue;
14854   manager_hints->initial_state=NormalState;
14855   manager_hints->window_group=windows->image.id;
14856   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14857     &windows->widget);
14858   windows->widget.highlight_stipple=XCreateBitmapFromData(display,
14859     windows->widget.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14860   windows->widget.shadow_stipple=XCreateBitmapFromData(display,
14861     windows->widget.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14862   (void) XSetTransientForHint(display,windows->widget.id,windows->image.id);
14863   if (display_image->debug != MagickFalse)
14864     (void) LogMagickEvent(X11Event,GetMagickModule(),
14865       "Window id: 0x%lx (widget)",windows->widget.id);
14866   /*
14867     Initialize popup window.
14868   */
14869   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14870     resource_info,&windows->popup);
14871   windows->popup.border_width=0;
14872   windows->popup.flags|=PPosition;
14873   windows->popup.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14874     ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14875     KeyReleaseMask | LeaveWindowMask | StructureNotifyMask;
14876   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14877   manager_hints->input=MagickTrue;
14878   manager_hints->initial_state=NormalState;
14879   manager_hints->window_group=windows->image.id;
14880   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14881     &windows->popup);
14882   windows->popup.highlight_stipple=XCreateBitmapFromData(display,
14883     windows->popup.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14884   windows->popup.shadow_stipple=XCreateBitmapFromData(display,
14885     windows->popup.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14886   (void) XSetTransientForHint(display,windows->popup.id,windows->image.id);
14887   if (display_image->debug != MagickFalse)
14888     (void) LogMagickEvent(X11Event,GetMagickModule(),
14889       "Window id: 0x%lx (pop up)",windows->popup.id);
14890   /*
14891     Initialize Magnify window and cursor.
14892   */
14893   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14894     resource_info,&windows->magnify);
14895   if (resource_info->use_shared_memory == MagickFalse)
14896     windows->magnify.shared_memory=MagickFalse;
14897   (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.magnify",
14898     resource_info->client_name);
14899   windows->magnify.geometry=XGetResourceClass(resource_info->resource_database,
14900     resource_name,"geometry",(char *) NULL);
14901   (void) FormatLocaleString(windows->magnify.name,MaxTextExtent,"Magnify %uX",
14902     resource_info->magnify);
14903   if (windows->magnify.cursor != (Cursor) NULL)
14904     (void) XFreeCursor(display,windows->magnify.cursor);
14905   windows->magnify.cursor=XMakeCursor(display,windows->image.id,
14906     map_info->colormap,resource_info->background_color,
14907     resource_info->foreground_color);
14908   if (windows->magnify.cursor == (Cursor) NULL)
14909     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateCursor",
14910       display_image->filename);
14911   windows->magnify.width=MagnifySize;
14912   windows->magnify.height=MagnifySize;
14913   windows->magnify.flags|=PPosition;
14914   windows->magnify.min_width=MagnifySize;
14915   windows->magnify.min_height=MagnifySize;
14916   windows->magnify.width_inc=MagnifySize;
14917   windows->magnify.height_inc=MagnifySize;
14918   windows->magnify.data=resource_info->magnify;
14919   windows->magnify.attributes.cursor=windows->magnify.cursor;
14920   windows->magnify.attributes.event_mask=ButtonPressMask | ButtonReleaseMask |
14921     ExposureMask | KeyPressMask | KeyReleaseMask | OwnerGrabButtonMask |
14922     StructureNotifyMask;
14923   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14924   manager_hints->input=MagickTrue;
14925   manager_hints->initial_state=NormalState;
14926   manager_hints->window_group=windows->image.id;
14927   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14928     &windows->magnify);
14929   if (display_image->debug != MagickFalse)
14930     (void) LogMagickEvent(X11Event,GetMagickModule(),
14931       "Window id: 0x%lx (magnify)",windows->magnify.id);
14932   (void) XSetTransientForHint(display,windows->magnify.id,windows->image.id);
14933   /*
14934     Initialize panning window.
14935   */
14936   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14937     resource_info,&windows->pan);
14938   (void) CloneString(&windows->pan.name,"Pan Icon");
14939   windows->pan.width=windows->icon.width;
14940   windows->pan.height=windows->icon.height;
14941   (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.pan",
14942     resource_info->client_name);
14943   windows->pan.geometry=XGetResourceClass(resource_info->resource_database,
14944     resource_name,"geometry",(char *) NULL);
14945   (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
14946     &windows->pan.width,&windows->pan.height);
14947   windows->pan.flags|=PPosition;
14948   windows->pan.immutable=MagickTrue;
14949   windows->pan.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14950     ButtonReleaseMask | ExposureMask | KeyPressMask | KeyReleaseMask |
14951     StructureNotifyMask;
14952   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14953   manager_hints->input=MagickFalse;
14954   manager_hints->initial_state=NormalState;
14955   manager_hints->window_group=windows->image.id;
14956   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14957     &windows->pan);
14958   if (display_image->debug != MagickFalse)
14959     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (pan)",
14960       windows->pan.id);
14961   (void) XSetTransientForHint(display,windows->pan.id,windows->image.id);
14962   if (windows->info.mapped != MagickFalse)
14963     (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14964   if ((windows->image.mapped == MagickFalse) ||
14965       (windows->backdrop.id != (Window) NULL))
14966     (void) XMapWindow(display,windows->image.id);
14967   /*
14968     Set our progress monitor and warning handlers.
14969   */
14970   if (warning_handler == (WarningHandler) NULL)
14971     {
14972       warning_handler=resource_info->display_warnings ?
14973         SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
14974       warning_handler=resource_info->display_warnings ?
14975         SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
14976     }
14977   /*
14978     Initialize Image and Magnify X images.
14979   */
14980   windows->image.x=0;
14981   windows->image.y=0;
14982   windows->magnify.shape=MagickFalse;
14983   width=(unsigned int) display_image->columns;
14984   height=(unsigned int) display_image->rows;
14985   if ((display_image->columns != width) || (display_image->rows != height))
14986     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
14987       display_image->filename);
14988   status=XMakeImage(display,resource_info,&windows->image,display_image,
14989     width,height,exception);
14990   if (status == MagickFalse)
14991     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
14992       display_image->filename);
14993   status=XMakeImage(display,resource_info,&windows->magnify,(Image *) NULL,
14994     windows->magnify.width,windows->magnify.height,exception);
14995   if (status == MagickFalse)
14996     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
14997       display_image->filename);
14998   if (windows->magnify.mapped != MagickFalse)
14999     (void) XMapRaised(display,windows->magnify.id);
15000   if (windows->pan.mapped != MagickFalse)
15001     (void) XMapRaised(display,windows->pan.id);
15002   windows->image.window_changes.width=(int) display_image->columns;
15003   windows->image.window_changes.height=(int) display_image->rows;
15004   (void) XConfigureImage(display,resource_info,windows,display_image,exception);
15005   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15006   (void) XSync(display,MagickFalse);
15007   /*
15008     Respond to events.
15009   */
15010   delay=display_image->delay/MagickMax(display_image->ticks_per_second,1L);
15011   timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15012   update_time=0;
15013   if (resource_info->update != MagickFalse)
15014     {
15015       MagickBooleanType
15016         status;
15017
15018       /*
15019         Determine when file data was last modified.
15020       */
15021       status=GetPathAttributes(display_image->filename,&attributes);
15022       if (status != MagickFalse)
15023         update_time=attributes.st_mtime;
15024     }
15025   *state&=(~FormerImageState);
15026   *state&=(~MontageImageState);
15027   *state&=(~NextImageState);
15028   do
15029   {
15030     /*
15031       Handle a window event.
15032     */
15033     if (windows->image.mapped != MagickFalse)
15034       if ((display_image->delay != 0) || (resource_info->update != 0))
15035         {
15036           if (timer < time((time_t *) NULL))
15037             {
15038               if (resource_info->update == MagickFalse)
15039                 *state|=NextImageState | ExitState;
15040               else
15041                 {
15042                   MagickBooleanType
15043                     status;
15044
15045                   /*
15046                     Determine if image file was modified.
15047                   */
15048                   status=GetPathAttributes(display_image->filename,&attributes);
15049                   if (status != MagickFalse)
15050                     if (update_time != attributes.st_mtime)
15051                       {
15052                         /*
15053                           Redisplay image.
15054                         */
15055                         (void) FormatLocaleString(
15056                           resource_info->image_info->filename,MaxTextExtent,
15057                           "%s:%s",display_image->magick,
15058                           display_image->filename);
15059                         nexus=ReadImage(resource_info->image_info,
15060                           &display_image->exception);
15061                         if (nexus != (Image *) NULL)
15062                           {
15063                             nexus=DestroyImage(nexus);
15064                             *state|=NextImageState | ExitState;
15065                           }
15066                       }
15067                   delay=display_image->delay/MagickMax(
15068                     display_image->ticks_per_second,1L);
15069                   timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15070                 }
15071             }
15072           if (XEventsQueued(display,QueuedAfterFlush) == 0)
15073             {
15074               /*
15075                 Do not block if delay > 0.
15076               */
15077               XDelay(display,SuspendTime << 2);
15078               continue;
15079             }
15080         }
15081     timestamp=time((time_t *) NULL);
15082     (void) XNextEvent(display,&event);
15083     if (windows->image.stasis == MagickFalse)
15084       windows->image.stasis=(time((time_t *) NULL)-timestamp) > 0 ?
15085         MagickTrue : MagickFalse;
15086     if (windows->magnify.stasis == MagickFalse)
15087       windows->magnify.stasis=(time((time_t *) NULL)-timestamp) > 0 ?
15088         MagickTrue : MagickFalse;
15089     if (event.xany.window == windows->command.id)
15090       {
15091         /*
15092           Select a command from the Command widget.
15093         */
15094         id=XCommandWidget(display,windows,CommandMenu,&event);
15095         if (id < 0)
15096           continue;
15097         (void) CopyMagickString(command,CommandMenu[id],MaxTextExtent);
15098         command_type=CommandMenus[id];
15099         if (id < MagickMenus)
15100           {
15101             /*
15102               Select a command from a pop-up menu.
15103             */
15104             entry=XMenuWidget(display,windows,CommandMenu[id],Menus[id],
15105               command);
15106             if (entry < 0)
15107               continue;
15108             (void) CopyMagickString(command,Menus[id][entry],MaxTextExtent);
15109             command_type=Commands[id][entry];
15110           }
15111         if (command_type != NullCommand)
15112           nexus=XMagickCommand(display,resource_info,windows,command_type,
15113             &display_image,exception);
15114         continue;
15115       }
15116     switch (event.type)
15117     {
15118       case ButtonPress:
15119       {
15120         if (display_image->debug != MagickFalse)
15121           (void) LogMagickEvent(X11Event,GetMagickModule(),
15122             "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
15123             event.xbutton.button,event.xbutton.x,event.xbutton.y);
15124         if ((event.xbutton.button == Button3) &&
15125             (event.xbutton.state & Mod1Mask))
15126           {
15127             /*
15128               Convert Alt-Button3 to Button2.
15129             */
15130             event.xbutton.button=Button2;
15131             event.xbutton.state&=(~Mod1Mask);
15132           }
15133         if (event.xbutton.window == windows->backdrop.id)
15134           {
15135             (void) XSetInputFocus(display,event.xbutton.window,RevertToParent,
15136               event.xbutton.time);
15137             break;
15138           }
15139         if (event.xbutton.window == windows->image.id)
15140           {
15141             switch (event.xbutton.button)
15142             {
15143               case Button1:
15144               {
15145                 if (resource_info->immutable)
15146                   {
15147                     /*
15148                       Select a command from the Virtual menu.
15149                     */
15150                     entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15151                       command);
15152                     if (entry >= 0)
15153                       nexus=XMagickCommand(display,resource_info,windows,
15154                         VirtualCommands[entry],&display_image,exception);
15155                     break;
15156                   }
15157                 /*
15158                   Map/unmap Command widget.
15159                 */
15160                 if (windows->command.mapped != MagickFalse)
15161                   (void) XWithdrawWindow(display,windows->command.id,
15162                     windows->command.screen);
15163                 else
15164                   {
15165                     (void) XCommandWidget(display,windows,CommandMenu,
15166                       (XEvent *) NULL);
15167                     (void) XMapRaised(display,windows->command.id);
15168                   }
15169                 break;
15170               }
15171               case Button2:
15172               {
15173                 /*
15174                   User pressed the image magnify button.
15175                 */
15176                 (void) XMagickCommand(display,resource_info,windows,ZoomCommand,
15177                   &display_image,exception);
15178                 XMagnifyImage(display,windows,&event);
15179                 break;
15180               }
15181               case Button3:
15182               {
15183                 if (resource_info->immutable)
15184                   {
15185                     /*
15186                       Select a command from the Virtual menu.
15187                     */
15188                     entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15189                       command);
15190                     if (entry >= 0)
15191                       nexus=XMagickCommand(display,resource_info,windows,
15192                         VirtualCommands[entry],&display_image,exception);
15193                     break;
15194                   }
15195                 if (display_image->montage != (char *) NULL)
15196                   {
15197                     /*
15198                       Open or delete a tile from a visual image directory.
15199                     */
15200                     nexus=XTileImage(display,resource_info,windows,
15201                       display_image,&event,exception);
15202                     if (nexus != (Image *) NULL)
15203                       *state|=MontageImageState | NextImageState | ExitState;
15204                     vid_info.x=(short int) windows->image.x;
15205                     vid_info.y=(short int) windows->image.y;
15206                     break;
15207                   }
15208                 /*
15209                   Select a command from the Short Cuts menu.
15210                 */
15211                 entry=XMenuWidget(display,windows,"Short Cuts",ShortCutsMenu,
15212                   command);
15213                 if (entry >= 0)
15214                   nexus=XMagickCommand(display,resource_info,windows,
15215                     ShortCutsCommands[entry],&display_image,exception);
15216                 break;
15217               }
15218               case Button4:
15219               {
15220                 /*
15221                   Wheel up.
15222                 */
15223                 XTranslateImage(display,windows,*image,XK_Up);
15224                 break;
15225               }
15226               case Button5:
15227               {
15228                 /*
15229                   Wheel down.
15230                 */
15231                 XTranslateImage(display,windows,*image,XK_Down);
15232                 break;
15233               }
15234               default:
15235                 break;
15236             }
15237             break;
15238           }
15239         if (event.xbutton.window == windows->magnify.id)
15240           {
15241             int
15242               factor;
15243
15244             static const char
15245               *MagnifyMenu[] =
15246               {
15247                 "2",
15248                 "4",
15249                 "5",
15250                 "6",
15251                 "7",
15252                 "8",
15253                 "9",
15254                 "3",
15255                 (char *) NULL,
15256               };
15257
15258             static KeySym
15259               MagnifyCommands[] =
15260               {
15261                 XK_2,
15262                 XK_4,
15263                 XK_5,
15264                 XK_6,
15265                 XK_7,
15266                 XK_8,
15267                 XK_9,
15268                 XK_3
15269               };
15270
15271             /*
15272               Select a magnify factor from the pop-up menu.
15273             */
15274             factor=XMenuWidget(display,windows,"Magnify",MagnifyMenu,command);
15275             if (factor >= 0)
15276               XMagnifyWindowCommand(display,windows,0,MagnifyCommands[factor]);
15277             break;
15278           }
15279         if (event.xbutton.window == windows->pan.id)
15280           {
15281             switch (event.xbutton.button)
15282             {
15283               case Button4:
15284               {
15285                 /*
15286                   Wheel up.
15287                 */
15288                 XTranslateImage(display,windows,*image,XK_Up);
15289                 break;
15290               }
15291               case Button5:
15292               {
15293                 /*
15294                   Wheel down.
15295                 */
15296                 XTranslateImage(display,windows,*image,XK_Down);
15297                 break;
15298               }
15299               default:
15300               {
15301                 XPanImage(display,windows,&event);
15302                 break;
15303               }
15304             }
15305             break;
15306           }
15307         delay=display_image->delay/MagickMax(display_image->ticks_per_second,
15308           1L);
15309         timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15310         break;
15311       }
15312       case ButtonRelease:
15313       {
15314         if (display_image->debug != MagickFalse)
15315           (void) LogMagickEvent(X11Event,GetMagickModule(),
15316             "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
15317             event.xbutton.button,event.xbutton.x,event.xbutton.y);
15318         break;
15319       }
15320       case ClientMessage:
15321       {
15322         if (display_image->debug != MagickFalse)
15323           (void) LogMagickEvent(X11Event,GetMagickModule(),
15324             "Client Message: 0x%lx 0x%lx %d 0x%lx",event.xclient.window,
15325             event.xclient.message_type,event.xclient.format,(unsigned long)
15326             event.xclient.data.l[0]);
15327         if (event.xclient.message_type == windows->im_protocols)
15328           {
15329             if (*event.xclient.data.l == (long) windows->im_update_widget)
15330               {
15331                 (void) CloneString(&windows->command.name,MagickTitle);
15332                 windows->command.data=MagickMenus;
15333                 (void) XCommandWidget(display,windows,CommandMenu,
15334                   (XEvent *) NULL);
15335                 break;
15336               }
15337             if (*event.xclient.data.l == (long) windows->im_update_colormap)
15338               {
15339                 /*
15340                   Update graphic context and window colormap.
15341                 */
15342                 for (i=0; i < (int) number_windows; i++)
15343                 {
15344                   if (magick_windows[i]->id == windows->icon.id)
15345                     continue;
15346                   context_values.background=pixel->background_color.pixel;
15347                   context_values.foreground=pixel->foreground_color.pixel;
15348                   (void) XChangeGC(display,magick_windows[i]->annotate_context,
15349                     context_mask,&context_values);
15350                   (void) XChangeGC(display,magick_windows[i]->widget_context,
15351                     context_mask,&context_values);
15352                   context_values.background=pixel->foreground_color.pixel;
15353                   context_values.foreground=pixel->background_color.pixel;
15354                   context_values.plane_mask=context_values.background ^
15355                     context_values.foreground;
15356                   (void) XChangeGC(display,magick_windows[i]->highlight_context,
15357                     (size_t) (context_mask | GCPlaneMask),
15358                     &context_values);
15359                   magick_windows[i]->attributes.background_pixel=
15360                     pixel->background_color.pixel;
15361                   magick_windows[i]->attributes.border_pixel=
15362                     pixel->border_color.pixel;
15363                   magick_windows[i]->attributes.colormap=map_info->colormap;
15364                   (void) XChangeWindowAttributes(display,magick_windows[i]->id,
15365                     (unsigned long) magick_windows[i]->mask,
15366                     &magick_windows[i]->attributes);
15367                 }
15368                 if (windows->pan.mapped != MagickFalse)
15369                   {
15370                     (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
15371                       windows->pan.pixmap);
15372                     (void) XClearWindow(display,windows->pan.id);
15373                     XDrawPanRectangle(display,windows);
15374                   }
15375                 if (windows->backdrop.id != (Window) NULL)
15376                   (void) XInstallColormap(display,map_info->colormap);
15377                 break;
15378               }
15379             if (*event.xclient.data.l == (long) windows->im_former_image)
15380               {
15381                 *state|=FormerImageState | ExitState;
15382                 break;
15383               }
15384             if (*event.xclient.data.l == (long) windows->im_next_image)
15385               {
15386                 *state|=NextImageState | ExitState;
15387                 break;
15388               }
15389             if (*event.xclient.data.l == (long) windows->im_retain_colors)
15390               {
15391                 *state|=RetainColorsState;
15392                 break;
15393               }
15394             if (*event.xclient.data.l == (long) windows->im_exit)
15395               {
15396                 *state|=ExitState;
15397                 break;
15398               }
15399             break;
15400           }
15401         if (event.xclient.message_type == windows->dnd_protocols)
15402           {
15403             Atom
15404               selection,
15405               type;
15406
15407             int
15408               format,
15409               status;
15410
15411             unsigned char
15412               *data;
15413
15414             unsigned long
15415               after,
15416               length;
15417
15418             /*
15419               Display image named by the Drag-and-Drop selection.
15420             */
15421             if ((*event.xclient.data.l != 2) && (*event.xclient.data.l != 128))
15422               break;
15423             selection=XInternAtom(display,"DndSelection",MagickFalse);
15424             status=XGetWindowProperty(display,root_window,selection,0L,(long)
15425               MaxTextExtent,MagickFalse,(Atom) AnyPropertyType,&type,&format,
15426               &length,&after,&data);
15427             if ((status != Success) || (length == 0))
15428               break;
15429             if (*event.xclient.data.l == 2)
15430               {
15431                 /*
15432                   Offix DND.
15433                 */
15434                 (void) CopyMagickString(resource_info->image_info->filename,
15435                   (char *) data,MaxTextExtent);
15436               }
15437             else
15438               {
15439                 /*
15440                   XDND.
15441                 */
15442                 if (strncmp((char *) data, "file:", 5) != 0)
15443                   {
15444                     (void) XFree((void *) data);
15445                     break;
15446                   }
15447                 (void) CopyMagickString(resource_info->image_info->filename,
15448                   ((char *) data)+5,MaxTextExtent);
15449               }
15450             nexus=ReadImage(resource_info->image_info,
15451               &display_image->exception);
15452             CatchException(&display_image->exception);
15453             if (nexus != (Image *) NULL)
15454               *state|=NextImageState | ExitState;
15455             (void) XFree((void *) data);
15456             break;
15457           }
15458         /*
15459           If client window delete message, exit.
15460         */
15461         if (event.xclient.message_type != windows->wm_protocols)
15462           break;
15463         if (*event.xclient.data.l != (long) windows->wm_delete_window)
15464           break;
15465         (void) XWithdrawWindow(display,event.xclient.window,
15466           visual_info->screen);
15467         if (event.xclient.window == windows->image.id)
15468           {
15469             *state|=ExitState;
15470             break;
15471           }
15472         if (event.xclient.window == windows->pan.id)
15473           {
15474             /*
15475               Restore original image size when pan window is deleted.
15476             */
15477             windows->image.window_changes.width=windows->image.ximage->width;
15478             windows->image.window_changes.height=windows->image.ximage->height;
15479             (void) XConfigureImage(display,resource_info,windows,
15480               display_image,exception);
15481           }
15482         break;
15483       }
15484       case ConfigureNotify:
15485       {
15486         if (display_image->debug != MagickFalse)
15487           (void) LogMagickEvent(X11Event,GetMagickModule(),
15488             "Configure Notify: 0x%lx %dx%d+%d+%d %d",event.xconfigure.window,
15489             event.xconfigure.width,event.xconfigure.height,event.xconfigure.x,
15490             event.xconfigure.y,event.xconfigure.send_event);
15491         if (event.xconfigure.window == windows->image.id)
15492           {
15493             /*
15494               Image window has a new configuration.
15495             */
15496             if (event.xconfigure.send_event != 0)
15497               {
15498                 XWindowChanges
15499                   window_changes;
15500
15501                 /*
15502                   Position the transient windows relative of the Image window.
15503                 */
15504                 if (windows->command.geometry == (char *) NULL)
15505                   if (windows->command.mapped == MagickFalse)
15506                     {
15507                       windows->command.x=event.xconfigure.x-
15508                         windows->command.width-25;
15509                       windows->command.y=event.xconfigure.y;
15510                       XConstrainWindowPosition(display,&windows->command);
15511                       window_changes.x=windows->command.x;
15512                       window_changes.y=windows->command.y;
15513                       (void) XReconfigureWMWindow(display,windows->command.id,
15514                         windows->command.screen,(unsigned int) (CWX | CWY),
15515                         &window_changes);
15516                     }
15517                 if (windows->widget.geometry == (char *) NULL)
15518                   if (windows->widget.mapped == MagickFalse)
15519                     {
15520                       windows->widget.x=event.xconfigure.x+
15521                         event.xconfigure.width/10;
15522                       windows->widget.y=event.xconfigure.y+
15523                         event.xconfigure.height/10;
15524                       XConstrainWindowPosition(display,&windows->widget);
15525                       window_changes.x=windows->widget.x;
15526                       window_changes.y=windows->widget.y;
15527                       (void) XReconfigureWMWindow(display,windows->widget.id,
15528                         windows->widget.screen,(unsigned int) (CWX | CWY),
15529                         &window_changes);
15530                     }
15531                 if (windows->magnify.geometry == (char *) NULL)
15532                   if (windows->magnify.mapped == MagickFalse)
15533                     {
15534                       windows->magnify.x=event.xconfigure.x+
15535                         event.xconfigure.width+25;
15536                       windows->magnify.y=event.xconfigure.y;
15537                       XConstrainWindowPosition(display,&windows->magnify);
15538                       window_changes.x=windows->magnify.x;
15539                       window_changes.y=windows->magnify.y;
15540                       (void) XReconfigureWMWindow(display,windows->magnify.id,
15541                         windows->magnify.screen,(unsigned int) (CWX | CWY),
15542                         &window_changes);
15543                     }
15544                 if (windows->pan.geometry == (char *) NULL)
15545                   if (windows->pan.mapped == MagickFalse)
15546                     {
15547                       windows->pan.x=event.xconfigure.x+
15548                         event.xconfigure.width+25;
15549                       windows->pan.y=event.xconfigure.y+
15550                         windows->magnify.height+50;
15551                       XConstrainWindowPosition(display,&windows->pan);
15552                       window_changes.x=windows->pan.x;
15553                       window_changes.y=windows->pan.y;
15554                       (void) XReconfigureWMWindow(display,windows->pan.id,
15555                         windows->pan.screen,(unsigned int) (CWX | CWY),
15556                         &window_changes);
15557                     }
15558               }
15559             if ((event.xconfigure.width == (int) windows->image.width) &&
15560                 (event.xconfigure.height == (int) windows->image.height))
15561               break;
15562             windows->image.width=(unsigned int) event.xconfigure.width;
15563             windows->image.height=(unsigned int) event.xconfigure.height;
15564             windows->image.x=0;
15565             windows->image.y=0;
15566             if (display_image->montage != (char *) NULL)
15567               {
15568                 windows->image.x=vid_info.x;
15569                 windows->image.y=vid_info.y;
15570               }
15571             if ((windows->image.mapped != MagickFalse) &&
15572                 (windows->image.stasis != MagickFalse))
15573               {
15574                 /*
15575                   Update image window configuration.
15576                 */
15577                 windows->image.window_changes.width=event.xconfigure.width;
15578                 windows->image.window_changes.height=event.xconfigure.height;
15579                 (void) XConfigureImage(display,resource_info,windows,
15580                   display_image,exception);
15581               }
15582             /*
15583               Update pan window configuration.
15584             */
15585             if ((event.xconfigure.width < windows->image.ximage->width) ||
15586                 (event.xconfigure.height < windows->image.ximage->height))
15587               {
15588                 (void) XMapRaised(display,windows->pan.id);
15589                 XDrawPanRectangle(display,windows);
15590               }
15591             else
15592               if (windows->pan.mapped != MagickFalse)
15593                 (void) XWithdrawWindow(display,windows->pan.id,
15594                   windows->pan.screen);
15595             break;
15596           }
15597         if (event.xconfigure.window == windows->magnify.id)
15598           {
15599             unsigned int
15600               magnify;
15601
15602             /*
15603               Magnify window has a new configuration.
15604             */
15605             windows->magnify.width=(unsigned int) event.xconfigure.width;
15606             windows->magnify.height=(unsigned int) event.xconfigure.height;
15607             if (windows->magnify.mapped == MagickFalse)
15608               break;
15609             magnify=1;
15610             while ((int) magnify <= event.xconfigure.width)
15611               magnify<<=1;
15612             while ((int) magnify <= event.xconfigure.height)
15613               magnify<<=1;
15614             magnify>>=1;
15615             if (((int) magnify != event.xconfigure.width) ||
15616                 ((int) magnify != event.xconfigure.height))
15617               {
15618                 window_changes.width=(int) magnify;
15619                 window_changes.height=(int) magnify;
15620                 (void) XReconfigureWMWindow(display,windows->magnify.id,
15621                   windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
15622                   &window_changes);
15623                 break;
15624               }
15625             if ((windows->magnify.mapped != MagickFalse) &&
15626                 (windows->magnify.stasis != MagickFalse))
15627               {
15628                 status=XMakeImage(display,resource_info,&windows->magnify,
15629                   display_image,windows->magnify.width,windows->magnify.height,
15630                   exception);
15631                 XMakeMagnifyImage(display,windows);
15632               }
15633             break;
15634           }
15635         if ((windows->magnify.mapped != MagickFalse) &&
15636             (event.xconfigure.window == windows->pan.id))
15637           {
15638             /*
15639               Pan icon window has a new configuration.
15640             */
15641             if (event.xconfigure.send_event != 0)
15642               {
15643                 windows->pan.x=event.xconfigure.x;
15644                 windows->pan.y=event.xconfigure.y;
15645               }
15646             windows->pan.width=(unsigned int) event.xconfigure.width;
15647             windows->pan.height=(unsigned int) event.xconfigure.height;
15648             break;
15649           }
15650         if (event.xconfigure.window == windows->icon.id)
15651           {
15652             /*
15653               Icon window has a new configuration.
15654             */
15655             windows->icon.width=(unsigned int) event.xconfigure.width;
15656             windows->icon.height=(unsigned int) event.xconfigure.height;
15657             break;
15658           }
15659         break;
15660       }
15661       case DestroyNotify:
15662       {
15663         /*
15664           Group leader has exited.
15665         */
15666         if (display_image->debug != MagickFalse)
15667           (void) LogMagickEvent(X11Event,GetMagickModule(),
15668             "Destroy Notify: 0x%lx",event.xdestroywindow.window);
15669         if (event.xdestroywindow.window == windows->group_leader.id)
15670           {
15671             *state|=ExitState;
15672             break;
15673           }
15674         break;
15675       }
15676       case EnterNotify:
15677       {
15678         /*
15679           Selectively install colormap.
15680         */
15681         if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15682           if (event.xcrossing.mode != NotifyUngrab)
15683             XInstallColormap(display,map_info->colormap);
15684         break;
15685       }
15686       case Expose:
15687       {
15688         if (display_image->debug != MagickFalse)
15689           (void) LogMagickEvent(X11Event,GetMagickModule(),
15690             "Expose: 0x%lx %dx%d+%d+%d",event.xexpose.window,
15691             event.xexpose.width,event.xexpose.height,event.xexpose.x,
15692             event.xexpose.y);
15693         /*
15694           Refresh windows that are now exposed.
15695         */
15696         if ((event.xexpose.window == windows->image.id) &&
15697             (windows->image.mapped != MagickFalse))
15698           {
15699             XRefreshWindow(display,&windows->image,&event);
15700             delay=display_image->delay/MagickMax(
15701               display_image->ticks_per_second,1L);
15702             timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15703             break;
15704           }
15705         if ((event.xexpose.window == windows->magnify.id) &&
15706             (windows->magnify.mapped != MagickFalse))
15707           {
15708             XMakeMagnifyImage(display,windows);
15709             break;
15710           }
15711         if (event.xexpose.window == windows->pan.id)
15712           {
15713             XDrawPanRectangle(display,windows);
15714             break;
15715           }
15716         if (event.xexpose.window == windows->icon.id)
15717           {
15718             XRefreshWindow(display,&windows->icon,&event);
15719             break;
15720           }
15721         break;
15722       }
15723       case KeyPress:
15724       {
15725         int
15726           length;
15727
15728         /*
15729           Respond to a user key press.
15730         */
15731         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
15732           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15733         *(command+length)='\0';
15734         if (display_image->debug != MagickFalse)
15735           (void) LogMagickEvent(X11Event,GetMagickModule(),
15736             "Key press: %d 0x%lx (%s)",event.xkey.state,(unsigned long)
15737             key_symbol,command);
15738         if (event.xkey.window == windows->image.id)
15739           {
15740             command_type=XImageWindowCommand(display,resource_info,windows,
15741               event.xkey.state,key_symbol,&display_image,exception);
15742             if (command_type != NullCommand)
15743               nexus=XMagickCommand(display,resource_info,windows,command_type,
15744                 &display_image,exception);
15745           }
15746         if (event.xkey.window == windows->magnify.id)
15747           XMagnifyWindowCommand(display,windows,event.xkey.state,key_symbol);
15748         if (event.xkey.window == windows->pan.id)
15749           {
15750             if ((key_symbol == XK_q) || (key_symbol == XK_Escape))
15751               (void) XWithdrawWindow(display,windows->pan.id,
15752                 windows->pan.screen);
15753             else
15754               if ((key_symbol == XK_F1) || (key_symbol == XK_Help))
15755                 XTextViewWidget(display,resource_info,windows,MagickFalse,
15756                   "Help Viewer - Image Pan",ImagePanHelp);
15757               else
15758                 XTranslateImage(display,windows,*image,key_symbol);
15759           }
15760         delay=display_image->delay/MagickMax(
15761           display_image->ticks_per_second,1L);
15762         timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15763         break;
15764       }
15765       case KeyRelease:
15766       {
15767         /*
15768           Respond to a user key release.
15769         */
15770         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
15771           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15772         if (display_image->debug != MagickFalse)
15773           (void) LogMagickEvent(X11Event,GetMagickModule(),
15774             "Key release: 0x%lx (%c)",(unsigned long) key_symbol,*command);
15775         break;
15776       }
15777       case LeaveNotify:
15778       {
15779         /*
15780           Selectively uninstall colormap.
15781         */
15782         if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15783           if (event.xcrossing.mode != NotifyUngrab)
15784             XUninstallColormap(display,map_info->colormap);
15785         break;
15786       }
15787       case MapNotify:
15788       {
15789         if (display_image->debug != MagickFalse)
15790           (void) LogMagickEvent(X11Event,GetMagickModule(),"Map Notify: 0x%lx",
15791             event.xmap.window);
15792         if (event.xmap.window == windows->backdrop.id)
15793           {
15794             (void) XSetInputFocus(display,event.xmap.window,RevertToParent,
15795               CurrentTime);
15796             windows->backdrop.mapped=MagickTrue;
15797             break;
15798           }
15799         if (event.xmap.window == windows->image.id)
15800           {
15801             if (windows->backdrop.id != (Window) NULL)
15802               (void) XInstallColormap(display,map_info->colormap);
15803             if (LocaleCompare(display_image->magick,"LOGO") == 0)
15804               {
15805                 if (LocaleCompare(display_image->filename,"LOGO") == 0)
15806                   nexus=XOpenImage(display,resource_info,windows,MagickFalse);
15807               }
15808             if (((int) windows->image.width < windows->image.ximage->width) ||
15809                 ((int) windows->image.height < windows->image.ximage->height))
15810               (void) XMapRaised(display,windows->pan.id);
15811             windows->image.mapped=MagickTrue;
15812             break;
15813           }
15814         if (event.xmap.window == windows->magnify.id)
15815           {
15816             XMakeMagnifyImage(display,windows);
15817             windows->magnify.mapped=MagickTrue;
15818             (void) XWithdrawWindow(display,windows->info.id,
15819               windows->info.screen);
15820             break;
15821           }
15822         if (event.xmap.window == windows->pan.id)
15823           {
15824             XMakePanImage(display,resource_info,windows,display_image,
15825               exception);
15826             windows->pan.mapped=MagickTrue;
15827             break;
15828           }
15829         if (event.xmap.window == windows->info.id)
15830           {
15831             windows->info.mapped=MagickTrue;
15832             break;
15833           }
15834         if (event.xmap.window == windows->icon.id)
15835           {
15836             MagickBooleanType
15837               taint;
15838
15839             /*
15840               Create an icon image.
15841             */
15842             taint=display_image->taint;
15843             XMakeStandardColormap(display,icon_visual,icon_resources,
15844               display_image,icon_map,icon_pixel);
15845             (void) XMakeImage(display,icon_resources,&windows->icon,
15846               display_image,windows->icon.width,windows->icon.height,
15847               exception);
15848             display_image->taint=taint;
15849             (void) XSetWindowBackgroundPixmap(display,windows->icon.id,
15850               windows->icon.pixmap);
15851             (void) XClearWindow(display,windows->icon.id);
15852             (void) XWithdrawWindow(display,windows->info.id,
15853               windows->info.screen);
15854             windows->icon.mapped=MagickTrue;
15855             break;
15856           }
15857         if (event.xmap.window == windows->command.id)
15858           {
15859             windows->command.mapped=MagickTrue;
15860             break;
15861           }
15862         if (event.xmap.window == windows->popup.id)
15863           {
15864             windows->popup.mapped=MagickTrue;
15865             break;
15866           }
15867         if (event.xmap.window == windows->widget.id)
15868           {
15869             windows->widget.mapped=MagickTrue;
15870             break;
15871           }
15872         break;
15873       }
15874       case MappingNotify:
15875       {
15876         (void) XRefreshKeyboardMapping(&event.xmapping);
15877         break;
15878       }
15879       case NoExpose:
15880         break;
15881       case PropertyNotify:
15882       {
15883         Atom
15884           type;
15885
15886         int
15887           format,
15888           status;
15889
15890         unsigned char
15891           *data;
15892
15893         unsigned long
15894           after,
15895           length;
15896
15897         if (display_image->debug != MagickFalse)
15898           (void) LogMagickEvent(X11Event,GetMagickModule(),
15899             "Property Notify: 0x%lx 0x%lx %d",event.xproperty.window,
15900             event.xproperty.atom,event.xproperty.state);
15901         if (event.xproperty.atom != windows->im_remote_command)
15902           break;
15903         /*
15904           Display image named by the remote command protocol.
15905         */
15906         status=XGetWindowProperty(display,event.xproperty.window,
15907           event.xproperty.atom,0L,(long) MaxTextExtent,MagickFalse,(Atom)
15908           AnyPropertyType,&type,&format,&length,&after,&data);
15909         if ((status != Success) || (length == 0))
15910           break;
15911         if (LocaleCompare((char *) data,"-quit") == 0)
15912           {
15913             XClientMessage(display,windows->image.id,windows->im_protocols,
15914               windows->im_exit,CurrentTime);
15915             (void) XFree((void *) data);
15916             break;
15917           }
15918         (void) CopyMagickString(resource_info->image_info->filename,
15919           (char *) data,MaxTextExtent);
15920         (void) XFree((void *) data);
15921         nexus=ReadImage(resource_info->image_info,&display_image->exception);
15922         CatchException(&display_image->exception);
15923         if (nexus != (Image *) NULL)
15924           *state|=NextImageState | ExitState;
15925         break;
15926       }
15927       case ReparentNotify:
15928       {
15929         if (display_image->debug != MagickFalse)
15930           (void) LogMagickEvent(X11Event,GetMagickModule(),
15931             "Reparent Notify: 0x%lx=>0x%lx",event.xreparent.parent,
15932             event.xreparent.window);
15933         break;
15934       }
15935       case UnmapNotify:
15936       {
15937         if (display_image->debug != MagickFalse)
15938           (void) LogMagickEvent(X11Event,GetMagickModule(),
15939             "Unmap Notify: 0x%lx",event.xunmap.window);
15940         if (event.xunmap.window == windows->backdrop.id)
15941           {
15942             windows->backdrop.mapped=MagickFalse;
15943             break;
15944           }
15945         if (event.xunmap.window == windows->image.id)
15946           {
15947             windows->image.mapped=MagickFalse;
15948             break;
15949           }
15950         if (event.xunmap.window == windows->magnify.id)
15951           {
15952             windows->magnify.mapped=MagickFalse;
15953             break;
15954           }
15955         if (event.xunmap.window == windows->pan.id)
15956           {
15957             windows->pan.mapped=MagickFalse;
15958             break;
15959           }
15960         if (event.xunmap.window == windows->info.id)
15961           {
15962             windows->info.mapped=MagickFalse;
15963             break;
15964           }
15965         if (event.xunmap.window == windows->icon.id)
15966           {
15967             if (map_info->colormap == icon_map->colormap)
15968               XConfigureImageColormap(display,resource_info,windows,
15969                 display_image);
15970             (void) XFreeStandardColormap(display,icon_visual,icon_map,
15971               icon_pixel);
15972             windows->icon.mapped=MagickFalse;
15973             break;
15974           }
15975         if (event.xunmap.window == windows->command.id)
15976           {
15977             windows->command.mapped=MagickFalse;
15978             break;
15979           }
15980         if (event.xunmap.window == windows->popup.id)
15981           {
15982             if (windows->backdrop.id != (Window) NULL)
15983               (void) XSetInputFocus(display,windows->image.id,RevertToParent,
15984                 CurrentTime);
15985             windows->popup.mapped=MagickFalse;
15986             break;
15987           }
15988         if (event.xunmap.window == windows->widget.id)
15989           {
15990             if (windows->backdrop.id != (Window) NULL)
15991               (void) XSetInputFocus(display,windows->image.id,RevertToParent,
15992                 CurrentTime);
15993             windows->widget.mapped=MagickFalse;
15994             break;
15995           }
15996         break;
15997       }
15998       default:
15999       {
16000         if (display_image->debug != MagickFalse)
16001           (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
16002             event.type);
16003         break;
16004       }
16005     }
16006   } while (!(*state & ExitState));
16007   if ((*state & ExitState) == 0)
16008     (void) XMagickCommand(display,resource_info,windows,FreeBuffersCommand,
16009       &display_image,exception);
16010   else
16011     if (resource_info->confirm_edit != MagickFalse)
16012       {
16013         /*
16014           Query user if image has changed.
16015         */
16016         if ((resource_info->immutable == MagickFalse) &&
16017             (display_image->taint != MagickFalse))
16018           {
16019             int
16020               status;
16021
16022             status=XConfirmWidget(display,windows,"Your image changed.",
16023               "Do you want to save it");
16024             if (status == 0)
16025               *state&=(~ExitState);
16026             else
16027               if (status > 0)
16028                 (void) XMagickCommand(display,resource_info,windows,SaveCommand,
16029                   &display_image,exception);
16030           }
16031       }
16032   if ((windows->visual_info->klass == GrayScale) ||
16033       (windows->visual_info->klass == PseudoColor) ||
16034       (windows->visual_info->klass == DirectColor))
16035     {
16036       /*
16037         Withdraw pan and Magnify window.
16038       */
16039       if (windows->info.mapped != MagickFalse)
16040         (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
16041       if (windows->magnify.mapped != MagickFalse)
16042         (void) XWithdrawWindow(display,windows->magnify.id,
16043           windows->magnify.screen);
16044       if (windows->command.mapped != MagickFalse)
16045         (void) XWithdrawWindow(display,windows->command.id,
16046           windows->command.screen);
16047     }
16048   if (windows->pan.mapped != MagickFalse)
16049     (void) XWithdrawWindow(display,windows->pan.id,windows->pan.screen);
16050   if (resource_info->backdrop == MagickFalse)
16051     if (windows->backdrop.mapped)
16052       {
16053         (void) XWithdrawWindow(display,windows->backdrop.id,
16054           windows->backdrop.screen);
16055         (void) XDestroyWindow(display,windows->backdrop.id);
16056         windows->backdrop.id=(Window) NULL;
16057         (void) XWithdrawWindow(display,windows->image.id,
16058           windows->image.screen);
16059         (void) XDestroyWindow(display,windows->image.id);
16060         windows->image.id=(Window) NULL;
16061       }
16062   XSetCursorState(display,windows,MagickTrue);
16063   XCheckRefreshWindows(display,windows);
16064   if (((*state & FormerImageState) != 0) || ((*state & NextImageState) != 0))
16065     *state&=(~ExitState);
16066   if (*state & ExitState)
16067     {
16068       /*
16069         Free Standard Colormap.
16070       */
16071       (void) XFreeStandardColormap(display,icon_visual,icon_map,icon_pixel);
16072       if (resource_info->map_type == (char *) NULL)
16073         (void) XFreeStandardColormap(display,visual_info,map_info,pixel);
16074       /*
16075         Free X resources.
16076       */
16077       if (resource_info->copy_image != (Image *) NULL)
16078         {
16079           resource_info->copy_image=DestroyImage(resource_info->copy_image);
16080           resource_info->copy_image=NewImageList();
16081         }
16082       DestroyXResources();
16083     }
16084   (void) XSync(display,MagickFalse);
16085   /*
16086     Restore our progress monitor and warning handlers.
16087   */
16088   (void) SetErrorHandler(warning_handler);
16089   (void) SetWarningHandler(warning_handler);
16090   /*
16091     Change to home directory.
16092   */
16093   directory=getcwd(working_directory,MaxTextExtent);
16094   (void) directory;
16095   {
16096     int
16097       status;
16098
16099     status=chdir(resource_info->home_directory);
16100     if (status == -1)
16101       (void) ThrowMagickException(&display_image->exception,GetMagickModule(),
16102         FileOpenError,"UnableToOpenFile","%s",resource_info->home_directory);
16103   }
16104   *image=display_image;
16105   return(nexus);
16106 }
16107 #else
16108 \f
16109 /*
16110 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16111 %                                                                             %
16112 %                                                                             %
16113 %                                                                             %
16114 +   D i s p l a y I m a g e s                                                 %
16115 %                                                                             %
16116 %                                                                             %
16117 %                                                                             %
16118 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16119 %
16120 %  DisplayImages() displays an image sequence to any X window screen.  It
16121 %  returns a value other than 0 if successful.  Check the exception member
16122 %  of image to determine the reason for any failure.
16123 %
16124 %  The format of the DisplayImages method is:
16125 %
16126 %      MagickBooleanType DisplayImages(const ImageInfo *image_info,
16127 %        Image *images,ExceptionInfo *exception)
16128 %
16129 %  A description of each parameter follows:
16130 %
16131 %    o image_info: the image info.
16132 %
16133 %    o image: the image.
16134 %
16135 %    o exception: return any errors or warnings in this structure.
16136 %
16137 */
16138 MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
16139   Image *image,ExceptionInfo *exception)
16140 {
16141   assert(image_info != (const ImageInfo *) NULL);
16142   assert(image_info->signature == MagickSignature);
16143   assert(image != (Image *) NULL);
16144   assert(image->signature == MagickSignature);
16145   if (image->debug != MagickFalse)
16146     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
16147   (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16148     "DelegateLibrarySupportNotBuiltIn","`%s' (X11)",image->filename);
16149   return(MagickFalse);
16150 }
16151 \f
16152 /*
16153 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16154 %                                                                             %
16155 %                                                                             %
16156 %                                                                             %
16157 +   R e m o t e D i s p l a y C o m m a n d                                   %
16158 %                                                                             %
16159 %                                                                             %
16160 %                                                                             %
16161 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16162 %
16163 %  RemoteDisplayCommand() encourages a remote display program to display the
16164 %  specified image filename.
16165 %
16166 %  The format of the RemoteDisplayCommand method is:
16167 %
16168 %      MagickBooleanType RemoteDisplayCommand(const ImageInfo *image,
16169 %        const char *window,const char *filename,ExceptionInfo *exception)
16170 %
16171 %  A description of each parameter follows:
16172 %
16173 %    o image_info: the image info.
16174 %
16175 %    o window: Specifies the name or id of an X window.
16176 %
16177 %    o filename: the name of the image filename to display.
16178 %
16179 %    o exception: return any errors or warnings in this structure.
16180 %
16181 */
16182 MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
16183   const char *window,const char *filename,ExceptionInfo *exception)
16184 {
16185   assert(image_info != (const ImageInfo *) NULL);
16186   assert(image_info->signature == MagickSignature);
16187   assert(filename != (char *) NULL);
16188   (void) window;
16189   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
16190   (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16191     "DelegateLibrarySupportNotBuiltIn","`%s' (X11)",image_info->filename);
16192   return(MagickFalse);
16193 }
16194 #endif