]> 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-2013 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/attribute.h"
45 #include "MagickCore/blob.h"
46 #include "MagickCore/cache.h"
47 #include "MagickCore/cache-private.h"
48 #include "MagickCore/client.h"
49 #include "MagickCore/color.h"
50 #include "MagickCore/colorspace.h"
51 #include "MagickCore/composite.h"
52 #include "MagickCore/constitute.h"
53 #include "MagickCore/decorate.h"
54 #include "MagickCore/delegate.h"
55 #include "MagickCore/display.h"
56 #include "MagickCore/display-private.h"
57 #include "MagickCore/distort.h"
58 #include "MagickCore/draw.h"
59 #include "MagickCore/effect.h"
60 #include "MagickCore/enhance.h"
61 #include "MagickCore/exception.h"
62 #include "MagickCore/exception-private.h"
63 #include "MagickCore/fx.h"
64 #include "MagickCore/geometry.h"
65 #include "MagickCore/image.h"
66 #include "MagickCore/image-private.h"
67 #include "MagickCore/list.h"
68 #include "MagickCore/log.h"
69 #include "MagickCore/magick.h"
70 #include "MagickCore/memory_.h"
71 #include "MagickCore/monitor.h"
72 #include "MagickCore/monitor-private.h"
73 #include "MagickCore/montage.h"
74 #include "MagickCore/option.h"
75 #include "MagickCore/paint.h"
76 #include "MagickCore/pixel.h"
77 #include "MagickCore/pixel-accessor.h"
78 #include "MagickCore/PreRvIcccm.h"
79 #include "MagickCore/property.h"
80 #include "MagickCore/quantum.h"
81 #include "MagickCore/quantum-private.h"
82 #include "MagickCore/resize.h"
83 #include "MagickCore/resource_.h"
84 #include "MagickCore/shear.h"
85 #include "MagickCore/segment.h"
86 #include "MagickCore/statistic.h"
87 #include "MagickCore/string_.h"
88 #include "MagickCore/string-private.h"
89 #include "MagickCore/transform.h"
90 #include "MagickCore/threshold.h"
91 #include "MagickCore/utility.h"
92 #include "MagickCore/utility-private.h"
93 #include "MagickCore/version.h"
94 #include "MagickCore/widget.h"
95 #include "MagickCore/widget-private.h"
96 #include "MagickCore/xwindow.h"
97 #include "MagickCore/xwindow-private.h"
98 \f
99 #if defined(MAGICKCORE_X11_DELEGATE)
100 /*
101   Define declarations.
102 */
103 #define MaxColors  MagickMin((ssize_t) windows->visual_info->colormap_size,256L)
104 \f
105 /*
106   Constant declarations.
107 */
108 static const unsigned char
109   HighlightBitmap[8] =
110   {
111     0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55
112   },
113   OpaqueBitmap[8] =
114   {
115     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
116   },
117   ShadowBitmap[8] =
118   {
119     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
120   };
121
122 static const char
123   *PageSizes[] =
124   {
125     "Letter",
126     "Tabloid",
127     "Ledger",
128     "Legal",
129     "Statement",
130     "Executive",
131     "A3",
132     "A4",
133     "A5",
134     "B4",
135     "B5",
136     "Folio",
137     "Quarto",
138     "10x14",
139     (char *) NULL
140   };
141 \f
142 /*
143   Help widget declarations.
144 */
145 static const char
146   *ImageAnnotateHelp[] =
147   {
148     "In annotate mode, the Command widget has these options:",
149     "",
150     "    Font Name",
151     "      fixed",
152     "      variable",
153     "      5x8",
154     "      6x10",
155     "      7x13bold",
156     "      8x13bold",
157     "      9x15bold",
158     "      10x20",
159     "      12x24",
160     "      Browser...",
161     "    Font Color",
162     "      black",
163     "      blue",
164     "      cyan",
165     "      green",
166     "      gray",
167     "      red",
168     "      magenta",
169     "      yellow",
170     "      white",
171     "      transparent",
172     "      Browser...",
173     "    Font Color",
174     "      black",
175     "      blue",
176     "      cyan",
177     "      green",
178     "      gray",
179     "      red",
180     "      magenta",
181     "      yellow",
182     "      white",
183     "      transparent",
184     "      Browser...",
185     "    Rotate Text",
186     "      -90",
187     "      -45",
188     "      -30",
189     "      0",
190     "      30",
191     "      45",
192     "      90",
193     "      180",
194     "      Dialog...",
195     "    Help",
196     "    Dismiss",
197     "",
198     "Choose a font name from the Font Name sub-menu.  Additional",
199     "font names can be specified with the font browser.  You can",
200     "change the menu names by setting the X resources font1",
201     "through font9.",
202     "",
203     "Choose a font color from the Font Color sub-menu.",
204     "Additional font colors can be specified with the color",
205     "browser.  You can change the menu colors by setting the X",
206     "resources pen1 through pen9.",
207     "",
208     "If you select the color browser and press Grab, you can",
209     "choose the font color by moving the pointer to the desired",
210     "color on the screen and press any button.",
211     "",
212     "If you choose to rotate the text, choose Rotate Text from the",
213     "menu and select an angle.  Typically you will only want to",
214     "rotate one line of text at a time.  Depending on the angle you",
215     "choose, subsequent lines may end up overwriting each other.",
216     "",
217     "Choosing a font and its color is optional.  The default font",
218     "is fixed and the default color is black.  However, you must",
219     "choose a location to begin entering text and press button 1.",
220     "An underscore character will appear at the location of the",
221     "pointer.  The cursor changes to a pencil to indicate you are",
222     "in text mode.  To exit immediately, press Dismiss.",
223     "",
224     "In text mode, any key presses will display the character at",
225     "the location of the underscore and advance the underscore",
226     "cursor.  Enter your text and once completed press Apply to",
227     "finish your image annotation.  To correct errors press BACK",
228     "SPACE.  To delete an entire line of text, press DELETE.  Any",
229     "text that exceeds the boundaries of the image window is",
230     "automagically continued onto the next line.",
231     "",
232     "The actual color you request for the font is saved in the",
233     "image.  However, the color that appears in your image window",
234     "may be different.  For example, on a monochrome screen the",
235     "text will appear black or white even if you choose the color",
236     "red as the font color.  However, the image saved to a file",
237     "with -write is written with red lettering.  To assure the",
238     "correct color text in the final image, any PseudoClass image",
239     "is promoted to DirectClass (see miff(5)).  To force a",
240     "PseudoClass image to remain PseudoClass, use -colors.",
241     (char *) NULL,
242   },
243   *ImageChopHelp[] =
244   {
245     "In chop mode, the Command widget has these options:",
246     "",
247     "    Direction",
248     "      horizontal",
249     "      vertical",
250     "    Help",
251     "    Dismiss",
252     "",
253     "If the you choose the horizontal direction (this the",
254     "default), the area of the image between the two horizontal",
255     "endpoints of the chop line is removed.  Otherwise, the area",
256     "of the image between the two vertical endpoints of the chop",
257     "line is removed.",
258     "",
259     "Select a location within the image window to begin your chop,",
260     "press and hold any button.  Next, move the pointer to",
261     "another location in the image.  As you move a line will",
262     "connect the initial location and the pointer.  When you",
263     "release the button, the area within the image to chop is",
264     "determined by which direction you choose from the Command",
265     "widget.",
266     "",
267     "To cancel the image chopping, move the pointer back to the",
268     "starting point of the line and release the button.",
269     (char *) NULL,
270   },
271   *ImageColorEditHelp[] =
272   {
273     "In color edit mode, the Command widget has these options:",
274     "",
275     "    Method",
276     "      point",
277     "      replace",
278     "      floodfill",
279     "      filltoborder",
280     "      reset",
281     "    Pixel Color",
282     "      black",
283     "      blue",
284     "      cyan",
285     "      green",
286     "      gray",
287     "      red",
288     "      magenta",
289     "      yellow",
290     "      white",
291     "      Browser...",
292     "    Border Color",
293     "      black",
294     "      blue",
295     "      cyan",
296     "      green",
297     "      gray",
298     "      red",
299     "      magenta",
300     "      yellow",
301     "      white",
302     "      Browser...",
303     "    Fuzz",
304     "      0%",
305     "      2%",
306     "      5%",
307     "      10%",
308     "      15%",
309     "      Dialog...",
310     "    Undo",
311     "    Help",
312     "    Dismiss",
313     "",
314     "Choose a color editing method from the Method sub-menu",
315     "of the Command widget.  The point method recolors any pixel",
316     "selected with the pointer until the button is released.  The",
317     "replace method recolors any pixel that matches the color of",
318     "the pixel you select with a button press.  Floodfill recolors",
319     "any pixel that matches the color of the pixel you select with",
320     "a button press and is a neighbor.  Whereas filltoborder recolors",
321     "any neighbor pixel that is not the border color.  Finally reset",
322     "changes the entire image to the designated color.",
323     "",
324     "Next, choose a pixel color from the Pixel Color sub-menu.",
325     "Additional pixel colors can be specified with the color",
326     "browser.  You can change the menu colors by setting the X",
327     "resources pen1 through pen9.",
328     "",
329     "Now press button 1 to select a pixel within the image window",
330     "to change its color.  Additional pixels may be recolored as",
331     "prescribed by the method you choose.",
332     "",
333     "If the Magnify widget is mapped, it can be helpful in positioning",
334     "your pointer within the image (refer to button 2).",
335     "",
336     "The actual color you request for the pixels is saved in the",
337     "image.  However, the color that appears in your image window",
338     "may be different.  For example, on a monochrome screen the",
339     "pixel will appear black or white even if you choose the",
340     "color red as the pixel color.  However, the image saved to a",
341     "file with -write is written with red pixels.  To assure the",
342     "correct color text in the final image, any PseudoClass image",
343     "is promoted to DirectClass (see miff(5)).  To force a",
344     "PseudoClass image to remain PseudoClass, use -colors.",
345     (char *) NULL,
346   },
347   *ImageCompositeHelp[] =
348   {
349     "First a widget window is displayed requesting you to enter an",
350     "image name. Press Composite, Grab or type a file name.",
351     "Press Cancel if you choose not to create a composite image.",
352     "When you choose Grab, move the pointer to the desired window",
353     "and press any button.",
354     "",
355     "If the Composite image does not have any matte information,",
356     "you are informed and the file browser is displayed again.",
357     "Enter the name of a mask image.  The image is typically",
358     "grayscale and the same size as the composite image.  If the",
359     "image is not grayscale, it is converted to grayscale and the",
360     "resulting intensities are used as matte information.",
361     "",
362     "A small window appears showing the location of the cursor in",
363     "the image window. You are now in composite mode.  To exit",
364     "immediately, press Dismiss.  In composite mode, the Command",
365     "widget has these options:",
366     "",
367     "    Operators",
368     "      Over",
369     "      In",
370     "      Out",
371     "      Atop",
372     "      Xor",
373     "      Plus",
374     "      Minus",
375     "      Add",
376     "      Subtract",
377     "      Difference",
378     "      Multiply",
379     "      Bumpmap",
380     "      Copy",
381     "      CopyRed",
382     "      CopyGreen",
383     "      CopyBlue",
384     "      CopyOpacity",
385     "      Clear",
386     "    Dissolve",
387     "    Displace",
388     "    Help",
389     "    Dismiss",
390     "",
391     "Choose a composite operation from the Operators sub-menu of",
392     "the Command widget.  How each operator behaves is described",
393     "below.  Image window is the image currently displayed on",
394     "your X server and image is the image obtained with the File",
395     "Browser widget.",
396     "",
397     "Over     The result is the union of the two image shapes,",
398     "         with image obscuring image window in the region of",
399     "         overlap.",
400     "",
401     "In       The result is simply image cut by the shape of",
402     "         image window.  None of the image data of image",
403     "         window is in the result.",
404     "",
405     "Out      The resulting image is image with the shape of",
406     "         image window cut out.",
407     "",
408     "Atop     The result is the same shape as image image window,",
409     "         with image obscuring image window where the image",
410     "         shapes overlap.  Note this differs from over",
411     "         because the portion of image outside image window's",
412     "         shape does not appear in the result.",
413     "",
414     "Xor      The result is the image data from both image and",
415     "         image window that is outside the overlap region.",
416     "         The overlap region is blank.",
417     "",
418     "Plus     The result is just the sum of the image data.",
419     "         Output values are cropped to QuantumRange (no overflow).",
420     "",
421     "Minus    The result of image - image window, with underflow",
422     "         cropped to zero.",
423     "",
424     "Add      The result of image + image window, with overflow",
425     "         wrapping around (mod 256).",
426     "",
427     "Subtract The result of image - image window, with underflow",
428     "         wrapping around (mod 256).  The add and subtract",
429     "         operators can be used to perform reversible",
430     "         transformations.",
431     "",
432     "Difference",
433     "         The result of abs(image - image window).  This",
434     "         useful for comparing two very similar images.",
435     "",
436     "Multiply",
437     "         The result of image * image window.  This",
438     "         useful for the creation of drop-shadows.",
439     "",
440     "Bumpmap  The result of surface normals from image * image",
441     "         window.",
442     "",
443     "Copy     The resulting image is image window replaced with",
444     "         image.  Here the matte information is ignored.",
445     "",
446     "CopyRed  The red layer of the image window is replace with",
447     "         the red layer of the image.  The other layers are",
448     "         untouched.",
449     "",
450     "CopyGreen",
451     "         The green layer of the image window is replace with",
452     "         the green layer of the image.  The other layers are",
453     "         untouched.",
454     "",
455     "CopyBlue The blue layer of the image window is replace with",
456     "         the blue layer of the image.  The other layers are",
457     "         untouched.",
458     "",
459     "CopyOpacity",
460     "         The matte layer of the image window is replace with",
461     "         the matte layer of the image.  The other layers are",
462     "         untouched.",
463     "",
464     "The image compositor requires a matte, or alpha channel in",
465     "the image for some operations.  This extra channel usually",
466     "defines a mask which represents a sort of a cookie-cutter",
467     "for the image.  This the case when matte is opaque (full",
468     "coverage) for pixels inside the shape, zero outside, and",
469     "between 0 and QuantumRange on the boundary.  If image does not",
470     "have a matte channel, it is initialized with 0 for any pixel",
471     "matching in color to pixel location (0,0), otherwise QuantumRange.",
472     "",
473     "If you choose Dissolve, the composite operator becomes Over.  The",
474     "image matte channel percent transparency is initialized to factor.",
475     "The image window is initialized to (100-factor). Where factor is the",
476     "value you specify in the Dialog widget.",
477     "",
478     "Displace shifts the image pixels as defined by a displacement",
479     "map.  With this option, image is used as a displacement map.",
480     "Black, within the displacement map, is a maximum positive",
481     "displacement.  White is a maximum negative displacement and",
482     "middle gray is neutral.  The displacement is scaled to determine",
483     "the pixel shift.  By default, the displacement applies in both the",
484     "horizontal and vertical directions.  However, if you specify a mask,",
485     "image is the horizontal X displacement and mask the vertical Y",
486     "displacement.",
487     "",
488     "Note that matte information for image window is not retained",
489     "for colormapped X server visuals (e.g. StaticColor,",
490     "StaticColor, GrayScale, PseudoColor).  Correct compositing",
491     "behavior may require a TrueColor or DirectColor visual or a",
492     "Standard Colormap.",
493     "",
494     "Choosing a composite operator is optional.  The default",
495     "operator is replace.  However, you must choose a location to",
496     "composite your image and press button 1.  Press and hold the",
497     "button before releasing and an outline of the image will",
498     "appear to help you identify your location.",
499     "",
500     "The actual colors of the composite image is saved.  However,",
501     "the color that appears in image window may be different.",
502     "For example, on a monochrome screen image window will appear",
503     "black or white even though your composited image may have",
504     "many colors.  If the image is saved to a file it is written",
505     "with the correct colors.  To assure the correct colors are",
506     "saved in the final image, any PseudoClass image is promoted",
507     "to DirectClass (see miff(5)).  To force a PseudoClass image",
508     "to remain PseudoClass, use -colors.",
509     (char *) NULL,
510   },
511   *ImageCutHelp[] =
512   {
513     "In cut mode, the Command widget has these options:",
514     "",
515     "    Help",
516     "    Dismiss",
517     "",
518     "To define a cut region, press button 1 and drag.  The",
519     "cut region is defined by a highlighted rectangle that",
520     "expands or contracts as it follows the pointer.  Once you",
521     "are satisfied with the cut region, release the button.",
522     "You are now in rectify mode.  In rectify mode, the Command",
523     "widget has these options:",
524     "",
525     "    Cut",
526     "    Help",
527     "    Dismiss",
528     "",
529     "You can make adjustments by moving the pointer to one of the",
530     "cut rectangle corners, pressing a button, and dragging.",
531     "Finally, press Cut to commit your copy region.  To",
532     "exit without cutting the image, press Dismiss.",
533     (char *) NULL,
534   },
535   *ImageCopyHelp[] =
536   {
537     "In copy mode, the Command widget has these options:",
538     "",
539     "    Help",
540     "    Dismiss",
541     "",
542     "To define a copy region, press button 1 and drag.  The",
543     "copy region is defined by a highlighted rectangle that",
544     "expands or contracts as it follows the pointer.  Once you",
545     "are satisfied with the copy region, release the button.",
546     "You are now in rectify mode.  In rectify mode, the Command",
547     "widget has these options:",
548     "",
549     "    Copy",
550     "    Help",
551     "    Dismiss",
552     "",
553     "You can make adjustments by moving the pointer to one of the",
554     "copy rectangle corners, pressing a button, and dragging.",
555     "Finally, press Copy to commit your copy region.  To",
556     "exit without copying the image, press Dismiss.",
557     (char *) NULL,
558   },
559   *ImageCropHelp[] =
560   {
561     "In crop mode, the Command widget has these options:",
562     "",
563     "    Help",
564     "    Dismiss",
565     "",
566     "To define a cropping region, press button 1 and drag.  The",
567     "cropping region is defined by a highlighted rectangle that",
568     "expands or contracts as it follows the pointer.  Once you",
569     "are satisfied with the cropping region, release the button.",
570     "You are now in rectify mode.  In rectify mode, the Command",
571     "widget has these options:",
572     "",
573     "    Crop",
574     "    Help",
575     "    Dismiss",
576     "",
577     "You can make adjustments by moving the pointer to one of the",
578     "cropping rectangle corners, pressing a button, and dragging.",
579     "Finally, press Crop to commit your cropping region.  To",
580     "exit without cropping the image, press Dismiss.",
581     (char *) NULL,
582   },
583   *ImageDrawHelp[] =
584   {
585     "The cursor changes to a crosshair to indicate you are in",
586     "draw mode.  To exit immediately, press Dismiss.  In draw mode,",
587     "the Command widget has these options:",
588     "",
589     "    Element",
590     "      point",
591     "      line",
592     "      rectangle",
593     "      fill rectangle",
594     "      circle",
595     "      fill circle",
596     "      ellipse",
597     "      fill ellipse",
598     "      polygon",
599     "      fill polygon",
600     "    Color",
601     "      black",
602     "      blue",
603     "      cyan",
604     "      green",
605     "      gray",
606     "      red",
607     "      magenta",
608     "      yellow",
609     "      white",
610     "      transparent",
611     "      Browser...",
612     "    Stipple",
613     "      Brick",
614     "      Diagonal",
615     "      Scales",
616     "      Vertical",
617     "      Wavy",
618     "      Translucent",
619     "      Opaque",
620     "      Open...",
621     "    Width",
622     "      1",
623     "      2",
624     "      4",
625     "      8",
626     "      16",
627     "      Dialog...",
628     "    Undo",
629     "    Help",
630     "    Dismiss",
631     "",
632     "Choose a drawing primitive from the Element sub-menu.",
633     "",
634     "Choose a color from the Color sub-menu.  Additional",
635     "colors can be specified with the color browser.",
636     "",
637     "If you choose the color browser and press Grab, you can",
638     "select the color by moving the pointer to the desired",
639     "color on the screen and press any button.  The transparent",
640     "color updates the image matte channel and is useful for",
641     "image compositing.",
642     "",
643     "Choose a stipple, if appropriate, from the Stipple sub-menu.",
644     "Additional stipples can be specified with the file browser.",
645     "Stipples obtained from the file browser must be on disk in the",
646     "X11 bitmap format.",
647     "",
648     "Choose a width, if appropriate, from the Width sub-menu.  To",
649     "choose a specific width select the Dialog widget.",
650     "",
651     "Choose a point in the Image window and press button 1 and",
652     "hold.  Next, move the pointer to another location in the",
653     "image.  As you move, a line connects the initial location and",
654     "the pointer.  When you release the button, the image is",
655     "updated with the primitive you just drew.  For polygons, the",
656     "image is updated when you press and release the button without",
657     "moving the pointer.",
658     "",
659     "To cancel image drawing, move the pointer back to the",
660     "starting point of the line and release the button.",
661     (char *) NULL,
662   },
663   *DisplayHelp[] =
664   {
665     "BUTTONS",
666     "  The effects of each button press is described below.  Three",
667     "  buttons are required.  If you have a two button mouse,",
668     "  button 1 and 3 are returned.  Press ALT and button 3 to",
669     "  simulate button 2.",
670     "",
671     "  1    Press this button to map or unmap the Command widget.",
672     "",
673     "  2    Press and drag to define a region of the image to",
674     "       magnify.",
675     "",
676     "  3    Press and drag to choose from a select set of commands.",
677     "       This button behaves differently if the image being",
678     "       displayed is a visual image directory.  Here, choose a",
679     "       particular tile of the directory and press this button and",
680     "       drag to select a command from a pop-up menu.  Choose from",
681     "       these menu items:",
682     "",
683     "           Open",
684     "           Next",
685     "           Former",
686     "           Delete",
687     "           Update",
688     "",
689     "       If you choose Open, the image represented by the tile is",
690     "       displayed.  To return to the visual image directory, choose",
691     "       Next from the Command widget.  Next and Former moves to the",
692     "       next or former image respectively.  Choose Delete to delete",
693     "       a particular image tile.  Finally, choose Update to",
694     "       synchronize all the image tiles with their respective",
695     "       images.",
696     "",
697     "COMMAND WIDGET",
698     "  The Command widget lists a number of sub-menus and commands.",
699     "  They are",
700     "",
701     "      File",
702     "        Open...",
703     "        Next",
704     "        Former",
705     "        Select...",
706     "        Save...",
707     "        Print...",
708     "        Delete...",
709     "        New...",
710     "        Visual Directory...",
711     "        Quit",
712     "      Edit",
713     "        Undo",
714     "        Redo",
715     "        Cut",
716     "        Copy",
717     "        Paste",
718     "      View",
719     "        Half Size",
720     "        Original Size",
721     "        Double Size",
722     "        Resize...",
723     "        Apply",
724     "        Refresh",
725     "        Restore",
726     "      Transform",
727     "        Crop",
728     "        Chop",
729     "        Flop",
730     "        Flip",
731     "        Rotate Right",
732     "        Rotate Left",
733     "        Rotate...",
734     "        Shear...",
735     "        Roll...",
736     "        Trim Edges",
737     "      Enhance",
738     "        Brightness...",
739     "        Saturation...",
740     "        Hue...",
741     "        Gamma...",
742     "        Sharpen...",
743     "        Dull",
744     "        Contrast Stretch...",
745     "        Sigmoidal Contrast...",
746     "        Normalize",
747     "        Equalize",
748     "        Negate",
749     "        Grayscale",
750     "        Map...",
751     "        Quantize...",
752     "      Effects",
753     "        Despeckle",
754     "        Emboss",
755     "        Reduce Noise",
756     "        Add Noise",
757     "        Sharpen...",
758     "        Blur...",
759     "        Threshold...",
760     "        Edge Detect...",
761     "        Spread...",
762     "        Shade...",
763     "        Painting...",
764     "        Segment...",
765     "      F/X",
766     "        Solarize...",
767     "        Sepia Tone...",
768     "        Swirl...",
769     "        Implode...",
770     "        Vignette...",
771     "        Wave...",
772     "        Oil Painting...",
773     "        Charcoal Drawing...",
774     "      Image Edit",
775     "        Annotate...",
776     "        Draw...",
777     "        Color...",
778     "        Matte...",
779     "        Composite...",
780     "        Add Border...",
781     "        Add Frame...",
782     "        Comment...",
783     "        Launch...",
784     "        Region of Interest...",
785     "      Miscellany",
786     "        Image Info",
787     "        Zoom Image",
788     "        Show Preview...",
789     "        Show Histogram",
790     "        Show Matte",
791     "        Background...",
792     "        Slide Show",
793     "        Preferences...",
794     "      Help",
795     "        Overview",
796     "        Browse Documentation",
797     "        About Display",
798     "",
799     "  Menu items with a indented triangle have a sub-menu.  They",
800     "  are represented above as the indented items.  To access a",
801     "  sub-menu item, move the pointer to the appropriate menu and",
802     "  press a button and drag.  When you find the desired sub-menu",
803     "  item, release the button and the command is executed.  Move",
804     "  the pointer away from the sub-menu if you decide not to",
805     "  execute a particular command.",
806     "",
807     "KEYBOARD ACCELERATORS",
808     "  Accelerators are one or two key presses that effect a",
809     "  particular command.  The keyboard accelerators that",
810     "  display(1) understands is:",
811     "",
812     "  Ctl+O     Press to open an image from a file.",
813     "",
814     "  space     Press to display the next image.",
815     "",
816     "            If the image is a multi-paged document such as a Postscript",
817     "            document, you can skip ahead several pages by preceding",
818     "            this command with a number.  For example to display the",
819     "            third page beyond the current page, press 3<space>.",
820     "",
821     "  backspace Press to display the former image.",
822     "",
823     "            If the image is a multi-paged document such as a Postscript",
824     "            document, you can skip behind several pages by preceding",
825     "            this command with a number.  For example to display the",
826     "            third page preceding the current page, press 3<backspace>.",
827     "",
828     "  Ctl+S     Press to write the image to a file.",
829     "",
830     "  Ctl+P     Press to print the image to a Postscript printer.",
831     "",
832     "  Ctl+D     Press to delete an image file.",
833     "",
834     "  Ctl+N     Press to create a blank canvas.",
835     "",
836     "  Ctl+Q     Press to discard all images and exit program.",
837     "",
838     "  Ctl+Z     Press to undo last image transformation.",
839     "",
840     "  Ctl+R     Press to redo last image transformation.",
841     "",
842     "  Ctl+X     Press to cut a region of the image.",
843     "",
844     "  Ctl+C     Press to copy a region of the image.",
845     "",
846     "  Ctl+V     Press to paste a region to the image.",
847     "",
848     "  <         Press to half the image size.",
849     "",
850     "  -         Press to return to the original image size.",
851     "",
852     "  >         Press to double the image size.",
853     "",
854     "  %         Press to resize the image to a width and height you",
855     "            specify.",
856     "",
857     "Cmd-A       Press to make any image transformations permanent."
858     "",
859     "            By default, any image size transformations are applied",
860     "            to the original image to create the image displayed on",
861     "            the X server.  However, the transformations are not",
862     "            permanent (i.e. the original image does not change",
863     "            size only the X image does).  For example, if you",
864     "            press > the X image will appear to double in size,",
865     "            but the original image will in fact remain the same size.",
866     "            To force the original image to double in size, press >",
867     "            followed by Cmd-A.",
868     "",
869     "  @         Press to refresh the image window.",
870     "",
871     "  C         Press to cut out a rectangular region of the image.",
872     "",
873     "  [         Press to chop the image.",
874     "",
875     "  H         Press to flop image in the horizontal direction.",
876     "",
877     "  V         Press to flip image in the vertical direction.",
878     "",
879     "  /         Press to rotate the image 90 degrees clockwise.",
880     "",
881     " \\         Press to rotate the image 90 degrees counter-clockwise.",
882     "",
883     "  *         Press to rotate the image the number of degrees you",
884     "            specify.",
885     "",
886     "  S         Press to shear the image the number of degrees you",
887     "            specify.",
888     "",
889     "  R         Press to roll the image.",
890     "",
891     "  T         Press to trim the image edges.",
892     "",
893     "  Shft-H    Press to vary the image hue.",
894     "",
895     "  Shft-S    Press to vary the color saturation.",
896     "",
897     "  Shft-L    Press to vary the color brightness.",
898     "",
899     "  Shft-G    Press to gamma correct the image.",
900     "",
901     "  Shft-C    Press to sharpen the image contrast.",
902     "",
903     "  Shft-Z    Press to dull the image contrast.",
904     "",
905     "  =         Press to perform histogram equalization on the image.",
906     "",
907     "  Shft-N    Press to perform histogram normalization on the image.",
908     "",
909     "  Shft-~    Press to negate the colors of the image.",
910     "",
911     "  .         Press to convert the image colors to gray.",
912     "",
913     "  Shft-#    Press to set the maximum number of unique colors in the",
914     "            image.",
915     "",
916     "  F2        Press to reduce the speckles in an image.",
917     "",
918     "  F3        Press to eliminate peak noise from an image.",
919     "",
920     "  F4        Press to add noise to an image.",
921     "",
922     "  F5        Press to sharpen an image.",
923     "",
924     "  F6        Press to delete an image file.",
925     "",
926     "  F7        Press to threshold the image.",
927     "",
928     "  F8        Press to detect edges within an image.",
929     "",
930     "  F9        Press to emboss an image.",
931     "",
932     "  F10       Press to displace pixels by a random amount.",
933     "",
934     "  F11       Press to negate all pixels above the threshold level.",
935     "",
936     "  F12       Press to shade the image using a distant light source.",
937     "",
938     "  F13       Press to lighten or darken image edges to create a 3-D effect.",
939     "",
940     "  F14       Press to segment the image by color.",
941     "",
942     "  Meta-S    Press to swirl image pixels about the center.",
943     "",
944     "  Meta-I    Press to implode image pixels about the center.",
945     "",
946     "  Meta-W    Press to alter an image along a sine wave.",
947     "",
948     "  Meta-P    Press to simulate an oil painting.",
949     "",
950     "  Meta-C    Press to simulate a charcoal drawing.",
951     "",
952     "  Alt-A     Press to annotate the image with text.",
953     "",
954     "  Alt-D     Press to draw on an image.",
955     "",
956     "  Alt-P     Press to edit an image pixel color.",
957     "",
958     "  Alt-M     Press to edit the image matte information.",
959     "",
960     "  Alt-V     Press to composite the image with another.",
961     "",
962     "  Alt-B     Press to add a border to the image.",
963     "",
964     "  Alt-F     Press to add an ornamental border to the image.",
965     "",
966     "  Alt-Shft-!",
967     "            Press to add an image comment.",
968     "",
969     "  Ctl-A     Press to apply image processing techniques to a region",
970     "            of interest.",
971     "",
972     "  Shft-?    Press to display information about the image.",
973     "",
974     "  Shft-+    Press to map the zoom image window.",
975     "",
976     "  Shft-P    Press to preview an image enhancement, effect, or f/x.",
977     "",
978     "  F1        Press to display helpful information about display(1).",
979     "",
980     "  Find      Press to browse documentation about ImageMagick.",
981     "",
982     "  1-9       Press to change the level of magnification.",
983     "",
984     "  Use the arrow keys to move the image one pixel up, down,",
985     "  left, or right within the magnify window.  Be sure to first",
986     "  map the magnify window by pressing button 2.",
987     "",
988     "  Press ALT and one of the arrow keys to trim off one pixel",
989     "  from any side of the image.",
990     (char *) NULL,
991   },
992   *ImageMatteEditHelp[] =
993   {
994     "Matte information within an image is useful for some",
995     "operations such as image compositing (See IMAGE",
996     "COMPOSITING).  This extra channel usually defines a mask",
997     "which represents a sort of a cookie-cutter for the image.",
998     "This the case when matte is opaque (full coverage) for",
999     "pixels inside the shape, zero outside, and between 0 and",
1000     "QuantumRange on the boundary.",
1001     "",
1002     "A small window appears showing the location of the cursor in",
1003     "the image window. You are now in matte edit mode.  To exit",
1004     "immediately, press Dismiss.  In matte edit mode, the Command",
1005     "widget has these options:",
1006     "",
1007     "    Method",
1008     "      point",
1009     "      replace",
1010     "      floodfill",
1011     "      filltoborder",
1012     "      reset",
1013     "    Border Color",
1014     "      black",
1015     "      blue",
1016     "      cyan",
1017     "      green",
1018     "      gray",
1019     "      red",
1020     "      magenta",
1021     "      yellow",
1022     "      white",
1023     "      Browser...",
1024     "    Fuzz",
1025     "      0%",
1026     "      2%",
1027     "      5%",
1028     "      10%",
1029     "      15%",
1030     "      Dialog...",
1031     "    Matte",
1032     "      Opaque",
1033     "      Transparent",
1034     "      Dialog...",
1035     "    Undo",
1036     "    Help",
1037     "    Dismiss",
1038     "",
1039     "Choose a matte editing method from the Method sub-menu of",
1040     "the Command widget.  The point method changes the matte value",
1041     "of any pixel selected with the pointer until the button is",
1042     "is released.  The replace method changes the matte value of",
1043     "any pixel that matches the color of the pixel you select with",
1044     "a button press.  Floodfill changes the matte value of any pixel",
1045     "that matches the color of the pixel you select with a button",
1046     "press and is a neighbor.  Whereas filltoborder changes the matte",
1047     "value any neighbor pixel that is not the border color.  Finally",
1048     "reset changes the entire image to the designated matte value.",
1049     "",
1050     "Choose Matte Value and pick Opaque or Transarent.  For other values",
1051     "select the Dialog entry.  Here a dialog appears requesting a matte",
1052     "value.  The value you select is assigned as the opacity value of the",
1053     "selected pixel or pixels.",
1054     "",
1055     "Now, press any button to select a pixel within the image",
1056     "window to change its matte value.",
1057     "",
1058     "If the Magnify widget is mapped, it can be helpful in positioning",
1059     "your pointer within the image (refer to button 2).",
1060     "",
1061     "Matte information is only valid in a DirectClass image.",
1062     "Therefore, any PseudoClass image is promoted to DirectClass",
1063     "(see miff(5)).  Note that matte information for PseudoClass",
1064     "is not retained for colormapped X server visuals (e.g.",
1065     "StaticColor, StaticColor, GrayScale, PseudoColor) unless you",
1066     "immediately save your image to a file (refer to Write).",
1067     "Correct matte editing behavior may require a TrueColor or",
1068     "DirectColor visual or a Standard Colormap.",
1069     (char *) NULL,
1070   },
1071   *ImagePanHelp[] =
1072   {
1073     "When an image exceeds the width or height of the X server",
1074     "screen, display maps a small panning icon.  The rectangle",
1075     "within the panning icon shows the area that is currently",
1076     "displayed in the image window.  To pan about the image,",
1077     "press any button and drag the pointer within the panning",
1078     "icon.  The pan rectangle moves with the pointer and the",
1079     "image window is updated to reflect the location of the",
1080     "rectangle within the panning icon.  When you have selected",
1081     "the area of the image you wish to view, release the button.",
1082     "",
1083     "Use the arrow keys to pan the image one pixel up, down,",
1084     "left, or right within the image window.",
1085     "",
1086     "The panning icon is withdrawn if the image becomes smaller",
1087     "than the dimensions of the X server screen.",
1088     (char *) NULL,
1089   },
1090   *ImagePasteHelp[] =
1091   {
1092     "A small window appears showing the location of the cursor in",
1093     "the image window. You are now in paste mode.  To exit",
1094     "immediately, press Dismiss.  In paste mode, the Command",
1095     "widget has these options:",
1096     "",
1097     "    Operators",
1098     "      over",
1099     "      in",
1100     "      out",
1101     "      atop",
1102     "      xor",
1103     "      plus",
1104     "      minus",
1105     "      add",
1106     "      subtract",
1107     "      difference",
1108     "      replace",
1109     "    Help",
1110     "    Dismiss",
1111     "",
1112     "Choose a composite operation from the Operators sub-menu of",
1113     "the Command widget.  How each operator behaves is described",
1114     "below.  Image window is the image currently displayed on",
1115     "your X server and image is the image obtained with the File",
1116     "Browser widget.",
1117     "",
1118     "Over     The result is the union of the two image shapes,",
1119     "         with image obscuring image window in the region of",
1120     "         overlap.",
1121     "",
1122     "In       The result is simply image cut by the shape of",
1123     "         image window.  None of the image data of image",
1124     "         window is in the result.",
1125     "",
1126     "Out      The resulting image is image with the shape of",
1127     "         image window cut out.",
1128     "",
1129     "Atop     The result is the same shape as image image window,",
1130     "         with image obscuring image window where the image",
1131     "         shapes overlap.  Note this differs from over",
1132     "         because the portion of image outside image window's",
1133     "         shape does not appear in the result.",
1134     "",
1135     "Xor      The result is the image data from both image and",
1136     "         image window that is outside the overlap region.",
1137     "         The overlap region is blank.",
1138     "",
1139     "Plus     The result is just the sum of the image data.",
1140     "         Output values are cropped to QuantumRange (no overflow).",
1141     "         This operation is independent of the matte",
1142     "         channels.",
1143     "",
1144     "Minus    The result of image - image window, with underflow",
1145     "         cropped to zero.",
1146     "",
1147     "Add      The result of image + image window, with overflow",
1148     "         wrapping around (mod 256).",
1149     "",
1150     "Subtract The result of image - image window, with underflow",
1151     "         wrapping around (mod 256).  The add and subtract",
1152     "         operators can be used to perform reversible",
1153     "         transformations.",
1154     "",
1155     "Difference",
1156     "         The result of abs(image - image window).  This",
1157     "         useful for comparing two very similar images.",
1158     "",
1159     "Copy     The resulting image is image window replaced with",
1160     "         image.  Here the matte information is ignored.",
1161     "",
1162     "CopyRed  The red layer of the image window is replace with",
1163     "         the red layer of the image.  The other layers are",
1164     "         untouched.",
1165     "",
1166     "CopyGreen",
1167     "         The green layer of the image window is replace with",
1168     "         the green layer of the image.  The other layers are",
1169     "         untouched.",
1170     "",
1171     "CopyBlue The blue layer of the image window is replace with",
1172     "         the blue layer of the image.  The other layers are",
1173     "         untouched.",
1174     "",
1175     "CopyOpacity",
1176     "         The matte layer of the image window is replace with",
1177     "         the matte layer of the image.  The other layers are",
1178     "         untouched.",
1179     "",
1180     "The image compositor requires a matte, or alpha channel in",
1181     "the image for some operations.  This extra channel usually",
1182     "defines a mask which represents a sort of a cookie-cutter",
1183     "for the image.  This the case when matte is opaque (full",
1184     "coverage) for pixels inside the shape, zero outside, and",
1185     "between 0 and QuantumRange on the boundary.  If image does not",
1186     "have a matte channel, it is initialized with 0 for any pixel",
1187     "matching in color to pixel location (0,0), otherwise QuantumRange.",
1188     "",
1189     "Note that matte information for image window is not retained",
1190     "for colormapped X server visuals (e.g. StaticColor,",
1191     "StaticColor, GrayScale, PseudoColor).  Correct compositing",
1192     "behavior may require a TrueColor or DirectColor visual or a",
1193     "Standard Colormap.",
1194     "",
1195     "Choosing a composite operator is optional.  The default",
1196     "operator is replace.  However, you must choose a location to",
1197     "paste your image and press button 1.  Press and hold the",
1198     "button before releasing and an outline of the image will",
1199     "appear to help you identify your location.",
1200     "",
1201     "The actual colors of the pasted image is saved.  However,",
1202     "the color that appears in image window may be different.",
1203     "For example, on a monochrome screen image window will appear",
1204     "black or white even though your pasted image may have",
1205     "many colors.  If the image is saved to a file it is written",
1206     "with the correct colors.  To assure the correct colors are",
1207     "saved in the final image, any PseudoClass image is promoted",
1208     "to DirectClass (see miff(5)).  To force a PseudoClass image",
1209     "to remain PseudoClass, use -colors.",
1210     (char *) NULL,
1211   },
1212   *ImageROIHelp[] =
1213   {
1214     "In region of interest mode, the Command widget has these",
1215     "options:",
1216     "",
1217     "    Help",
1218     "    Dismiss",
1219     "",
1220     "To define a region of interest, press button 1 and drag.",
1221     "The region of interest is defined by a highlighted rectangle",
1222     "that expands or contracts as it follows the pointer.  Once",
1223     "you are satisfied with the region of interest, release the",
1224     "button.  You are now in apply mode.  In apply mode the",
1225     "Command widget has these options:",
1226     "",
1227     "      File",
1228     "        Save...",
1229     "        Print...",
1230     "      Edit",
1231     "        Undo",
1232     "        Redo",
1233     "      Transform",
1234     "        Flop",
1235     "        Flip",
1236     "        Rotate Right",
1237     "        Rotate Left",
1238     "      Enhance",
1239     "        Hue...",
1240     "        Saturation...",
1241     "        Brightness...",
1242     "        Gamma...",
1243     "        Spiff",
1244     "        Dull",
1245     "        Contrast Stretch",
1246     "        Sigmoidal Contrast...",
1247     "        Normalize",
1248     "        Equalize",
1249     "        Negate",
1250     "        Grayscale",
1251     "        Map...",
1252     "        Quantize...",
1253     "      Effects",
1254     "        Despeckle",
1255     "        Emboss",
1256     "        Reduce Noise",
1257     "        Sharpen...",
1258     "        Blur...",
1259     "        Threshold...",
1260     "        Edge Detect...",
1261     "        Spread...",
1262     "        Shade...",
1263     "        Raise...",
1264     "        Segment...",
1265     "      F/X",
1266     "        Solarize...",
1267     "        Sepia Tone...",
1268     "        Swirl...",
1269     "        Implode...",
1270     "        Vignette...",
1271     "        Wave...",
1272     "        Oil Painting...",
1273     "        Charcoal Drawing...",
1274     "      Miscellany",
1275     "        Image Info",
1276     "        Zoom Image",
1277     "        Show Preview...",
1278     "        Show Histogram",
1279     "        Show Matte",
1280     "      Help",
1281     "      Dismiss",
1282     "",
1283     "You can make adjustments to the region of interest by moving",
1284     "the pointer to one of the rectangle corners, pressing a",
1285     "button, and dragging.  Finally, choose an image processing",
1286     "technique from the Command widget.  You can choose more than",
1287     "one image processing technique to apply to an area.",
1288     "Alternatively, you can move the region of interest before",
1289     "applying another image processing technique.  To exit, press",
1290     "Dismiss.",
1291     (char *) NULL,
1292   },
1293   *ImageRotateHelp[] =
1294   {
1295     "In rotate mode, the Command widget has these options:",
1296     "",
1297     "    Pixel Color",
1298     "      black",
1299     "      blue",
1300     "      cyan",
1301     "      green",
1302     "      gray",
1303     "      red",
1304     "      magenta",
1305     "      yellow",
1306     "      white",
1307     "      Browser...",
1308     "    Direction",
1309     "      horizontal",
1310     "      vertical",
1311     "    Help",
1312     "    Dismiss",
1313     "",
1314     "Choose a background color from the Pixel Color sub-menu.",
1315     "Additional background colors can be specified with the color",
1316     "browser.  You can change the menu colors by setting the X",
1317     "resources pen1 through pen9.",
1318     "",
1319     "If you choose the color browser and press Grab, you can",
1320     "select the background color by moving the pointer to the",
1321     "desired color on the screen and press any button.",
1322     "",
1323     "Choose a point in the image window and press this button and",
1324     "hold.  Next, move the pointer to another location in the",
1325     "image.  As you move a line connects the initial location and",
1326     "the pointer.  When you release the button, the degree of",
1327     "image rotation is determined by the slope of the line you",
1328     "just drew.  The slope is relative to the direction you",
1329     "choose from the Direction sub-menu of the Command widget.",
1330     "",
1331     "To cancel the image rotation, move the pointer back to the",
1332     "starting point of the line and release the button.",
1333     (char *) NULL,
1334   };
1335 \f
1336 /*
1337   Enumeration declarations.
1338 */
1339 typedef enum
1340 {
1341   CopyMode,
1342   CropMode,
1343   CutMode
1344 } ClipboardMode;
1345
1346 typedef enum
1347 {
1348   OpenCommand,
1349   NextCommand,
1350   FormerCommand,
1351   SelectCommand,
1352   SaveCommand,
1353   PrintCommand,
1354   DeleteCommand,
1355   NewCommand,
1356   VisualDirectoryCommand,
1357   QuitCommand,
1358   UndoCommand,
1359   RedoCommand,
1360   CutCommand,
1361   CopyCommand,
1362   PasteCommand,
1363   HalfSizeCommand,
1364   OriginalSizeCommand,
1365   DoubleSizeCommand,
1366   ResizeCommand,
1367   ApplyCommand,
1368   RefreshCommand,
1369   RestoreCommand,
1370   CropCommand,
1371   ChopCommand,
1372   FlopCommand,
1373   FlipCommand,
1374   RotateRightCommand,
1375   RotateLeftCommand,
1376   RotateCommand,
1377   ShearCommand,
1378   RollCommand,
1379   TrimCommand,
1380   HueCommand,
1381   SaturationCommand,
1382   BrightnessCommand,
1383   GammaCommand,
1384   SpiffCommand,
1385   DullCommand,
1386   ContrastStretchCommand,
1387   SigmoidalContrastCommand,
1388   NormalizeCommand,
1389   EqualizeCommand,
1390   NegateCommand,
1391   GrayscaleCommand,
1392   MapCommand,
1393   QuantizeCommand,
1394   DespeckleCommand,
1395   EmbossCommand,
1396   ReduceNoiseCommand,
1397   AddNoiseCommand,
1398   SharpenCommand,
1399   BlurCommand,
1400   ThresholdCommand,
1401   EdgeDetectCommand,
1402   SpreadCommand,
1403   ShadeCommand,
1404   RaiseCommand,
1405   SegmentCommand,
1406   SolarizeCommand,
1407   SepiaToneCommand,
1408   SwirlCommand,
1409   ImplodeCommand,
1410   VignetteCommand,
1411   WaveCommand,
1412   OilPaintCommand,
1413   CharcoalDrawCommand,
1414   AnnotateCommand,
1415   DrawCommand,
1416   ColorCommand,
1417   MatteCommand,
1418   CompositeCommand,
1419   AddBorderCommand,
1420   AddFrameCommand,
1421   CommentCommand,
1422   LaunchCommand,
1423   RegionofInterestCommand,
1424   ROIHelpCommand,
1425   ROIDismissCommand,
1426   InfoCommand,
1427   ZoomCommand,
1428   ShowPreviewCommand,
1429   ShowHistogramCommand,
1430   ShowMatteCommand,
1431   BackgroundCommand,
1432   SlideShowCommand,
1433   PreferencesCommand,
1434   HelpCommand,
1435   BrowseDocumentationCommand,
1436   VersionCommand,
1437   SaveToUndoBufferCommand,
1438   FreeBuffersCommand,
1439   NullCommand
1440 } CommandType;
1441
1442 typedef enum
1443 {
1444   AnnotateNameCommand,
1445   AnnotateFontColorCommand,
1446   AnnotateBackgroundColorCommand,
1447   AnnotateRotateCommand,
1448   AnnotateHelpCommand,
1449   AnnotateDismissCommand,
1450   TextHelpCommand,
1451   TextApplyCommand,
1452   ChopDirectionCommand,
1453   ChopHelpCommand,
1454   ChopDismissCommand,
1455   HorizontalChopCommand,
1456   VerticalChopCommand,
1457   ColorEditMethodCommand,
1458   ColorEditColorCommand,
1459   ColorEditBorderCommand,
1460   ColorEditFuzzCommand,
1461   ColorEditUndoCommand,
1462   ColorEditHelpCommand,
1463   ColorEditDismissCommand,
1464   CompositeOperatorsCommand,
1465   CompositeDissolveCommand,
1466   CompositeDisplaceCommand,
1467   CompositeHelpCommand,
1468   CompositeDismissCommand,
1469   CropHelpCommand,
1470   CropDismissCommand,
1471   RectifyCopyCommand,
1472   RectifyHelpCommand,
1473   RectifyDismissCommand,
1474   DrawElementCommand,
1475   DrawColorCommand,
1476   DrawStippleCommand,
1477   DrawWidthCommand,
1478   DrawUndoCommand,
1479   DrawHelpCommand,
1480   DrawDismissCommand,
1481   MatteEditMethod,
1482   MatteEditBorderCommand,
1483   MatteEditFuzzCommand,
1484   MatteEditValueCommand,
1485   MatteEditUndoCommand,
1486   MatteEditHelpCommand,
1487   MatteEditDismissCommand,
1488   PasteOperatorsCommand,
1489   PasteHelpCommand,
1490   PasteDismissCommand,
1491   RotateColorCommand,
1492   RotateDirectionCommand,
1493   RotateCropCommand,
1494   RotateSharpenCommand,
1495   RotateHelpCommand,
1496   RotateDismissCommand,
1497   HorizontalRotateCommand,
1498   VerticalRotateCommand,
1499   TileLoadCommand,
1500   TileNextCommand,
1501   TileFormerCommand,
1502   TileDeleteCommand,
1503   TileUpdateCommand
1504 } ModeType;
1505 \f
1506 /*
1507   Stipples.
1508 */
1509 #define BricksWidth  20
1510 #define BricksHeight  20
1511 #define DiagonalWidth  16
1512 #define DiagonalHeight  16
1513 #define HighlightWidth  8
1514 #define HighlightHeight  8
1515 #define OpaqueWidth  8
1516 #define OpaqueHeight  8
1517 #define ScalesWidth  16
1518 #define ScalesHeight  16
1519 #define ShadowWidth  8
1520 #define ShadowHeight  8
1521 #define VerticalWidth  16
1522 #define VerticalHeight  16
1523 #define WavyWidth  16
1524 #define WavyHeight  16
1525 \f
1526 /*
1527   Constant declaration.
1528 */
1529 static const int
1530   RoiDelta = 8;
1531
1532 static const unsigned char
1533   BricksBitmap[] =
1534   {
1535     0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00,
1536     0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01,
1537     0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00,
1538     0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f,
1539     0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01
1540   },
1541   DiagonalBitmap[] =
1542   {
1543     0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88,
1544     0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22,
1545     0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22
1546   },
1547   ScalesBitmap[] =
1548   {
1549     0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3, 0x80, 0x80, 0x80, 0x80,
1550     0x41, 0x41, 0x3e, 0x3e, 0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3,
1551     0x80, 0x80, 0x80, 0x80, 0x41, 0x41, 0x3e, 0x3e
1552   },
1553   VerticalBitmap[] =
1554   {
1555     0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1556     0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1557     0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11
1558   },
1559   WavyBitmap[] =
1560   {
1561     0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfb, 0xff,
1562     0xe7, 0xff, 0x1f, 0xff, 0xff, 0xf8, 0xff, 0xe7, 0xff, 0xdf, 0xff, 0xbf,
1563     0xff, 0xbf, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f
1564   };
1565 \f
1566 /*
1567   Function prototypes.
1568 */
1569 static CommandType
1570   XImageWindowCommand(Display *,XResourceInfo *,XWindows *,
1571     const MagickStatusType,KeySym,Image **,ExceptionInfo *);
1572
1573 static Image
1574   *XMagickCommand(Display *,XResourceInfo *,XWindows *,const CommandType,
1575     Image **,ExceptionInfo *),
1576   *XOpenImage(Display *,XResourceInfo *,XWindows *,const MagickBooleanType),
1577   *XTileImage(Display *,XResourceInfo *,XWindows *,Image *,XEvent *,
1578     ExceptionInfo *),
1579   *XVisualDirectoryImage(Display *,XResourceInfo *,XWindows *,
1580     ExceptionInfo *);
1581
1582 static MagickBooleanType
1583   XAnnotateEditImage(Display *,XResourceInfo *,XWindows *,Image *,
1584     ExceptionInfo *),
1585   XBackgroundImage(Display *,XResourceInfo *,XWindows *,Image **,
1586     ExceptionInfo *),
1587   XChopImage(Display *,XResourceInfo *,XWindows *,Image **,
1588     ExceptionInfo *),
1589   XCropImage(Display *,XResourceInfo *,XWindows *,Image *,const ClipboardMode,
1590     ExceptionInfo *),
1591   XColorEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1592     ExceptionInfo *),
1593   XCompositeImage(Display *,XResourceInfo *,XWindows *,Image *,
1594     ExceptionInfo *),
1595   XConfigureImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1596   XDrawEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1597     ExceptionInfo *),
1598   XMatteEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1599     ExceptionInfo *),
1600   XPasteImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1601   XPrintImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1602   XRotateImage(Display *,XResourceInfo *,XWindows *,double,Image **,
1603     ExceptionInfo *),
1604   XROIImage(Display *,XResourceInfo *,XWindows *,Image **,ExceptionInfo *),
1605   XSaveImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1606   XTrimImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *);
1607
1608 static void
1609   XDrawPanRectangle(Display *,XWindows *),
1610   XImageCache(Display *,XResourceInfo *,XWindows *,const CommandType,Image **,
1611     ExceptionInfo *),
1612   XMagnifyImage(Display *,XWindows *,XEvent *,ExceptionInfo *),
1613   XMakePanImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1614   XPanImage(Display *,XWindows *,XEvent *,ExceptionInfo *),
1615   XMagnifyWindowCommand(Display *,XWindows *,const MagickStatusType,
1616     const KeySym,ExceptionInfo *),
1617   XSetCropGeometry(Display *,XWindows *,RectangleInfo *,Image *),
1618   XScreenEvent(Display *,XWindows *,XEvent *,ExceptionInfo *),
1619   XTranslateImage(Display *,XWindows *,Image *,const KeySym);
1620 \f
1621 /*
1622 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1623 %                                                                             %
1624 %                                                                             %
1625 %                                                                             %
1626 %   D i s p l a y I m a g e s                                                 %
1627 %                                                                             %
1628 %                                                                             %
1629 %                                                                             %
1630 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1631 %
1632 %  DisplayImages() displays an image sequence to any X window screen.  It
1633 %  returns a value other than 0 if successful.  Check the exception member
1634 %  of image to determine the reason for any failure.
1635 %
1636 %  The format of the DisplayImages method is:
1637 %
1638 %      MagickBooleanType DisplayImages(const ImageInfo *image_info,
1639 %        Image *images,ExceptionInfo *exception)
1640 %
1641 %  A description of each parameter follows:
1642 %
1643 %    o image_info: the image info.
1644 %
1645 %    o image: the image.
1646 %
1647 %    o exception: return any errors or warnings in this structure.
1648 %
1649 */
1650 MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
1651   Image *images,ExceptionInfo *exception)
1652 {
1653   char
1654     *argv[1];
1655
1656   Display
1657     *display;
1658
1659   Image
1660     *image;
1661
1662   register ssize_t
1663     i;
1664
1665   size_t
1666     state;
1667
1668   XrmDatabase
1669     resource_database;
1670
1671   XResourceInfo
1672     resource_info;
1673
1674   assert(image_info != (const ImageInfo *) NULL);
1675   assert(image_info->signature == MagickSignature);
1676   assert(images != (Image *) NULL);
1677   assert(images->signature == MagickSignature);
1678   if( IfMagickTrue(images->debug) )
1679     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
1680   display=XOpenDisplay(image_info->server_name);
1681   if (display == (Display *) NULL)
1682     {
1683       (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
1684         "UnableToOpenXServer","`%s'",XDisplayName(image_info->server_name));
1685       return(MagickFalse);
1686     }
1687   if (exception->severity != UndefinedException)
1688     CatchException(exception);
1689   (void) XSetErrorHandler(XError);
1690   resource_database=XGetResourceDatabase(display,GetClientName());
1691   (void) ResetMagickMemory(&resource_info,0,sizeof(resource_info));
1692   XGetResourceInfo(image_info,resource_database,GetClientName(),&resource_info);
1693   if (image_info->page != (char *) NULL)
1694     resource_info.image_geometry=AcquireString(image_info->page);
1695   resource_info.immutable=MagickTrue;
1696   argv[0]=AcquireString(GetClientName());
1697   state=DefaultState;
1698   for (i=0; (state & ExitState) == 0; i++)
1699   {
1700     if ((images->iterations != 0) && (i >= (ssize_t) images->iterations))
1701       break;
1702     image=GetImageFromList(images,i % GetImageListLength(images));
1703     (void) XDisplayImage(display,&resource_info,argv,1,&image,&state,exception);
1704   }
1705   (void) SetErrorHandler((ErrorHandler) NULL);
1706   (void) SetWarningHandler((WarningHandler) NULL);
1707   argv[0]=DestroyString(argv[0]);
1708   (void) XCloseDisplay(display);
1709   XDestroyResourceInfo(&resource_info);
1710   if (exception->severity != UndefinedException)
1711     return(MagickFalse);
1712   return(MagickTrue);
1713 }
1714 \f
1715 /*
1716 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1717 %                                                                             %
1718 %                                                                             %
1719 %                                                                             %
1720 %   R e m o t e D i s p l a y C o m m a n d                                   %
1721 %                                                                             %
1722 %                                                                             %
1723 %                                                                             %
1724 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1725 %
1726 %  RemoteDisplayCommand() encourages a remote display program to display the
1727 %  specified image filename.
1728 %
1729 %  The format of the RemoteDisplayCommand method is:
1730 %
1731 %      MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1732 %        const char *window,const char *filename,ExceptionInfo *exception)
1733 %
1734 %  A description of each parameter follows:
1735 %
1736 %    o image_info: the image info.
1737 %
1738 %    o window: Specifies the name or id of an X window.
1739 %
1740 %    o filename: the name of the image filename to display.
1741 %
1742 %    o exception: return any errors or warnings in this structure.
1743 %
1744 */
1745 MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1746   const char *window,const char *filename,ExceptionInfo *exception)
1747 {
1748   Display
1749     *display;
1750
1751   MagickStatusType
1752     status;
1753
1754   assert(image_info != (const ImageInfo *) NULL);
1755   assert(image_info->signature == MagickSignature);
1756   assert(filename != (char *) NULL);
1757   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
1758   display=XOpenDisplay(image_info->server_name);
1759   if (display == (Display *) NULL)
1760     {
1761       (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
1762         "UnableToOpenXServer","`%s'",XDisplayName(image_info->server_name));
1763       return(MagickFalse);
1764     }
1765   (void) XSetErrorHandler(XError);
1766   status=XRemoteCommand(display,window,filename);
1767   (void) XCloseDisplay(display);
1768   return(IsMagickTrue(status));
1769 }
1770 \f
1771 /*
1772 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1773 %                                                                             %
1774 %                                                                             %
1775 %                                                                             %
1776 +   X A n n o t a t e E d i t I m a g e                                       %
1777 %                                                                             %
1778 %                                                                             %
1779 %                                                                             %
1780 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1781 %
1782 %  XAnnotateEditImage() annotates the image with text.
1783 %
1784 %  The format of the XAnnotateEditImage method is:
1785 %
1786 %      MagickBooleanType XAnnotateEditImage(Display *display,
1787 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
1788 %        ExceptionInfo *exception)
1789 %
1790 %  A description of each parameter follows:
1791 %
1792 %    o display: Specifies a connection to an X server;  returned from
1793 %      XOpenDisplay.
1794 %
1795 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
1796 %
1797 %    o windows: Specifies a pointer to a XWindows structure.
1798 %
1799 %    o image: the image; returned from ReadImage.
1800 %
1801 */
1802
1803 static inline ssize_t MagickMax(const ssize_t x,const ssize_t y)
1804 {
1805   if (x > y)
1806     return(x);
1807   return(y);
1808 }
1809
1810 static inline ssize_t MagickMin(const ssize_t x,const ssize_t y)
1811 {
1812   if (x < y)
1813     return(x);
1814   return(y);
1815 }
1816
1817 static MagickBooleanType XAnnotateEditImage(Display *display,
1818   XResourceInfo *resource_info,XWindows *windows,Image *image,
1819   ExceptionInfo *exception)
1820 {
1821   static const char
1822     *AnnotateMenu[] =
1823     {
1824       "Font Name",
1825       "Font Color",
1826       "Box Color",
1827       "Rotate Text",
1828       "Help",
1829       "Dismiss",
1830       (char *) NULL
1831     },
1832     *TextMenu[] =
1833     {
1834       "Help",
1835       "Apply",
1836       (char *) NULL
1837     };
1838
1839   static const ModeType
1840     AnnotateCommands[] =
1841     {
1842       AnnotateNameCommand,
1843       AnnotateFontColorCommand,
1844       AnnotateBackgroundColorCommand,
1845       AnnotateRotateCommand,
1846       AnnotateHelpCommand,
1847       AnnotateDismissCommand
1848     },
1849     TextCommands[] =
1850     {
1851       TextHelpCommand,
1852       TextApplyCommand
1853     };
1854
1855   static MagickBooleanType
1856     transparent_box = MagickTrue,
1857     transparent_pen = MagickFalse;
1858
1859   static double
1860     degrees = 0.0;
1861
1862   static unsigned int
1863     box_id = MaxNumberPens-2,
1864     font_id = 0,
1865     pen_id = 0;
1866
1867   char
1868     command[MaxTextExtent],
1869     text[MaxTextExtent];
1870
1871   const char
1872     *ColorMenu[MaxNumberPens+1];
1873
1874   Cursor
1875     cursor;
1876
1877   GC
1878     annotate_context;
1879
1880   int
1881     id,
1882     pen_number,
1883     status,
1884     x,
1885     y;
1886
1887   KeySym
1888     key_symbol;
1889
1890   register char
1891     *p;
1892
1893   register ssize_t
1894     i;
1895
1896   unsigned int
1897     height,
1898     width;
1899
1900   size_t
1901     state;
1902
1903   XAnnotateInfo
1904     *annotate_info,
1905     *previous_info;
1906
1907   XColor
1908     color;
1909
1910   XFontStruct
1911     *font_info;
1912
1913   XEvent
1914     event,
1915     text_event;
1916
1917   /*
1918     Map Command widget.
1919   */
1920   (void) CloneString(&windows->command.name,"Annotate");
1921   windows->command.data=4;
1922   (void) XCommandWidget(display,windows,AnnotateMenu,(XEvent *) NULL);
1923   (void) XMapRaised(display,windows->command.id);
1924   XClientMessage(display,windows->image.id,windows->im_protocols,
1925     windows->im_update_widget,CurrentTime);
1926   /*
1927     Track pointer until button 1 is pressed.
1928   */
1929   XQueryPosition(display,windows->image.id,&x,&y);
1930   (void) XSelectInput(display,windows->image.id,
1931     windows->image.attributes.event_mask | PointerMotionMask);
1932   cursor=XCreateFontCursor(display,XC_left_side);
1933   (void) XCheckDefineCursor(display,windows->image.id,cursor);
1934   state=DefaultState;
1935   do
1936   {
1937     if( IfMagickTrue(windows->info.mapped) )
1938       {
1939         /*
1940           Display pointer position.
1941         */
1942         (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
1943           x+windows->image.x,y+windows->image.y);
1944         XInfoWidget(display,windows,text);
1945       }
1946     /*
1947       Wait for next event.
1948     */
1949     XScreenEvent(display,windows,&event,exception);
1950     if (event.xany.window == windows->command.id)
1951       {
1952         /*
1953           Select a command from the Command widget.
1954         */
1955         id=XCommandWidget(display,windows,AnnotateMenu,&event);
1956         (void) XCheckDefineCursor(display,windows->image.id,cursor);
1957         if (id < 0)
1958           continue;
1959         switch (AnnotateCommands[id])
1960         {
1961           case AnnotateNameCommand:
1962           {
1963             const char
1964               *FontMenu[MaxNumberFonts];
1965
1966             int
1967               font_number;
1968
1969             /*
1970               Initialize menu selections.
1971             */
1972             for (i=0; i < MaxNumberFonts; i++)
1973               FontMenu[i]=resource_info->font_name[i];
1974             FontMenu[MaxNumberFonts-2]="Browser...";
1975             FontMenu[MaxNumberFonts-1]=(const char *) NULL;
1976             /*
1977               Select a font name from the pop-up menu.
1978             */
1979             font_number=XMenuWidget(display,windows,AnnotateMenu[id],
1980               (const char **) FontMenu,command);
1981             if (font_number < 0)
1982               break;
1983             if (font_number == (MaxNumberFonts-2))
1984               {
1985                 static char
1986                   font_name[MaxTextExtent] = "fixed";
1987
1988                 /*
1989                   Select a font name from a browser.
1990                 */
1991                 resource_info->font_name[font_number]=font_name;
1992                 XFontBrowserWidget(display,windows,"Select",font_name);
1993                 if (*font_name == '\0')
1994                   break;
1995               }
1996             /*
1997               Initialize font info.
1998             */
1999             font_info=XLoadQueryFont(display,resource_info->font_name[
2000               font_number]);
2001             if (font_info == (XFontStruct *) NULL)
2002               {
2003                 XNoticeWidget(display,windows,"Unable to load font:",
2004                   resource_info->font_name[font_number]);
2005                 break;
2006               }
2007             font_id=(unsigned int) font_number;
2008             (void) XFreeFont(display,font_info);
2009             break;
2010           }
2011           case AnnotateFontColorCommand:
2012           {
2013             /*
2014               Initialize menu selections.
2015             */
2016             for (i=0; i < (int) (MaxNumberPens-2); i++)
2017               ColorMenu[i]=resource_info->pen_colors[i];
2018             ColorMenu[MaxNumberPens-2]="transparent";
2019             ColorMenu[MaxNumberPens-1]="Browser...";
2020             ColorMenu[MaxNumberPens]=(const char *) NULL;
2021             /*
2022               Select a pen color from the pop-up menu.
2023             */
2024             pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
2025               (const char **) ColorMenu,command);
2026             if (pen_number < 0)
2027               break;
2028             transparent_pen=pen_number == (MaxNumberPens-2) ? MagickTrue :
2029               MagickFalse;
2030             if( IfMagickTrue(transparent_pen) )
2031               break;
2032             if (pen_number == (MaxNumberPens-1))
2033               {
2034                 static char
2035                   color_name[MaxTextExtent] = "gray";
2036
2037                 /*
2038                   Select a pen color from a dialog.
2039                 */
2040                 resource_info->pen_colors[pen_number]=color_name;
2041                 XColorBrowserWidget(display,windows,"Select",color_name);
2042                 if (*color_name == '\0')
2043                   break;
2044               }
2045             /*
2046               Set pen color.
2047             */
2048             (void) XParseColor(display,windows->map_info->colormap,
2049               resource_info->pen_colors[pen_number],&color);
2050             XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
2051               (unsigned int) MaxColors,&color);
2052             windows->pixel_info->pen_colors[pen_number]=color;
2053             pen_id=(unsigned int) pen_number;
2054             break;
2055           }
2056           case AnnotateBackgroundColorCommand:
2057           {
2058             /*
2059               Initialize menu selections.
2060             */
2061             for (i=0; i < (int) (MaxNumberPens-2); i++)
2062               ColorMenu[i]=resource_info->pen_colors[i];
2063             ColorMenu[MaxNumberPens-2]="transparent";
2064             ColorMenu[MaxNumberPens-1]="Browser...";
2065             ColorMenu[MaxNumberPens]=(const char *) NULL;
2066             /*
2067               Select a pen color from the pop-up menu.
2068             */
2069             pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
2070               (const char **) ColorMenu,command);
2071             if (pen_number < 0)
2072               break;
2073             transparent_box=pen_number == (MaxNumberPens-2) ? MagickTrue :
2074               MagickFalse;
2075             if( IfMagickTrue(transparent_box) )
2076               break;
2077             if (pen_number == (MaxNumberPens-1))
2078               {
2079                 static char
2080                   color_name[MaxTextExtent] = "gray";
2081
2082                 /*
2083                   Select a pen color from a dialog.
2084                 */
2085                 resource_info->pen_colors[pen_number]=color_name;
2086                 XColorBrowserWidget(display,windows,"Select",color_name);
2087                 if (*color_name == '\0')
2088                   break;
2089               }
2090             /*
2091               Set pen color.
2092             */
2093             (void) XParseColor(display,windows->map_info->colormap,
2094               resource_info->pen_colors[pen_number],&color);
2095             XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
2096               (unsigned int) MaxColors,&color);
2097             windows->pixel_info->pen_colors[pen_number]=color;
2098             box_id=(unsigned int) pen_number;
2099             break;
2100           }
2101           case AnnotateRotateCommand:
2102           {
2103             int
2104               entry;
2105
2106             static char
2107               angle[MaxTextExtent] = "30.0";
2108
2109             static const char
2110               *RotateMenu[] =
2111               {
2112                 "-90",
2113                 "-45",
2114                 "-30",
2115                 "0",
2116                 "30",
2117                 "45",
2118                 "90",
2119                 "180",
2120                 "Dialog...",
2121                 (char *) NULL,
2122               };
2123
2124             /*
2125               Select a command from the pop-up menu.
2126             */
2127             entry=XMenuWidget(display,windows,AnnotateMenu[id],RotateMenu,
2128               command);
2129             if (entry < 0)
2130               break;
2131             if (entry != 8)
2132               {
2133                 degrees=StringToDouble(RotateMenu[entry],(char **) NULL);
2134                 break;
2135               }
2136             (void) XDialogWidget(display,windows,"OK","Enter rotation angle:",
2137               angle);
2138             if (*angle == '\0')
2139               break;
2140             degrees=StringToDouble(angle,(char **) NULL);
2141             break;
2142           }
2143           case AnnotateHelpCommand:
2144           {
2145             XTextViewWidget(display,resource_info,windows,MagickFalse,
2146               "Help Viewer - Image Annotation",ImageAnnotateHelp);
2147             break;
2148           }
2149           case AnnotateDismissCommand:
2150           {
2151             /*
2152               Prematurely exit.
2153             */
2154             state|=EscapeState;
2155             state|=ExitState;
2156             break;
2157           }
2158           default:
2159             break;
2160         }
2161         continue;
2162       }
2163     switch (event.type)
2164     {
2165       case ButtonPress:
2166       {
2167         if (event.xbutton.button != Button1)
2168           break;
2169         if (event.xbutton.window != windows->image.id)
2170           break;
2171         /*
2172           Change to text entering mode.
2173         */
2174         x=event.xbutton.x;
2175         y=event.xbutton.y;
2176         state|=ExitState;
2177         break;
2178       }
2179       case ButtonRelease:
2180         break;
2181       case Expose:
2182         break;
2183       case KeyPress:
2184       {
2185         if (event.xkey.window != windows->image.id)
2186           break;
2187         /*
2188           Respond to a user key press.
2189         */
2190         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2191           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2192         switch ((int) key_symbol)
2193         {
2194           case XK_Escape:
2195           case XK_F20:
2196           {
2197             /*
2198               Prematurely exit.
2199             */
2200             state|=EscapeState;
2201             state|=ExitState;
2202             break;
2203           }
2204           case XK_F1:
2205           case XK_Help:
2206           {
2207             XTextViewWidget(display,resource_info,windows,MagickFalse,
2208               "Help Viewer - Image Annotation",ImageAnnotateHelp);
2209             break;
2210           }
2211           default:
2212           {
2213             (void) XBell(display,0);
2214             break;
2215           }
2216         }
2217         break;
2218       }
2219       case MotionNotify:
2220       {
2221         /*
2222           Map and unmap Info widget as cursor crosses its boundaries.
2223         */
2224         x=event.xmotion.x;
2225         y=event.xmotion.y;
2226         if( IfMagickTrue(windows->info.mapped) )
2227           {
2228             if ((x < (int) (windows->info.x+windows->info.width)) &&
2229                 (y < (int) (windows->info.y+windows->info.height)))
2230               (void) XWithdrawWindow(display,windows->info.id,
2231                 windows->info.screen);
2232           }
2233         else
2234           if ((x > (int) (windows->info.x+windows->info.width)) ||
2235               (y > (int) (windows->info.y+windows->info.height)))
2236             (void) XMapWindow(display,windows->info.id);
2237         break;
2238       }
2239       default:
2240         break;
2241     }
2242   } while ((state & ExitState) == 0);
2243   (void) XSelectInput(display,windows->image.id,
2244     windows->image.attributes.event_mask);
2245   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
2246   if ((state & EscapeState) != 0)
2247     return(MagickTrue);
2248   /*
2249     Set font info and check boundary conditions.
2250   */
2251   font_info=XLoadQueryFont(display,resource_info->font_name[font_id]);
2252   if (font_info == (XFontStruct *) NULL)
2253     {
2254       XNoticeWidget(display,windows,"Unable to load font:",
2255         resource_info->font_name[font_id]);
2256       font_info=windows->font_info;
2257     }
2258   if ((x+font_info->max_bounds.width) >= (int) windows->image.width)
2259     x=(int) windows->image.width-font_info->max_bounds.width;
2260   if (y < (int) (font_info->ascent+font_info->descent))
2261     y=(int) font_info->ascent+font_info->descent;
2262   if (((int) font_info->max_bounds.width > (int) windows->image.width) ||
2263       ((font_info->ascent+font_info->descent) >= (int) windows->image.height))
2264     return(MagickFalse);
2265   /*
2266     Initialize annotate structure.
2267   */
2268   annotate_info=(XAnnotateInfo *) AcquireMagickMemory(sizeof(*annotate_info));
2269   if (annotate_info == (XAnnotateInfo *) NULL)
2270     return(MagickFalse);
2271   XGetAnnotateInfo(annotate_info);
2272   annotate_info->x=x;
2273   annotate_info->y=y;
2274   if( IfMagickFalse(transparent_box) && IfMagickFalse(transparent_pen))
2275     annotate_info->stencil=OpaqueStencil;
2276   else
2277     if( IfMagickFalse(transparent_box) )
2278       annotate_info->stencil=BackgroundStencil;
2279     else
2280       annotate_info->stencil=ForegroundStencil;
2281   annotate_info->height=(unsigned int) font_info->ascent+font_info->descent;
2282   annotate_info->degrees=degrees;
2283   annotate_info->font_info=font_info;
2284   annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2285     windows->image.width/MagickMax((ssize_t) font_info->min_bounds.width,1)+2UL,
2286     sizeof(*annotate_info->text));
2287   if (annotate_info->text == (char *) NULL)
2288     return(MagickFalse);
2289   /*
2290     Create cursor and set graphic context.
2291   */
2292   cursor=XCreateFontCursor(display,XC_pencil);
2293   (void) XCheckDefineCursor(display,windows->image.id,cursor);
2294   annotate_context=windows->image.annotate_context;
2295   (void) XSetFont(display,annotate_context,font_info->fid);
2296   (void) XSetBackground(display,annotate_context,
2297     windows->pixel_info->pen_colors[box_id].pixel);
2298   (void) XSetForeground(display,annotate_context,
2299     windows->pixel_info->pen_colors[pen_id].pixel);
2300   /*
2301     Begin annotating the image with text.
2302   */
2303   (void) CloneString(&windows->command.name,"Text");
2304   windows->command.data=0;
2305   (void) XCommandWidget(display,windows,TextMenu,(XEvent *) NULL);
2306   state=DefaultState;
2307   (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2308   text_event.xexpose.width=(int) font_info->max_bounds.width;
2309   text_event.xexpose.height=font_info->max_bounds.ascent+
2310     font_info->max_bounds.descent;
2311   p=annotate_info->text;
2312   do
2313   {
2314     /*
2315       Display text cursor.
2316     */
2317     *p='\0';
2318     (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2319     /*
2320       Wait for next event.
2321     */
2322     XScreenEvent(display,windows,&event,exception);
2323     if (event.xany.window == windows->command.id)
2324       {
2325         /*
2326           Select a command from the Command widget.
2327         */
2328         (void) XSetBackground(display,annotate_context,
2329           windows->pixel_info->background_color.pixel);
2330         (void) XSetForeground(display,annotate_context,
2331           windows->pixel_info->foreground_color.pixel);
2332         id=XCommandWidget(display,windows,AnnotateMenu,&event);
2333         (void) XSetBackground(display,annotate_context,
2334           windows->pixel_info->pen_colors[box_id].pixel);
2335         (void) XSetForeground(display,annotate_context,
2336           windows->pixel_info->pen_colors[pen_id].pixel);
2337         if (id < 0)
2338           continue;
2339         switch (TextCommands[id])
2340         {
2341           case TextHelpCommand:
2342           {
2343             XTextViewWidget(display,resource_info,windows,MagickFalse,
2344               "Help Viewer - Image Annotation",ImageAnnotateHelp);
2345             (void) XCheckDefineCursor(display,windows->image.id,cursor);
2346             break;
2347           }
2348           case TextApplyCommand:
2349           {
2350             /*
2351               Finished annotating.
2352             */
2353             annotate_info->width=(unsigned int) XTextWidth(font_info,
2354               annotate_info->text,(int) strlen(annotate_info->text));
2355             XRefreshWindow(display,&windows->image,&text_event);
2356             state|=ExitState;
2357             break;
2358           }
2359           default:
2360             break;
2361         }
2362         continue;
2363       }
2364     /*
2365       Erase text cursor.
2366     */
2367     text_event.xexpose.x=x;
2368     text_event.xexpose.y=y-font_info->max_bounds.ascent;
2369     (void) XClearArea(display,windows->image.id,x,text_event.xexpose.y,
2370       (unsigned int) text_event.xexpose.width,(unsigned int)
2371       text_event.xexpose.height,MagickFalse);
2372     XRefreshWindow(display,&windows->image,&text_event);
2373     switch (event.type)
2374     {
2375       case ButtonPress:
2376       {
2377         if (event.xbutton.window != windows->image.id)
2378           break;
2379         if (event.xbutton.button == Button2)
2380           {
2381             /*
2382               Request primary selection.
2383             */
2384             (void) XConvertSelection(display,XA_PRIMARY,XA_STRING,XA_STRING,
2385               windows->image.id,CurrentTime);
2386             break;
2387           }
2388         break;
2389       }
2390       case Expose:
2391       {
2392         if (event.xexpose.count == 0)
2393           {
2394             XAnnotateInfo
2395               *text_info;
2396
2397             /*
2398               Refresh Image window.
2399             */
2400             XRefreshWindow(display,&windows->image,(XEvent *) NULL);
2401             text_info=annotate_info;
2402             while (text_info != (XAnnotateInfo *) NULL)
2403             {
2404               if (annotate_info->stencil == ForegroundStencil)
2405                 (void) XDrawString(display,windows->image.id,annotate_context,
2406                   text_info->x,text_info->y,text_info->text,
2407                   (int) strlen(text_info->text));
2408               else
2409                 (void) XDrawImageString(display,windows->image.id,
2410                   annotate_context,text_info->x,text_info->y,text_info->text,
2411                   (int) strlen(text_info->text));
2412               text_info=text_info->previous;
2413             }
2414             (void) XDrawString(display,windows->image.id,annotate_context,
2415               x,y,"_",1);
2416           }
2417         break;
2418       }
2419       case KeyPress:
2420       {
2421         int
2422           length;
2423
2424         if (event.xkey.window != windows->image.id)
2425           break;
2426         /*
2427           Respond to a user key press.
2428         */
2429         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
2430           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2431         *(command+length)='\0';
2432         if (((event.xkey.state & ControlMask) != 0) ||
2433             ((event.xkey.state & Mod1Mask) != 0))
2434           state|=ModifierState;
2435         if ((state & ModifierState) != 0)
2436           switch ((int) key_symbol)
2437           {
2438             case XK_u:
2439             case XK_U:
2440             {
2441               key_symbol=DeleteCommand;
2442               break;
2443             }
2444             default:
2445               break;
2446           }
2447         switch ((int) key_symbol)
2448         {
2449           case XK_BackSpace:
2450           {
2451             /*
2452               Erase one character.
2453             */
2454             if (p == annotate_info->text)
2455               {
2456                 if (annotate_info->previous == (XAnnotateInfo *) NULL)
2457                   break;
2458                 else
2459                   {
2460                     /*
2461                       Go to end of the previous line of text.
2462                     */
2463                     annotate_info=annotate_info->previous;
2464                     p=annotate_info->text;
2465                     x=annotate_info->x+annotate_info->width;
2466                     y=annotate_info->y;
2467                     if (annotate_info->width != 0)
2468                       p+=strlen(annotate_info->text);
2469                     break;
2470                   }
2471               }
2472             p--;
2473             x-=XTextWidth(font_info,p,1);
2474             text_event.xexpose.x=x;
2475             text_event.xexpose.y=y-font_info->max_bounds.ascent;
2476             XRefreshWindow(display,&windows->image,&text_event);
2477             break;
2478           }
2479           case XK_bracketleft:
2480           {
2481             key_symbol=XK_Escape;
2482             break;
2483           }
2484           case DeleteCommand:
2485           {
2486             /*
2487               Erase the entire line of text.
2488             */
2489             while (p != annotate_info->text)
2490             {
2491               p--;
2492               x-=XTextWidth(font_info,p,1);
2493               text_event.xexpose.x=x;
2494               XRefreshWindow(display,&windows->image,&text_event);
2495             }
2496             break;
2497           }
2498           case XK_Escape:
2499           case XK_F20:
2500           {
2501             /*
2502               Finished annotating.
2503             */
2504             annotate_info->width=(unsigned int) XTextWidth(font_info,
2505               annotate_info->text,(int) strlen(annotate_info->text));
2506             XRefreshWindow(display,&windows->image,&text_event);
2507             state|=ExitState;
2508             break;
2509           }
2510           default:
2511           {
2512             /*
2513               Draw a single character on the Image window.
2514             */
2515             if ((state & ModifierState) != 0)
2516               break;
2517             if (*command == '\0')
2518               break;
2519             *p=(*command);
2520             if (annotate_info->stencil == ForegroundStencil)
2521               (void) XDrawString(display,windows->image.id,annotate_context,
2522                 x,y,p,1);
2523             else
2524               (void) XDrawImageString(display,windows->image.id,
2525                 annotate_context,x,y,p,1);
2526             x+=XTextWidth(font_info,p,1);
2527             p++;
2528             if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2529               break;
2530           }
2531           case XK_Return:
2532           case XK_KP_Enter:
2533           {
2534             /*
2535               Advance to the next line of text.
2536             */
2537             *p='\0';
2538             annotate_info->width=(unsigned int) XTextWidth(font_info,
2539               annotate_info->text,(int) strlen(annotate_info->text));
2540             if (annotate_info->next != (XAnnotateInfo *) NULL)
2541               {
2542                 /*
2543                   Line of text already exists.
2544                 */
2545                 annotate_info=annotate_info->next;
2546                 x=annotate_info->x;
2547                 y=annotate_info->y;
2548                 p=annotate_info->text;
2549                 break;
2550               }
2551             annotate_info->next=(XAnnotateInfo *) AcquireMagickMemory(
2552               sizeof(*annotate_info->next));
2553             if (annotate_info->next == (XAnnotateInfo *) NULL)
2554               return(MagickFalse);
2555             *annotate_info->next=(*annotate_info);
2556             annotate_info->next->previous=annotate_info;
2557             annotate_info=annotate_info->next;
2558             annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2559               windows->image.width/MagickMax((ssize_t)
2560               font_info->min_bounds.width,1)+2UL,sizeof(*annotate_info->text));
2561             if (annotate_info->text == (char *) NULL)
2562               return(MagickFalse);
2563             annotate_info->y+=annotate_info->height;
2564             if (annotate_info->y > (int) windows->image.height)
2565               annotate_info->y=(int) annotate_info->height;
2566             annotate_info->next=(XAnnotateInfo *) NULL;
2567             x=annotate_info->x;
2568             y=annotate_info->y;
2569             p=annotate_info->text;
2570             break;
2571           }
2572         }
2573         break;
2574       }
2575       case KeyRelease:
2576       {
2577         /*
2578           Respond to a user key release.
2579         */
2580         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2581           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2582         state&=(~ModifierState);
2583         break;
2584       }
2585       case SelectionNotify:
2586       {
2587         Atom
2588           type;
2589
2590         int
2591           format;
2592
2593         unsigned char
2594           *data;
2595
2596         unsigned long
2597           after,
2598           length;
2599
2600         /*
2601           Obtain response from primary selection.
2602         */
2603         if (event.xselection.property == (Atom) None)
2604           break;
2605         status=XGetWindowProperty(display,event.xselection.requestor,
2606           event.xselection.property,0L,(long) MaxTextExtent,True,XA_STRING,
2607           &type,&format,&length,&after,&data);
2608         if ((status != Success) || (type != XA_STRING) || (format == 32) ||
2609             (length == 0))
2610           break;
2611         /*
2612           Annotate Image window with primary selection.
2613         */
2614         for (i=0; i < (ssize_t) length; i++)
2615         {
2616           if ((char) data[i] != '\n')
2617             {
2618               /*
2619                 Draw a single character on the Image window.
2620               */
2621               *p=(char) data[i];
2622               (void) XDrawString(display,windows->image.id,annotate_context,
2623                 x,y,p,1);
2624               x+=XTextWidth(font_info,p,1);
2625               p++;
2626               if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2627                 continue;
2628             }
2629           /*
2630             Advance to the next line of text.
2631           */
2632           *p='\0';
2633           annotate_info->width=(unsigned int) XTextWidth(font_info,
2634             annotate_info->text,(int) strlen(annotate_info->text));
2635           if (annotate_info->next != (XAnnotateInfo *) NULL)
2636             {
2637               /*
2638                 Line of text already exists.
2639               */
2640               annotate_info=annotate_info->next;
2641               x=annotate_info->x;
2642               y=annotate_info->y;
2643               p=annotate_info->text;
2644               continue;
2645             }
2646           annotate_info->next=(XAnnotateInfo *) AcquireMagickMemory(
2647             sizeof(*annotate_info->next));
2648           if (annotate_info->next == (XAnnotateInfo *) NULL)
2649             return(MagickFalse);
2650           *annotate_info->next=(*annotate_info);
2651           annotate_info->next->previous=annotate_info;
2652           annotate_info=annotate_info->next;
2653           annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2654             windows->image.width/MagickMax((ssize_t)
2655             font_info->min_bounds.width,1)+2UL,sizeof(*annotate_info->text));
2656           if (annotate_info->text == (char *) NULL)
2657             return(MagickFalse);
2658           annotate_info->y+=annotate_info->height;
2659           if (annotate_info->y > (int) windows->image.height)
2660             annotate_info->y=(int) annotate_info->height;
2661           annotate_info->next=(XAnnotateInfo *) NULL;
2662           x=annotate_info->x;
2663           y=annotate_info->y;
2664           p=annotate_info->text;
2665         }
2666         (void) XFree((void *) data);
2667         break;
2668       }
2669       default:
2670         break;
2671     }
2672   } while ((state & ExitState) == 0);
2673   (void) XFreeCursor(display,cursor);
2674   /*
2675     Annotation is relative to image configuration.
2676   */
2677   width=(unsigned int) image->columns;
2678   height=(unsigned int) image->rows;
2679   x=0;
2680   y=0;
2681   if (windows->image.crop_geometry != (char *) NULL)
2682     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
2683   /*
2684     Initialize annotated image.
2685   */
2686   XSetCursorState(display,windows,MagickTrue);
2687   XCheckRefreshWindows(display,windows);
2688   while (annotate_info != (XAnnotateInfo *) NULL)
2689   {
2690     if (annotate_info->width == 0)
2691       {
2692         /*
2693           No text on this line--  go to the next line of text.
2694         */
2695         previous_info=annotate_info->previous;
2696         annotate_info->text=(char *)
2697           RelinquishMagickMemory(annotate_info->text);
2698         annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2699         annotate_info=previous_info;
2700         continue;
2701       }
2702     /*
2703       Determine pixel index for box and pen color.
2704     */
2705     windows->pixel_info->box_color=windows->pixel_info->pen_colors[box_id];
2706     if (windows->pixel_info->colors != 0)
2707       for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
2708         if (windows->pixel_info->pixels[i] ==
2709             windows->pixel_info->pen_colors[box_id].pixel)
2710           {
2711             windows->pixel_info->box_index=(unsigned short) i;
2712             break;
2713           }
2714     windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
2715     if (windows->pixel_info->colors != 0)
2716       for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
2717         if (windows->pixel_info->pixels[i] ==
2718             windows->pixel_info->pen_colors[pen_id].pixel)
2719           {
2720             windows->pixel_info->pen_index=(unsigned short) i;
2721             break;
2722           }
2723     /*
2724       Define the annotate geometry string.
2725     */
2726     annotate_info->x=(int)
2727       width*(annotate_info->x+windows->image.x)/windows->image.ximage->width;
2728     annotate_info->y=(int) height*(annotate_info->y-font_info->ascent+
2729       windows->image.y)/windows->image.ximage->height;
2730     (void) FormatLocaleString(annotate_info->geometry,MaxTextExtent,
2731       "%ux%u%+d%+d",width*annotate_info->width/windows->image.ximage->width,
2732       height*annotate_info->height/windows->image.ximage->height,
2733       annotate_info->x+x,annotate_info->y+y);
2734     /*
2735       Annotate image with text.
2736     */
2737     status=XAnnotateImage(display,windows->pixel_info,annotate_info,image,
2738       exception);
2739     if (status == 0)
2740       return(MagickFalse);
2741     /*
2742       Free up memory.
2743     */
2744     previous_info=annotate_info->previous;
2745     annotate_info->text=DestroyString(annotate_info->text);
2746     annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2747     annotate_info=previous_info;
2748   }
2749   (void) XSetForeground(display,annotate_context,
2750     windows->pixel_info->foreground_color.pixel);
2751   (void) XSetBackground(display,annotate_context,
2752     windows->pixel_info->background_color.pixel);
2753   (void) XSetFont(display,annotate_context,windows->font_info->fid);
2754   XSetCursorState(display,windows,MagickFalse);
2755   (void) XFreeFont(display,font_info);
2756   /*
2757     Update image configuration.
2758   */
2759   XConfigureImageColormap(display,resource_info,windows,image,exception);
2760   (void) XConfigureImage(display,resource_info,windows,image,exception);
2761   return(MagickTrue);
2762 }
2763 \f
2764 /*
2765 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2766 %                                                                             %
2767 %                                                                             %
2768 %                                                                             %
2769 +   X B a c k g r o u n d I m a g e                                           %
2770 %                                                                             %
2771 %                                                                             %
2772 %                                                                             %
2773 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2774 %
2775 %  XBackgroundImage() displays the image in the background of a window.
2776 %
2777 %  The format of the XBackgroundImage method is:
2778 %
2779 %      MagickBooleanType XBackgroundImage(Display *display,
2780 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
2781 %        ExceptionInfo *exception)
2782 %
2783 %  A description of each parameter follows:
2784 %
2785 %    o display: Specifies a connection to an X server; returned from
2786 %      XOpenDisplay.
2787 %
2788 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2789 %
2790 %    o windows: Specifies a pointer to a XWindows structure.
2791 %
2792 %    o image: the image.
2793 %
2794 %    o exception: return any errors or warnings in this structure.
2795 %
2796 */
2797 static MagickBooleanType XBackgroundImage(Display *display,
2798   XResourceInfo *resource_info,XWindows *windows,Image **image,
2799   ExceptionInfo *exception)
2800 {
2801 #define BackgroundImageTag  "Background/Image"
2802
2803   int
2804     status;
2805
2806   static char
2807     window_id[MaxTextExtent] = "root";
2808
2809   XResourceInfo
2810     background_resources;
2811
2812   /*
2813     Put image in background.
2814   */
2815   status=XDialogWidget(display,windows,"Background",
2816     "Enter window id (id 0x00 selects window with pointer):",window_id);
2817   if (*window_id == '\0')
2818     return(MagickFalse);
2819   (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
2820     exception);
2821   XInfoWidget(display,windows,BackgroundImageTag);
2822   XSetCursorState(display,windows,MagickTrue);
2823   XCheckRefreshWindows(display,windows);
2824   background_resources=(*resource_info);
2825   background_resources.window_id=window_id;
2826   background_resources.backdrop=IsMagickTrue(status);
2827   status=XDisplayBackgroundImage(display,&background_resources,*image,
2828     exception);
2829   if (IfMagickTrue(status))
2830     XClientMessage(display,windows->image.id,windows->im_protocols,
2831       windows->im_retain_colors,CurrentTime);
2832   XSetCursorState(display,windows,MagickFalse);
2833   (void) XMagickCommand(display,resource_info,windows,UndoCommand,image,
2834     exception);
2835   return(MagickTrue);
2836 }
2837 \f
2838 /*
2839 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2840 %                                                                             %
2841 %                                                                             %
2842 %                                                                             %
2843 +   X C h o p I m a g e                                                       %
2844 %                                                                             %
2845 %                                                                             %
2846 %                                                                             %
2847 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2848 %
2849 %  XChopImage() chops the X image.
2850 %
2851 %  The format of the XChopImage method is:
2852 %
2853 %    MagickBooleanType XChopImage(Display *display,XResourceInfo *resource_info,
2854 %      XWindows *windows,Image **image,ExceptionInfo *exception)
2855 %
2856 %  A description of each parameter follows:
2857 %
2858 %    o display: Specifies a connection to an X server; returned from
2859 %      XOpenDisplay.
2860 %
2861 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2862 %
2863 %    o windows: Specifies a pointer to a XWindows structure.
2864 %
2865 %    o image: the image.
2866 %
2867 %    o exception: return any errors or warnings in this structure.
2868 %
2869 */
2870 static MagickBooleanType XChopImage(Display *display,
2871   XResourceInfo *resource_info,XWindows *windows,Image **image,
2872   ExceptionInfo *exception)
2873 {
2874   static const char
2875     *ChopMenu[] =
2876     {
2877       "Direction",
2878       "Help",
2879       "Dismiss",
2880       (char *) NULL
2881     };
2882
2883   static ModeType
2884     direction = HorizontalChopCommand;
2885
2886   static const ModeType
2887     ChopCommands[] =
2888     {
2889       ChopDirectionCommand,
2890       ChopHelpCommand,
2891       ChopDismissCommand
2892     },
2893     DirectionCommands[] =
2894     {
2895       HorizontalChopCommand,
2896       VerticalChopCommand
2897     };
2898
2899   char
2900     text[MaxTextExtent];
2901
2902   Image
2903     *chop_image;
2904
2905   int
2906     id,
2907     x,
2908     y;
2909
2910   double
2911     scale_factor;
2912
2913   RectangleInfo
2914     chop_info;
2915
2916   unsigned int
2917     distance,
2918     height,
2919     width;
2920
2921   size_t
2922     state;
2923
2924   XEvent
2925     event;
2926
2927   XSegment
2928     segment_info;
2929
2930   /*
2931     Map Command widget.
2932   */
2933   (void) CloneString(&windows->command.name,"Chop");
2934   windows->command.data=1;
2935   (void) XCommandWidget(display,windows,ChopMenu,(XEvent *) NULL);
2936   (void) XMapRaised(display,windows->command.id);
2937   XClientMessage(display,windows->image.id,windows->im_protocols,
2938     windows->im_update_widget,CurrentTime);
2939   /*
2940     Track pointer until button 1 is pressed.
2941   */
2942   XQueryPosition(display,windows->image.id,&x,&y);
2943   (void) XSelectInput(display,windows->image.id,
2944     windows->image.attributes.event_mask | PointerMotionMask);
2945   state=DefaultState;
2946   do
2947   {
2948     if( IfMagickTrue(windows->info.mapped) )
2949       {
2950         /*
2951           Display pointer position.
2952         */
2953         (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
2954           x+windows->image.x,y+windows->image.y);
2955         XInfoWidget(display,windows,text);
2956       }
2957     /*
2958       Wait for next event.
2959     */
2960     XScreenEvent(display,windows,&event,exception);
2961     if (event.xany.window == windows->command.id)
2962       {
2963         /*
2964           Select a command from the Command widget.
2965         */
2966         id=XCommandWidget(display,windows,ChopMenu,&event);
2967         if (id < 0)
2968           continue;
2969         switch (ChopCommands[id])
2970         {
2971           case ChopDirectionCommand:
2972           {
2973             char
2974               command[MaxTextExtent];
2975
2976             static const char
2977               *Directions[] =
2978               {
2979                 "horizontal",
2980                 "vertical",
2981                 (char *) NULL,
2982               };
2983
2984             /*
2985               Select a command from the pop-up menu.
2986             */
2987             id=XMenuWidget(display,windows,ChopMenu[id],Directions,command);
2988             if (id >= 0)
2989               direction=DirectionCommands[id];
2990             break;
2991           }
2992           case ChopHelpCommand:
2993           {
2994             XTextViewWidget(display,resource_info,windows,MagickFalse,
2995               "Help Viewer - Image Chop",ImageChopHelp);
2996             break;
2997           }
2998           case ChopDismissCommand:
2999           {
3000             /*
3001               Prematurely exit.
3002             */
3003             state|=EscapeState;
3004             state|=ExitState;
3005             break;
3006           }
3007           default:
3008             break;
3009         }
3010         continue;
3011       }
3012     switch (event.type)
3013     {
3014       case ButtonPress:
3015       {
3016         if (event.xbutton.button != Button1)
3017           break;
3018         if (event.xbutton.window != windows->image.id)
3019           break;
3020         /*
3021           User has committed to start point of chopping line.
3022         */
3023         segment_info.x1=(short int) event.xbutton.x;
3024         segment_info.x2=(short int) event.xbutton.x;
3025         segment_info.y1=(short int) event.xbutton.y;
3026         segment_info.y2=(short int) event.xbutton.y;
3027         state|=ExitState;
3028         break;
3029       }
3030       case ButtonRelease:
3031         break;
3032       case Expose:
3033         break;
3034       case KeyPress:
3035       {
3036         char
3037           command[MaxTextExtent];
3038
3039         KeySym
3040           key_symbol;
3041
3042         if (event.xkey.window != windows->image.id)
3043           break;
3044         /*
3045           Respond to a user key press.
3046         */
3047         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3048           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3049         switch ((int) key_symbol)
3050         {
3051           case XK_Escape:
3052           case XK_F20:
3053           {
3054             /*
3055               Prematurely exit.
3056             */
3057             state|=EscapeState;
3058             state|=ExitState;
3059             break;
3060           }
3061           case XK_F1:
3062           case XK_Help:
3063           {
3064             (void) XSetFunction(display,windows->image.highlight_context,
3065               GXcopy);
3066             XTextViewWidget(display,resource_info,windows,MagickFalse,
3067               "Help Viewer - Image Chop",ImageChopHelp);
3068             (void) XSetFunction(display,windows->image.highlight_context,
3069               GXinvert);
3070             break;
3071           }
3072           default:
3073           {
3074             (void) XBell(display,0);
3075             break;
3076           }
3077         }
3078         break;
3079       }
3080       case MotionNotify:
3081       {
3082         /*
3083           Map and unmap Info widget as text cursor crosses its boundaries.
3084         */
3085         x=event.xmotion.x;
3086         y=event.xmotion.y;
3087         if( IfMagickTrue(windows->info.mapped) )
3088           {
3089             if ((x < (int) (windows->info.x+windows->info.width)) &&
3090                 (y < (int) (windows->info.y+windows->info.height)))
3091               (void) XWithdrawWindow(display,windows->info.id,
3092                 windows->info.screen);
3093           }
3094         else
3095           if ((x > (int) (windows->info.x+windows->info.width)) ||
3096               (y > (int) (windows->info.y+windows->info.height)))
3097             (void) XMapWindow(display,windows->info.id);
3098       }
3099     }
3100   } while ((state & ExitState) == 0);
3101   (void) XSelectInput(display,windows->image.id,
3102     windows->image.attributes.event_mask);
3103   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3104   if ((state & EscapeState) != 0)
3105     return(MagickTrue);
3106   /*
3107     Draw line as pointer moves until the mouse button is released.
3108   */
3109   chop_info.width=0;
3110   chop_info.height=0;
3111   chop_info.x=0;
3112   chop_info.y=0;
3113   distance=0;
3114   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
3115   state=DefaultState;
3116   do
3117   {
3118     if (distance > 9)
3119       {
3120         /*
3121           Display info and draw chopping line.
3122         */
3123         if( IfMagickFalse(windows->info.mapped) )
3124           (void) XMapWindow(display,windows->info.id);
3125         (void) FormatLocaleString(text,MaxTextExtent,
3126           " %.20gx%.20g%+.20g%+.20g",(double) chop_info.width,(double)
3127           chop_info.height,(double) chop_info.x,(double) chop_info.y);
3128         XInfoWidget(display,windows,text);
3129         XHighlightLine(display,windows->image.id,
3130           windows->image.highlight_context,&segment_info);
3131       }
3132     else
3133       if( IfMagickTrue(windows->info.mapped) )
3134         (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3135     /*
3136       Wait for next event.
3137     */
3138     XScreenEvent(display,windows,&event,exception);
3139     if (distance > 9)
3140       XHighlightLine(display,windows->image.id,
3141         windows->image.highlight_context,&segment_info);
3142     switch (event.type)
3143     {
3144       case ButtonPress:
3145       {
3146         segment_info.x2=(short int) event.xmotion.x;
3147         segment_info.y2=(short int) event.xmotion.y;
3148         break;
3149       }
3150       case ButtonRelease:
3151       {
3152         /*
3153           User has committed to chopping line.
3154         */
3155         segment_info.x2=(short int) event.xbutton.x;
3156         segment_info.y2=(short int) event.xbutton.y;
3157         state|=ExitState;
3158         break;
3159       }
3160       case Expose:
3161         break;
3162       case MotionNotify:
3163       {
3164         segment_info.x2=(short int) event.xmotion.x;
3165         segment_info.y2=(short int) event.xmotion.y;
3166       }
3167       default:
3168         break;
3169     }
3170     /*
3171       Check boundary conditions.
3172     */
3173     if (segment_info.x2 < 0)
3174       segment_info.x2=0;
3175     else
3176       if (segment_info.x2 > windows->image.ximage->width)
3177         segment_info.x2=windows->image.ximage->width;
3178     if (segment_info.y2 < 0)
3179       segment_info.y2=0;
3180     else
3181       if (segment_info.y2 > windows->image.ximage->height)
3182         segment_info.y2=windows->image.ximage->height;
3183     distance=(unsigned int)
3184       (((segment_info.x2-segment_info.x1)*(segment_info.x2-segment_info.x1))+
3185        ((segment_info.y2-segment_info.y1)*(segment_info.y2-segment_info.y1)));
3186     /*
3187       Compute chopping geometry.
3188     */
3189     if (direction == HorizontalChopCommand)
3190       {
3191         chop_info.width=(size_t) (segment_info.x2-segment_info.x1+1);
3192         chop_info.x=(ssize_t) windows->image.x+segment_info.x1;
3193         chop_info.height=0;
3194         chop_info.y=0;
3195         if (segment_info.x1 > (int) segment_info.x2)
3196           {
3197             chop_info.width=(size_t) (segment_info.x1-segment_info.x2+1);
3198             chop_info.x=(ssize_t) windows->image.x+segment_info.x2;
3199           }
3200       }
3201     else
3202       {
3203         chop_info.width=0;
3204         chop_info.height=(size_t) (segment_info.y2-segment_info.y1+1);
3205         chop_info.x=0;
3206         chop_info.y=(ssize_t) windows->image.y+segment_info.y1;
3207         if (segment_info.y1 > segment_info.y2)
3208           {
3209             chop_info.height=(size_t) (segment_info.y1-segment_info.y2+1);
3210             chop_info.y=(ssize_t) windows->image.y+segment_info.y2;
3211           }
3212       }
3213   } while ((state & ExitState) == 0);
3214   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
3215   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3216   if (distance <= 9)
3217     return(MagickTrue);
3218   /*
3219     Image chopping is relative to image configuration.
3220   */
3221   (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
3222     exception);
3223   XSetCursorState(display,windows,MagickTrue);
3224   XCheckRefreshWindows(display,windows);
3225   windows->image.window_changes.width=windows->image.ximage->width-
3226     (unsigned int) chop_info.width;
3227   windows->image.window_changes.height=windows->image.ximage->height-
3228     (unsigned int) chop_info.height;
3229   width=(unsigned int) (*image)->columns;
3230   height=(unsigned int) (*image)->rows;
3231   x=0;
3232   y=0;
3233   if (windows->image.crop_geometry != (char *) NULL)
3234     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
3235   scale_factor=(double) width/windows->image.ximage->width;
3236   chop_info.x+=x;
3237   chop_info.x=(ssize_t) (scale_factor*chop_info.x+0.5);
3238   chop_info.width=(unsigned int) (scale_factor*chop_info.width+0.5);
3239   scale_factor=(double) height/windows->image.ximage->height;
3240   chop_info.y+=y;
3241   chop_info.y=(ssize_t) (scale_factor*chop_info.y+0.5);
3242   chop_info.height=(unsigned int) (scale_factor*chop_info.height+0.5);
3243   /*
3244     Chop image.
3245   */
3246   chop_image=ChopImage(*image,&chop_info,exception);
3247   XSetCursorState(display,windows,MagickFalse);
3248   if (chop_image == (Image *) NULL)
3249     return(MagickFalse);
3250   *image=DestroyImage(*image);
3251   *image=chop_image;
3252   /*
3253     Update image configuration.
3254   */
3255   XConfigureImageColormap(display,resource_info,windows,*image,exception);
3256   (void) XConfigureImage(display,resource_info,windows,*image,exception);
3257   return(MagickTrue);
3258 }
3259 \f
3260 /*
3261 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3262 %                                                                             %
3263 %                                                                             %
3264 %                                                                             %
3265 +   X C o l o r E d i t I m a g e                                             %
3266 %                                                                             %
3267 %                                                                             %
3268 %                                                                             %
3269 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3270 %
3271 %  XColorEditImage() allows the user to interactively change the color of one
3272 %  pixel for a DirectColor image or one colormap entry for a PseudoClass image.
3273 %
3274 %  The format of the XColorEditImage method is:
3275 %
3276 %      MagickBooleanType XColorEditImage(Display *display,
3277 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
3278 %          ExceptionInfo *exception)
3279 %
3280 %  A description of each parameter follows:
3281 %
3282 %    o display: Specifies a connection to an X server;  returned from
3283 %      XOpenDisplay.
3284 %
3285 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3286 %
3287 %    o windows: Specifies a pointer to a XWindows structure.
3288 %
3289 %    o image: the image; returned from ReadImage.
3290 %
3291 %    o exception: return any errors or warnings in this structure.
3292 %
3293 */
3294 static MagickBooleanType XColorEditImage(Display *display,
3295   XResourceInfo *resource_info,XWindows *windows,Image **image,
3296   ExceptionInfo *exception)
3297 {
3298   static const char
3299     *ColorEditMenu[] =
3300     {
3301       "Method",
3302       "Pixel Color",
3303       "Border Color",
3304       "Fuzz",
3305       "Undo",
3306       "Help",
3307       "Dismiss",
3308       (char *) NULL
3309     };
3310
3311   static const ModeType
3312     ColorEditCommands[] =
3313     {
3314       ColorEditMethodCommand,
3315       ColorEditColorCommand,
3316       ColorEditBorderCommand,
3317       ColorEditFuzzCommand,
3318       ColorEditUndoCommand,
3319       ColorEditHelpCommand,
3320       ColorEditDismissCommand
3321     };
3322
3323   static PaintMethod
3324     method = PointMethod;
3325
3326   static unsigned int
3327     pen_id = 0;
3328
3329   static XColor
3330     border_color = { 0, 0, 0, 0, 0, 0 };
3331
3332   char
3333     command[MaxTextExtent],
3334     text[MaxTextExtent];
3335
3336   Cursor
3337     cursor;
3338
3339   int
3340     entry,
3341     id,
3342     x,
3343     x_offset,
3344     y,
3345     y_offset;
3346
3347   register Quantum
3348     *q;
3349
3350   register ssize_t
3351     i;
3352
3353   unsigned int
3354     height,
3355     width;
3356
3357   size_t
3358     state;
3359
3360   XColor
3361     color;
3362
3363   XEvent
3364     event;
3365
3366   /*
3367     Map Command widget.
3368   */
3369   (void) CloneString(&windows->command.name,"Color Edit");
3370   windows->command.data=4;
3371   (void) XCommandWidget(display,windows,ColorEditMenu,(XEvent *) NULL);
3372   (void) XMapRaised(display,windows->command.id);
3373   XClientMessage(display,windows->image.id,windows->im_protocols,
3374     windows->im_update_widget,CurrentTime);
3375   /*
3376     Make cursor.
3377   */
3378   cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
3379     resource_info->background_color,resource_info->foreground_color);
3380   (void) XCheckDefineCursor(display,windows->image.id,cursor);
3381   /*
3382     Track pointer until button 1 is pressed.
3383   */
3384   XQueryPosition(display,windows->image.id,&x,&y);
3385   (void) XSelectInput(display,windows->image.id,
3386     windows->image.attributes.event_mask | PointerMotionMask);
3387   state=DefaultState;
3388   do
3389   {
3390     if( IfMagickTrue(windows->info.mapped) )
3391       {
3392         /*
3393           Display pointer position.
3394         */
3395         (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
3396           x+windows->image.x,y+windows->image.y);
3397         XInfoWidget(display,windows,text);
3398       }
3399     /*
3400       Wait for next event.
3401     */
3402     XScreenEvent(display,windows,&event,exception);
3403     if (event.xany.window == windows->command.id)
3404       {
3405         /*
3406           Select a command from the Command widget.
3407         */
3408         id=XCommandWidget(display,windows,ColorEditMenu,&event);
3409         if (id < 0)
3410           {
3411             (void) XCheckDefineCursor(display,windows->image.id,cursor);
3412             continue;
3413           }
3414         switch (ColorEditCommands[id])
3415         {
3416           case ColorEditMethodCommand:
3417           {
3418             char
3419               **methods;
3420
3421             /*
3422               Select a method from the pop-up menu.
3423             */
3424             methods=(char **) GetCommandOptions(MagickMethodOptions);
3425             if (methods == (char **) NULL)
3426               break;
3427             entry=XMenuWidget(display,windows,ColorEditMenu[id],
3428               (const char **) methods,command);
3429             if (entry >= 0)
3430               method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
3431                 MagickFalse,methods[entry]);
3432             methods=DestroyStringList(methods);
3433             break;
3434           }
3435           case ColorEditColorCommand:
3436           {
3437             const char
3438               *ColorMenu[MaxNumberPens];
3439
3440             int
3441               pen_number;
3442
3443             /*
3444               Initialize menu selections.
3445             */
3446             for (i=0; i < (int) (MaxNumberPens-2); i++)
3447               ColorMenu[i]=resource_info->pen_colors[i];
3448             ColorMenu[MaxNumberPens-2]="Browser...";
3449             ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3450             /*
3451               Select a pen color from the pop-up menu.
3452             */
3453             pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3454               (const char **) ColorMenu,command);
3455             if (pen_number < 0)
3456               break;
3457             if (pen_number == (MaxNumberPens-2))
3458               {
3459                 static char
3460                   color_name[MaxTextExtent] = "gray";
3461
3462                 /*
3463                   Select a pen color from a dialog.
3464                 */
3465                 resource_info->pen_colors[pen_number]=color_name;
3466                 XColorBrowserWidget(display,windows,"Select",color_name);
3467                 if (*color_name == '\0')
3468                   break;
3469               }
3470             /*
3471               Set pen color.
3472             */
3473             (void) XParseColor(display,windows->map_info->colormap,
3474               resource_info->pen_colors[pen_number],&color);
3475             XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
3476               (unsigned int) MaxColors,&color);
3477             windows->pixel_info->pen_colors[pen_number]=color;
3478             pen_id=(unsigned int) pen_number;
3479             break;
3480           }
3481           case ColorEditBorderCommand:
3482           {
3483             const char
3484               *ColorMenu[MaxNumberPens];
3485
3486             int
3487               pen_number;
3488
3489             /*
3490               Initialize menu selections.
3491             */
3492             for (i=0; i < (int) (MaxNumberPens-2); i++)
3493               ColorMenu[i]=resource_info->pen_colors[i];
3494             ColorMenu[MaxNumberPens-2]="Browser...";
3495             ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3496             /*
3497               Select a pen color from the pop-up menu.
3498             */
3499             pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3500               (const char **) ColorMenu,command);
3501             if (pen_number < 0)
3502               break;
3503             if (pen_number == (MaxNumberPens-2))
3504               {
3505                 static char
3506                   color_name[MaxTextExtent] = "gray";
3507
3508                 /*
3509                   Select a pen color from a dialog.
3510                 */
3511                 resource_info->pen_colors[pen_number]=color_name;
3512                 XColorBrowserWidget(display,windows,"Select",color_name);
3513                 if (*color_name == '\0')
3514                   break;
3515               }
3516             /*
3517               Set border color.
3518             */
3519             (void) XParseColor(display,windows->map_info->colormap,
3520               resource_info->pen_colors[pen_number],&border_color);
3521             break;
3522           }
3523           case ColorEditFuzzCommand:
3524           {
3525             static char
3526               fuzz[MaxTextExtent];
3527
3528             static const char
3529               *FuzzMenu[] =
3530               {
3531                 "0%",
3532                 "2%",
3533                 "5%",
3534                 "10%",
3535                 "15%",
3536                 "Dialog...",
3537                 (char *) NULL,
3538               };
3539
3540             /*
3541               Select a command from the pop-up menu.
3542             */
3543             entry=XMenuWidget(display,windows,ColorEditMenu[id],FuzzMenu,
3544               command);
3545             if (entry < 0)
3546               break;
3547             if (entry != 5)
3548               {
3549                 (*image)->fuzz=StringToDoubleInterval(FuzzMenu[entry],(double)
3550                   QuantumRange+1.0);
3551                 break;
3552               }
3553             (void) (void) CopyMagickString(fuzz,"20%",MaxTextExtent);
3554             (void) XDialogWidget(display,windows,"Ok",
3555               "Enter fuzz factor (0.0 - 99.9%):",fuzz);
3556             if (*fuzz == '\0')
3557               break;
3558             (void) ConcatenateMagickString(fuzz,"%",MaxTextExtent);
3559             (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+
3560               1.0);
3561             break;
3562           }
3563           case ColorEditUndoCommand:
3564           {
3565             (void) XMagickCommand(display,resource_info,windows,UndoCommand,
3566               image,exception);
3567             break;
3568           }
3569           case ColorEditHelpCommand:
3570           default:
3571           {
3572             XTextViewWidget(display,resource_info,windows,MagickFalse,
3573               "Help Viewer - Image Annotation",ImageColorEditHelp);
3574             break;
3575           }
3576           case ColorEditDismissCommand:
3577           {
3578             /*
3579               Prematurely exit.
3580             */
3581             state|=EscapeState;
3582             state|=ExitState;
3583             break;
3584           }
3585         }
3586         (void) XCheckDefineCursor(display,windows->image.id,cursor);
3587         continue;
3588       }
3589     switch (event.type)
3590     {
3591       case ButtonPress:
3592       {
3593         if (event.xbutton.button != Button1)
3594           break;
3595         if ((event.xbutton.window != windows->image.id) &&
3596             (event.xbutton.window != windows->magnify.id))
3597           break;
3598         /*
3599           exit loop.
3600         */
3601         x=event.xbutton.x;
3602         y=event.xbutton.y;
3603         (void) XMagickCommand(display,resource_info,windows,
3604           SaveToUndoBufferCommand,image,exception);
3605         state|=UpdateConfigurationState;
3606         break;
3607       }
3608       case ButtonRelease:
3609       {
3610         if (event.xbutton.button != Button1)
3611           break;
3612         if ((event.xbutton.window != windows->image.id) &&
3613             (event.xbutton.window != windows->magnify.id))
3614           break;
3615         /*
3616           Update colormap information.
3617         */
3618         x=event.xbutton.x;
3619         y=event.xbutton.y;
3620         XConfigureImageColormap(display,resource_info,windows,*image,exception);
3621         (void) XConfigureImage(display,resource_info,windows,*image,exception);
3622         XInfoWidget(display,windows,text);
3623         (void) XCheckDefineCursor(display,windows->image.id,cursor);
3624         state&=(~UpdateConfigurationState);
3625         break;
3626       }
3627       case Expose:
3628         break;
3629       case KeyPress:
3630       {
3631         KeySym
3632           key_symbol;
3633
3634         if (event.xkey.window == windows->magnify.id)
3635           {
3636             Window
3637               window;
3638
3639             window=windows->magnify.id;
3640             while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
3641           }
3642         if (event.xkey.window != windows->image.id)
3643           break;
3644         /*
3645           Respond to a user key press.
3646         */
3647         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3648           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3649         switch ((int) key_symbol)
3650         {
3651           case XK_Escape:
3652           case XK_F20:
3653           {
3654             /*
3655               Prematurely exit.
3656             */
3657             state|=ExitState;
3658             break;
3659           }
3660           case XK_F1:
3661           case XK_Help:
3662           {
3663             XTextViewWidget(display,resource_info,windows,MagickFalse,
3664               "Help Viewer - Image Annotation",ImageColorEditHelp);
3665             break;
3666           }
3667           default:
3668           {
3669             (void) XBell(display,0);
3670             break;
3671           }
3672         }
3673         break;
3674       }
3675       case MotionNotify:
3676       {
3677         /*
3678           Map and unmap Info widget as cursor crosses its boundaries.
3679         */
3680         x=event.xmotion.x;
3681         y=event.xmotion.y;
3682         if( IfMagickTrue(windows->info.mapped) )
3683           {
3684             if ((x < (int) (windows->info.x+windows->info.width)) &&
3685                 (y < (int) (windows->info.y+windows->info.height)))
3686               (void) XWithdrawWindow(display,windows->info.id,
3687                 windows->info.screen);
3688           }
3689         else
3690           if ((x > (int) (windows->info.x+windows->info.width)) ||
3691               (y > (int) (windows->info.y+windows->info.height)))
3692             (void) XMapWindow(display,windows->info.id);
3693         break;
3694       }
3695       default:
3696         break;
3697     }
3698     if (event.xany.window == windows->magnify.id)
3699       {
3700         x=windows->magnify.x-windows->image.x;
3701         y=windows->magnify.y-windows->image.y;
3702       }
3703     x_offset=x;
3704     y_offset=y;
3705     if ((state & UpdateConfigurationState) != 0)
3706       {
3707         CacheView
3708           *image_view;
3709
3710         int
3711           x,
3712           y;
3713
3714         /*
3715           Pixel edit is relative to image configuration.
3716         */
3717         (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
3718           MagickTrue);
3719         color=windows->pixel_info->pen_colors[pen_id];
3720         XPutPixel(windows->image.ximage,x_offset,y_offset,color.pixel);
3721         width=(unsigned int) (*image)->columns;
3722         height=(unsigned int) (*image)->rows;
3723         x=0;
3724         y=0;
3725         if (windows->image.crop_geometry != (char *) NULL)
3726           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
3727             &width,&height);
3728         x_offset=(int)
3729           (width*(windows->image.x+x_offset)/windows->image.ximage->width+x);
3730         y_offset=(int)
3731           (height*(windows->image.y+y_offset)/windows->image.ximage->height+y);
3732         if ((x_offset < 0) || (y_offset < 0))
3733           continue;
3734         if ((x_offset >= (int) (*image)->columns) ||
3735             (y_offset >= (int) (*image)->rows))
3736           continue;
3737         image_view=AcquireAuthenticCacheView(*image,exception);
3738         switch (method)
3739         {
3740           case PointMethod:
3741           default:
3742           {
3743             /*
3744               Update color information using point algorithm.
3745             */
3746             if( IfMagickFalse(SetImageStorageClass(*image,DirectClass,exception)) )
3747               return(MagickFalse);
3748             q=GetCacheViewAuthenticPixels(image_view,(ssize_t)x_offset,
3749               (ssize_t) y_offset,1,1,exception);
3750             if (q == (Quantum *) NULL)
3751               break;
3752             SetPixelRed(*image,ScaleShortToQuantum(color.red),q);
3753             SetPixelGreen(*image,ScaleShortToQuantum(color.green),q);
3754             SetPixelBlue(*image,ScaleShortToQuantum(color.blue),q);
3755             (void) SyncCacheViewAuthenticPixels(image_view,exception);
3756             break;
3757           }
3758           case ReplaceMethod:
3759           {
3760             PixelInfo
3761               pixel,
3762               target;
3763
3764             /*
3765               Update color information using replace algorithm.
3766             */
3767             (void) GetOneCacheViewVirtualPixelInfo(image_view,(ssize_t)
3768               x_offset,(ssize_t) y_offset,&target,exception);
3769             if ((*image)->storage_class == DirectClass)
3770               {
3771                 for (y=0; y < (int) (*image)->rows; y++)
3772                 {
3773                   q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3774                     (*image)->columns,1,exception);
3775                   if (q == (Quantum *) NULL)
3776                     break;
3777                   for (x=0; x < (int) (*image)->columns; x++)
3778                   {
3779                     GetPixelInfoPixel(*image,q,&pixel);
3780                     if (IsFuzzyEquivalencePixelInfo(&pixel,&target))
3781                       {
3782                         SetPixelRed(*image,ScaleShortToQuantum(
3783                           color.red),q);
3784                         SetPixelGreen(*image,ScaleShortToQuantum(
3785                           color.green),q);
3786                         SetPixelBlue(*image,ScaleShortToQuantum(
3787                           color.blue),q);
3788                       }
3789                     q+=GetPixelChannels(*image);
3790                   }
3791                   if( IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
3792                     break;
3793                 }
3794               }
3795             else
3796               {
3797                 for (i=0; i < (ssize_t) (*image)->colors; i++)
3798                   if (IsFuzzyEquivalencePixelInfo((*image)->colormap+i,&target))
3799                     {
3800                       (*image)->colormap[i].red=(double) ScaleShortToQuantum(
3801                         color.red);
3802                       (*image)->colormap[i].green=(double) ScaleShortToQuantum(
3803                         color.green);
3804                       (*image)->colormap[i].blue=(double) ScaleShortToQuantum(
3805                         color.blue);
3806                     }
3807                 (void) SyncImage(*image,exception);
3808               }
3809             break;
3810           }
3811           case FloodfillMethod:
3812           case FillToBorderMethod:
3813           {
3814             DrawInfo
3815               *draw_info;
3816
3817             PixelInfo
3818               target;
3819
3820             /*
3821               Update color information using floodfill algorithm.
3822             */
3823             (void) GetOneVirtualPixelInfo(*image,
3824               GetPixelCacheVirtualMethod(*image),(ssize_t) x_offset,(ssize_t)
3825               y_offset,&target,exception);
3826             if (method == FillToBorderMethod)
3827               {
3828                 target.red=(double)
3829                   ScaleShortToQuantum(border_color.red);
3830                 target.green=(double)
3831                   ScaleShortToQuantum(border_color.green);
3832                 target.blue=(double)
3833                   ScaleShortToQuantum(border_color.blue);
3834               }
3835             draw_info=CloneDrawInfo(resource_info->image_info,
3836               (DrawInfo *) NULL);
3837             (void) QueryColorCompliance(resource_info->pen_colors[pen_id],
3838               AllCompliance,&draw_info->fill,exception);
3839             (void) FloodfillPaintImage(*image,draw_info,&target,
3840               (ssize_t)x_offset,(ssize_t)y_offset,
3841               IsMagickFalse(method == FloodfillMethod),exception);
3842             draw_info=DestroyDrawInfo(draw_info);
3843             break;
3844           }
3845           case ResetMethod:
3846           {
3847             /*
3848               Update color information using reset algorithm.
3849             */
3850             if( IfMagickFalse(SetImageStorageClass(*image,DirectClass,exception)) )
3851               return(MagickFalse);
3852             for (y=0; y < (int) (*image)->rows; y++)
3853             {
3854               q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3855                 (*image)->columns,1,exception);
3856               if (q == (Quantum *) NULL)
3857                 break;
3858               for (x=0; x < (int) (*image)->columns; x++)
3859               {
3860                 SetPixelRed(*image,ScaleShortToQuantum(color.red),q);
3861                 SetPixelGreen(*image,ScaleShortToQuantum(color.green),q);
3862                 SetPixelBlue(*image,ScaleShortToQuantum(color.blue),q);
3863                 q+=GetPixelChannels(*image);
3864               }
3865               if( IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
3866                 break;
3867             }
3868             break;
3869           }
3870         }
3871         image_view=DestroyCacheView(image_view);
3872         state&=(~UpdateConfigurationState);
3873       }
3874   } while ((state & ExitState) == 0);
3875   (void) XSelectInput(display,windows->image.id,
3876     windows->image.attributes.event_mask);
3877   XSetCursorState(display,windows,MagickFalse);
3878   (void) XFreeCursor(display,cursor);
3879   return(MagickTrue);
3880 }
3881 \f
3882 /*
3883 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3884 %                                                                             %
3885 %                                                                             %
3886 %                                                                             %
3887 +   X C o m p o s i t e I m a g e                                             %
3888 %                                                                             %
3889 %                                                                             %
3890 %                                                                             %
3891 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3892 %
3893 %  XCompositeImage() requests an image name from the user, reads the image and
3894 %  composites it with the X window image at a location the user chooses with
3895 %  the pointer.
3896 %
3897 %  The format of the XCompositeImage method is:
3898 %
3899 %      MagickBooleanType XCompositeImage(Display *display,
3900 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
3901 %        ExceptionInfo *exception)
3902 %
3903 %  A description of each parameter follows:
3904 %
3905 %    o display: Specifies a connection to an X server;  returned from
3906 %      XOpenDisplay.
3907 %
3908 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3909 %
3910 %    o windows: Specifies a pointer to a XWindows structure.
3911 %
3912 %    o image: the image; returned from ReadImage.
3913 %
3914 %    o exception: return any errors or warnings in this structure.
3915 %
3916 */
3917 static MagickBooleanType XCompositeImage(Display *display,
3918   XResourceInfo *resource_info,XWindows *windows,Image *image,
3919   ExceptionInfo *exception)
3920 {
3921   static char
3922     displacement_geometry[MaxTextExtent] = "30x30",
3923     filename[MaxTextExtent] = "\0";
3924
3925   static const char
3926     *CompositeMenu[] =
3927     {
3928       "Operators",
3929       "Dissolve",
3930       "Displace",
3931       "Help",
3932       "Dismiss",
3933       (char *) NULL
3934     };
3935
3936   static CompositeOperator
3937     compose = CopyCompositeOp;
3938
3939   static const ModeType
3940     CompositeCommands[] =
3941     {
3942       CompositeOperatorsCommand,
3943       CompositeDissolveCommand,
3944       CompositeDisplaceCommand,
3945       CompositeHelpCommand,
3946       CompositeDismissCommand
3947     };
3948
3949   char
3950     text[MaxTextExtent];
3951
3952   Cursor
3953     cursor;
3954
3955   Image
3956     *composite_image;
3957
3958   int
3959     entry,
3960     id,
3961     x,
3962     y;
3963
3964   double
3965     blend,
3966     scale_factor;
3967
3968   RectangleInfo
3969     highlight_info,
3970     composite_info;
3971
3972   unsigned int
3973     height,
3974     width;
3975
3976   size_t
3977     state;
3978
3979   XEvent
3980     event;
3981
3982   /*
3983     Request image file name from user.
3984   */
3985   XFileBrowserWidget(display,windows,"Composite",filename);
3986   if (*filename == '\0')
3987     return(MagickTrue);
3988   /*
3989     Read image.
3990   */
3991   XSetCursorState(display,windows,MagickTrue);
3992   XCheckRefreshWindows(display,windows);
3993   (void) CopyMagickString(resource_info->image_info->filename,filename,
3994     MaxTextExtent);
3995   composite_image=ReadImage(resource_info->image_info,exception);
3996   CatchException(exception);
3997   XSetCursorState(display,windows,MagickFalse);
3998   if (composite_image == (Image *) NULL)
3999     return(MagickFalse);
4000   /*
4001     Map Command widget.
4002   */
4003   (void) CloneString(&windows->command.name,"Composite");
4004   windows->command.data=1;
4005   (void) XCommandWidget(display,windows,CompositeMenu,(XEvent *) NULL);
4006   (void) XMapRaised(display,windows->command.id);
4007   XClientMessage(display,windows->image.id,windows->im_protocols,
4008     windows->im_update_widget,CurrentTime);
4009   /*
4010     Track pointer until button 1 is pressed.
4011   */
4012   XQueryPosition(display,windows->image.id,&x,&y);
4013   (void) XSelectInput(display,windows->image.id,
4014     windows->image.attributes.event_mask | PointerMotionMask);
4015   composite_info.x=(ssize_t) windows->image.x+x;
4016   composite_info.y=(ssize_t) windows->image.y+y;
4017   composite_info.width=0;
4018   composite_info.height=0;
4019   cursor=XCreateFontCursor(display,XC_ul_angle);
4020   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
4021   blend=0.0;
4022   state=DefaultState;
4023   do
4024   {
4025     if( IfMagickTrue(windows->info.mapped) )
4026       {
4027         /*
4028           Display pointer position.
4029         */
4030         (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
4031           (long) composite_info.x,(long) composite_info.y);
4032         XInfoWidget(display,windows,text);
4033       }
4034     highlight_info=composite_info;
4035     highlight_info.x=composite_info.x-windows->image.x;
4036     highlight_info.y=composite_info.y-windows->image.y;
4037     XHighlightRectangle(display,windows->image.id,
4038       windows->image.highlight_context,&highlight_info);
4039     /*
4040       Wait for next event.
4041     */
4042     XScreenEvent(display,windows,&event,exception);
4043     XHighlightRectangle(display,windows->image.id,
4044       windows->image.highlight_context,&highlight_info);
4045     if (event.xany.window == windows->command.id)
4046       {
4047         /*
4048           Select a command from the Command widget.
4049         */
4050         id=XCommandWidget(display,windows,CompositeMenu,&event);
4051         if (id < 0)
4052           continue;
4053         switch (CompositeCommands[id])
4054         {
4055           case CompositeOperatorsCommand:
4056           {
4057             char
4058               command[MaxTextExtent],
4059               **operators;
4060
4061             /*
4062               Select a command from the pop-up menu.
4063             */
4064             operators=GetCommandOptions(MagickComposeOptions);
4065             if (operators == (char **) NULL)
4066               break;
4067             entry=XMenuWidget(display,windows,CompositeMenu[id],
4068               (const char **) operators,command);
4069             if (entry >= 0)
4070               compose=(CompositeOperator) ParseCommandOption(
4071                 MagickComposeOptions,MagickFalse,operators[entry]);
4072             operators=DestroyStringList(operators);
4073             break;
4074           }
4075           case CompositeDissolveCommand:
4076           {
4077             static char
4078               factor[MaxTextExtent] = "20.0";
4079
4080             /*
4081               Dissolve the two images a given percent.
4082             */
4083             (void) XSetFunction(display,windows->image.highlight_context,
4084               GXcopy);
4085             (void) XDialogWidget(display,windows,"Dissolve",
4086               "Enter the blend factor (0.0 - 99.9%):",factor);
4087             (void) XSetFunction(display,windows->image.highlight_context,
4088               GXinvert);
4089             if (*factor == '\0')
4090               break;
4091             blend=StringToDouble(factor,(char **) NULL);
4092             compose=DissolveCompositeOp;
4093             break;
4094           }
4095           case CompositeDisplaceCommand:
4096           {
4097             /*
4098               Get horizontal and vertical scale displacement geometry.
4099             */
4100             (void) XSetFunction(display,windows->image.highlight_context,
4101               GXcopy);
4102             (void) XDialogWidget(display,windows,"Displace",
4103               "Enter the horizontal and vertical scale:",displacement_geometry);
4104             (void) XSetFunction(display,windows->image.highlight_context,
4105               GXinvert);
4106             if (*displacement_geometry == '\0')
4107               break;
4108             compose=DisplaceCompositeOp;
4109             break;
4110           }
4111           case CompositeHelpCommand:
4112           {
4113             (void) XSetFunction(display,windows->image.highlight_context,
4114               GXcopy);
4115             XTextViewWidget(display,resource_info,windows,MagickFalse,
4116               "Help Viewer - Image Composite",ImageCompositeHelp);
4117             (void) XSetFunction(display,windows->image.highlight_context,
4118               GXinvert);
4119             break;
4120           }
4121           case CompositeDismissCommand:
4122           {
4123             /*
4124               Prematurely exit.
4125             */
4126             state|=EscapeState;
4127             state|=ExitState;
4128             break;
4129           }
4130           default:
4131             break;
4132         }
4133         continue;
4134       }
4135     switch (event.type)
4136     {
4137       case ButtonPress:
4138       {
4139         if( IfMagickTrue(image->debug) )
4140           (void) LogMagickEvent(X11Event,GetMagickModule(),
4141             "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
4142             event.xbutton.button,event.xbutton.x,event.xbutton.y);
4143         if (event.xbutton.button != Button1)
4144           break;
4145         if (event.xbutton.window != windows->image.id)
4146           break;
4147         /*
4148           Change cursor.
4149         */
4150         composite_info.width=composite_image->columns;
4151         composite_info.height=composite_image->rows;
4152         (void) XCheckDefineCursor(display,windows->image.id,cursor);
4153         composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4154         composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4155         break;
4156       }
4157       case ButtonRelease:
4158       {
4159         if( IfMagickTrue(image->debug) )
4160           (void) LogMagickEvent(X11Event,GetMagickModule(),
4161             "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
4162             event.xbutton.button,event.xbutton.x,event.xbutton.y);
4163         if (event.xbutton.button != Button1)
4164           break;
4165         if (event.xbutton.window != windows->image.id)
4166           break;
4167         if ((composite_info.width != 0) && (composite_info.height != 0))
4168           {
4169             /*
4170               User has selected the location of the composite image.
4171             */
4172             composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4173             composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4174             state|=ExitState;
4175           }
4176         break;
4177       }
4178       case Expose:
4179         break;
4180       case KeyPress:
4181       {
4182         char
4183           command[MaxTextExtent];
4184
4185         KeySym
4186           key_symbol;
4187
4188         int
4189           length;
4190
4191         if (event.xkey.window != windows->image.id)
4192           break;
4193         /*
4194           Respond to a user key press.
4195         */
4196         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
4197           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4198         *(command+length)='\0';
4199         if( IfMagickTrue(image->debug) )
4200           (void) LogMagickEvent(X11Event,GetMagickModule(),
4201             "Key press: 0x%lx (%s)",(unsigned long) key_symbol,command);
4202         switch ((int) key_symbol)
4203         {
4204           case XK_Escape:
4205           case XK_F20:
4206           {
4207             /*
4208               Prematurely exit.
4209             */
4210             composite_image=DestroyImage(composite_image);
4211             state|=EscapeState;
4212             state|=ExitState;
4213             break;
4214           }
4215           case XK_F1:
4216           case XK_Help:
4217           {
4218             (void) XSetFunction(display,windows->image.highlight_context,
4219               GXcopy);
4220             XTextViewWidget(display,resource_info,windows,MagickFalse,
4221               "Help Viewer - Image Composite",ImageCompositeHelp);
4222             (void) XSetFunction(display,windows->image.highlight_context,
4223               GXinvert);
4224             break;
4225           }
4226           default:
4227           {
4228             (void) XBell(display,0);
4229             break;
4230           }
4231         }
4232         break;
4233       }
4234       case MotionNotify:
4235       {
4236         /*
4237           Map and unmap Info widget as text cursor crosses its boundaries.
4238         */
4239         x=event.xmotion.x;
4240         y=event.xmotion.y;
4241         if( IfMagickTrue(windows->info.mapped) )
4242           {
4243             if ((x < (int) (windows->info.x+windows->info.width)) &&
4244                 (y < (int) (windows->info.y+windows->info.height)))
4245               (void) XWithdrawWindow(display,windows->info.id,
4246                 windows->info.screen);
4247           }
4248         else
4249           if ((x > (int) (windows->info.x+windows->info.width)) ||
4250               (y > (int) (windows->info.y+windows->info.height)))
4251             (void) XMapWindow(display,windows->info.id);
4252         composite_info.x=(ssize_t) windows->image.x+x;
4253         composite_info.y=(ssize_t) windows->image.y+y;
4254         break;
4255       }
4256       default:
4257       {
4258         if( IfMagickTrue(image->debug) )
4259           (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
4260             event.type);
4261         break;
4262       }
4263     }
4264   } while ((state & ExitState) == 0);
4265   (void) XSelectInput(display,windows->image.id,
4266     windows->image.attributes.event_mask);
4267   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
4268   XSetCursorState(display,windows,MagickFalse);
4269   (void) XFreeCursor(display,cursor);
4270   if ((state & EscapeState) != 0)
4271     return(MagickTrue);
4272   /*
4273     Image compositing is relative to image configuration.
4274   */
4275   XSetCursorState(display,windows,MagickTrue);
4276   XCheckRefreshWindows(display,windows);
4277   width=(unsigned int) image->columns;
4278   height=(unsigned int) image->rows;
4279   x=0;
4280   y=0;
4281   if (windows->image.crop_geometry != (char *) NULL)
4282     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
4283   scale_factor=(double) width/windows->image.ximage->width;
4284   composite_info.x+=x;
4285   composite_info.x=(ssize_t) (scale_factor*composite_info.x+0.5);
4286   composite_info.width=(unsigned int) (scale_factor*composite_info.width+0.5);
4287   scale_factor=(double) height/windows->image.ximage->height;
4288   composite_info.y+=y;
4289   composite_info.y=(ssize_t) (scale_factor*composite_info.y+0.5);
4290   composite_info.height=(unsigned int) (scale_factor*composite_info.height+0.5);
4291   if ((composite_info.width != composite_image->columns) ||
4292       (composite_info.height != composite_image->rows))
4293     {
4294       Image
4295         *resize_image;
4296
4297       /*
4298         Scale composite image.
4299       */
4300       resize_image=ResizeImage(composite_image,composite_info.width,
4301         composite_info.height,composite_image->filter,exception);
4302       composite_image=DestroyImage(composite_image);
4303       if (resize_image == (Image *) NULL)
4304         {
4305           XSetCursorState(display,windows,MagickFalse);
4306           return(MagickFalse);
4307         }
4308       composite_image=resize_image;
4309     }
4310   if (compose == DisplaceCompositeOp)
4311     (void) SetImageArtifact(composite_image,"compose:args",
4312       displacement_geometry);
4313   if (blend != 0.0)
4314     {
4315       CacheView
4316         *image_view;
4317
4318       int
4319         y;
4320
4321       Quantum
4322         opacity;
4323
4324       register int
4325         x;
4326
4327       register Quantum
4328         *q;
4329
4330       /*
4331         Create mattes for blending.
4332       */
4333       (void) SetImageAlphaChannel(composite_image,OpaqueAlphaChannel,exception);
4334       opacity=(Quantum) (ScaleQuantumToChar(QuantumRange)-
4335         ((ssize_t) ScaleQuantumToChar(QuantumRange)*blend)/100);
4336       if( IfMagickFalse(SetImageStorageClass(image,DirectClass,exception)) )
4337         return(MagickFalse);
4338       image->alpha_trait=BlendPixelTrait;
4339       image_view=AcquireAuthenticCacheView(image,exception);
4340       for (y=0; y < (int) image->rows; y++)
4341       {
4342         q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,image->columns,1,
4343           exception);
4344         if (q == (Quantum *) NULL)
4345           break;
4346         for (x=0; x < (int) image->columns; x++)
4347         {
4348           SetPixelAlpha(image,opacity,q);
4349           q+=GetPixelChannels(image);
4350         }
4351         if( IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
4352           break;
4353       }
4354       image_view=DestroyCacheView(image_view);
4355     }
4356   /*
4357     Composite image with X Image window.
4358   */
4359   (void) CompositeImage(image,composite_image,compose,MagickTrue,
4360     composite_info.x,composite_info.y,exception);
4361   composite_image=DestroyImage(composite_image);
4362   XSetCursorState(display,windows,MagickFalse);
4363   /*
4364     Update image configuration.
4365   */
4366   XConfigureImageColormap(display,resource_info,windows,image,exception);
4367   (void) XConfigureImage(display,resource_info,windows,image,exception);
4368   return(MagickTrue);
4369 }
4370 \f
4371 /*
4372 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4373 %                                                                             %
4374 %                                                                             %
4375 %                                                                             %
4376 +   X C o n f i g u r e I m a g e                                             %
4377 %                                                                             %
4378 %                                                                             %
4379 %                                                                             %
4380 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4381 %
4382 %  XConfigureImage() creates a new X image.  It also notifies the window
4383 %  manager of the new image size and configures the transient widows.
4384 %
4385 %  The format of the XConfigureImage method is:
4386 %
4387 %      MagickBooleanType XConfigureImage(Display *display,
4388 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
4389 %        ExceptionInfo *exception)
4390 %
4391 %  A description of each parameter follows:
4392 %
4393 %    o display: Specifies a connection to an X server; returned from
4394 %      XOpenDisplay.
4395 %
4396 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4397 %
4398 %    o windows: Specifies a pointer to a XWindows structure.
4399 %
4400 %    o image: the image.
4401 %
4402 %    o exception: return any errors or warnings in this structure.
4403 %
4404 %    o exception: return any errors or warnings in this structure.
4405 %
4406 */
4407 static MagickBooleanType XConfigureImage(Display *display,
4408   XResourceInfo *resource_info,XWindows *windows,Image *image,
4409   ExceptionInfo *exception)
4410 {
4411   char
4412     geometry[MaxTextExtent];
4413
4414   MagickStatusType
4415     status;
4416
4417   size_t
4418     mask,
4419     height,
4420     width;
4421
4422   ssize_t
4423     x,
4424     y;
4425
4426   XSizeHints
4427     *size_hints;
4428
4429   XWindowChanges
4430     window_changes;
4431
4432   /*
4433     Dismiss if window dimensions are zero.
4434   */
4435   width=(unsigned int) windows->image.window_changes.width;
4436   height=(unsigned int) windows->image.window_changes.height;
4437   if( IfMagickTrue(image->debug) )
4438     (void) LogMagickEvent(X11Event,GetMagickModule(),
4439       "Configure Image: %dx%d=>%.20gx%.20g",windows->image.ximage->width,
4440       windows->image.ximage->height,(double) width,(double) height);
4441   if ((width*height) == 0)
4442     return(MagickTrue);
4443   x=0;
4444   y=0;
4445   /*
4446     Resize image to fit Image window dimensions.
4447   */
4448   XSetCursorState(display,windows,MagickTrue);
4449   (void) XFlush(display);
4450   if (((int) width != windows->image.ximage->width) ||
4451       ((int) height != windows->image.ximage->height))
4452     image->taint=MagickTrue;
4453   windows->magnify.x=(int)
4454     width*windows->magnify.x/windows->image.ximage->width;
4455   windows->magnify.y=(int)
4456     height*windows->magnify.y/windows->image.ximage->height;
4457   windows->image.x=(int) (width*windows->image.x/windows->image.ximage->width);
4458   windows->image.y=(int)
4459     (height*windows->image.y/windows->image.ximage->height);
4460   status=XMakeImage(display,resource_info,&windows->image,image,
4461     (unsigned int) width,(unsigned int) height,exception);
4462   if( IfMagickFalse(status) )
4463     XNoticeWidget(display,windows,"Unable to configure X image:",
4464       windows->image.name);
4465   /*
4466     Notify window manager of the new configuration.
4467   */
4468   if (resource_info->image_geometry != (char *) NULL)
4469     (void) FormatLocaleString(geometry,MaxTextExtent,"%s>!",
4470       resource_info->image_geometry);
4471   else
4472     (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>!",
4473       XDisplayWidth(display,windows->image.screen),
4474       XDisplayHeight(display,windows->image.screen));
4475   (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
4476   window_changes.width=(int) width;
4477   if (window_changes.width > XDisplayWidth(display,windows->image.screen))
4478     window_changes.width=XDisplayWidth(display,windows->image.screen);
4479   window_changes.height=(int) height;
4480   if (window_changes.height > XDisplayHeight(display,windows->image.screen))
4481     window_changes.height=XDisplayHeight(display,windows->image.screen);
4482   mask=(size_t) (CWWidth | CWHeight);
4483   if (resource_info->backdrop)
4484     {
4485       mask|=CWX | CWY;
4486       window_changes.x=(int)
4487         ((XDisplayWidth(display,windows->image.screen)/2)-(width/2));
4488       window_changes.y=(int)
4489         ((XDisplayHeight(display,windows->image.screen)/2)-(height/2));
4490     }
4491   (void) XReconfigureWMWindow(display,windows->image.id,windows->image.screen,
4492     (unsigned int) mask,&window_changes);
4493   (void) XClearWindow(display,windows->image.id);
4494   XRefreshWindow(display,&windows->image,(XEvent *) NULL);
4495   /*
4496     Update Magnify window configuration.
4497   */
4498   if( IfMagickTrue(windows->magnify.mapped) )
4499     XMakeMagnifyImage(display,windows,exception);
4500   windows->pan.crop_geometry=windows->image.crop_geometry;
4501   XBestIconSize(display,&windows->pan,image);
4502   while (((windows->pan.width << 1) < MaxIconSize) &&
4503          ((windows->pan.height << 1) < MaxIconSize))
4504   {
4505     windows->pan.width<<=1;
4506     windows->pan.height<<=1;
4507   }
4508   if (windows->pan.geometry != (char *) NULL)
4509     (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
4510       &windows->pan.width,&windows->pan.height);
4511   window_changes.width=(int) windows->pan.width;
4512   window_changes.height=(int) windows->pan.height;
4513   size_hints=XAllocSizeHints();
4514   if (size_hints != (XSizeHints *) NULL)
4515     {
4516       /*
4517         Set new size hints.
4518       */
4519       size_hints->flags=PSize | PMinSize | PMaxSize;
4520       size_hints->width=window_changes.width;
4521       size_hints->height=window_changes.height;
4522       size_hints->min_width=size_hints->width;
4523       size_hints->min_height=size_hints->height;
4524       size_hints->max_width=size_hints->width;
4525       size_hints->max_height=size_hints->height;
4526       (void) XSetNormalHints(display,windows->pan.id,size_hints);
4527       (void) XFree((void *) size_hints);
4528     }
4529   (void) XReconfigureWMWindow(display,windows->pan.id,windows->pan.screen,
4530     (unsigned int) (CWWidth | CWHeight),&window_changes);
4531   /*
4532     Update icon window configuration.
4533   */
4534   windows->icon.crop_geometry=windows->image.crop_geometry;
4535   XBestIconSize(display,&windows->icon,image);
4536   window_changes.width=(int) windows->icon.width;
4537   window_changes.height=(int) windows->icon.height;
4538   (void) XReconfigureWMWindow(display,windows->icon.id,windows->icon.screen,
4539     (unsigned int) (CWWidth | CWHeight),&window_changes);
4540   XSetCursorState(display,windows,MagickFalse);
4541   return(IsMagickTrue(status));
4542 }
4543 \f
4544 /*
4545 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4546 %                                                                             %
4547 %                                                                             %
4548 %                                                                             %
4549 +   X C r o p I m a g e                                                       %
4550 %                                                                             %
4551 %                                                                             %
4552 %                                                                             %
4553 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4554 %
4555 %  XCropImage() allows the user to select a region of the image and crop, copy,
4556 %  or cut it.  For copy or cut, the image can subsequently be composited onto
4557 %  the image with XPasteImage.
4558 %
4559 %  The format of the XCropImage method is:
4560 %
4561 %      MagickBooleanType XCropImage(Display *display,
4562 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
4563 %        const ClipboardMode mode,ExceptionInfo *exception)
4564 %
4565 %  A description of each parameter follows:
4566 %
4567 %    o display: Specifies a connection to an X server; returned from
4568 %      XOpenDisplay.
4569 %
4570 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4571 %
4572 %    o windows: Specifies a pointer to a XWindows structure.
4573 %
4574 %    o image: the image; returned from ReadImage.
4575 %
4576 %    o mode: This unsigned value specified whether the image should be
4577 %      cropped, copied, or cut.
4578 %
4579 %    o exception: return any errors or warnings in this structure.
4580 %
4581 */
4582 static MagickBooleanType XCropImage(Display *display,
4583   XResourceInfo *resource_info,XWindows *windows,Image *image,
4584   const ClipboardMode mode,ExceptionInfo *exception)
4585 {
4586   static const char
4587     *CropModeMenu[] =
4588     {
4589       "Help",
4590       "Dismiss",
4591       (char *) NULL
4592     },
4593     *RectifyModeMenu[] =
4594     {
4595       "Crop",
4596       "Help",
4597       "Dismiss",
4598       (char *) NULL
4599     };
4600
4601   static const ModeType
4602     CropCommands[] =
4603     {
4604       CropHelpCommand,
4605       CropDismissCommand
4606     },
4607     RectifyCommands[] =
4608     {
4609       RectifyCopyCommand,
4610       RectifyHelpCommand,
4611       RectifyDismissCommand
4612     };
4613
4614   CacheView
4615     *image_view;
4616
4617   char
4618     command[MaxTextExtent],
4619     text[MaxTextExtent];
4620
4621   Cursor
4622     cursor;
4623
4624   int
4625     id,
4626     x,
4627     y;
4628
4629   KeySym
4630     key_symbol;
4631
4632   Image
4633     *crop_image;
4634
4635   double
4636     scale_factor;
4637
4638   RectangleInfo
4639     crop_info,
4640     highlight_info;
4641
4642   register Quantum
4643     *q;
4644
4645   unsigned int
4646     height,
4647     width;
4648
4649   size_t
4650     state;
4651
4652   XEvent
4653     event;
4654
4655   /*
4656     Map Command widget.
4657   */
4658   switch (mode)
4659   {
4660     case CopyMode:
4661     {
4662       (void) CloneString(&windows->command.name,"Copy");
4663       break;
4664     }
4665     case CropMode:
4666     {
4667       (void) CloneString(&windows->command.name,"Crop");
4668       break;
4669     }
4670     case CutMode:
4671     {
4672       (void) CloneString(&windows->command.name,"Cut");
4673       break;
4674     }
4675   }
4676   RectifyModeMenu[0]=windows->command.name;
4677   windows->command.data=0;
4678   (void) XCommandWidget(display,windows,CropModeMenu,(XEvent *) NULL);
4679   (void) XMapRaised(display,windows->command.id);
4680   XClientMessage(display,windows->image.id,windows->im_protocols,
4681     windows->im_update_widget,CurrentTime);
4682   /*
4683     Track pointer until button 1 is pressed.
4684   */
4685   XQueryPosition(display,windows->image.id,&x,&y);
4686   (void) XSelectInput(display,windows->image.id,
4687     windows->image.attributes.event_mask | PointerMotionMask);
4688   crop_info.x=(ssize_t) windows->image.x+x;
4689   crop_info.y=(ssize_t) windows->image.y+y;
4690   crop_info.width=0;
4691   crop_info.height=0;
4692   cursor=XCreateFontCursor(display,XC_fleur);
4693   state=DefaultState;
4694   do
4695   {
4696     if( IfMagickTrue(windows->info.mapped) )
4697       {
4698         /*
4699           Display pointer position.
4700         */
4701         (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
4702           (long) crop_info.x,(long) crop_info.y);
4703         XInfoWidget(display,windows,text);
4704       }
4705     /*
4706       Wait for next event.
4707     */
4708     XScreenEvent(display,windows,&event,exception);
4709     if (event.xany.window == windows->command.id)
4710       {
4711         /*
4712           Select a command from the Command widget.
4713         */
4714         id=XCommandWidget(display,windows,CropModeMenu,&event);
4715         if (id < 0)
4716           continue;
4717         switch (CropCommands[id])
4718         {
4719           case CropHelpCommand:
4720           {
4721             switch (mode)
4722             {
4723               case CopyMode:
4724               {
4725                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4726                   "Help Viewer - Image Copy",ImageCopyHelp);
4727                 break;
4728               }
4729               case CropMode:
4730               {
4731                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4732                   "Help Viewer - Image Crop",ImageCropHelp);
4733                 break;
4734               }
4735               case CutMode:
4736               {
4737                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4738                   "Help Viewer - Image Cut",ImageCutHelp);
4739                 break;
4740               }
4741             }
4742             break;
4743           }
4744           case CropDismissCommand:
4745           {
4746             /*
4747               Prematurely exit.
4748             */
4749             state|=EscapeState;
4750             state|=ExitState;
4751             break;
4752           }
4753           default:
4754             break;
4755         }
4756         continue;
4757       }
4758     switch (event.type)
4759     {
4760       case ButtonPress:
4761       {
4762         if (event.xbutton.button != Button1)
4763           break;
4764         if (event.xbutton.window != windows->image.id)
4765           break;
4766         /*
4767           Note first corner of cropping rectangle-- exit loop.
4768         */
4769         (void) XCheckDefineCursor(display,windows->image.id,cursor);
4770         crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4771         crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4772         state|=ExitState;
4773         break;
4774       }
4775       case ButtonRelease:
4776         break;
4777       case Expose:
4778         break;
4779       case KeyPress:
4780       {
4781         if (event.xkey.window != windows->image.id)
4782           break;
4783         /*
4784           Respond to a user key press.
4785         */
4786         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
4787           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4788         switch ((int) key_symbol)
4789         {
4790           case XK_Escape:
4791           case XK_F20:
4792           {
4793             /*
4794               Prematurely exit.
4795             */
4796             state|=EscapeState;
4797             state|=ExitState;
4798             break;
4799           }
4800           case XK_F1:
4801           case XK_Help:
4802           {
4803             switch (mode)
4804             {
4805               case CopyMode:
4806               {
4807                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4808                   "Help Viewer - Image Copy",ImageCopyHelp);
4809                 break;
4810               }
4811               case CropMode:
4812               {
4813                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4814                   "Help Viewer - Image Crop",ImageCropHelp);
4815                 break;
4816               }
4817               case CutMode:
4818               {
4819                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4820                   "Help Viewer - Image Cut",ImageCutHelp);
4821                 break;
4822               }
4823             }
4824             break;
4825           }
4826           default:
4827           {
4828             (void) XBell(display,0);
4829             break;
4830           }
4831         }
4832         break;
4833       }
4834       case MotionNotify:
4835       {
4836         if (event.xmotion.window != windows->image.id)
4837           break;
4838         /*
4839           Map and unmap Info widget as text cursor crosses its boundaries.
4840         */
4841         x=event.xmotion.x;
4842         y=event.xmotion.y;
4843         if( IfMagickTrue(windows->info.mapped) )
4844           {
4845             if ((x < (int) (windows->info.x+windows->info.width)) &&
4846                 (y < (int) (windows->info.y+windows->info.height)))
4847               (void) XWithdrawWindow(display,windows->info.id,
4848                 windows->info.screen);
4849           }
4850         else
4851           if ((x > (int) (windows->info.x+windows->info.width)) ||
4852               (y > (int) (windows->info.y+windows->info.height)))
4853             (void) XMapWindow(display,windows->info.id);
4854         crop_info.x=(ssize_t) windows->image.x+x;
4855         crop_info.y=(ssize_t) windows->image.y+y;
4856         break;
4857       }
4858       default:
4859         break;
4860     }
4861   } while ((state & ExitState) == 0);
4862   (void) XSelectInput(display,windows->image.id,
4863     windows->image.attributes.event_mask);
4864   if ((state & EscapeState) != 0)
4865     {
4866       /*
4867         User want to exit without cropping.
4868       */
4869       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4870       (void) XFreeCursor(display,cursor);
4871       return(MagickTrue);
4872     }
4873   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
4874   do
4875   {
4876     /*
4877       Size rectangle as pointer moves until the mouse button is released.
4878     */
4879     x=(int) crop_info.x;
4880     y=(int) crop_info.y;
4881     crop_info.width=0;
4882     crop_info.height=0;
4883     state=DefaultState;
4884     do
4885     {
4886       highlight_info=crop_info;
4887       highlight_info.x=crop_info.x-windows->image.x;
4888       highlight_info.y=crop_info.y-windows->image.y;
4889       if ((highlight_info.width > 3) && (highlight_info.height > 3))
4890         {
4891           /*
4892             Display info and draw cropping rectangle.
4893           */
4894           if( IfMagickFalse(windows->info.mapped) )
4895             (void) XMapWindow(display,windows->info.id);
4896           (void) FormatLocaleString(text,MaxTextExtent,
4897             " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
4898             crop_info.height,(double) crop_info.x,(double) crop_info.y);
4899           XInfoWidget(display,windows,text);
4900           XHighlightRectangle(display,windows->image.id,
4901             windows->image.highlight_context,&highlight_info);
4902         }
4903       else
4904         if( IfMagickTrue(windows->info.mapped) )
4905           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4906       /*
4907         Wait for next event.
4908       */
4909       XScreenEvent(display,windows,&event,exception);
4910       if ((highlight_info.width > 3) && (highlight_info.height > 3))
4911         XHighlightRectangle(display,windows->image.id,
4912           windows->image.highlight_context,&highlight_info);
4913       switch (event.type)
4914       {
4915         case ButtonPress:
4916         {
4917           crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4918           crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4919           break;
4920         }
4921         case ButtonRelease:
4922         {
4923           /*
4924             User has committed to cropping rectangle.
4925           */
4926           crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4927           crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4928           XSetCursorState(display,windows,MagickFalse);
4929           state|=ExitState;
4930           windows->command.data=0;
4931           (void) XCommandWidget(display,windows,RectifyModeMenu,
4932             (XEvent *) NULL);
4933           break;
4934         }
4935         case Expose:
4936           break;
4937         case MotionNotify:
4938         {
4939           crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
4940           crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
4941         }
4942         default:
4943           break;
4944       }
4945       if ((((int) crop_info.x != x) && ((int) crop_info.y != y)) ||
4946           ((state & ExitState) != 0))
4947         {
4948           /*
4949             Check boundary conditions.
4950           */
4951           if (crop_info.x < 0)
4952             crop_info.x=0;
4953           else
4954             if (crop_info.x > (ssize_t) windows->image.ximage->width)
4955               crop_info.x=(ssize_t) windows->image.ximage->width;
4956           if ((int) crop_info.x < x)
4957             crop_info.width=(unsigned int) (x-crop_info.x);
4958           else
4959             {
4960               crop_info.width=(unsigned int) (crop_info.x-x);
4961               crop_info.x=(ssize_t) x;
4962             }
4963           if (crop_info.y < 0)
4964             crop_info.y=0;
4965           else
4966             if (crop_info.y > (ssize_t) windows->image.ximage->height)
4967               crop_info.y=(ssize_t) windows->image.ximage->height;
4968           if ((int) crop_info.y < y)
4969             crop_info.height=(unsigned int) (y-crop_info.y);
4970           else
4971             {
4972               crop_info.height=(unsigned int) (crop_info.y-y);
4973               crop_info.y=(ssize_t) y;
4974             }
4975         }
4976     } while ((state & ExitState) == 0);
4977     /*
4978       Wait for user to grab a corner of the rectangle or press return.
4979     */
4980     state=DefaultState;
4981     (void) XMapWindow(display,windows->info.id);
4982     do
4983     {
4984       if( IfMagickTrue(windows->info.mapped) )
4985         {
4986           /*
4987             Display pointer position.
4988           */
4989           (void) FormatLocaleString(text,MaxTextExtent,
4990             " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
4991             crop_info.height,(double) crop_info.x,(double) crop_info.y);
4992           XInfoWidget(display,windows,text);
4993         }
4994       highlight_info=crop_info;
4995       highlight_info.x=crop_info.x-windows->image.x;
4996       highlight_info.y=crop_info.y-windows->image.y;
4997       if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
4998         {
4999           state|=EscapeState;
5000           state|=ExitState;
5001           break;
5002         }
5003       XHighlightRectangle(display,windows->image.id,
5004         windows->image.highlight_context,&highlight_info);
5005       XScreenEvent(display,windows,&event,exception);
5006       if (event.xany.window == windows->command.id)
5007         {
5008           /*
5009             Select a command from the Command widget.
5010           */
5011           (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
5012           id=XCommandWidget(display,windows,RectifyModeMenu,&event);
5013           (void) XSetFunction(display,windows->image.highlight_context,
5014             GXinvert);
5015           XHighlightRectangle(display,windows->image.id,
5016             windows->image.highlight_context,&highlight_info);
5017           if (id >= 0)
5018             switch (RectifyCommands[id])
5019             {
5020               case RectifyCopyCommand:
5021               {
5022                 state|=ExitState;
5023                 break;
5024               }
5025               case RectifyHelpCommand:
5026               {
5027                 (void) XSetFunction(display,windows->image.highlight_context,
5028                   GXcopy);
5029                 switch (mode)
5030                 {
5031                   case CopyMode:
5032                   {
5033                     XTextViewWidget(display,resource_info,windows,MagickFalse,
5034                       "Help Viewer - Image Copy",ImageCopyHelp);
5035                     break;
5036                   }
5037                   case CropMode:
5038                   {
5039                     XTextViewWidget(display,resource_info,windows,MagickFalse,
5040                       "Help Viewer - Image Crop",ImageCropHelp);
5041                     break;
5042                   }
5043                   case CutMode:
5044                   {
5045                     XTextViewWidget(display,resource_info,windows,MagickFalse,
5046                       "Help Viewer - Image Cut",ImageCutHelp);
5047                     break;
5048                   }
5049                 }
5050                 (void) XSetFunction(display,windows->image.highlight_context,
5051                   GXinvert);
5052                 break;
5053               }
5054               case RectifyDismissCommand:
5055               {
5056                 /*
5057                   Prematurely exit.
5058                 */
5059                 state|=EscapeState;
5060                 state|=ExitState;
5061                 break;
5062               }
5063               default:
5064                 break;
5065             }
5066           continue;
5067         }
5068       XHighlightRectangle(display,windows->image.id,
5069         windows->image.highlight_context,&highlight_info);
5070       switch (event.type)
5071       {
5072         case ButtonPress:
5073         {
5074           if (event.xbutton.button != Button1)
5075             break;
5076           if (event.xbutton.window != windows->image.id)
5077             break;
5078           x=windows->image.x+event.xbutton.x;
5079           y=windows->image.y+event.xbutton.y;
5080           if ((x < (int) (crop_info.x+RoiDelta)) &&
5081               (x > (int) (crop_info.x-RoiDelta)) &&
5082               (y < (int) (crop_info.y+RoiDelta)) &&
5083               (y > (int) (crop_info.y-RoiDelta)))
5084             {
5085               crop_info.x=(ssize_t) (crop_info.x+crop_info.width);
5086               crop_info.y=(ssize_t) (crop_info.y+crop_info.height);
5087               state|=UpdateConfigurationState;
5088               break;
5089             }
5090           if ((x < (int) (crop_info.x+RoiDelta)) &&
5091               (x > (int) (crop_info.x-RoiDelta)) &&
5092               (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5093               (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5094             {
5095               crop_info.x=(ssize_t) (crop_info.x+crop_info.width);
5096               state|=UpdateConfigurationState;
5097               break;
5098             }
5099           if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5100               (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5101               (y < (int) (crop_info.y+RoiDelta)) &&
5102               (y > (int) (crop_info.y-RoiDelta)))
5103             {
5104               crop_info.y=(ssize_t) (crop_info.y+crop_info.height);
5105               state|=UpdateConfigurationState;
5106               break;
5107             }
5108           if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5109               (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5110               (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5111               (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5112             {
5113               state|=UpdateConfigurationState;
5114               break;
5115             }
5116         }
5117         case ButtonRelease:
5118         {
5119           if (event.xbutton.window == windows->pan.id)
5120             if ((highlight_info.x != crop_info.x-windows->image.x) ||
5121                 (highlight_info.y != crop_info.y-windows->image.y))
5122               XHighlightRectangle(display,windows->image.id,
5123                 windows->image.highlight_context,&highlight_info);
5124           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5125             event.xbutton.time);
5126           break;
5127         }
5128         case Expose:
5129         {
5130           if (event.xexpose.window == windows->image.id)
5131             if (event.xexpose.count == 0)
5132               {
5133                 event.xexpose.x=(int) highlight_info.x;
5134                 event.xexpose.y=(int) highlight_info.y;
5135                 event.xexpose.width=(int) highlight_info.width;
5136                 event.xexpose.height=(int) highlight_info.height;
5137                 XRefreshWindow(display,&windows->image,&event);
5138               }
5139           if (event.xexpose.window == windows->info.id)
5140             if (event.xexpose.count == 0)
5141               XInfoWidget(display,windows,text);
5142           break;
5143         }
5144         case KeyPress:
5145         {
5146           if (event.xkey.window != windows->image.id)
5147             break;
5148           /*
5149             Respond to a user key press.
5150           */
5151           (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5152             sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5153           switch ((int) key_symbol)
5154           {
5155             case XK_Escape:
5156             case XK_F20:
5157               state|=EscapeState;
5158             case XK_Return:
5159             {
5160               state|=ExitState;
5161               break;
5162             }
5163             case XK_Home:
5164             case XK_KP_Home:
5165             {
5166               crop_info.x=(ssize_t) (windows->image.width/2L-
5167                 crop_info.width/2L);
5168               crop_info.y=(ssize_t) (windows->image.height/2L-
5169                 crop_info.height/2L);
5170               break;
5171             }
5172             case XK_Left:
5173             case XK_KP_Left:
5174             {
5175               crop_info.x--;
5176               break;
5177             }
5178             case XK_Up:
5179             case XK_KP_Up:
5180             case XK_Next:
5181             {
5182               crop_info.y--;
5183               break;
5184             }
5185             case XK_Right:
5186             case XK_KP_Right:
5187             {
5188               crop_info.x++;
5189               break;
5190             }
5191             case XK_Prior:
5192             case XK_Down:
5193             case XK_KP_Down:
5194             {
5195               crop_info.y++;
5196               break;
5197             }
5198             case XK_F1:
5199             case XK_Help:
5200             {
5201               (void) XSetFunction(display,windows->image.highlight_context,
5202                 GXcopy);
5203               switch (mode)
5204               {
5205                 case CopyMode:
5206                 {
5207                   XTextViewWidget(display,resource_info,windows,MagickFalse,
5208                     "Help Viewer - Image Copy",ImageCopyHelp);
5209                   break;
5210                 }
5211                 case CropMode:
5212                 {
5213                   XTextViewWidget(display,resource_info,windows,MagickFalse,
5214                     "Help Viewer - Image Cropg",ImageCropHelp);
5215                   break;
5216                 }
5217                 case CutMode:
5218                 {
5219                   XTextViewWidget(display,resource_info,windows,MagickFalse,
5220                     "Help Viewer - Image Cutg",ImageCutHelp);
5221                   break;
5222                 }
5223               }
5224               (void) XSetFunction(display,windows->image.highlight_context,
5225                 GXinvert);
5226               break;
5227             }
5228             default:
5229             {
5230               (void) XBell(display,0);
5231               break;
5232             }
5233           }
5234           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5235             event.xkey.time);
5236           break;
5237         }
5238         case KeyRelease:
5239           break;
5240         case MotionNotify:
5241         {
5242           if (event.xmotion.window != windows->image.id)
5243             break;
5244           /*
5245             Map and unmap Info widget as text cursor crosses its boundaries.
5246           */
5247           x=event.xmotion.x;
5248           y=event.xmotion.y;
5249           if( IfMagickTrue(windows->info.mapped) )
5250             {
5251               if ((x < (int) (windows->info.x+windows->info.width)) &&
5252                   (y < (int) (windows->info.y+windows->info.height)))
5253                 (void) XWithdrawWindow(display,windows->info.id,
5254                   windows->info.screen);
5255             }
5256           else
5257             if ((x > (int) (windows->info.x+windows->info.width)) ||
5258                 (y > (int) (windows->info.y+windows->info.height)))
5259               (void) XMapWindow(display,windows->info.id);
5260           crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
5261           crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
5262           break;
5263         }
5264         case SelectionRequest:
5265         {
5266           XSelectionEvent
5267             notify;
5268
5269           XSelectionRequestEvent
5270             *request;
5271
5272           /*
5273             Set primary selection.
5274           */
5275           (void) FormatLocaleString(text,MaxTextExtent,
5276             "%.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
5277             crop_info.height,(double) crop_info.x,(double) crop_info.y);
5278           request=(&(event.xselectionrequest));
5279           (void) XChangeProperty(request->display,request->requestor,
5280             request->property,request->target,8,PropModeReplace,
5281             (unsigned char *) text,(int) strlen(text));
5282           notify.type=SelectionNotify;
5283           notify.display=request->display;
5284           notify.requestor=request->requestor;
5285           notify.selection=request->selection;
5286           notify.target=request->target;
5287           notify.time=request->time;
5288           if (request->property == None)
5289             notify.property=request->target;
5290           else
5291             notify.property=request->property;
5292           (void) XSendEvent(request->display,request->requestor,False,0,
5293             (XEvent *) &notify);
5294         }
5295         default:
5296           break;
5297       }
5298       if ((state & UpdateConfigurationState) != 0)
5299         {
5300           (void) XPutBackEvent(display,&event);
5301           (void) XCheckDefineCursor(display,windows->image.id,cursor);
5302           break;
5303         }
5304     } while ((state & ExitState) == 0);
5305   } while ((state & ExitState) == 0);
5306   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
5307   XSetCursorState(display,windows,MagickFalse);
5308   if ((state & EscapeState) != 0)
5309     return(MagickTrue);
5310   if (mode == CropMode)
5311     if (((int) crop_info.width != windows->image.ximage->width) ||
5312         ((int) crop_info.height != windows->image.ximage->height))
5313       {
5314         /*
5315           Reconfigure Image window as defined by cropping rectangle.
5316         */
5317         XSetCropGeometry(display,windows,&crop_info,image);
5318         windows->image.window_changes.width=(int) crop_info.width;
5319         windows->image.window_changes.height=(int) crop_info.height;
5320         (void) XConfigureImage(display,resource_info,windows,image,exception);
5321         return(MagickTrue);
5322       }
5323   /*
5324     Copy image before applying image transforms.
5325   */
5326   XSetCursorState(display,windows,MagickTrue);
5327   XCheckRefreshWindows(display,windows);
5328   width=(unsigned int) image->columns;
5329   height=(unsigned int) image->rows;
5330   x=0;
5331   y=0;
5332   if (windows->image.crop_geometry != (char *) NULL)
5333     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
5334   scale_factor=(double) width/windows->image.ximage->width;
5335   crop_info.x+=x;
5336   crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
5337   crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
5338   scale_factor=(double) height/windows->image.ximage->height;
5339   crop_info.y+=y;
5340   crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
5341   crop_info.height=(unsigned int) (scale_factor*crop_info.height+0.5);
5342   crop_image=CropImage(image,&crop_info,exception);
5343   XSetCursorState(display,windows,MagickFalse);
5344   if (crop_image == (Image *) NULL)
5345     return(MagickFalse);
5346   if (resource_info->copy_image != (Image *) NULL)
5347     resource_info->copy_image=DestroyImage(resource_info->copy_image);
5348   resource_info->copy_image=crop_image;
5349   if (mode == CopyMode)
5350     {
5351       (void) XConfigureImage(display,resource_info,windows,image,exception);
5352       return(MagickTrue);
5353     }
5354   /*
5355     Cut image.
5356   */
5357   if( IfMagickFalse(SetImageStorageClass(image,DirectClass,exception)) )
5358     return(MagickFalse);
5359   image->alpha_trait=BlendPixelTrait;
5360   image_view=AcquireAuthenticCacheView(image,exception);
5361   for (y=0; y < (int) crop_info.height; y++)
5362   {
5363     q=GetCacheViewAuthenticPixels(image_view,crop_info.x,y+crop_info.y,
5364       crop_info.width,1,exception);
5365     if (q == (Quantum *) NULL)
5366       break;
5367     for (x=0; x < (int) crop_info.width; x++)
5368     {
5369       SetPixelAlpha(image,TransparentAlpha,q);
5370       q+=GetPixelChannels(image);
5371     }
5372     if( IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
5373       break;
5374   }
5375   image_view=DestroyCacheView(image_view);
5376   /*
5377     Update image configuration.
5378   */
5379   XConfigureImageColormap(display,resource_info,windows,image,exception);
5380   (void) XConfigureImage(display,resource_info,windows,image,exception);
5381   return(MagickTrue);
5382 }
5383 \f
5384 /*
5385 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5386 %                                                                             %
5387 %                                                                             %
5388 %                                                                             %
5389 +   X D r a w I m a g e                                                       %
5390 %                                                                             %
5391 %                                                                             %
5392 %                                                                             %
5393 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5394 %
5395 %  XDrawEditImage() draws a graphic element (point, line, rectangle, etc.) on
5396 %  the image.
5397 %
5398 %  The format of the XDrawEditImage method is:
5399 %
5400 %      MagickBooleanType XDrawEditImage(Display *display,
5401 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
5402 %        ExceptionInfo *exception)
5403 %
5404 %  A description of each parameter follows:
5405 %
5406 %    o display: Specifies a connection to an X server; returned from
5407 %      XOpenDisplay.
5408 %
5409 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
5410 %
5411 %    o windows: Specifies a pointer to a XWindows structure.
5412 %
5413 %    o image: the image.
5414 %
5415 %    o exception: return any errors or warnings in this structure.
5416 %
5417 */
5418 static MagickBooleanType XDrawEditImage(Display *display,
5419   XResourceInfo *resource_info,XWindows *windows,Image **image,
5420   ExceptionInfo *exception)
5421 {
5422   static const char
5423     *DrawMenu[] =
5424     {
5425       "Element",
5426       "Color",
5427       "Stipple",
5428       "Width",
5429       "Undo",
5430       "Help",
5431       "Dismiss",
5432       (char *) NULL
5433     };
5434
5435   static ElementType
5436     element = PointElement;
5437
5438   static const ModeType
5439     DrawCommands[] =
5440     {
5441       DrawElementCommand,
5442       DrawColorCommand,
5443       DrawStippleCommand,
5444       DrawWidthCommand,
5445       DrawUndoCommand,
5446       DrawHelpCommand,
5447       DrawDismissCommand
5448     };
5449
5450   static Pixmap
5451     stipple = (Pixmap) NULL;
5452
5453   static unsigned int
5454     pen_id = 0,
5455     line_width = 1;
5456
5457   char
5458     command[MaxTextExtent],
5459     text[MaxTextExtent];
5460
5461   Cursor
5462     cursor;
5463
5464   int
5465     entry,
5466     id,
5467     number_coordinates,
5468     x,
5469     y;
5470
5471   double
5472     degrees;
5473
5474   MagickStatusType
5475     status;
5476
5477   RectangleInfo
5478     rectangle_info;
5479
5480   register int
5481     i;
5482
5483   unsigned int
5484     distance,
5485     height,
5486     max_coordinates,
5487     width;
5488
5489   size_t
5490     state;
5491
5492   Window
5493     root_window;
5494
5495   XDrawInfo
5496     draw_info;
5497
5498   XEvent
5499     event;
5500
5501   XPoint
5502     *coordinate_info;
5503
5504   XSegment
5505     line_info;
5506
5507   /*
5508     Allocate polygon info.
5509   */
5510   max_coordinates=2048;
5511   coordinate_info=(XPoint *) AcquireQuantumMemory((size_t) max_coordinates,
5512     sizeof(*coordinate_info));
5513   if (coordinate_info == (XPoint *) NULL)
5514     {
5515       (void) ThrowMagickException(exception,GetMagickModule(),
5516         ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
5517       return(MagickFalse);
5518     }
5519   /*
5520     Map Command widget.
5521   */
5522   (void) CloneString(&windows->command.name,"Draw");
5523   windows->command.data=4;
5524   (void) XCommandWidget(display,windows,DrawMenu,(XEvent *) NULL);
5525   (void) XMapRaised(display,windows->command.id);
5526   XClientMessage(display,windows->image.id,windows->im_protocols,
5527     windows->im_update_widget,CurrentTime);
5528   /*
5529     Wait for first button press.
5530   */
5531   root_window=XRootWindow(display,XDefaultScreen(display));
5532   draw_info.stencil=OpaqueStencil;
5533   status=MagickTrue;
5534   cursor=XCreateFontCursor(display,XC_tcross);
5535   for ( ; ; )
5536   {
5537     XQueryPosition(display,windows->image.id,&x,&y);
5538     (void) XSelectInput(display,windows->image.id,
5539       windows->image.attributes.event_mask | PointerMotionMask);
5540     (void) XCheckDefineCursor(display,windows->image.id,cursor);
5541     state=DefaultState;
5542     do
5543     {
5544       if( IfMagickTrue(windows->info.mapped) )
5545         {
5546           /*
5547             Display pointer position.
5548           */
5549           (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
5550             x+windows->image.x,y+windows->image.y);
5551           XInfoWidget(display,windows,text);
5552         }
5553       /*
5554         Wait for next event.
5555       */
5556       XScreenEvent(display,windows,&event,exception);
5557       if (event.xany.window == windows->command.id)
5558         {
5559           /*
5560             Select a command from the Command widget.
5561           */
5562           id=XCommandWidget(display,windows,DrawMenu,&event);
5563           if (id < 0)
5564             continue;
5565           switch (DrawCommands[id])
5566           {
5567             case DrawElementCommand:
5568             {
5569               static const char
5570                 *Elements[] =
5571                 {
5572                   "point",
5573                   "line",
5574                   "rectangle",
5575                   "fill rectangle",
5576                   "circle",
5577                   "fill circle",
5578                   "ellipse",
5579                   "fill ellipse",
5580                   "polygon",
5581                   "fill polygon",
5582                   (char *) NULL,
5583                 };
5584
5585               /*
5586                 Select a command from the pop-up menu.
5587               */
5588               element=(ElementType) (XMenuWidget(display,windows,
5589                 DrawMenu[id],Elements,command)+1);
5590               break;
5591             }
5592             case DrawColorCommand:
5593             {
5594               const char
5595                 *ColorMenu[MaxNumberPens+1];
5596
5597               int
5598                 pen_number;
5599
5600               MagickBooleanType
5601                 transparent;
5602
5603               XColor
5604                 color;
5605
5606               /*
5607                 Initialize menu selections.
5608               */
5609               for (i=0; i < (int) (MaxNumberPens-2); i++)
5610                 ColorMenu[i]=resource_info->pen_colors[i];
5611               ColorMenu[MaxNumberPens-2]="transparent";
5612               ColorMenu[MaxNumberPens-1]="Browser...";
5613               ColorMenu[MaxNumberPens]=(char *) NULL;
5614               /*
5615                 Select a pen color from the pop-up menu.
5616               */
5617               pen_number=XMenuWidget(display,windows,DrawMenu[id],
5618                 (const char **) ColorMenu,command);
5619               if (pen_number < 0)
5620                 break;
5621               transparent=pen_number == (MaxNumberPens-2) ? MagickTrue :
5622                 MagickFalse;
5623               if( IfMagickTrue(transparent) )
5624                 {
5625                   draw_info.stencil=TransparentStencil;
5626                   break;
5627                 }
5628               if (pen_number == (MaxNumberPens-1))
5629                 {
5630                   static char
5631                     color_name[MaxTextExtent] = "gray";
5632
5633                   /*
5634                     Select a pen color from a dialog.
5635                   */
5636                   resource_info->pen_colors[pen_number]=color_name;
5637                   XColorBrowserWidget(display,windows,"Select",color_name);
5638                   if (*color_name == '\0')
5639                     break;
5640                 }
5641               /*
5642                 Set pen color.
5643               */
5644               (void) XParseColor(display,windows->map_info->colormap,
5645                 resource_info->pen_colors[pen_number],&color);
5646               XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
5647                 (unsigned int) MaxColors,&color);
5648               windows->pixel_info->pen_colors[pen_number]=color;
5649               pen_id=(unsigned int) pen_number;
5650               draw_info.stencil=OpaqueStencil;
5651               break;
5652             }
5653             case DrawStippleCommand:
5654             {
5655               Image
5656                 *stipple_image;
5657
5658               ImageInfo
5659                 *image_info;
5660
5661               int
5662                 status;
5663
5664               static char
5665                 filename[MaxTextExtent] = "\0";
5666
5667               static const char
5668                 *StipplesMenu[] =
5669                 {
5670                   "Brick",
5671                   "Diagonal",
5672                   "Scales",
5673                   "Vertical",
5674                   "Wavy",
5675                   "Translucent",
5676                   "Opaque",
5677                   (char *) NULL,
5678                   (char *) NULL,
5679                 };
5680
5681               /*
5682                 Select a command from the pop-up menu.
5683               */
5684               StipplesMenu[7]="Open...";
5685               entry=XMenuWidget(display,windows,DrawMenu[id],StipplesMenu,
5686                 command);
5687               if (entry < 0)
5688                 break;
5689               if (stipple != (Pixmap) NULL)
5690                 (void) XFreePixmap(display,stipple);
5691               stipple=(Pixmap) NULL;
5692               if (entry != 7)
5693                 {
5694                   switch (entry)
5695                   {
5696                     case 0:
5697                     {
5698                       stipple=XCreateBitmapFromData(display,root_window,
5699                         (char *) BricksBitmap,BricksWidth,BricksHeight);
5700                       break;
5701                     }
5702                     case 1:
5703                     {
5704                       stipple=XCreateBitmapFromData(display,root_window,
5705                         (char *) DiagonalBitmap,DiagonalWidth,DiagonalHeight);
5706                       break;
5707                     }
5708                     case 2:
5709                     {
5710                       stipple=XCreateBitmapFromData(display,root_window,
5711                         (char *) ScalesBitmap,ScalesWidth,ScalesHeight);
5712                       break;
5713                     }
5714                     case 3:
5715                     {
5716                       stipple=XCreateBitmapFromData(display,root_window,
5717                         (char *) VerticalBitmap,VerticalWidth,VerticalHeight);
5718                       break;
5719                     }
5720                     case 4:
5721                     {
5722                       stipple=XCreateBitmapFromData(display,root_window,
5723                         (char *) WavyBitmap,WavyWidth,WavyHeight);
5724                       break;
5725                     }
5726                     case 5:
5727                     {
5728                       stipple=XCreateBitmapFromData(display,root_window,
5729                         (char *) HighlightBitmap,HighlightWidth,
5730                         HighlightHeight);
5731                       break;
5732                     }
5733                     case 6:
5734                     default:
5735                     {
5736                       stipple=XCreateBitmapFromData(display,root_window,
5737                         (char *) OpaqueBitmap,OpaqueWidth,OpaqueHeight);
5738                       break;
5739                     }
5740                   }
5741                   break;
5742                 }
5743               XFileBrowserWidget(display,windows,"Stipple",filename);
5744               if (*filename == '\0')
5745                 break;
5746               /*
5747                 Read image.
5748               */
5749               XSetCursorState(display,windows,MagickTrue);
5750               XCheckRefreshWindows(display,windows);
5751               image_info=AcquireImageInfo();
5752               (void) CopyMagickString(image_info->filename,filename,
5753                 MaxTextExtent);
5754               stipple_image=ReadImage(image_info,exception);
5755               CatchException(exception);
5756               XSetCursorState(display,windows,MagickFalse);
5757               if (stipple_image == (Image *) NULL)
5758                 break;
5759               (void) AcquireUniqueFileResource(filename);
5760               (void) FormatLocaleString(stipple_image->filename,MaxTextExtent,
5761                 "xbm:%s",filename);
5762               (void) WriteImage(image_info,stipple_image,exception);
5763               stipple_image=DestroyImage(stipple_image);
5764               image_info=DestroyImageInfo(image_info);
5765               status=XReadBitmapFile(display,root_window,filename,&width,
5766                 &height,&stipple,&x,&y);
5767               (void) RelinquishUniqueFileResource(filename);
5768               if ((status != BitmapSuccess) != 0)
5769                 XNoticeWidget(display,windows,"Unable to read X bitmap image:",
5770                   filename);
5771               break;
5772             }
5773             case DrawWidthCommand:
5774             {
5775               static char
5776                 width[MaxTextExtent] = "0";
5777
5778               static const char
5779                 *WidthsMenu[] =
5780                 {
5781                   "1",
5782                   "2",
5783                   "4",
5784                   "8",
5785                   "16",
5786                   "Dialog...",
5787                   (char *) NULL,
5788                 };
5789
5790               /*
5791                 Select a command from the pop-up menu.
5792               */
5793               entry=XMenuWidget(display,windows,DrawMenu[id],WidthsMenu,
5794                 command);
5795               if (entry < 0)
5796                 break;
5797               if (entry != 5)
5798                 {
5799                   line_width=(unsigned int) StringToUnsignedLong(
5800                     WidthsMenu[entry]);
5801                   break;
5802                 }
5803               (void) XDialogWidget(display,windows,"Ok","Enter line width:",
5804                 width);
5805               if (*width == '\0')
5806                 break;
5807               line_width=(unsigned int) StringToUnsignedLong(width);
5808               break;
5809             }
5810             case DrawUndoCommand:
5811             {
5812               (void) XMagickCommand(display,resource_info,windows,UndoCommand,
5813                 image,exception);
5814               break;
5815             }
5816             case DrawHelpCommand:
5817             {
5818               XTextViewWidget(display,resource_info,windows,MagickFalse,
5819                 "Help Viewer - Image Rotation",ImageDrawHelp);
5820               (void) XCheckDefineCursor(display,windows->image.id,cursor);
5821               break;
5822             }
5823             case DrawDismissCommand:
5824             {
5825               /*
5826                 Prematurely exit.
5827               */
5828               state|=EscapeState;
5829               state|=ExitState;
5830               break;
5831             }
5832             default:
5833               break;
5834           }
5835           (void) XCheckDefineCursor(display,windows->image.id,cursor);
5836           continue;
5837         }
5838       switch (event.type)
5839       {
5840         case ButtonPress:
5841         {
5842           if (event.xbutton.button != Button1)
5843             break;
5844           if (event.xbutton.window != windows->image.id)
5845             break;
5846           /*
5847             exit loop.
5848           */
5849           x=event.xbutton.x;
5850           y=event.xbutton.y;
5851           state|=ExitState;
5852           break;
5853         }
5854         case ButtonRelease:
5855           break;
5856         case Expose:
5857           break;
5858         case KeyPress:
5859         {
5860           KeySym
5861             key_symbol;
5862
5863           if (event.xkey.window != windows->image.id)
5864             break;
5865           /*
5866             Respond to a user key press.
5867           */
5868           (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5869             sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5870           switch ((int) key_symbol)
5871           {
5872             case XK_Escape:
5873             case XK_F20:
5874             {
5875               /*
5876                 Prematurely exit.
5877               */
5878               state|=EscapeState;
5879               state|=ExitState;
5880               break;
5881             }
5882             case XK_F1:
5883             case XK_Help:
5884             {
5885               XTextViewWidget(display,resource_info,windows,MagickFalse,
5886                 "Help Viewer - Image Rotation",ImageDrawHelp);
5887               break;
5888             }
5889             default:
5890             {
5891               (void) XBell(display,0);
5892               break;
5893             }
5894           }
5895           break;
5896         }
5897         case MotionNotify:
5898         {
5899           /*
5900             Map and unmap Info widget as text cursor crosses its boundaries.
5901           */
5902           x=event.xmotion.x;
5903           y=event.xmotion.y;
5904           if( IfMagickTrue(windows->info.mapped) )
5905             {
5906               if ((x < (int) (windows->info.x+windows->info.width)) &&
5907                   (y < (int) (windows->info.y+windows->info.height)))
5908                 (void) XWithdrawWindow(display,windows->info.id,
5909                   windows->info.screen);
5910             }
5911           else
5912             if ((x > (int) (windows->info.x+windows->info.width)) ||
5913                 (y > (int) (windows->info.y+windows->info.height)))
5914               (void) XMapWindow(display,windows->info.id);
5915           break;
5916         }
5917       }
5918     } while ((state & ExitState) == 0);
5919     (void) XSelectInput(display,windows->image.id,
5920       windows->image.attributes.event_mask);
5921     (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
5922     if ((state & EscapeState) != 0)
5923       break;
5924     /*
5925       Draw element as pointer moves until the button is released.
5926     */
5927     distance=0;
5928     degrees=0.0;
5929     line_info.x1=x;
5930     line_info.y1=y;
5931     line_info.x2=x;
5932     line_info.y2=y;
5933     rectangle_info.x=(ssize_t) x;
5934     rectangle_info.y=(ssize_t) y;
5935     rectangle_info.width=0;
5936     rectangle_info.height=0;
5937     number_coordinates=1;
5938     coordinate_info->x=x;
5939     coordinate_info->y=y;
5940     (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
5941     state=DefaultState;
5942     do
5943     {
5944       switch (element)
5945       {
5946         case PointElement:
5947         default:
5948         {
5949           if (number_coordinates > 1)
5950             {
5951               (void) XDrawLines(display,windows->image.id,
5952                 windows->image.highlight_context,coordinate_info,
5953                 number_coordinates,CoordModeOrigin);
5954               (void) FormatLocaleString(text,MaxTextExtent," %+d%+d",
5955                 coordinate_info[number_coordinates-1].x,
5956                 coordinate_info[number_coordinates-1].y);
5957               XInfoWidget(display,windows,text);
5958             }
5959           break;
5960         }
5961         case LineElement:
5962         {
5963           if (distance > 9)
5964             {
5965               /*
5966                 Display angle of the line.
5967               */
5968               degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
5969                 line_info.y1),(double) (line_info.x2-line_info.x1)));
5970               (void) FormatLocaleString(text,MaxTextExtent," %g",
5971                 (double) degrees);
5972               XInfoWidget(display,windows,text);
5973               XHighlightLine(display,windows->image.id,
5974                 windows->image.highlight_context,&line_info);
5975             }
5976           else
5977             if( IfMagickTrue(windows->info.mapped) )
5978               (void) XWithdrawWindow(display,windows->info.id,
5979                 windows->info.screen);
5980           break;
5981         }
5982         case RectangleElement:
5983         case FillRectangleElement:
5984         {
5985           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
5986             {
5987               /*
5988                 Display info and draw drawing rectangle.
5989               */
5990               (void) FormatLocaleString(text,MaxTextExtent,
5991                 " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
5992                 (double) rectangle_info.height,(double) rectangle_info.x,
5993                 (double) rectangle_info.y);
5994               XInfoWidget(display,windows,text);
5995               XHighlightRectangle(display,windows->image.id,
5996                 windows->image.highlight_context,&rectangle_info);
5997             }
5998           else
5999             if( IfMagickTrue(windows->info.mapped) )
6000               (void) XWithdrawWindow(display,windows->info.id,
6001                 windows->info.screen);
6002           break;
6003         }
6004         case CircleElement:
6005         case FillCircleElement:
6006         case EllipseElement:
6007         case FillEllipseElement:
6008         {
6009           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6010             {
6011               /*
6012                 Display info and draw drawing rectangle.
6013               */
6014               (void) FormatLocaleString(text,MaxTextExtent,
6015                 " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
6016                 (double) rectangle_info.height,(double) rectangle_info.x,
6017                 (double) rectangle_info.y);
6018               XInfoWidget(display,windows,text);
6019               XHighlightEllipse(display,windows->image.id,
6020                 windows->image.highlight_context,&rectangle_info);
6021             }
6022           else
6023             if( IfMagickTrue(windows->info.mapped) )
6024               (void) XWithdrawWindow(display,windows->info.id,
6025                 windows->info.screen);
6026           break;
6027         }
6028         case PolygonElement:
6029         case FillPolygonElement:
6030         {
6031           if (number_coordinates > 1)
6032             (void) XDrawLines(display,windows->image.id,
6033               windows->image.highlight_context,coordinate_info,
6034               number_coordinates,CoordModeOrigin);
6035           if (distance > 9)
6036             {
6037               /*
6038                 Display angle of the line.
6039               */
6040               degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
6041                 line_info.y1),(double) (line_info.x2-line_info.x1)));
6042               (void) FormatLocaleString(text,MaxTextExtent," %g",
6043                 (double) degrees);
6044               XInfoWidget(display,windows,text);
6045               XHighlightLine(display,windows->image.id,
6046                 windows->image.highlight_context,&line_info);
6047             }
6048           else
6049             if( IfMagickTrue(windows->info.mapped) )
6050               (void) XWithdrawWindow(display,windows->info.id,
6051                 windows->info.screen);
6052           break;
6053         }
6054       }
6055       /*
6056         Wait for next event.
6057       */
6058       XScreenEvent(display,windows,&event,exception);
6059       switch (element)
6060       {
6061         case PointElement:
6062         default:
6063         {
6064           if (number_coordinates > 1)
6065             (void) XDrawLines(display,windows->image.id,
6066               windows->image.highlight_context,coordinate_info,
6067               number_coordinates,CoordModeOrigin);
6068           break;
6069         }
6070         case LineElement:
6071         {
6072           if (distance > 9)
6073             XHighlightLine(display,windows->image.id,
6074               windows->image.highlight_context,&line_info);
6075           break;
6076         }
6077         case RectangleElement:
6078         case FillRectangleElement:
6079         {
6080           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6081             XHighlightRectangle(display,windows->image.id,
6082               windows->image.highlight_context,&rectangle_info);
6083           break;
6084         }
6085         case CircleElement:
6086         case FillCircleElement:
6087         case EllipseElement:
6088         case FillEllipseElement:
6089         {
6090           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6091             XHighlightEllipse(display,windows->image.id,
6092               windows->image.highlight_context,&rectangle_info);
6093           break;
6094         }
6095         case PolygonElement:
6096         case FillPolygonElement:
6097         {
6098           if (number_coordinates > 1)
6099             (void) XDrawLines(display,windows->image.id,
6100               windows->image.highlight_context,coordinate_info,
6101               number_coordinates,CoordModeOrigin);
6102           if (distance > 9)
6103             XHighlightLine(display,windows->image.id,
6104               windows->image.highlight_context,&line_info);
6105           break;
6106         }
6107       }
6108       switch (event.type)
6109       {
6110         case ButtonPress:
6111           break;
6112         case ButtonRelease:
6113         {
6114           /*
6115             User has committed to element.
6116           */
6117           line_info.x2=event.xbutton.x;
6118           line_info.y2=event.xbutton.y;
6119           rectangle_info.x=(ssize_t) event.xbutton.x;
6120           rectangle_info.y=(ssize_t) event.xbutton.y;
6121           coordinate_info[number_coordinates].x=event.xbutton.x;
6122           coordinate_info[number_coordinates].y=event.xbutton.y;
6123           if (((element != PolygonElement) &&
6124                (element != FillPolygonElement)) || (distance <= 9))
6125             {
6126               state|=ExitState;
6127               break;
6128             }
6129           number_coordinates++;
6130           if (number_coordinates < (int) max_coordinates)
6131             {
6132               line_info.x1=event.xbutton.x;
6133               line_info.y1=event.xbutton.y;
6134               break;
6135             }
6136           max_coordinates<<=1;
6137           coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6138             max_coordinates,sizeof(*coordinate_info));
6139           if (coordinate_info == (XPoint *) NULL)
6140             (void) ThrowMagickException(exception,GetMagickModule(),
6141               ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6142           break;
6143         }
6144         case Expose:
6145           break;
6146         case MotionNotify:
6147         {
6148           if (event.xmotion.window != windows->image.id)
6149             break;
6150           if (element != PointElement)
6151             {
6152               line_info.x2=event.xmotion.x;
6153               line_info.y2=event.xmotion.y;
6154               rectangle_info.x=(ssize_t) event.xmotion.x;
6155               rectangle_info.y=(ssize_t) event.xmotion.y;
6156               break;
6157             }
6158           coordinate_info[number_coordinates].x=event.xbutton.x;
6159           coordinate_info[number_coordinates].y=event.xbutton.y;
6160           number_coordinates++;
6161           if (number_coordinates < (int) max_coordinates)
6162             break;
6163           max_coordinates<<=1;
6164           coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6165             max_coordinates,sizeof(*coordinate_info));
6166           if (coordinate_info == (XPoint *) NULL)
6167             (void) ThrowMagickException(exception,GetMagickModule(),
6168               ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6169           break;
6170         }
6171         default:
6172           break;
6173       }
6174       /*
6175         Check boundary conditions.
6176       */
6177       if (line_info.x2 < 0)
6178         line_info.x2=0;
6179       else
6180         if (line_info.x2 > (int) windows->image.width)
6181           line_info.x2=(short) windows->image.width;
6182       if (line_info.y2 < 0)
6183         line_info.y2=0;
6184       else
6185         if (line_info.y2 > (int) windows->image.height)
6186           line_info.y2=(short) windows->image.height;
6187       distance=(unsigned int)
6188         (((line_info.x2-line_info.x1+1)*(line_info.x2-line_info.x1+1))+
6189          ((line_info.y2-line_info.y1+1)*(line_info.y2-line_info.y1+1)));
6190       if ((((int) rectangle_info.x != x) && ((int) rectangle_info.y != y)) ||
6191           ((state & ExitState) != 0))
6192         {
6193           if (rectangle_info.x < 0)
6194             rectangle_info.x=0;
6195           else
6196             if (rectangle_info.x > (ssize_t) windows->image.width)
6197               rectangle_info.x=(ssize_t) windows->image.width;
6198           if ((int) rectangle_info.x < x)
6199             rectangle_info.width=(unsigned int) (x-rectangle_info.x);
6200           else
6201             {
6202               rectangle_info.width=(unsigned int) (rectangle_info.x-x);
6203               rectangle_info.x=(ssize_t) x;
6204             }
6205           if (rectangle_info.y < 0)
6206             rectangle_info.y=0;
6207           else
6208             if (rectangle_info.y > (ssize_t) windows->image.height)
6209               rectangle_info.y=(ssize_t) windows->image.height;
6210           if ((int) rectangle_info.y < y)
6211             rectangle_info.height=(unsigned int) (y-rectangle_info.y);
6212           else
6213             {
6214               rectangle_info.height=(unsigned int) (rectangle_info.y-y);
6215               rectangle_info.y=(ssize_t) y;
6216             }
6217         }
6218     } while ((state & ExitState) == 0);
6219     (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
6220     if ((element == PointElement) || (element == PolygonElement) ||
6221         (element == FillPolygonElement))
6222       {
6223         /*
6224           Determine polygon bounding box.
6225         */
6226         rectangle_info.x=(ssize_t) coordinate_info->x;
6227         rectangle_info.y=(ssize_t) coordinate_info->y;
6228         x=coordinate_info->x;
6229         y=coordinate_info->y;
6230         for (i=1; i < number_coordinates; i++)
6231         {
6232           if (coordinate_info[i].x > x)
6233             x=coordinate_info[i].x;
6234           if (coordinate_info[i].y > y)
6235             y=coordinate_info[i].y;
6236           if ((ssize_t) coordinate_info[i].x < rectangle_info.x)
6237             rectangle_info.x=MagickMax((ssize_t) coordinate_info[i].x,0);
6238           if ((ssize_t) coordinate_info[i].y < rectangle_info.y)
6239             rectangle_info.y=MagickMax((ssize_t) coordinate_info[i].y,0);
6240         }
6241         rectangle_info.width=(size_t) (x-rectangle_info.x);
6242         rectangle_info.height=(size_t) (y-rectangle_info.y);
6243         for (i=0; i < number_coordinates; i++)
6244         {
6245           coordinate_info[i].x-=rectangle_info.x;
6246           coordinate_info[i].y-=rectangle_info.y;
6247         }
6248       }
6249     else
6250       if (distance <= 9)
6251         continue;
6252       else
6253         if ((element == RectangleElement) ||
6254             (element == CircleElement) || (element == EllipseElement))
6255           {
6256             rectangle_info.width--;
6257             rectangle_info.height--;
6258           }
6259     /*
6260       Drawing is relative to image configuration.
6261     */
6262     draw_info.x=(int) rectangle_info.x;
6263     draw_info.y=(int) rectangle_info.y;
6264     (void) XMagickCommand(display,resource_info,windows,SaveToUndoBufferCommand,
6265       image,exception);
6266     width=(unsigned int) (*image)->columns;
6267     height=(unsigned int) (*image)->rows;
6268     x=0;
6269     y=0;
6270     if (windows->image.crop_geometry != (char *) NULL)
6271       (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
6272     draw_info.x+=windows->image.x-(line_width/2);
6273     if (draw_info.x < 0)
6274       draw_info.x=0;
6275     draw_info.x=(int) (width*draw_info.x/windows->image.ximage->width);
6276     draw_info.y+=windows->image.y-(line_width/2);
6277     if (draw_info.y < 0)
6278       draw_info.y=0;
6279     draw_info.y=(int) height*draw_info.y/windows->image.ximage->height;
6280     draw_info.width=(unsigned int) rectangle_info.width+(line_width << 1);
6281     if (draw_info.width > (unsigned int) (*image)->columns)
6282       draw_info.width=(unsigned int) (*image)->columns;
6283     draw_info.height=(unsigned int) rectangle_info.height+(line_width << 1);
6284     if (draw_info.height > (unsigned int) (*image)->rows)
6285       draw_info.height=(unsigned int) (*image)->rows;
6286     (void) FormatLocaleString(draw_info.geometry,MaxTextExtent,"%ux%u%+d%+d",
6287       width*draw_info.width/windows->image.ximage->width,
6288       height*draw_info.height/windows->image.ximage->height,
6289       draw_info.x+x,draw_info.y+y);
6290     /*
6291       Initialize drawing attributes.
6292     */
6293     draw_info.degrees=0.0;
6294     draw_info.element=element;
6295     draw_info.stipple=stipple;
6296     draw_info.line_width=line_width;
6297     draw_info.line_info=line_info;
6298     if (line_info.x1 > (int) (line_width/2))
6299       draw_info.line_info.x1=(short) line_width/2;
6300     if (line_info.y1 > (int) (line_width/2))
6301       draw_info.line_info.y1=(short) line_width/2;
6302     draw_info.line_info.x2=(short) (line_info.x2-line_info.x1+(line_width/2));
6303     draw_info.line_info.y2=(short) (line_info.y2-line_info.y1+(line_width/2));
6304     if ((draw_info.line_info.x2 < 0) && (draw_info.line_info.y2 < 0))
6305       {
6306         draw_info.line_info.x2=(-draw_info.line_info.x2);
6307         draw_info.line_info.y2=(-draw_info.line_info.y2);
6308       }
6309     if (draw_info.line_info.x2 < 0)
6310       {
6311         draw_info.line_info.x2=(-draw_info.line_info.x2);
6312         Swap(draw_info.line_info.x1,draw_info.line_info.x2);
6313       }
6314     if (draw_info.line_info.y2 < 0)
6315       {
6316         draw_info.line_info.y2=(-draw_info.line_info.y2);
6317         Swap(draw_info.line_info.y1,draw_info.line_info.y2);
6318       }
6319     draw_info.rectangle_info=rectangle_info;
6320     if (draw_info.rectangle_info.x > (ssize_t) (line_width/2))
6321       draw_info.rectangle_info.x=(ssize_t) line_width/2;
6322     if (draw_info.rectangle_info.y > (ssize_t) (line_width/2))
6323       draw_info.rectangle_info.y=(ssize_t) line_width/2;
6324     draw_info.number_coordinates=(unsigned int) number_coordinates;
6325     draw_info.coordinate_info=coordinate_info;
6326     windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
6327     /*
6328       Draw element on image.
6329     */
6330     XSetCursorState(display,windows,MagickTrue);
6331     XCheckRefreshWindows(display,windows);
6332     status=XDrawImage(display,windows->pixel_info,&draw_info,*image,exception);
6333     XSetCursorState(display,windows,MagickFalse);
6334     /*
6335       Update image colormap and return to image drawing.
6336     */
6337     XConfigureImageColormap(display,resource_info,windows,*image,exception);
6338     (void) XConfigureImage(display,resource_info,windows,*image,exception);
6339   }
6340   XSetCursorState(display,windows,MagickFalse);
6341   coordinate_info=(XPoint *) RelinquishMagickMemory(coordinate_info);
6342   return(IsMagickTrue(status));
6343 }
6344 \f
6345 /*
6346 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6347 %                                                                             %
6348 %                                                                             %
6349 %                                                                             %
6350 +   X D r a w P a n R e c t a n g l e                                         %
6351 %                                                                             %
6352 %                                                                             %
6353 %                                                                             %
6354 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6355 %
6356 %  XDrawPanRectangle() draws a rectangle in the pan window.  The pan window
6357 %  displays a zoom image and the rectangle shows which portion of the image is
6358 %  displayed in the Image window.
6359 %
6360 %  The format of the XDrawPanRectangle method is:
6361 %
6362 %      XDrawPanRectangle(Display *display,XWindows *windows)
6363 %
6364 %  A description of each parameter follows:
6365 %
6366 %    o display: Specifies a connection to an X server;  returned from
6367 %      XOpenDisplay.
6368 %
6369 %    o windows: Specifies a pointer to a XWindows structure.
6370 %
6371 */
6372 static void XDrawPanRectangle(Display *display,XWindows *windows)
6373 {
6374   double
6375     scale_factor;
6376
6377   RectangleInfo
6378     highlight_info;
6379
6380   /*
6381     Determine dimensions of the panning rectangle.
6382   */
6383   scale_factor=(double) windows->pan.width/windows->image.ximage->width;
6384   highlight_info.x=(ssize_t) (scale_factor*windows->image.x+0.5);
6385   highlight_info.width=(unsigned int) (scale_factor*windows->image.width+0.5);
6386   scale_factor=(double)
6387     windows->pan.height/windows->image.ximage->height;
6388   highlight_info.y=(ssize_t) (scale_factor*windows->image.y+0.5);
6389   highlight_info.height=(unsigned int) (scale_factor*windows->image.height+0.5);
6390   /*
6391     Display the panning rectangle.
6392   */
6393   (void) XClearWindow(display,windows->pan.id);
6394   XHighlightRectangle(display,windows->pan.id,windows->pan.annotate_context,
6395     &highlight_info);
6396 }
6397 \f
6398 /*
6399 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6400 %                                                                             %
6401 %                                                                             %
6402 %                                                                             %
6403 +   X I m a g e C a c h e                                                     %
6404 %                                                                             %
6405 %                                                                             %
6406 %                                                                             %
6407 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6408 %
6409 %  XImageCache() handles the creation, manipulation, and destruction of the
6410 %  image cache (undo and redo buffers).
6411 %
6412 %  The format of the XImageCache method is:
6413 %
6414 %      void XImageCache(Display *display,XResourceInfo *resource_info,
6415 %        XWindows *windows,const CommandType command,Image **image,
6416 %        ExceptionInfo *exception)
6417 %
6418 %  A description of each parameter follows:
6419 %
6420 %    o display: Specifies a connection to an X server; returned from
6421 %      XOpenDisplay.
6422 %
6423 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6424 %
6425 %    o windows: Specifies a pointer to a XWindows structure.
6426 %
6427 %    o command: Specifies a command to perform.
6428 %
6429 %    o image: the image;  XImageCache may transform the image and return a new
6430 %      image pointer.
6431 %
6432 %    o exception: return any errors or warnings in this structure.
6433 %
6434 */
6435 static void XImageCache(Display *display,XResourceInfo *resource_info,
6436   XWindows *windows,const CommandType command,Image **image,
6437   ExceptionInfo *exception)
6438 {
6439   Image
6440     *cache_image;
6441
6442   static Image
6443     *redo_image = (Image *) NULL,
6444     *undo_image = (Image *) NULL;
6445
6446   switch (command)
6447   {
6448     case FreeBuffersCommand:
6449     {
6450       /*
6451         Free memory from the undo and redo cache.
6452       */
6453       while (undo_image != (Image *) NULL)
6454       {
6455         cache_image=undo_image;
6456         undo_image=GetPreviousImageInList(undo_image);
6457         cache_image->list=DestroyImage(cache_image->list);
6458         cache_image=DestroyImage(cache_image);
6459       }
6460       undo_image=NewImageList();
6461       if (redo_image != (Image *) NULL)
6462         redo_image=DestroyImage(redo_image);
6463       redo_image=NewImageList();
6464       return;
6465     }
6466     case UndoCommand:
6467     {
6468       char
6469         image_geometry[MaxTextExtent];
6470
6471       /*
6472         Undo the last image transformation.
6473       */
6474       if (undo_image == (Image *) NULL)
6475         {
6476           (void) XBell(display,0);
6477           return;
6478         }
6479       cache_image=undo_image;
6480       undo_image=GetPreviousImageInList(undo_image);
6481       windows->image.window_changes.width=(int) cache_image->columns;
6482       windows->image.window_changes.height=(int) cache_image->rows;
6483       (void) FormatLocaleString(image_geometry,MaxTextExtent,"%dx%d!",
6484         windows->image.ximage->width,windows->image.ximage->height);
6485       (void) TransformImage(image,windows->image.crop_geometry,image_geometry,
6486         exception);
6487       if (windows->image.crop_geometry != (char *) NULL)
6488         windows->image.crop_geometry=(char *) RelinquishMagickMemory(
6489           windows->image.crop_geometry);
6490       windows->image.crop_geometry=cache_image->geometry;
6491       if (redo_image != (Image *) NULL)
6492         redo_image=DestroyImage(redo_image);
6493       redo_image=(*image);
6494       *image=cache_image->list;
6495       cache_image=DestroyImage(cache_image);
6496       if( IfMagickTrue(windows->image.orphan) )
6497         return;
6498       XConfigureImageColormap(display,resource_info,windows,*image,exception);
6499       (void) XConfigureImage(display,resource_info,windows,*image,exception);
6500       return;
6501     }
6502     case CutCommand:
6503     case PasteCommand:
6504     case ApplyCommand:
6505     case HalfSizeCommand:
6506     case OriginalSizeCommand:
6507     case DoubleSizeCommand:
6508     case ResizeCommand:
6509     case TrimCommand:
6510     case CropCommand:
6511     case ChopCommand:
6512     case FlipCommand:
6513     case FlopCommand:
6514     case RotateRightCommand:
6515     case RotateLeftCommand:
6516     case RotateCommand:
6517     case ShearCommand:
6518     case RollCommand:
6519     case NegateCommand:
6520     case ContrastStretchCommand:
6521     case SigmoidalContrastCommand:
6522     case NormalizeCommand:
6523     case EqualizeCommand:
6524     case HueCommand:
6525     case SaturationCommand:
6526     case BrightnessCommand:
6527     case GammaCommand:
6528     case SpiffCommand:
6529     case DullCommand:
6530     case GrayscaleCommand:
6531     case MapCommand:
6532     case QuantizeCommand:
6533     case DespeckleCommand:
6534     case EmbossCommand:
6535     case ReduceNoiseCommand:
6536     case AddNoiseCommand:
6537     case SharpenCommand:
6538     case BlurCommand:
6539     case ThresholdCommand:
6540     case EdgeDetectCommand:
6541     case SpreadCommand:
6542     case ShadeCommand:
6543     case RaiseCommand:
6544     case SegmentCommand:
6545     case SolarizeCommand:
6546     case SepiaToneCommand:
6547     case SwirlCommand:
6548     case ImplodeCommand:
6549     case VignetteCommand:
6550     case WaveCommand:
6551     case OilPaintCommand:
6552     case CharcoalDrawCommand:
6553     case AnnotateCommand:
6554     case AddBorderCommand:
6555     case AddFrameCommand:
6556     case CompositeCommand:
6557     case CommentCommand:
6558     case LaunchCommand:
6559     case RegionofInterestCommand:
6560     case SaveToUndoBufferCommand:
6561     case RedoCommand:
6562     {
6563       Image
6564         *previous_image;
6565
6566       ssize_t
6567         bytes;
6568
6569       bytes=(ssize_t) ((*image)->columns*(*image)->rows*sizeof(PixelInfo));
6570       if (undo_image != (Image *) NULL)
6571         {
6572           /*
6573             Ensure the undo cache has enough memory available.
6574           */
6575           previous_image=undo_image;
6576           while (previous_image != (Image *) NULL)
6577           {
6578             bytes+=previous_image->list->columns*previous_image->list->rows*
6579               sizeof(PixelInfo);
6580             if (bytes <= (ssize_t) (resource_info->undo_cache << 20))
6581               {
6582                 previous_image=GetPreviousImageInList(previous_image);
6583                 continue;
6584               }
6585             bytes-=previous_image->list->columns*previous_image->list->rows*
6586               sizeof(PixelInfo);
6587             if (previous_image == undo_image)
6588               undo_image=NewImageList();
6589             else
6590               previous_image->next->previous=NewImageList();
6591             break;
6592           }
6593           while (previous_image != (Image *) NULL)
6594           {
6595             /*
6596               Delete any excess memory from undo cache.
6597             */
6598             cache_image=previous_image;
6599             previous_image=GetPreviousImageInList(previous_image);
6600             cache_image->list=DestroyImage(cache_image->list);
6601             cache_image=DestroyImage(cache_image);
6602           }
6603         }
6604       if (bytes > (ssize_t) (resource_info->undo_cache << 20))
6605         break;
6606       /*
6607         Save image before transformations are applied.
6608       */
6609       cache_image=AcquireImage((ImageInfo *) NULL,exception);
6610       if (cache_image == (Image *) NULL)
6611         break;
6612       XSetCursorState(display,windows,MagickTrue);
6613       XCheckRefreshWindows(display,windows);
6614       cache_image->list=CloneImage(*image,0,0,MagickTrue,exception);
6615       XSetCursorState(display,windows,MagickFalse);
6616       if (cache_image->list == (Image *) NULL)
6617         {
6618           cache_image=DestroyImage(cache_image);
6619           break;
6620         }
6621       cache_image->columns=(size_t) windows->image.ximage->width;
6622       cache_image->rows=(size_t) windows->image.ximage->height;
6623       cache_image->geometry=windows->image.crop_geometry;
6624       if (windows->image.crop_geometry != (char *) NULL)
6625         {
6626           cache_image->geometry=AcquireString((char *) NULL);
6627           (void) CopyMagickString(cache_image->geometry,
6628             windows->image.crop_geometry,MaxTextExtent);
6629         }
6630       if (undo_image == (Image *) NULL)
6631         {
6632           undo_image=cache_image;
6633           break;
6634         }
6635       undo_image->next=cache_image;
6636       undo_image->next->previous=undo_image;
6637       undo_image=undo_image->next;
6638       break;
6639     }
6640     default:
6641       break;
6642   }
6643   if (command == RedoCommand)
6644     {
6645       /*
6646         Redo the last image transformation.
6647       */
6648       if (redo_image == (Image *) NULL)
6649         {
6650           (void) XBell(display,0);
6651           return;
6652         }
6653       windows->image.window_changes.width=(int) redo_image->columns;
6654       windows->image.window_changes.height=(int) redo_image->rows;
6655       if (windows->image.crop_geometry != (char *) NULL)
6656         windows->image.crop_geometry=(char *)
6657           RelinquishMagickMemory(windows->image.crop_geometry);
6658       windows->image.crop_geometry=redo_image->geometry;
6659       *image=DestroyImage(*image);
6660       *image=redo_image;
6661       redo_image=NewImageList();
6662       if( IfMagickTrue(windows->image.orphan) )
6663         return;
6664       XConfigureImageColormap(display,resource_info,windows,*image,exception);
6665       (void) XConfigureImage(display,resource_info,windows,*image,exception);
6666       return;
6667     }
6668   if (command != InfoCommand)
6669     return;
6670   /*
6671     Display image info.
6672   */
6673   XSetCursorState(display,windows,MagickTrue);
6674   XCheckRefreshWindows(display,windows);
6675   XDisplayImageInfo(display,resource_info,windows,undo_image,*image,exception);
6676   XSetCursorState(display,windows,MagickFalse);
6677 }
6678 \f
6679 /*
6680 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6681 %                                                                             %
6682 %                                                                             %
6683 %                                                                             %
6684 +   X I m a g e W i n d o w C o m m a n d                                     %
6685 %                                                                             %
6686 %                                                                             %
6687 %                                                                             %
6688 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6689 %
6690 %  XImageWindowCommand() makes a transform to the image or Image window as
6691 %  specified by a user menu button or keyboard command.
6692 %
6693 %  The format of the XImageWindowCommand method is:
6694 %
6695 %      CommandType XImageWindowCommand(Display *display,
6696 %        XResourceInfo *resource_info,XWindows *windows,
6697 %        const MagickStatusType state,KeySym key_symbol,Image **image,
6698 %        ExceptionInfo *exception)
6699 %
6700 %  A description of each parameter follows:
6701 %
6702 %    o nexus:  Method XImageWindowCommand returns an image when the
6703 %      user chooses 'Open Image' from the command menu.  Otherwise a null
6704 %      image is returned.
6705 %
6706 %    o display: Specifies a connection to an X server; returned from
6707 %      XOpenDisplay.
6708 %
6709 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6710 %
6711 %    o windows: Specifies a pointer to a XWindows structure.
6712 %
6713 %    o state: key mask.
6714 %
6715 %    o key_symbol: Specifies a command to perform.
6716 %
6717 %    o image: the image;  XImageWIndowCommand may transform the image and
6718 %      return a new image pointer.
6719 %
6720 %    o exception: return any errors or warnings in this structure.
6721 %
6722 */
6723 static CommandType XImageWindowCommand(Display *display,
6724   XResourceInfo *resource_info,XWindows *windows,const MagickStatusType state,
6725   KeySym key_symbol,Image **image,ExceptionInfo *exception)
6726 {
6727   static char
6728     delta[MaxTextExtent] = "";
6729
6730   static const char
6731     Digits[] = "01234567890";
6732
6733   static KeySym
6734     last_symbol = XK_0;
6735
6736   if ((key_symbol >= XK_0) && (key_symbol <= XK_9))
6737     {
6738       if (((last_symbol < XK_0) || (last_symbol > XK_9)))
6739         {
6740           *delta='\0';
6741           resource_info->quantum=1;
6742         }
6743       last_symbol=key_symbol;
6744       delta[strlen(delta)+1]='\0';
6745       delta[strlen(delta)]=Digits[key_symbol-XK_0];
6746       resource_info->quantum=StringToLong(delta);
6747       return(NullCommand);
6748     }
6749   last_symbol=key_symbol;
6750   if (resource_info->immutable)
6751     {
6752       /*
6753         Virtual image window has a restricted command set.
6754       */
6755       switch (key_symbol)
6756       {
6757         case XK_question:
6758           return(InfoCommand);
6759         case XK_p:
6760         case XK_Print:
6761           return(PrintCommand);
6762         case XK_space:
6763           return(NextCommand);
6764         case XK_q:
6765         case XK_Escape:
6766           return(QuitCommand);
6767         default:
6768           break;
6769       }
6770       return(NullCommand);
6771     }
6772   switch ((int) key_symbol)
6773   {
6774     case XK_o:
6775     {
6776       if ((state & ControlMask) == 0)
6777         break;
6778       return(OpenCommand);
6779     }
6780     case XK_space:
6781       return(NextCommand);
6782     case XK_BackSpace:
6783       return(FormerCommand);
6784     case XK_s:
6785     {
6786       if ((state & Mod1Mask) != 0)
6787         return(SwirlCommand);
6788       if ((state & ControlMask) == 0)
6789         return(ShearCommand);
6790       return(SaveCommand);
6791     }
6792     case XK_p:
6793     case XK_Print:
6794     {
6795       if ((state & Mod1Mask) != 0)
6796         return(OilPaintCommand);
6797       if ((state & Mod4Mask) != 0)
6798         return(ColorCommand);
6799       if ((state & ControlMask) == 0)
6800         return(NullCommand);
6801       return(PrintCommand);
6802     }
6803     case XK_d:
6804     {
6805       if ((state & Mod4Mask) != 0)
6806         return(DrawCommand);
6807       if ((state & ControlMask) == 0)
6808         return(NullCommand);
6809       return(DeleteCommand);
6810     }
6811     case XK_Select:
6812     {
6813       if ((state & ControlMask) == 0)
6814         return(NullCommand);
6815       return(SelectCommand);
6816     }
6817     case XK_n:
6818     {
6819       if ((state & ControlMask) == 0)
6820         return(NullCommand);
6821       return(NewCommand);
6822     }
6823     case XK_q:
6824     case XK_Escape:
6825       return(QuitCommand);
6826     case XK_z:
6827     case XK_Undo:
6828     {
6829       if ((state & ControlMask) == 0)
6830         return(NullCommand);
6831       return(UndoCommand);
6832     }
6833     case XK_r:
6834     case XK_Redo:
6835     {
6836       if ((state & ControlMask) == 0)
6837         return(RollCommand);
6838       return(RedoCommand);
6839     }
6840     case XK_x:
6841     {
6842       if ((state & ControlMask) == 0)
6843         return(NullCommand);
6844       return(CutCommand);
6845     }
6846     case XK_c:
6847     {
6848       if ((state & Mod1Mask) != 0)
6849         return(CharcoalDrawCommand);
6850       if ((state & ControlMask) == 0)
6851         return(CropCommand);
6852       return(CopyCommand);
6853     }
6854     case XK_v:
6855     case XK_Insert:
6856     {
6857       if ((state & Mod4Mask) != 0)
6858         return(CompositeCommand);
6859       if ((state & ControlMask) == 0)
6860         return(FlipCommand);
6861       return(PasteCommand);
6862     }
6863     case XK_less:
6864       return(HalfSizeCommand);
6865     case XK_minus:
6866       return(OriginalSizeCommand);
6867     case XK_greater:
6868       return(DoubleSizeCommand);
6869     case XK_percent:
6870       return(ResizeCommand);
6871     case XK_at:
6872       return(RefreshCommand);
6873     case XK_bracketleft:
6874       return(ChopCommand);
6875     case XK_h:
6876       return(FlopCommand);
6877     case XK_slash:
6878       return(RotateRightCommand);
6879     case XK_backslash:
6880       return(RotateLeftCommand);
6881     case XK_asterisk:
6882       return(RotateCommand);
6883     case XK_t:
6884       return(TrimCommand);
6885     case XK_H:
6886       return(HueCommand);
6887     case XK_S:
6888       return(SaturationCommand);
6889     case XK_L:
6890       return(BrightnessCommand);
6891     case XK_G:
6892       return(GammaCommand);
6893     case XK_C:
6894       return(SpiffCommand);
6895     case XK_Z:
6896       return(DullCommand);
6897     case XK_N:
6898       return(NormalizeCommand);
6899     case XK_equal:
6900       return(EqualizeCommand);
6901     case XK_asciitilde:
6902       return(NegateCommand);
6903     case XK_period:
6904       return(GrayscaleCommand);
6905     case XK_numbersign:
6906       return(QuantizeCommand);
6907     case XK_F2:
6908       return(DespeckleCommand);
6909     case XK_F3:
6910       return(EmbossCommand);
6911     case XK_F4:
6912       return(ReduceNoiseCommand);
6913     case XK_F5:
6914       return(AddNoiseCommand);
6915     case XK_F6:
6916       return(SharpenCommand);
6917     case XK_F7:
6918       return(BlurCommand);
6919     case XK_F8:
6920       return(ThresholdCommand);
6921     case XK_F9:
6922       return(EdgeDetectCommand);
6923     case XK_F10:
6924       return(SpreadCommand);
6925     case XK_F11:
6926       return(ShadeCommand);
6927     case XK_F12:
6928       return(RaiseCommand);
6929     case XK_F13:
6930       return(SegmentCommand);
6931     case XK_i:
6932     {
6933       if ((state & Mod1Mask) == 0)
6934         return(NullCommand);
6935       return(ImplodeCommand);
6936     }
6937     case XK_w:
6938     {
6939       if ((state & Mod1Mask) == 0)
6940         return(NullCommand);
6941       return(WaveCommand);
6942     }
6943     case XK_m:
6944     {
6945       if ((state & Mod4Mask) == 0)
6946         return(NullCommand);
6947       return(MatteCommand);
6948     }
6949     case XK_b:
6950     {
6951       if ((state & Mod4Mask) == 0)
6952         return(NullCommand);
6953       return(AddBorderCommand);
6954     }
6955     case XK_f:
6956     {
6957       if ((state & Mod4Mask) == 0)
6958         return(NullCommand);
6959       return(AddFrameCommand);
6960     }
6961     case XK_exclam:
6962     {
6963       if ((state & Mod4Mask) == 0)
6964         return(NullCommand);
6965       return(CommentCommand);
6966     }
6967     case XK_a:
6968     {
6969       if ((state & Mod1Mask) != 0)
6970         return(ApplyCommand);
6971       if ((state & Mod4Mask) != 0)
6972         return(AnnotateCommand);
6973       if ((state & ControlMask) == 0)
6974         return(NullCommand);
6975       return(RegionofInterestCommand);
6976     }
6977     case XK_question:
6978       return(InfoCommand);
6979     case XK_plus:
6980       return(ZoomCommand);
6981     case XK_P:
6982     {
6983       if ((state & ShiftMask) == 0)
6984         return(NullCommand);
6985       return(ShowPreviewCommand);
6986     }
6987     case XK_Execute:
6988       return(LaunchCommand);
6989     case XK_F1:
6990       return(HelpCommand);
6991     case XK_Find:
6992       return(BrowseDocumentationCommand);
6993     case XK_Menu:
6994     {
6995       (void) XMapRaised(display,windows->command.id);
6996       return(NullCommand);
6997     }
6998     case XK_Next:
6999     case XK_Prior:
7000     case XK_Home:
7001     case XK_KP_Home:
7002     {
7003       XTranslateImage(display,windows,*image,key_symbol);
7004       return(NullCommand);
7005     }
7006     case XK_Up:
7007     case XK_KP_Up:
7008     case XK_Down:
7009     case XK_KP_Down:
7010     case XK_Left:
7011     case XK_KP_Left:
7012     case XK_Right:
7013     case XK_KP_Right:
7014     {
7015       if ((state & Mod1Mask) != 0)
7016         {
7017           RectangleInfo
7018             crop_info;
7019
7020           /*
7021             Trim one pixel from edge of image.
7022           */
7023           crop_info.x=0;
7024           crop_info.y=0;
7025           crop_info.width=(size_t) windows->image.ximage->width;
7026           crop_info.height=(size_t) windows->image.ximage->height;
7027           if ((key_symbol == XK_Up) || (key_symbol == XK_KP_Up))
7028             {
7029               if (resource_info->quantum >= (int) crop_info.height)
7030                 resource_info->quantum=(int) crop_info.height-1;
7031               crop_info.height-=resource_info->quantum;
7032             }
7033           if ((key_symbol == XK_Down) || (key_symbol == XK_KP_Down))
7034             {
7035               if (resource_info->quantum >= (int) (crop_info.height-crop_info.y))
7036                 resource_info->quantum=(int) (crop_info.height-crop_info.y-1);
7037               crop_info.y+=resource_info->quantum;
7038               crop_info.height-=resource_info->quantum;
7039             }
7040           if ((key_symbol == XK_Left) || (key_symbol == XK_KP_Left))
7041             {
7042               if (resource_info->quantum >= (int) crop_info.width)
7043                 resource_info->quantum=(int) crop_info.width-1;
7044               crop_info.width-=resource_info->quantum;
7045             }
7046           if ((key_symbol == XK_Right) || (key_symbol == XK_KP_Right))
7047             {
7048               if (resource_info->quantum >= (int) (crop_info.width-crop_info.x))
7049                 resource_info->quantum=(int) (crop_info.width-crop_info.x-1);
7050               crop_info.x+=resource_info->quantum;
7051               crop_info.width-=resource_info->quantum;
7052             }
7053           if ((int) (windows->image.x+windows->image.width) >
7054               (int) crop_info.width)
7055             windows->image.x=(int) (crop_info.width-windows->image.width);
7056           if ((int) (windows->image.y+windows->image.height) >
7057               (int) crop_info.height)
7058             windows->image.y=(int) (crop_info.height-windows->image.height);
7059           XSetCropGeometry(display,windows,&crop_info,*image);
7060           windows->image.window_changes.width=(int) crop_info.width;
7061           windows->image.window_changes.height=(int) crop_info.height;
7062           (void) XSetWindowBackgroundPixmap(display,windows->image.id,None);
7063           (void) XConfigureImage(display,resource_info,windows,*image,
7064             exception);
7065           return(NullCommand);
7066         }
7067       XTranslateImage(display,windows,*image,key_symbol);
7068       return(NullCommand);
7069     }
7070     default:
7071       return(NullCommand);
7072   }
7073   return(NullCommand);
7074 }
7075 \f
7076 /*
7077 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7078 %                                                                             %
7079 %                                                                             %
7080 %                                                                             %
7081 +   X M a g i c k C o m m a n d                                               %
7082 %                                                                             %
7083 %                                                                             %
7084 %                                                                             %
7085 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7086 %
7087 %  XMagickCommand() makes a transform to the image or Image window as
7088 %  specified by a user menu button or keyboard command.
7089 %
7090 %  The format of the XMagickCommand method is:
7091 %
7092 %      Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7093 %        XWindows *windows,const CommandType command,Image **image,
7094 %        ExceptionInfo *exception)
7095 %
7096 %  A description of each parameter follows:
7097 %
7098 %    o display: Specifies a connection to an X server; returned from
7099 %      XOpenDisplay.
7100 %
7101 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
7102 %
7103 %    o windows: Specifies a pointer to a XWindows structure.
7104 %
7105 %    o command: Specifies a command to perform.
7106 %
7107 %    o image: the image;  XMagickCommand may transform the image and return a
7108 %      new image pointer.
7109 %
7110 %    o exception: return any errors or warnings in this structure.
7111 %
7112 */
7113 static Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7114   XWindows *windows,const CommandType command,Image **image,
7115   ExceptionInfo *exception)
7116 {
7117   char
7118     filename[MaxTextExtent],
7119     geometry[MaxTextExtent],
7120     modulate_factors[MaxTextExtent];
7121
7122   GeometryInfo
7123     geometry_info;
7124
7125   Image
7126     *nexus;
7127
7128   ImageInfo
7129     *image_info;
7130
7131   int
7132     x,
7133     y;
7134
7135   MagickStatusType
7136     flags,
7137     status;
7138
7139   QuantizeInfo
7140     quantize_info;
7141
7142   RectangleInfo
7143     page_geometry;
7144
7145   register int
7146     i;
7147
7148   static char
7149     color[MaxTextExtent] = "gray";
7150
7151   unsigned int
7152     height,
7153     width;
7154
7155   /*
7156     Process user command.
7157   */
7158   XCheckRefreshWindows(display,windows);
7159   XImageCache(display,resource_info,windows,command,image,exception);
7160   nexus=NewImageList();
7161   windows->image.window_changes.width=windows->image.ximage->width;
7162   windows->image.window_changes.height=windows->image.ximage->height;
7163   image_info=CloneImageInfo(resource_info->image_info);
7164   SetGeometryInfo(&geometry_info);
7165   GetQuantizeInfo(&quantize_info);
7166   switch (command)
7167   {
7168     case OpenCommand:
7169     {
7170       /*
7171         Load image.
7172       */
7173       nexus=XOpenImage(display,resource_info,windows,MagickFalse);
7174       break;
7175     }
7176     case NextCommand:
7177     {
7178       /*
7179         Display next image.
7180       */
7181       for (i=0; i < resource_info->quantum; i++)
7182         XClientMessage(display,windows->image.id,windows->im_protocols,
7183           windows->im_next_image,CurrentTime);
7184       break;
7185     }
7186     case FormerCommand:
7187     {
7188       /*
7189         Display former image.
7190       */
7191       for (i=0; i < resource_info->quantum; i++)
7192         XClientMessage(display,windows->image.id,windows->im_protocols,
7193           windows->im_former_image,CurrentTime);
7194       break;
7195     }
7196     case SelectCommand:
7197     {
7198       int
7199         status;
7200
7201       /*
7202         Select image.
7203       */
7204       if (*resource_info->home_directory == '\0')
7205         (void) CopyMagickString(resource_info->home_directory,".",
7206           MaxTextExtent);
7207       status=chdir(resource_info->home_directory);
7208       if (status == -1)
7209         (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
7210           "UnableToOpenFile","%s",resource_info->home_directory);
7211       nexus=XOpenImage(display,resource_info,windows,MagickTrue);
7212       break;
7213     }
7214     case SaveCommand:
7215     {
7216       /*
7217         Save image.
7218       */
7219       status=XSaveImage(display,resource_info,windows,*image,exception);
7220       if( IfMagickFalse(status) )
7221         {
7222           char
7223             message[MaxTextExtent];
7224
7225           (void) FormatLocaleString(message,MaxTextExtent,"%s:%s",
7226             exception->reason != (char *) NULL ? exception->reason : "",
7227             exception->description != (char *) NULL ? exception->description :
7228             "");
7229           XNoticeWidget(display,windows,"Unable to save file:",message);
7230           break;
7231         }
7232       break;
7233     }
7234     case PrintCommand:
7235     {
7236       /*
7237         Print image.
7238       */
7239       status=XPrintImage(display,resource_info,windows,*image,exception);
7240       if( IfMagickFalse(status) )
7241         {
7242           char
7243             message[MaxTextExtent];
7244
7245           (void) FormatLocaleString(message,MaxTextExtent,"%s:%s",
7246             exception->reason != (char *) NULL ? exception->reason : "",
7247             exception->description != (char *) NULL ? exception->description :
7248             "");
7249           XNoticeWidget(display,windows,"Unable to print file:",message);
7250           break;
7251         }
7252       break;
7253     }
7254     case DeleteCommand:
7255     {
7256       static char
7257         filename[MaxTextExtent] = "\0";
7258
7259       /*
7260         Delete image file.
7261       */
7262       XFileBrowserWidget(display,windows,"Delete",filename);
7263       if (*filename == '\0')
7264         break;
7265       status=IsMagickTrue(remove_utf8(filename));
7266       if( IfMagickTrue(status) )
7267         XNoticeWidget(display,windows,"Unable to delete image file:",filename);
7268       break;
7269     }
7270     case NewCommand:
7271     {
7272       int
7273         status;
7274
7275       static char
7276         color[MaxTextExtent] = "gray",
7277         geometry[MaxTextExtent] = "640x480";
7278
7279       static const char
7280         *format = "gradient";
7281
7282       /*
7283         Query user for canvas geometry.
7284       */
7285       status=XDialogWidget(display,windows,"New","Enter image geometry:",
7286         geometry);
7287       if (*geometry == '\0')
7288         break;
7289       if (status == 0)
7290         format="xc";
7291       XColorBrowserWidget(display,windows,"Select",color);
7292       if (*color == '\0')
7293         break;
7294       /*
7295         Create canvas.
7296       */
7297       (void) FormatLocaleString(image_info->filename,MaxTextExtent,
7298         "%s:%s",format,color);
7299       (void) CloneString(&image_info->size,geometry);
7300       nexus=ReadImage(image_info,exception);
7301       CatchException(exception);
7302       XClientMessage(display,windows->image.id,windows->im_protocols,
7303         windows->im_next_image,CurrentTime);
7304       break;
7305     }
7306     case VisualDirectoryCommand:
7307     {
7308       /*
7309         Visual Image directory.
7310       */
7311       nexus=XVisualDirectoryImage(display,resource_info,windows,exception);
7312       break;
7313     }
7314     case QuitCommand:
7315     {
7316       /*
7317         exit program.
7318       */
7319       if( IfMagickFalse(resource_info->confirm_exit) )
7320         XClientMessage(display,windows->image.id,windows->im_protocols,
7321           windows->im_exit,CurrentTime);
7322       else
7323         {
7324           int
7325             status;
7326
7327           /*
7328             Confirm program exit.
7329           */
7330           status=XConfirmWidget(display,windows,"Do you really want to exit",
7331             resource_info->client_name);
7332           if (status > 0)
7333             XClientMessage(display,windows->image.id,windows->im_protocols,
7334               windows->im_exit,CurrentTime);
7335         }
7336       break;
7337     }
7338     case CutCommand:
7339     {
7340       /*
7341         Cut image.
7342       */
7343       (void) XCropImage(display,resource_info,windows,*image,CutMode,exception);
7344       break;
7345     }
7346     case CopyCommand:
7347     {
7348       /*
7349         Copy image.
7350       */
7351       (void) XCropImage(display,resource_info,windows,*image,CopyMode,
7352         exception);
7353       break;
7354     }
7355     case PasteCommand:
7356     {
7357       /*
7358         Paste image.
7359       */
7360       status=XPasteImage(display,resource_info,windows,*image,exception);
7361       if( IfMagickFalse(status) )
7362         {
7363           XNoticeWidget(display,windows,"Unable to paste X image",
7364             (*image)->filename);
7365           break;
7366         }
7367       break;
7368     }
7369     case HalfSizeCommand:
7370     {
7371       /*
7372         Half image size.
7373       */
7374       windows->image.window_changes.width=windows->image.ximage->width/2;
7375       windows->image.window_changes.height=windows->image.ximage->height/2;
7376       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7377       break;
7378     }
7379     case OriginalSizeCommand:
7380     {
7381       /*
7382         Original image size.
7383       */
7384       windows->image.window_changes.width=(int) (*image)->columns;
7385       windows->image.window_changes.height=(int) (*image)->rows;
7386       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7387       break;
7388     }
7389     case DoubleSizeCommand:
7390     {
7391       /*
7392         Double the image size.
7393       */
7394       windows->image.window_changes.width=windows->image.ximage->width << 1;
7395       windows->image.window_changes.height=windows->image.ximage->height << 1;
7396       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7397       break;
7398     }
7399     case ResizeCommand:
7400     {
7401       int
7402         status;
7403
7404       size_t
7405         height,
7406         width;
7407
7408       ssize_t
7409         x,
7410         y;
7411
7412       /*
7413         Resize image.
7414       */
7415       width=(size_t) windows->image.ximage->width;
7416       height=(size_t) windows->image.ximage->height;
7417       x=0;
7418       y=0;
7419       (void) FormatLocaleString(geometry,MaxTextExtent,"%.20gx%.20g+0+0",
7420         (double) width,(double) height);
7421       status=XDialogWidget(display,windows,"Resize",
7422         "Enter resize geometry (e.g. 640x480, 200%):",geometry);
7423       if (*geometry == '\0')
7424         break;
7425       if (status == 0)
7426         (void) ConcatenateMagickString(geometry,"!",MaxTextExtent);
7427       (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
7428       windows->image.window_changes.width=(int) width;
7429       windows->image.window_changes.height=(int) height;
7430       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7431       break;
7432     }
7433     case ApplyCommand:
7434     {
7435       char
7436         image_geometry[MaxTextExtent];
7437
7438       if ((windows->image.crop_geometry == (char *) NULL) &&
7439           ((int) (*image)->columns == windows->image.ximage->width) &&
7440           ((int) (*image)->rows == windows->image.ximage->height))
7441         break;
7442       /*
7443         Apply size transforms to image.
7444       */
7445       XSetCursorState(display,windows,MagickTrue);
7446       XCheckRefreshWindows(display,windows);
7447       /*
7448         Crop and/or scale displayed image.
7449       */
7450       (void) FormatLocaleString(image_geometry,MaxTextExtent,"%dx%d!",
7451         windows->image.ximage->width,windows->image.ximage->height);
7452       (void) TransformImage(image,windows->image.crop_geometry,image_geometry,
7453         exception);
7454       if (windows->image.crop_geometry != (char *) NULL)
7455         windows->image.crop_geometry=(char *) RelinquishMagickMemory(
7456           windows->image.crop_geometry);
7457       windows->image.x=0;
7458       windows->image.y=0;
7459       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7460       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7461       break;
7462     }
7463     case RefreshCommand:
7464     {
7465       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7466       break;
7467     }
7468     case RestoreCommand:
7469     {
7470       /*
7471         Restore Image window to its original size.
7472       */
7473       if ((windows->image.width == (unsigned int) (*image)->columns) &&
7474           (windows->image.height == (unsigned int) (*image)->rows) &&
7475           (windows->image.crop_geometry == (char *) NULL))
7476         {
7477           (void) XBell(display,0);
7478           break;
7479         }
7480       windows->image.window_changes.width=(int) (*image)->columns;
7481       windows->image.window_changes.height=(int) (*image)->rows;
7482       if (windows->image.crop_geometry != (char *) NULL)
7483         {
7484           windows->image.crop_geometry=(char *)
7485             RelinquishMagickMemory(windows->image.crop_geometry);
7486           windows->image.crop_geometry=(char *) NULL;
7487           windows->image.x=0;
7488           windows->image.y=0;
7489         }
7490       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7491       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7492       break;
7493     }
7494     case CropCommand:
7495     {
7496       /*
7497         Crop image.
7498       */
7499       (void) XCropImage(display,resource_info,windows,*image,CropMode,
7500         exception);
7501       break;
7502     }
7503     case ChopCommand:
7504     {
7505       /*
7506         Chop image.
7507       */
7508       status=XChopImage(display,resource_info,windows,image,exception);
7509       if( IfMagickFalse(status) )
7510         {
7511           XNoticeWidget(display,windows,"Unable to cut X image",
7512             (*image)->filename);
7513           break;
7514         }
7515       break;
7516     }
7517     case FlopCommand:
7518     {
7519       Image
7520         *flop_image;
7521
7522       /*
7523         Flop image scanlines.
7524       */
7525       XSetCursorState(display,windows,MagickTrue);
7526       XCheckRefreshWindows(display,windows);
7527       flop_image=FlopImage(*image,exception);
7528       if (flop_image != (Image *) NULL)
7529         {
7530           *image=DestroyImage(*image);
7531           *image=flop_image;
7532         }
7533       CatchException(exception);
7534       XSetCursorState(display,windows,MagickFalse);
7535       if (windows->image.crop_geometry != (char *) NULL)
7536         {
7537           /*
7538             Flop crop geometry.
7539           */
7540           width=(unsigned int) (*image)->columns;
7541           height=(unsigned int) (*image)->rows;
7542           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7543             &width,&height);
7544           (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
7545             "%ux%u%+d%+d",width,height,(int) (*image)->columns-(int) width-x,y);
7546         }
7547       if( IfMagickTrue(windows->image.orphan) )
7548         break;
7549       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7550       break;
7551     }
7552     case FlipCommand:
7553     {
7554       Image
7555         *flip_image;
7556
7557       /*
7558         Flip image scanlines.
7559       */
7560       XSetCursorState(display,windows,MagickTrue);
7561       XCheckRefreshWindows(display,windows);
7562       flip_image=FlipImage(*image,exception);
7563       if (flip_image != (Image *) NULL)
7564         {
7565           *image=DestroyImage(*image);
7566           *image=flip_image;
7567         }
7568       CatchException(exception);
7569       XSetCursorState(display,windows,MagickFalse);
7570       if (windows->image.crop_geometry != (char *) NULL)
7571         {
7572           /*
7573             Flip crop geometry.
7574           */
7575           width=(unsigned int) (*image)->columns;
7576           height=(unsigned int) (*image)->rows;
7577           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7578             &width,&height);
7579           (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
7580             "%ux%u%+d%+d",width,height,x,(int) (*image)->rows-(int) height-y);
7581         }
7582       if( IfMagickTrue(windows->image.orphan) )
7583         break;
7584       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7585       break;
7586     }
7587     case RotateRightCommand:
7588     {
7589       /*
7590         Rotate image 90 degrees clockwise.
7591       */
7592       status=XRotateImage(display,resource_info,windows,90.0,image,exception);
7593       if( IfMagickFalse(status) )
7594         {
7595           XNoticeWidget(display,windows,"Unable to rotate X image",
7596             (*image)->filename);
7597           break;
7598         }
7599       break;
7600     }
7601     case RotateLeftCommand:
7602     {
7603       /*
7604         Rotate image 90 degrees counter-clockwise.
7605       */
7606       status=XRotateImage(display,resource_info,windows,-90.0,image,exception);
7607       if( IfMagickFalse(status) )
7608         {
7609           XNoticeWidget(display,windows,"Unable to rotate X image",
7610             (*image)->filename);
7611           break;
7612         }
7613       break;
7614     }
7615     case RotateCommand:
7616     {
7617       /*
7618         Rotate image.
7619       */
7620       status=XRotateImage(display,resource_info,windows,0.0,image,exception);
7621       if( IfMagickFalse(status) )
7622         {
7623           XNoticeWidget(display,windows,"Unable to rotate X image",
7624             (*image)->filename);
7625           break;
7626         }
7627       break;
7628     }
7629     case ShearCommand:
7630     {
7631       Image
7632         *shear_image;
7633
7634       static char
7635         geometry[MaxTextExtent] = "45.0x45.0";
7636
7637       /*
7638         Query user for shear color and geometry.
7639       */
7640       XColorBrowserWidget(display,windows,"Select",color);
7641       if (*color == '\0')
7642         break;
7643       (void) XDialogWidget(display,windows,"Shear","Enter shear geometry:",
7644         geometry);
7645       if (*geometry == '\0')
7646         break;
7647       /*
7648         Shear image.
7649       */
7650       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
7651         exception);
7652       XSetCursorState(display,windows,MagickTrue);
7653       XCheckRefreshWindows(display,windows);
7654       (void) QueryColorCompliance(color,AllCompliance,
7655         &(*image)->background_color,exception);
7656       flags=ParseGeometry(geometry,&geometry_info);
7657       if ((flags & SigmaValue) == 0)
7658         geometry_info.sigma=geometry_info.rho;
7659       shear_image=ShearImage(*image,geometry_info.rho,geometry_info.sigma,
7660         exception);
7661       if (shear_image != (Image *) NULL)
7662         {
7663           *image=DestroyImage(*image);
7664           *image=shear_image;
7665         }
7666       CatchException(exception);
7667       XSetCursorState(display,windows,MagickFalse);
7668       if( IfMagickTrue(windows->image.orphan) )
7669         break;
7670       windows->image.window_changes.width=(int) (*image)->columns;
7671       windows->image.window_changes.height=(int) (*image)->rows;
7672       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7673       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7674       break;
7675     }
7676     case RollCommand:
7677     {
7678       Image
7679         *roll_image;
7680
7681       static char
7682         geometry[MaxTextExtent] = "+2+2";
7683
7684       /*
7685         Query user for the roll geometry.
7686       */
7687       (void) XDialogWidget(display,windows,"Roll","Enter roll geometry:",
7688         geometry);
7689       if (*geometry == '\0')
7690         break;
7691       /*
7692         Roll image.
7693       */
7694       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
7695         exception);
7696       XSetCursorState(display,windows,MagickTrue);
7697       XCheckRefreshWindows(display,windows);
7698       (void) ParsePageGeometry(*image,geometry,&page_geometry,
7699         exception);
7700       roll_image=RollImage(*image,page_geometry.x,page_geometry.y,
7701         exception);
7702       if (roll_image != (Image *) NULL)
7703         {
7704           *image=DestroyImage(*image);
7705           *image=roll_image;
7706         }
7707       CatchException(exception);
7708       XSetCursorState(display,windows,MagickFalse);
7709       if( IfMagickTrue(windows->image.orphan) )
7710         break;
7711       windows->image.window_changes.width=(int) (*image)->columns;
7712       windows->image.window_changes.height=(int) (*image)->rows;
7713       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7714       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7715       break;
7716     }
7717     case TrimCommand:
7718     {
7719       static char
7720         fuzz[MaxTextExtent];
7721
7722       /*
7723         Query user for the fuzz factor.
7724       */
7725       (void) FormatLocaleString(fuzz,MaxTextExtent,"%g%%",100.0*
7726         (*image)->fuzz/(QuantumRange+1.0));
7727       (void) XDialogWidget(display,windows,"Trim","Enter fuzz factor:",fuzz);
7728       if (*fuzz == '\0')
7729         break;
7730       (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+1.0);
7731       /*
7732         Trim image.
7733       */
7734       status=XTrimImage(display,resource_info,windows,*image,exception);
7735       if( IfMagickFalse(status) )
7736         {
7737           XNoticeWidget(display,windows,"Unable to trim X image",
7738             (*image)->filename);
7739           break;
7740         }
7741       break;
7742     }
7743     case HueCommand:
7744     {
7745       static char
7746         hue_percent[MaxTextExtent] = "110";
7747
7748       /*
7749         Query user for percent hue change.
7750       */
7751       (void) XDialogWidget(display,windows,"Apply",
7752         "Enter percent change in image hue (0-200):",hue_percent);
7753       if (*hue_percent == '\0')
7754         break;
7755       /*
7756         Vary the image hue.
7757       */
7758       XSetCursorState(display,windows,MagickTrue);
7759       XCheckRefreshWindows(display,windows);
7760       (void) CopyMagickString(modulate_factors,"100.0/100.0/",MaxTextExtent);
7761       (void) ConcatenateMagickString(modulate_factors,hue_percent,
7762         MaxTextExtent);
7763       (void) ModulateImage(*image,modulate_factors,exception);
7764       XSetCursorState(display,windows,MagickFalse);
7765       if( IfMagickTrue(windows->image.orphan) )
7766         break;
7767       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7768       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7769       break;
7770     }
7771     case SaturationCommand:
7772     {
7773       static char
7774         saturation_percent[MaxTextExtent] = "110";
7775
7776       /*
7777         Query user for percent saturation change.
7778       */
7779       (void) XDialogWidget(display,windows,"Apply",
7780         "Enter percent change in color saturation (0-200):",saturation_percent);
7781       if (*saturation_percent == '\0')
7782         break;
7783       /*
7784         Vary color saturation.
7785       */
7786       XSetCursorState(display,windows,MagickTrue);
7787       XCheckRefreshWindows(display,windows);
7788       (void) CopyMagickString(modulate_factors,"100.0/",MaxTextExtent);
7789       (void) ConcatenateMagickString(modulate_factors,saturation_percent,
7790         MaxTextExtent);
7791       (void) ModulateImage(*image,modulate_factors,exception);
7792       XSetCursorState(display,windows,MagickFalse);
7793       if( IfMagickTrue(windows->image.orphan) )
7794         break;
7795       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7796       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7797       break;
7798     }
7799     case BrightnessCommand:
7800     {
7801       static char
7802         brightness_percent[MaxTextExtent] = "110";
7803
7804       /*
7805         Query user for percent brightness change.
7806       */
7807       (void) XDialogWidget(display,windows,"Apply",
7808         "Enter percent change in color brightness (0-200):",brightness_percent);
7809       if (*brightness_percent == '\0')
7810         break;
7811       /*
7812         Vary the color brightness.
7813       */
7814       XSetCursorState(display,windows,MagickTrue);
7815       XCheckRefreshWindows(display,windows);
7816       (void) CopyMagickString(modulate_factors,brightness_percent,
7817         MaxTextExtent);
7818       (void) ModulateImage(*image,modulate_factors,exception);
7819       XSetCursorState(display,windows,MagickFalse);
7820       if( IfMagickTrue(windows->image.orphan) )
7821         break;
7822       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7823       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7824       break;
7825     }
7826     case GammaCommand:
7827     {
7828       static char
7829         factor[MaxTextExtent] = "1.6";
7830
7831       /*
7832         Query user for gamma value.
7833       */
7834       (void) XDialogWidget(display,windows,"Gamma",
7835         "Enter gamma value (e.g. 1.2):",factor);
7836       if (*factor == '\0')
7837         break;
7838       /*
7839         Gamma correct image.
7840       */
7841       XSetCursorState(display,windows,MagickTrue);
7842       XCheckRefreshWindows(display,windows);
7843       (void) GammaImage(*image,atof(factor),exception);
7844       XSetCursorState(display,windows,MagickFalse);
7845       if( IfMagickTrue(windows->image.orphan) )
7846         break;
7847       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7848       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7849       break;
7850     }
7851     case SpiffCommand:
7852     {
7853       /*
7854         Sharpen the image contrast.
7855       */
7856       XSetCursorState(display,windows,MagickTrue);
7857       XCheckRefreshWindows(display,windows);
7858       (void) ContrastImage(*image,MagickTrue,exception);
7859       XSetCursorState(display,windows,MagickFalse);
7860       if( IfMagickTrue(windows->image.orphan) )
7861         break;
7862       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7863       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7864       break;
7865     }
7866     case DullCommand:
7867     {
7868       /*
7869         Dull the image contrast.
7870       */
7871       XSetCursorState(display,windows,MagickTrue);
7872       XCheckRefreshWindows(display,windows);
7873       (void) ContrastImage(*image,MagickFalse,exception);
7874       XSetCursorState(display,windows,MagickFalse);
7875       if( IfMagickTrue(windows->image.orphan) )
7876         break;
7877       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7878       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7879       break;
7880     }
7881     case ContrastStretchCommand:
7882     {
7883       double
7884         black_point,
7885         white_point;
7886
7887       static char
7888         levels[MaxTextExtent] = "1%";
7889
7890       /*
7891         Query user for gamma value.
7892       */
7893       (void) XDialogWidget(display,windows,"Contrast Stretch",
7894         "Enter black and white points:",levels);
7895       if (*levels == '\0')
7896         break;
7897       /*
7898         Contrast stretch image.
7899       */
7900       XSetCursorState(display,windows,MagickTrue);
7901       XCheckRefreshWindows(display,windows);
7902       flags=ParseGeometry(levels,&geometry_info);
7903       black_point=geometry_info.rho;
7904       white_point=(flags & SigmaValue) != 0 ? geometry_info.sigma : black_point;
7905       if ((flags & PercentValue) != 0)
7906         {
7907           black_point*=(double) (*image)->columns*(*image)->rows/100.0;
7908           white_point*=(double) (*image)->columns*(*image)->rows/100.0;
7909         }
7910       white_point=(double) (*image)->columns*(*image)->rows-white_point;
7911       (void) ContrastStretchImage(*image,black_point,white_point,
7912         exception);
7913       XSetCursorState(display,windows,MagickFalse);
7914       if( IfMagickTrue(windows->image.orphan) )
7915         break;
7916       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7917       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7918       break;
7919     }
7920     case SigmoidalContrastCommand:
7921     {
7922       GeometryInfo
7923         geometry_info;
7924
7925       MagickStatusType
7926         flags;
7927
7928       static char
7929         levels[MaxTextExtent] = "3x50%";
7930
7931       /*
7932         Query user for gamma value.
7933       */
7934       (void) XDialogWidget(display,windows,"Sigmoidal Contrast",
7935         "Enter contrast and midpoint:",levels);
7936       if (*levels == '\0')
7937         break;
7938       /*
7939         Contrast stretch image.
7940       */
7941       XSetCursorState(display,windows,MagickTrue);
7942       XCheckRefreshWindows(display,windows);
7943       flags=ParseGeometry(levels,&geometry_info);
7944       if ((flags & SigmaValue) == 0)
7945         geometry_info.sigma=1.0*QuantumRange/2.0;
7946       if ((flags & PercentValue) != 0)
7947         geometry_info.sigma=1.0*QuantumRange*geometry_info.sigma/100.0;
7948       (void) SigmoidalContrastImage(*image,MagickTrue,geometry_info.rho,
7949         geometry_info.sigma,exception);
7950       XSetCursorState(display,windows,MagickFalse);
7951       if( IfMagickTrue(windows->image.orphan) )
7952         break;
7953       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7954       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7955       break;
7956     }
7957     case NormalizeCommand:
7958     {
7959       /*
7960         Perform histogram normalization on the image.
7961       */
7962       XSetCursorState(display,windows,MagickTrue);
7963       XCheckRefreshWindows(display,windows);
7964       (void) NormalizeImage(*image,exception);
7965       XSetCursorState(display,windows,MagickFalse);
7966       if( IfMagickTrue(windows->image.orphan) )
7967         break;
7968       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7969       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7970       break;
7971     }
7972     case EqualizeCommand:
7973     {
7974       /*
7975         Perform histogram equalization on the image.
7976       */
7977       XSetCursorState(display,windows,MagickTrue);
7978       XCheckRefreshWindows(display,windows);
7979       (void) EqualizeImage(*image,exception);
7980       XSetCursorState(display,windows,MagickFalse);
7981       if( IfMagickTrue(windows->image.orphan) )
7982         break;
7983       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7984       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7985       break;
7986     }
7987     case NegateCommand:
7988     {
7989       /*
7990         Negate colors in image.
7991       */
7992       XSetCursorState(display,windows,MagickTrue);
7993       XCheckRefreshWindows(display,windows);
7994       (void) NegateImage(*image,MagickFalse,exception);
7995       XSetCursorState(display,windows,MagickFalse);
7996       if( IfMagickTrue(windows->image.orphan) )
7997         break;
7998       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7999       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8000       break;
8001     }
8002     case GrayscaleCommand:
8003     {
8004       /*
8005         Convert image to grayscale.
8006       */
8007       XSetCursorState(display,windows,MagickTrue);
8008       XCheckRefreshWindows(display,windows);
8009       (void) SetImageType(*image,(*image)->alpha_trait != BlendPixelTrait ?
8010         GrayscaleType : GrayscaleMatteType,exception);
8011       XSetCursorState(display,windows,MagickFalse);
8012       if( IfMagickTrue(windows->image.orphan) )
8013         break;
8014       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8015       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8016       break;
8017     }
8018     case MapCommand:
8019     {
8020       Image
8021         *affinity_image;
8022
8023       static char
8024         filename[MaxTextExtent] = "\0";
8025
8026       /*
8027         Request image file name from user.
8028       */
8029       XFileBrowserWidget(display,windows,"Map",filename);
8030       if (*filename == '\0')
8031         break;
8032       /*
8033         Map image.
8034       */
8035       XSetCursorState(display,windows,MagickTrue);
8036       XCheckRefreshWindows(display,windows);
8037       (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
8038       affinity_image=ReadImage(image_info,exception);
8039       if (affinity_image != (Image *) NULL)
8040         {
8041           (void) RemapImage(&quantize_info,*image,affinity_image,exception);
8042           affinity_image=DestroyImage(affinity_image);
8043         }
8044       CatchException(exception);
8045       XSetCursorState(display,windows,MagickFalse);
8046       if( IfMagickTrue(windows->image.orphan) )
8047         break;
8048       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8049       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8050       break;
8051     }
8052     case QuantizeCommand:
8053     {
8054       int
8055         status;
8056
8057       static char
8058         colors[MaxTextExtent] = "256";
8059
8060       /*
8061         Query user for maximum number of colors.
8062       */
8063       status=XDialogWidget(display,windows,"Quantize",
8064         "Maximum number of colors:",colors);
8065       if (*colors == '\0')
8066         break;
8067       /*
8068         Color reduce the image.
8069       */
8070       XSetCursorState(display,windows,MagickTrue);
8071       XCheckRefreshWindows(display,windows);
8072       quantize_info.number_colors=StringToUnsignedLong(colors);
8073       quantize_info.dither_method=status != 0 ? RiemersmaDitherMethod :
8074         NoDitherMethod;
8075       (void) QuantizeImage(&quantize_info,*image,exception);
8076       XSetCursorState(display,windows,MagickFalse);
8077       if( IfMagickTrue(windows->image.orphan) )
8078         break;
8079       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8080       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8081       break;
8082     }
8083     case DespeckleCommand:
8084     {
8085       Image
8086         *despeckle_image;
8087
8088       /*
8089         Despeckle image.
8090       */
8091       XSetCursorState(display,windows,MagickTrue);
8092       XCheckRefreshWindows(display,windows);
8093       despeckle_image=DespeckleImage(*image,exception);
8094       if (despeckle_image != (Image *) NULL)
8095         {
8096           *image=DestroyImage(*image);
8097           *image=despeckle_image;
8098         }
8099       CatchException(exception);
8100       XSetCursorState(display,windows,MagickFalse);
8101       if( IfMagickTrue(windows->image.orphan) )
8102         break;
8103       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8104       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8105       break;
8106     }
8107     case EmbossCommand:
8108     {
8109       Image
8110         *emboss_image;
8111
8112       static char
8113         radius[MaxTextExtent] = "0.0x1.0";
8114
8115       /*
8116         Query user for emboss radius.
8117       */
8118       (void) XDialogWidget(display,windows,"Emboss",
8119         "Enter the emboss radius and standard deviation:",radius);
8120       if (*radius == '\0')
8121         break;
8122       /*
8123         Reduce noise in the image.
8124       */
8125       XSetCursorState(display,windows,MagickTrue);
8126       XCheckRefreshWindows(display,windows);
8127       flags=ParseGeometry(radius,&geometry_info);
8128       if ((flags & SigmaValue) == 0)
8129         geometry_info.sigma=1.0;
8130       emboss_image=EmbossImage(*image,geometry_info.rho,geometry_info.sigma,
8131         exception);
8132       if (emboss_image != (Image *) NULL)
8133         {
8134           *image=DestroyImage(*image);
8135           *image=emboss_image;
8136         }
8137       CatchException(exception);
8138       XSetCursorState(display,windows,MagickFalse);
8139       if( IfMagickTrue(windows->image.orphan) )
8140         break;
8141       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8142       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8143       break;
8144     }
8145     case ReduceNoiseCommand:
8146     {
8147       Image
8148         *noise_image;
8149
8150       static char
8151         radius[MaxTextExtent] = "0";
8152
8153       /*
8154         Query user for noise radius.
8155       */
8156       (void) XDialogWidget(display,windows,"Reduce Noise",
8157         "Enter the noise radius:",radius);
8158       if (*radius == '\0')
8159         break;
8160       /*
8161         Reduce noise in the image.
8162       */
8163       XSetCursorState(display,windows,MagickTrue);
8164       XCheckRefreshWindows(display,windows);
8165       flags=ParseGeometry(radius,&geometry_info);
8166       noise_image=StatisticImage(*image,NonpeakStatistic,(size_t)
8167         geometry_info.rho,(size_t) geometry_info.rho,exception);
8168       if (noise_image != (Image *) NULL)
8169         {
8170           *image=DestroyImage(*image);
8171           *image=noise_image;
8172         }
8173       CatchException(exception);
8174       XSetCursorState(display,windows,MagickFalse);
8175       if( IfMagickTrue(windows->image.orphan) )
8176         break;
8177       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8178       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8179       break;
8180     }
8181     case AddNoiseCommand:
8182     {
8183       char
8184         **noises;
8185
8186       Image
8187         *noise_image;
8188
8189       static char
8190         noise_type[MaxTextExtent] = "Gaussian";
8191
8192       /*
8193         Add noise to the image.
8194       */
8195       noises=GetCommandOptions(MagickNoiseOptions);
8196       if (noises == (char **) NULL)
8197         break;
8198       XListBrowserWidget(display,windows,&windows->widget,
8199         (const char **) noises,"Add Noise",
8200         "Select a type of noise to add to your image:",noise_type);
8201       noises=DestroyStringList(noises);
8202       if (*noise_type == '\0')
8203         break;
8204       XSetCursorState(display,windows,MagickTrue);
8205       XCheckRefreshWindows(display,windows);
8206       noise_image=AddNoiseImage(*image,(NoiseType) ParseCommandOption(
8207         MagickNoiseOptions,MagickFalse,noise_type),1.0,exception);
8208       if (noise_image != (Image *) NULL)
8209         {
8210           *image=DestroyImage(*image);
8211           *image=noise_image;
8212         }
8213       CatchException(exception);
8214       XSetCursorState(display,windows,MagickFalse);
8215       if( IfMagickTrue(windows->image.orphan) )
8216         break;
8217       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8218       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8219       break;
8220     }
8221     case SharpenCommand:
8222     {
8223       Image
8224         *sharp_image;
8225
8226       static char
8227         radius[MaxTextExtent] = "0.0x1.0";
8228
8229       /*
8230         Query user for sharpen radius.
8231       */
8232       (void) XDialogWidget(display,windows,"Sharpen",
8233         "Enter the sharpen radius and standard deviation:",radius);
8234       if (*radius == '\0')
8235         break;
8236       /*
8237         Sharpen image scanlines.
8238       */
8239       XSetCursorState(display,windows,MagickTrue);
8240       XCheckRefreshWindows(display,windows);
8241       flags=ParseGeometry(radius,&geometry_info);
8242       sharp_image=SharpenImage(*image,geometry_info.rho,geometry_info.sigma,
8243         exception);
8244       if (sharp_image != (Image *) NULL)
8245         {
8246           *image=DestroyImage(*image);
8247           *image=sharp_image;
8248         }
8249       CatchException(exception);
8250       XSetCursorState(display,windows,MagickFalse);
8251       if( IfMagickTrue(windows->image.orphan) )
8252         break;
8253       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8254       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8255       break;
8256     }
8257     case BlurCommand:
8258     {
8259       Image
8260         *blur_image;
8261
8262       static char
8263         radius[MaxTextExtent] = "0.0x1.0";
8264
8265       /*
8266         Query user for blur radius.
8267       */
8268       (void) XDialogWidget(display,windows,"Blur",
8269         "Enter the blur radius and standard deviation:",radius);
8270       if (*radius == '\0')
8271         break;
8272       /*
8273         Blur an image.
8274       */
8275       XSetCursorState(display,windows,MagickTrue);
8276       XCheckRefreshWindows(display,windows);
8277       flags=ParseGeometry(radius,&geometry_info);
8278       blur_image=BlurImage(*image,geometry_info.rho,geometry_info.sigma,
8279         exception);
8280       if (blur_image != (Image *) NULL)
8281         {
8282           *image=DestroyImage(*image);
8283           *image=blur_image;
8284         }
8285       CatchException(exception);
8286       XSetCursorState(display,windows,MagickFalse);
8287       if( IfMagickTrue(windows->image.orphan) )
8288         break;
8289       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8290       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8291       break;
8292     }
8293     case ThresholdCommand:
8294     {
8295       double
8296         threshold;
8297
8298       static char
8299         factor[MaxTextExtent] = "128";
8300
8301       /*
8302         Query user for threshold value.
8303       */
8304       (void) XDialogWidget(display,windows,"Threshold",
8305         "Enter threshold value:",factor);
8306       if (*factor == '\0')
8307         break;
8308       /*
8309         Gamma correct image.
8310       */
8311       XSetCursorState(display,windows,MagickTrue);
8312       XCheckRefreshWindows(display,windows);
8313       threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8314       (void) BilevelImage(*image,threshold,exception);
8315       XSetCursorState(display,windows,MagickFalse);
8316       if( IfMagickTrue(windows->image.orphan) )
8317         break;
8318       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8319       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8320       break;
8321     }
8322     case EdgeDetectCommand:
8323     {
8324       Image
8325         *edge_image;
8326
8327       static char
8328         radius[MaxTextExtent] = "0";
8329
8330       /*
8331         Query user for edge factor.
8332       */
8333       (void) XDialogWidget(display,windows,"Detect Edges",
8334         "Enter the edge detect radius:",radius);
8335       if (*radius == '\0')
8336         break;
8337       /*
8338         Detect edge in image.
8339       */
8340       XSetCursorState(display,windows,MagickTrue);
8341       XCheckRefreshWindows(display,windows);
8342       flags=ParseGeometry(radius,&geometry_info);
8343       edge_image=EdgeImage(*image,geometry_info.rho,geometry_info.sigma,
8344         exception);
8345       if (edge_image != (Image *) NULL)
8346         {
8347           *image=DestroyImage(*image);
8348           *image=edge_image;
8349         }
8350       CatchException(exception);
8351       XSetCursorState(display,windows,MagickFalse);
8352       if( IfMagickTrue(windows->image.orphan) )
8353         break;
8354       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8355       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8356       break;
8357     }
8358     case SpreadCommand:
8359     {
8360       Image
8361         *spread_image;
8362
8363       static char
8364         amount[MaxTextExtent] = "2";
8365
8366       /*
8367         Query user for spread amount.
8368       */
8369       (void) XDialogWidget(display,windows,"Spread",
8370         "Enter the displacement amount:",amount);
8371       if (*amount == '\0')
8372         break;
8373       /*
8374         Displace image pixels by a random amount.
8375       */
8376       XSetCursorState(display,windows,MagickTrue);
8377       XCheckRefreshWindows(display,windows);
8378       flags=ParseGeometry(amount,&geometry_info);
8379       spread_image=EdgeImage(*image,geometry_info.rho,geometry_info.sigma,
8380         exception);
8381       if (spread_image != (Image *) NULL)
8382         {
8383           *image=DestroyImage(*image);
8384           *image=spread_image;
8385         }
8386       CatchException(exception);
8387       XSetCursorState(display,windows,MagickFalse);
8388       if( IfMagickTrue(windows->image.orphan) )
8389         break;
8390       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8391       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8392       break;
8393     }
8394     case ShadeCommand:
8395     {
8396       Image
8397         *shade_image;
8398
8399       int
8400         status;
8401
8402       static char
8403         geometry[MaxTextExtent] = "30x30";
8404
8405       /*
8406         Query user for the shade geometry.
8407       */
8408       status=XDialogWidget(display,windows,"Shade",
8409         "Enter the azimuth and elevation of the light source:",geometry);
8410       if (*geometry == '\0')
8411         break;
8412       /*
8413         Shade image pixels.
8414       */
8415       XSetCursorState(display,windows,MagickTrue);
8416       XCheckRefreshWindows(display,windows);
8417       flags=ParseGeometry(geometry,&geometry_info);
8418       if ((flags & SigmaValue) == 0)
8419         geometry_info.sigma=1.0;
8420       shade_image=ShadeImage(*image,IsMagickTrue(status),
8421         geometry_info.rho,geometry_info.sigma,exception);
8422       if (shade_image != (Image *) NULL)
8423         {
8424           *image=DestroyImage(*image);
8425           *image=shade_image;
8426         }
8427       CatchException(exception);
8428       XSetCursorState(display,windows,MagickFalse);
8429       if( IfMagickTrue(windows->image.orphan) )
8430         break;
8431       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8432       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8433       break;
8434     }
8435     case RaiseCommand:
8436     {
8437       static char
8438         bevel_width[MaxTextExtent] = "10";
8439
8440       /*
8441         Query user for bevel width.
8442       */
8443       (void) XDialogWidget(display,windows,"Raise","Bevel width:",bevel_width);
8444       if (*bevel_width == '\0')
8445         break;
8446       /*
8447         Raise an image.
8448       */
8449       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8450         exception);
8451       XSetCursorState(display,windows,MagickTrue);
8452       XCheckRefreshWindows(display,windows);
8453       (void) ParsePageGeometry(*image,bevel_width,&page_geometry,
8454         exception);
8455       (void) RaiseImage(*image,&page_geometry,MagickTrue,exception);
8456       XSetCursorState(display,windows,MagickFalse);
8457       if( IfMagickTrue(windows->image.orphan) )
8458         break;
8459       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8460       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8461       break;
8462     }
8463     case SegmentCommand:
8464     {
8465       static char
8466         threshold[MaxTextExtent] = "1.0x1.5";
8467
8468       /*
8469         Query user for smoothing threshold.
8470       */
8471       (void) XDialogWidget(display,windows,"Segment","Smooth threshold:",
8472         threshold);
8473       if (*threshold == '\0')
8474         break;
8475       /*
8476         Segment an image.
8477       */
8478       XSetCursorState(display,windows,MagickTrue);
8479       XCheckRefreshWindows(display,windows);
8480       flags=ParseGeometry(threshold,&geometry_info);
8481       if ((flags & SigmaValue) == 0)
8482         geometry_info.sigma=1.0;
8483       (void) SegmentImage(*image,sRGBColorspace,MagickFalse,geometry_info.rho,
8484         geometry_info.sigma,exception);
8485       XSetCursorState(display,windows,MagickFalse);
8486       if( IfMagickTrue(windows->image.orphan) )
8487         break;
8488       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8489       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8490       break;
8491     }
8492     case SepiaToneCommand:
8493     {
8494       double
8495         threshold;
8496
8497       Image
8498         *sepia_image;
8499
8500       static char
8501         factor[MaxTextExtent] = "80%";
8502
8503       /*
8504         Query user for sepia-tone factor.
8505       */
8506       (void) XDialogWidget(display,windows,"Sepia Tone",
8507         "Enter the sepia tone factor (0 - 99.9%):",factor);
8508       if (*factor == '\0')
8509         break;
8510       /*
8511         Sepia tone image pixels.
8512       */
8513       XSetCursorState(display,windows,MagickTrue);
8514       XCheckRefreshWindows(display,windows);
8515       threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8516       sepia_image=SepiaToneImage(*image,threshold,exception);
8517       if (sepia_image != (Image *) NULL)
8518         {
8519           *image=DestroyImage(*image);
8520           *image=sepia_image;
8521         }
8522       CatchException(exception);
8523       XSetCursorState(display,windows,MagickFalse);
8524       if( IfMagickTrue(windows->image.orphan) )
8525         break;
8526       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8527       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8528       break;
8529     }
8530     case SolarizeCommand:
8531     {
8532       double
8533         threshold;
8534
8535       static char
8536         factor[MaxTextExtent] = "60%";
8537
8538       /*
8539         Query user for solarize factor.
8540       */
8541       (void) XDialogWidget(display,windows,"Solarize",
8542         "Enter the solarize factor (0 - 99.9%):",factor);
8543       if (*factor == '\0')
8544         break;
8545       /*
8546         Solarize image pixels.
8547       */
8548       XSetCursorState(display,windows,MagickTrue);
8549       XCheckRefreshWindows(display,windows);
8550       threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8551       (void) SolarizeImage(*image,threshold,exception);
8552       XSetCursorState(display,windows,MagickFalse);
8553       if( IfMagickTrue(windows->image.orphan) )
8554         break;
8555       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8556       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8557       break;
8558     }
8559     case SwirlCommand:
8560     {
8561       Image
8562         *swirl_image;
8563
8564       static char
8565         degrees[MaxTextExtent] = "60";
8566
8567       /*
8568         Query user for swirl angle.
8569       */
8570       (void) XDialogWidget(display,windows,"Swirl","Enter the swirl angle:",
8571         degrees);
8572       if (*degrees == '\0')
8573         break;
8574       /*
8575         Swirl image pixels about the center.
8576       */
8577       XSetCursorState(display,windows,MagickTrue);
8578       XCheckRefreshWindows(display,windows);
8579       flags=ParseGeometry(degrees,&geometry_info);
8580       swirl_image=SwirlImage(*image,geometry_info.rho,(*image)->interpolate,
8581         exception);
8582       if (swirl_image != (Image *) NULL)
8583         {
8584           *image=DestroyImage(*image);
8585           *image=swirl_image;
8586         }
8587       CatchException(exception);
8588       XSetCursorState(display,windows,MagickFalse);
8589       if( IfMagickTrue(windows->image.orphan) )
8590         break;
8591       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8592       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8593       break;
8594     }
8595     case ImplodeCommand:
8596     {
8597       Image
8598         *implode_image;
8599
8600       static char
8601         factor[MaxTextExtent] = "0.3";
8602
8603       /*
8604         Query user for implode factor.
8605       */
8606       (void) XDialogWidget(display,windows,"Implode",
8607         "Enter the implosion/explosion factor (-1.0 - 1.0):",factor);
8608       if (*factor == '\0')
8609         break;
8610       /*
8611         Implode image pixels about the center.
8612       */
8613       XSetCursorState(display,windows,MagickTrue);
8614       XCheckRefreshWindows(display,windows);
8615       flags=ParseGeometry(factor,&geometry_info);
8616       implode_image=ImplodeImage(*image,geometry_info.rho,(*image)->interpolate,
8617         exception);
8618       if (implode_image != (Image *) NULL)
8619         {
8620           *image=DestroyImage(*image);
8621           *image=implode_image;
8622         }
8623       CatchException(exception);
8624       XSetCursorState(display,windows,MagickFalse);
8625       if( IfMagickTrue(windows->image.orphan) )
8626         break;
8627       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8628       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8629       break;
8630     }
8631     case VignetteCommand:
8632     {
8633       Image
8634         *vignette_image;
8635
8636       static char
8637         geometry[MaxTextExtent] = "0x20";
8638
8639       /*
8640         Query user for the vignette geometry.
8641       */
8642       (void) XDialogWidget(display,windows,"Vignette",
8643         "Enter the radius, sigma, and x and y offsets:",geometry);
8644       if (*geometry == '\0')
8645         break;
8646       /*
8647         Soften the edges of the image in vignette style
8648       */
8649       XSetCursorState(display,windows,MagickTrue);
8650       XCheckRefreshWindows(display,windows);
8651       flags=ParseGeometry(geometry,&geometry_info);
8652       if ((flags & SigmaValue) == 0)
8653         geometry_info.sigma=1.0;
8654       if ((flags & XiValue) == 0)
8655         geometry_info.xi=0.1*(*image)->columns;
8656       if ((flags & PsiValue) == 0)
8657         geometry_info.psi=0.1*(*image)->rows;
8658       vignette_image=VignetteImage(*image,geometry_info.rho,0.0,(ssize_t)
8659         ceil(geometry_info.xi-0.5),(ssize_t) ceil(geometry_info.psi-0.5),
8660         exception);
8661       if (vignette_image != (Image *) NULL)
8662         {
8663           *image=DestroyImage(*image);
8664           *image=vignette_image;
8665         }
8666       CatchException(exception);
8667       XSetCursorState(display,windows,MagickFalse);
8668       if( IfMagickTrue(windows->image.orphan) )
8669         break;
8670       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8671       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8672       break;
8673     }
8674     case WaveCommand:
8675     {
8676       Image
8677         *wave_image;
8678
8679       static char
8680         geometry[MaxTextExtent] = "25x150";
8681
8682       /*
8683         Query user for the wave geometry.
8684       */
8685       (void) XDialogWidget(display,windows,"Wave",
8686         "Enter the amplitude and length of the wave:",geometry);
8687       if (*geometry == '\0')
8688         break;
8689       /*
8690         Alter an image along a sine wave.
8691       */
8692       XSetCursorState(display,windows,MagickTrue);
8693       XCheckRefreshWindows(display,windows);
8694       flags=ParseGeometry(geometry,&geometry_info);
8695       if ((flags & SigmaValue) == 0)
8696         geometry_info.sigma=1.0;
8697       wave_image=WaveImage(*image,geometry_info.rho,geometry_info.sigma,
8698         (*image)->interpolate,exception);
8699       if (wave_image != (Image *) NULL)
8700         {
8701           *image=DestroyImage(*image);
8702           *image=wave_image;
8703         }
8704       CatchException(exception);
8705       XSetCursorState(display,windows,MagickFalse);
8706       if( IfMagickTrue(windows->image.orphan) )
8707         break;
8708       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8709       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8710       break;
8711     }
8712     case OilPaintCommand:
8713     {
8714       Image
8715         *paint_image;
8716
8717       static char
8718         radius[MaxTextExtent] = "0";
8719
8720       /*
8721         Query user for circular neighborhood radius.
8722       */
8723       (void) XDialogWidget(display,windows,"Oil Paint",
8724         "Enter the mask radius:",radius);
8725       if (*radius == '\0')
8726         break;
8727       /*
8728         OilPaint image scanlines.
8729       */
8730       XSetCursorState(display,windows,MagickTrue);
8731       XCheckRefreshWindows(display,windows);
8732       flags=ParseGeometry(radius,&geometry_info);
8733       paint_image=OilPaintImage(*image,geometry_info.rho,geometry_info.sigma,
8734         exception);
8735       if (paint_image != (Image *) NULL)
8736         {
8737           *image=DestroyImage(*image);
8738           *image=paint_image;
8739         }
8740       CatchException(exception);
8741       XSetCursorState(display,windows,MagickFalse);
8742       if( IfMagickTrue(windows->image.orphan) )
8743         break;
8744       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8745       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8746       break;
8747     }
8748     case CharcoalDrawCommand:
8749     {
8750       Image
8751         *charcoal_image;
8752
8753       static char
8754         radius[MaxTextExtent] = "0x1";
8755
8756       /*
8757         Query user for charcoal radius.
8758       */
8759       (void) XDialogWidget(display,windows,"Charcoal Draw",
8760         "Enter the charcoal radius and sigma:",radius);
8761       if (*radius == '\0')
8762         break;
8763       /*
8764         Charcoal the image.
8765       */
8766       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8767         exception);
8768       XSetCursorState(display,windows,MagickTrue);
8769       XCheckRefreshWindows(display,windows);
8770       flags=ParseGeometry(radius,&geometry_info);
8771       if ((flags & SigmaValue) == 0)
8772         geometry_info.sigma=geometry_info.rho;
8773       charcoal_image=CharcoalImage(*image,geometry_info.rho,geometry_info.sigma,
8774         exception);
8775       if (charcoal_image != (Image *) NULL)
8776         {
8777           *image=DestroyImage(*image);
8778           *image=charcoal_image;
8779         }
8780       CatchException(exception);
8781       XSetCursorState(display,windows,MagickFalse);
8782       if( IfMagickTrue(windows->image.orphan) )
8783         break;
8784       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8785       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8786       break;
8787     }
8788     case AnnotateCommand:
8789     {
8790       /*
8791         Annotate the image with text.
8792       */
8793       status=XAnnotateEditImage(display,resource_info,windows,*image,exception);
8794       if( IfMagickFalse(status) )
8795         {
8796           XNoticeWidget(display,windows,"Unable to annotate X image",
8797             (*image)->filename);
8798           break;
8799         }
8800       break;
8801     }
8802     case DrawCommand:
8803     {
8804       /*
8805         Draw image.
8806       */
8807       status=XDrawEditImage(display,resource_info,windows,image,exception);
8808       if( IfMagickFalse(status) )
8809         {
8810           XNoticeWidget(display,windows,"Unable to draw on the X image",
8811             (*image)->filename);
8812           break;
8813         }
8814       break;
8815     }
8816     case ColorCommand:
8817     {
8818       /*
8819         Color edit.
8820       */
8821       status=XColorEditImage(display,resource_info,windows,image,exception);
8822       if( IfMagickFalse(status) )
8823         {
8824           XNoticeWidget(display,windows,"Unable to pixel edit X image",
8825             (*image)->filename);
8826           break;
8827         }
8828       break;
8829     }
8830     case MatteCommand:
8831     {
8832       /*
8833         Matte edit.
8834       */
8835       status=XMatteEditImage(display,resource_info,windows,image,exception);
8836       if( IfMagickFalse(status) )
8837         {
8838           XNoticeWidget(display,windows,"Unable to matte edit X image",
8839             (*image)->filename);
8840           break;
8841         }
8842       break;
8843     }
8844     case CompositeCommand:
8845     {
8846       /*
8847         Composite image.
8848       */
8849       status=XCompositeImage(display,resource_info,windows,*image,
8850         exception);
8851       if( IfMagickFalse(status) )
8852         {
8853           XNoticeWidget(display,windows,"Unable to composite X image",
8854             (*image)->filename);
8855           break;
8856         }
8857       break;
8858     }
8859     case AddBorderCommand:
8860     {
8861       Image
8862         *border_image;
8863
8864       static char
8865         geometry[MaxTextExtent] = "6x6";
8866
8867       /*
8868         Query user for border color and geometry.
8869       */
8870       XColorBrowserWidget(display,windows,"Select",color);
8871       if (*color == '\0')
8872         break;
8873       (void) XDialogWidget(display,windows,"Add Border",
8874         "Enter border geometry:",geometry);
8875       if (*geometry == '\0')
8876         break;
8877       /*
8878         Add a border to the image.
8879       */
8880       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8881         exception);
8882       XSetCursorState(display,windows,MagickTrue);
8883       XCheckRefreshWindows(display,windows);
8884       (void) QueryColorCompliance(color,AllCompliance,&(*image)->border_color,
8885         exception);
8886       (void) ParsePageGeometry(*image,geometry,&page_geometry,
8887         exception);
8888       border_image=BorderImage(*image,&page_geometry,(*image)->compose,
8889         exception);
8890       if (border_image != (Image *) NULL)
8891         {
8892           *image=DestroyImage(*image);
8893           *image=border_image;
8894         }
8895       CatchException(exception);
8896       XSetCursorState(display,windows,MagickFalse);
8897       if( IfMagickTrue(windows->image.orphan) )
8898         break;
8899       windows->image.window_changes.width=(int) (*image)->columns;
8900       windows->image.window_changes.height=(int) (*image)->rows;
8901       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8902       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8903       break;
8904     }
8905     case AddFrameCommand:
8906     {
8907       FrameInfo
8908         frame_info;
8909
8910       Image
8911         *frame_image;
8912
8913       static char
8914         geometry[MaxTextExtent] = "6x6";
8915
8916       /*
8917         Query user for frame color and geometry.
8918       */
8919       XColorBrowserWidget(display,windows,"Select",color);
8920       if (*color == '\0')
8921         break;
8922       (void) XDialogWidget(display,windows,"Add Frame","Enter frame geometry:",
8923         geometry);
8924       if (*geometry == '\0')
8925         break;
8926       /*
8927         Surround image with an ornamental border.
8928       */
8929       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8930         exception);
8931       XSetCursorState(display,windows,MagickTrue);
8932       XCheckRefreshWindows(display,windows);
8933       (void) QueryColorCompliance(color,AllCompliance,&(*image)->matte_color,
8934         exception);
8935       (void) ParsePageGeometry(*image,geometry,&page_geometry,
8936         exception);
8937       frame_info.width=page_geometry.width;
8938       frame_info.height=page_geometry.height;
8939       frame_info.outer_bevel=page_geometry.x;
8940       frame_info.inner_bevel=page_geometry.y;
8941       frame_info.x=(ssize_t) frame_info.width;
8942       frame_info.y=(ssize_t) frame_info.height;
8943       frame_info.width=(*image)->columns+2*frame_info.width;
8944       frame_info.height=(*image)->rows+2*frame_info.height;
8945       frame_image=FrameImage(*image,&frame_info,(*image)->compose,exception);
8946       if (frame_image != (Image *) NULL)
8947         {
8948           *image=DestroyImage(*image);
8949           *image=frame_image;
8950         }
8951       CatchException(exception);
8952       XSetCursorState(display,windows,MagickFalse);
8953       if( IfMagickTrue(windows->image.orphan) )
8954         break;
8955       windows->image.window_changes.width=(int) (*image)->columns;
8956       windows->image.window_changes.height=(int) (*image)->rows;
8957       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8958       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8959       break;
8960     }
8961     case CommentCommand:
8962     {
8963       const char
8964         *value;
8965
8966       FILE
8967         *file;
8968
8969       int
8970         unique_file;
8971
8972       /*
8973         Edit image comment.
8974       */
8975       unique_file=AcquireUniqueFileResource(image_info->filename);
8976       if (unique_file == -1)
8977         XNoticeWidget(display,windows,"Unable to edit image comment",
8978           image_info->filename);
8979       value=GetImageProperty(*image,"comment",exception);
8980       if (value == (char *) NULL)
8981         unique_file=close(unique_file)-1;
8982       else
8983         {
8984           register const char
8985             *p;
8986
8987           file=fdopen(unique_file,"w");
8988           if (file == (FILE *) NULL)
8989             {
8990               XNoticeWidget(display,windows,"Unable to edit image comment",
8991                 image_info->filename);
8992               break;
8993             }
8994           for (p=value; *p != '\0'; p++)
8995             (void) fputc((int) *p,file);
8996           (void) fputc('\n',file);
8997           (void) fclose(file);
8998         }
8999       XSetCursorState(display,windows,MagickTrue);
9000       XCheckRefreshWindows(display,windows);
9001       status=InvokeDelegate(image_info,*image,"edit",(char *) NULL,
9002         exception);
9003       if( IfMagickFalse(status) )
9004         XNoticeWidget(display,windows,"Unable to edit image comment",
9005           (char *) NULL);
9006       else
9007         {
9008           char
9009             *comment;
9010
9011           comment=FileToString(image_info->filename,~0UL,exception);
9012           if (comment != (char *) NULL)
9013             {
9014               (void) SetImageProperty(*image,"comment",comment,exception);
9015               (*image)->taint=MagickTrue;
9016             }
9017         }
9018       (void) RelinquishUniqueFileResource(image_info->filename);
9019       XSetCursorState(display,windows,MagickFalse);
9020       break;
9021     }
9022     case LaunchCommand:
9023     {
9024       /*
9025         Launch program.
9026       */
9027       XSetCursorState(display,windows,MagickTrue);
9028       XCheckRefreshWindows(display,windows);
9029       (void) AcquireUniqueFilename(filename);
9030       (void) FormatLocaleString((*image)->filename,MaxTextExtent,"launch:%s",
9031         filename);
9032       status=WriteImage(image_info,*image,exception);
9033       if( IfMagickFalse(status) )
9034         XNoticeWidget(display,windows,"Unable to launch image editor",
9035           (char *) NULL);
9036       else
9037         {
9038           nexus=ReadImage(resource_info->image_info,exception);
9039           CatchException(exception);
9040           XClientMessage(display,windows->image.id,windows->im_protocols,
9041             windows->im_next_image,CurrentTime);
9042         }
9043       (void) RelinquishUniqueFileResource(filename);
9044       XSetCursorState(display,windows,MagickFalse);
9045       break;
9046     }
9047     case RegionofInterestCommand:
9048     {
9049       /*
9050         Apply an image processing technique to a region of interest.
9051       */
9052       (void) XROIImage(display,resource_info,windows,image,exception);
9053       break;
9054     }
9055     case InfoCommand:
9056       break;
9057     case ZoomCommand:
9058     {
9059       /*
9060         Zoom image.
9061       */
9062       if( IfMagickTrue(windows->magnify.mapped) )
9063         (void) XRaiseWindow(display,windows->magnify.id);
9064       else
9065         {
9066           /*
9067             Make magnify image.
9068           */
9069           XSetCursorState(display,windows,MagickTrue);
9070           (void) XMapRaised(display,windows->magnify.id);
9071           XSetCursorState(display,windows,MagickFalse);
9072         }
9073       break;
9074     }
9075     case ShowPreviewCommand:
9076     {
9077       char
9078         **previews;
9079
9080       Image
9081         *preview_image;
9082
9083       static char
9084         preview_type[MaxTextExtent] = "Gamma";
9085
9086       /*
9087         Select preview type from menu.
9088       */
9089       previews=GetCommandOptions(MagickPreviewOptions);
9090       if (previews == (char **) NULL)
9091         break;
9092       XListBrowserWidget(display,windows,&windows->widget,
9093         (const char **) previews,"Preview",
9094         "Select an enhancement, effect, or F/X:",preview_type);
9095       previews=DestroyStringList(previews);
9096       if (*preview_type == '\0')
9097         break;
9098       /*
9099         Show image preview.
9100       */
9101       XSetCursorState(display,windows,MagickTrue);
9102       XCheckRefreshWindows(display,windows);
9103       image_info->preview_type=(PreviewType)
9104         ParseCommandOption(MagickPreviewOptions,MagickFalse,preview_type);
9105       image_info->group=(ssize_t) windows->image.id;
9106       (void) DeleteImageProperty(*image,"label");
9107       (void) SetImageProperty(*image,"label","Preview",exception);
9108       (void) AcquireUniqueFilename(filename);
9109       (void) FormatLocaleString((*image)->filename,MaxTextExtent,"preview:%s",
9110         filename);
9111       status=WriteImage(image_info,*image,exception);
9112       (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9113       preview_image=ReadImage(image_info,exception);
9114       (void) RelinquishUniqueFileResource(filename);
9115       if (preview_image == (Image *) NULL)
9116         break;
9117       (void) FormatLocaleString(preview_image->filename,MaxTextExtent,"show:%s",
9118         filename);
9119       status=WriteImage(image_info,preview_image,exception);
9120       preview_image=DestroyImage(preview_image);
9121       if( IfMagickFalse(status) )
9122         XNoticeWidget(display,windows,"Unable to show image preview",
9123           (*image)->filename);
9124       XDelay(display,1500);
9125       XSetCursorState(display,windows,MagickFalse);
9126       break;
9127     }
9128     case ShowHistogramCommand:
9129     {
9130       Image
9131         *histogram_image;
9132
9133       /*
9134         Show image histogram.
9135       */
9136       XSetCursorState(display,windows,MagickTrue);
9137       XCheckRefreshWindows(display,windows);
9138       image_info->group=(ssize_t) windows->image.id;
9139       (void) DeleteImageProperty(*image,"label");
9140       (void) SetImageProperty(*image,"label","Histogram",exception);
9141       (void) AcquireUniqueFilename(filename);
9142       (void) FormatLocaleString((*image)->filename,MaxTextExtent,"histogram:%s",
9143         filename);
9144       status=WriteImage(image_info,*image,exception);
9145       (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9146       histogram_image=ReadImage(image_info,exception);
9147       (void) RelinquishUniqueFileResource(filename);
9148       if (histogram_image == (Image *) NULL)
9149         break;
9150       (void) FormatLocaleString(histogram_image->filename,MaxTextExtent,
9151         "show:%s",filename);
9152       status=WriteImage(image_info,histogram_image,exception);
9153       histogram_image=DestroyImage(histogram_image);
9154       if( IfMagickFalse(status) )
9155         XNoticeWidget(display,windows,"Unable to show histogram",
9156           (*image)->filename);
9157       XDelay(display,1500);
9158       XSetCursorState(display,windows,MagickFalse);
9159       break;
9160     }
9161     case ShowMatteCommand:
9162     {
9163       Image
9164         *matte_image;
9165
9166       if ((*image)->alpha_trait != BlendPixelTrait)
9167         {
9168           XNoticeWidget(display,windows,
9169             "Image does not have any matte information",(*image)->filename);
9170           break;
9171         }
9172       /*
9173         Show image matte.
9174       */
9175       XSetCursorState(display,windows,MagickTrue);
9176       XCheckRefreshWindows(display,windows);
9177       image_info->group=(ssize_t) windows->image.id;
9178       (void) DeleteImageProperty(*image,"label");
9179       (void) SetImageProperty(*image,"label","Matte",exception);
9180       (void) AcquireUniqueFilename(filename);
9181       (void) FormatLocaleString((*image)->filename,MaxTextExtent,"matte:%s",
9182         filename);
9183       status=WriteImage(image_info,*image,exception);
9184       (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9185       matte_image=ReadImage(image_info,exception);
9186       (void) RelinquishUniqueFileResource(filename);
9187       if (matte_image == (Image *) NULL)
9188         break;
9189       (void) FormatLocaleString(matte_image->filename,MaxTextExtent,"show:%s",
9190         filename);
9191       status=WriteImage(image_info,matte_image,exception);
9192       matte_image=DestroyImage(matte_image);
9193       if( IfMagickFalse(status) )
9194         XNoticeWidget(display,windows,"Unable to show matte",
9195           (*image)->filename);
9196       XDelay(display,1500);
9197       XSetCursorState(display,windows,MagickFalse);
9198       break;
9199     }
9200     case BackgroundCommand:
9201     {
9202       /*
9203         Background image.
9204       */
9205       status=XBackgroundImage(display,resource_info,windows,image,exception);
9206       if( IfMagickFalse(status) )
9207         break;
9208       nexus=CloneImage(*image,0,0,MagickTrue,exception);
9209       if (nexus != (Image *) NULL)
9210         XClientMessage(display,windows->image.id,windows->im_protocols,
9211           windows->im_next_image,CurrentTime);
9212       break;
9213     }
9214     case SlideShowCommand:
9215     {
9216       static char
9217         delay[MaxTextExtent] = "5";
9218
9219       /*
9220         Display next image after pausing.
9221       */
9222       (void) XDialogWidget(display,windows,"Slide Show",
9223         "Pause how many 1/100ths of a second between images:",delay);
9224       if (*delay == '\0')
9225         break;
9226       resource_info->delay=StringToUnsignedLong(delay);
9227       XClientMessage(display,windows->image.id,windows->im_protocols,
9228         windows->im_next_image,CurrentTime);
9229       break;
9230     }
9231     case PreferencesCommand:
9232     {
9233       /*
9234         Set user preferences.
9235       */
9236       status=XPreferencesWidget(display,resource_info,windows);
9237       if( IfMagickFalse(status) )
9238         break;
9239       nexus=CloneImage(*image,0,0,MagickTrue,exception);
9240       if (nexus != (Image *) NULL)
9241         XClientMessage(display,windows->image.id,windows->im_protocols,
9242           windows->im_next_image,CurrentTime);
9243       break;
9244     }
9245     case HelpCommand:
9246     {
9247       /*
9248         User requested help.
9249       */
9250       XTextViewWidget(display,resource_info,windows,MagickFalse,
9251         "Help Viewer - Display",DisplayHelp);
9252       break;
9253     }
9254     case BrowseDocumentationCommand:
9255     {
9256       Atom
9257         mozilla_atom;
9258
9259       Window
9260         mozilla_window,
9261         root_window;
9262
9263       /*
9264         Browse the ImageMagick documentation.
9265       */
9266       root_window=XRootWindow(display,XDefaultScreen(display));
9267       mozilla_atom=XInternAtom(display,"_MOZILLA_VERSION",MagickFalse);
9268       mozilla_window=XWindowByProperty(display,root_window,mozilla_atom);
9269       if (mozilla_window != (Window) NULL)
9270         {
9271           char
9272             command[MaxTextExtent],
9273             *url;
9274
9275           /*
9276             Display documentation using Netscape remote control.
9277           */
9278           url=GetMagickHomeURL();
9279           (void) FormatLocaleString(command,MaxTextExtent,
9280             "openurl(%s,new-tab)",url);
9281           url=DestroyString(url);
9282           mozilla_atom=XInternAtom(display,"_MOZILLA_COMMAND",MagickFalse);
9283           (void) XChangeProperty(display,mozilla_window,mozilla_atom,XA_STRING,
9284             8,PropModeReplace,(unsigned char *) command,(int) strlen(command));
9285           XSetCursorState(display,windows,MagickFalse);
9286           break;
9287         }
9288       XSetCursorState(display,windows,MagickTrue);
9289       XCheckRefreshWindows(display,windows);
9290       status=InvokeDelegate(image_info,*image,"browse",(char *) NULL,
9291         exception);
9292       if( IfMagickFalse(status) )
9293         XNoticeWidget(display,windows,"Unable to browse documentation",
9294           (char *) NULL);
9295       XDelay(display,1500);
9296       XSetCursorState(display,windows,MagickFalse);
9297       break;
9298     }
9299     case VersionCommand:
9300     {
9301       XNoticeWidget(display,windows,GetMagickVersion((size_t *) NULL),
9302         GetMagickCopyright());
9303       break;
9304     }
9305     case SaveToUndoBufferCommand:
9306       break;
9307     default:
9308     {
9309       (void) XBell(display,0);
9310       break;
9311     }
9312   }
9313   image_info=DestroyImageInfo(image_info);
9314   return(nexus);
9315 }
9316 \f
9317 /*
9318 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9319 %                                                                             %
9320 %                                                                             %
9321 %                                                                             %
9322 +   X M a g n i f y I m a g e                                                 %
9323 %                                                                             %
9324 %                                                                             %
9325 %                                                                             %
9326 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9327 %
9328 %  XMagnifyImage() magnifies portions of the image as indicated by the pointer.
9329 %  The magnified portion is displayed in a separate window.
9330 %
9331 %  The format of the XMagnifyImage method is:
9332 %
9333 %      void XMagnifyImage(Display *display,XWindows *windows,XEvent *event,
9334 %        ExceptionInfo *exception)
9335 %
9336 %  A description of each parameter follows:
9337 %
9338 %    o display: Specifies a connection to an X server;  returned from
9339 %      XOpenDisplay.
9340 %
9341 %    o windows: Specifies a pointer to a XWindows structure.
9342 %
9343 %    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
9344 %      the entire image is refreshed.
9345 %
9346 %    o exception: return any errors or warnings in this structure.
9347 %
9348 */
9349 static void XMagnifyImage(Display *display,XWindows *windows,XEvent *event,
9350   ExceptionInfo *exception)
9351 {
9352   char
9353     text[MaxTextExtent];
9354
9355   register int
9356     x,
9357     y;
9358
9359   size_t
9360     state;
9361
9362   /*
9363     Update magnified image until the mouse button is released.
9364   */
9365   (void) XCheckDefineCursor(display,windows->image.id,windows->magnify.cursor);
9366   state=DefaultState;
9367   x=event->xbutton.x;
9368   y=event->xbutton.y;
9369   windows->magnify.x=(int) windows->image.x+x;
9370   windows->magnify.y=(int) windows->image.y+y;
9371   do
9372   {
9373     /*
9374       Map and unmap Info widget as text cursor crosses its boundaries.
9375     */
9376     if( IfMagickTrue(windows->info.mapped) )
9377       {
9378         if ((x < (int) (windows->info.x+windows->info.width)) &&
9379             (y < (int) (windows->info.y+windows->info.height)))
9380           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
9381       }
9382     else
9383       if ((x > (int) (windows->info.x+windows->info.width)) ||
9384           (y > (int) (windows->info.y+windows->info.height)))
9385         (void) XMapWindow(display,windows->info.id);
9386     if( IfMagickTrue(windows->info.mapped) )
9387       {
9388         /*
9389           Display pointer position.
9390         */
9391         (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
9392           windows->magnify.x,windows->magnify.y);
9393         XInfoWidget(display,windows,text);
9394       }
9395     /*
9396       Wait for next event.
9397     */
9398     XScreenEvent(display,windows,event,exception);
9399     switch (event->type)
9400     {
9401       case ButtonPress:
9402         break;
9403       case ButtonRelease:
9404       {
9405         /*
9406           User has finished magnifying image.
9407         */
9408         x=event->xbutton.x;
9409         y=event->xbutton.y;
9410         state|=ExitState;
9411         break;
9412       }
9413       case Expose:
9414         break;
9415       case MotionNotify:
9416       {
9417         x=event->xmotion.x;
9418         y=event->xmotion.y;
9419         break;
9420       }
9421       default:
9422         break;
9423     }
9424     /*
9425       Check boundary conditions.
9426     */
9427     if (x < 0)
9428       x=0;
9429     else
9430       if (x >= (int) windows->image.width)
9431         x=(int) windows->image.width-1;
9432     if (y < 0)
9433       y=0;
9434     else
9435      if (y >= (int) windows->image.height)
9436        y=(int) windows->image.height-1;
9437   } while ((state & ExitState) == 0);
9438   /*
9439     Display magnified image.
9440   */
9441   XSetCursorState(display,windows,MagickFalse);
9442 }
9443 \f
9444 /*
9445 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9446 %                                                                             %
9447 %                                                                             %
9448 %                                                                             %
9449 +   X M a g n i f y W i n d o w C o m m a n d                                 %
9450 %                                                                             %
9451 %                                                                             %
9452 %                                                                             %
9453 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9454 %
9455 %  XMagnifyWindowCommand() moves the image within an Magnify window by one
9456 %  pixel as specified by the key symbol.
9457 %
9458 %  The format of the XMagnifyWindowCommand method is:
9459 %
9460 %      void XMagnifyWindowCommand(Display *display,XWindows *windows,
9461 %        const MagickStatusType state,const KeySym key_symbol,
9462 %        ExceptionInfo *exception)
9463 %
9464 %  A description of each parameter follows:
9465 %
9466 %    o display: Specifies a connection to an X server; returned from
9467 %      XOpenDisplay.
9468 %
9469 %    o windows: Specifies a pointer to a XWindows structure.
9470 %
9471 %    o state: key mask.
9472 %
9473 %    o key_symbol: Specifies a KeySym which indicates which side of the image
9474 %      to trim.
9475 %
9476 %    o exception: return any errors or warnings in this structure.
9477 %
9478 */
9479 static void XMagnifyWindowCommand(Display *display,XWindows *windows,
9480   const MagickStatusType state,const KeySym key_symbol,ExceptionInfo *exception)
9481 {
9482   unsigned int
9483     quantum;
9484
9485   /*
9486     User specified a magnify factor or position.
9487   */
9488   quantum=1;
9489   if ((state & Mod1Mask) != 0)
9490     quantum=10;
9491   switch ((int) key_symbol)
9492   {
9493     case QuitCommand:
9494     {
9495       (void) XWithdrawWindow(display,windows->magnify.id,
9496         windows->magnify.screen);
9497       break;
9498     }
9499     case XK_Home:
9500     case XK_KP_Home:
9501     {
9502       windows->magnify.x=(int) windows->image.width/2;
9503       windows->magnify.y=(int) windows->image.height/2;
9504       break;
9505     }
9506     case XK_Left:
9507     case XK_KP_Left:
9508     {
9509       if (windows->magnify.x > 0)
9510         windows->magnify.x-=quantum;
9511       break;
9512     }
9513     case XK_Up:
9514     case XK_KP_Up:
9515     {
9516       if (windows->magnify.y > 0)
9517         windows->magnify.y-=quantum;
9518       break;
9519     }
9520     case XK_Right:
9521     case XK_KP_Right:
9522     {
9523       if (windows->magnify.x < (int) (windows->image.ximage->width-1))
9524         windows->magnify.x+=quantum;
9525       break;
9526     }
9527     case XK_Down:
9528     case XK_KP_Down:
9529     {
9530       if (windows->magnify.y < (int) (windows->image.ximage->height-1))
9531         windows->magnify.y+=quantum;
9532       break;
9533     }
9534     case XK_0:
9535     case XK_1:
9536     case XK_2:
9537     case XK_3:
9538     case XK_4:
9539     case XK_5:
9540     case XK_6:
9541     case XK_7:
9542     case XK_8:
9543     case XK_9:
9544     {
9545       windows->magnify.data=(key_symbol-XK_0);
9546       break;
9547     }
9548     case XK_KP_0:
9549     case XK_KP_1:
9550     case XK_KP_2:
9551     case XK_KP_3:
9552     case XK_KP_4:
9553     case XK_KP_5:
9554     case XK_KP_6:
9555     case XK_KP_7:
9556     case XK_KP_8:
9557     case XK_KP_9:
9558     {
9559       windows->magnify.data=(key_symbol-XK_KP_0);
9560       break;
9561     }
9562     default:
9563       break;
9564   }
9565   XMakeMagnifyImage(display,windows,exception);
9566 }
9567 \f
9568 /*
9569 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9570 %                                                                             %
9571 %                                                                             %
9572 %                                                                             %
9573 +   X M a k e P a n I m a g e                                                 %
9574 %                                                                             %
9575 %                                                                             %
9576 %                                                                             %
9577 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9578 %
9579 %  XMakePanImage() creates a thumbnail of the image and displays it in the Pan
9580 %  icon window.
9581 %
9582 %  The format of the XMakePanImage method is:
9583 %
9584 %        void XMakePanImage(Display *display,XResourceInfo *resource_info,
9585 %          XWindows *windows,Image *image,ExceptionInfo *exception)
9586 %
9587 %  A description of each parameter follows:
9588 %
9589 %    o display: Specifies a connection to an X server;  returned from
9590 %      XOpenDisplay.
9591 %
9592 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9593 %
9594 %    o windows: Specifies a pointer to a XWindows structure.
9595 %
9596 %    o image: the image.
9597 %
9598 %    o exception: return any errors or warnings in this structure.
9599 %
9600 */
9601 static void XMakePanImage(Display *display,XResourceInfo *resource_info,
9602   XWindows *windows,Image *image,ExceptionInfo *exception)
9603 {
9604   MagickStatusType
9605     status;
9606
9607   /*
9608     Create and display image for panning icon.
9609   */
9610   XSetCursorState(display,windows,MagickTrue);
9611   XCheckRefreshWindows(display,windows);
9612   windows->pan.x=(int) windows->image.x;
9613   windows->pan.y=(int) windows->image.y;
9614   status=XMakeImage(display,resource_info,&windows->pan,image,
9615     windows->pan.width,windows->pan.height,exception);
9616   if( IfMagickFalse(status) )
9617     ThrowXWindowFatalException(ResourceLimitError,
9618      "MemoryAllocationFailed",image->filename);
9619   (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
9620     windows->pan.pixmap);
9621   (void) XClearWindow(display,windows->pan.id);
9622   XDrawPanRectangle(display,windows);
9623   XSetCursorState(display,windows,MagickFalse);
9624 }
9625 \f
9626 /*
9627 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9628 %                                                                             %
9629 %                                                                             %
9630 %                                                                             %
9631 +   X M a t t a E d i t I m a g e                                             %
9632 %                                                                             %
9633 %                                                                             %
9634 %                                                                             %
9635 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9636 %
9637 %  XMatteEditImage() allows the user to interactively change the Matte channel
9638 %  of an image.  If the image is PseudoClass it is promoted to DirectClass
9639 %  before the matte information is stored.
9640 %
9641 %  The format of the XMatteEditImage method is:
9642 %
9643 %      MagickBooleanType XMatteEditImage(Display *display,
9644 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
9645 %        ExceptionInfo *exception)
9646 %
9647 %  A description of each parameter follows:
9648 %
9649 %    o display: Specifies a connection to an X server;  returned from
9650 %      XOpenDisplay.
9651 %
9652 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9653 %
9654 %    o windows: Specifies a pointer to a XWindows structure.
9655 %
9656 %    o image: the image; returned from ReadImage.
9657 %
9658 %    o exception: return any errors or warnings in this structure.
9659 %
9660 */
9661 static MagickBooleanType XMatteEditImage(Display *display,
9662   XResourceInfo *resource_info,XWindows *windows,Image **image,
9663   ExceptionInfo *exception)
9664 {
9665   static char
9666     matte[MaxTextExtent] = "0";
9667
9668   static const char
9669     *MatteEditMenu[] =
9670     {
9671       "Method",
9672       "Border Color",
9673       "Fuzz",
9674       "Matte Value",
9675       "Undo",
9676       "Help",
9677       "Dismiss",
9678       (char *) NULL
9679     };
9680
9681   static const ModeType
9682     MatteEditCommands[] =
9683     {
9684       MatteEditMethod,
9685       MatteEditBorderCommand,
9686       MatteEditFuzzCommand,
9687       MatteEditValueCommand,
9688       MatteEditUndoCommand,
9689       MatteEditHelpCommand,
9690       MatteEditDismissCommand
9691     };
9692
9693   static PaintMethod
9694     method = PointMethod;
9695
9696   static XColor
9697     border_color = { 0, 0, 0, 0, 0, 0 };
9698
9699   char
9700     command[MaxTextExtent],
9701     text[MaxTextExtent];
9702
9703   Cursor
9704     cursor;
9705
9706   int
9707     entry,
9708     id,
9709     x,
9710     x_offset,
9711     y,
9712     y_offset;
9713
9714   register int
9715     i;
9716
9717   register Quantum
9718     *q;
9719
9720   unsigned int
9721     height,
9722     width;
9723
9724   size_t
9725     state;
9726
9727   XEvent
9728     event;
9729
9730   /*
9731     Map Command widget.
9732   */
9733   (void) CloneString(&windows->command.name,"Matte Edit");
9734   windows->command.data=4;
9735   (void) XCommandWidget(display,windows,MatteEditMenu,(XEvent *) NULL);
9736   (void) XMapRaised(display,windows->command.id);
9737   XClientMessage(display,windows->image.id,windows->im_protocols,
9738     windows->im_update_widget,CurrentTime);
9739   /*
9740     Make cursor.
9741   */
9742   cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
9743     resource_info->background_color,resource_info->foreground_color);
9744   (void) XCheckDefineCursor(display,windows->image.id,cursor);
9745   /*
9746     Track pointer until button 1 is pressed.
9747   */
9748   XQueryPosition(display,windows->image.id,&x,&y);
9749   (void) XSelectInput(display,windows->image.id,
9750     windows->image.attributes.event_mask | PointerMotionMask);
9751   state=DefaultState;
9752   do
9753   {
9754     if( IfMagickTrue(windows->info.mapped) )
9755       {
9756         /*
9757           Display pointer position.
9758         */
9759         (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
9760           x+windows->image.x,y+windows->image.y);
9761         XInfoWidget(display,windows,text);
9762       }
9763     /*
9764       Wait for next event.
9765     */
9766     XScreenEvent(display,windows,&event,exception);
9767     if (event.xany.window == windows->command.id)
9768       {
9769         /*
9770           Select a command from the Command widget.
9771         */
9772         id=XCommandWidget(display,windows,MatteEditMenu,&event);
9773         if (id < 0)
9774           {
9775             (void) XCheckDefineCursor(display,windows->image.id,cursor);
9776             continue;
9777           }
9778         switch (MatteEditCommands[id])
9779         {
9780           case MatteEditMethod:
9781           {
9782             char
9783               **methods;
9784
9785             /*
9786               Select a method from the pop-up menu.
9787             */
9788             methods=GetCommandOptions(MagickMethodOptions);
9789             if (methods == (char **) NULL)
9790               break;
9791             entry=XMenuWidget(display,windows,MatteEditMenu[id],
9792               (const char **) methods,command);
9793             if (entry >= 0)
9794               method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
9795                 MagickFalse,methods[entry]);
9796             methods=DestroyStringList(methods);
9797             break;
9798           }
9799           case MatteEditBorderCommand:
9800           {
9801             const char
9802               *ColorMenu[MaxNumberPens];
9803
9804             int
9805               pen_number;
9806
9807             /*
9808               Initialize menu selections.
9809             */
9810             for (i=0; i < (int) (MaxNumberPens-2); i++)
9811               ColorMenu[i]=resource_info->pen_colors[i];
9812             ColorMenu[MaxNumberPens-2]="Browser...";
9813             ColorMenu[MaxNumberPens-1]=(const char *) NULL;
9814             /*
9815               Select a pen color from the pop-up menu.
9816             */
9817             pen_number=XMenuWidget(display,windows,MatteEditMenu[id],
9818               (const char **) ColorMenu,command);
9819             if (pen_number < 0)
9820               break;
9821             if (pen_number == (MaxNumberPens-2))
9822               {
9823                 static char
9824                   color_name[MaxTextExtent] = "gray";
9825
9826                 /*
9827                   Select a pen color from a dialog.
9828                 */
9829                 resource_info->pen_colors[pen_number]=color_name;
9830                 XColorBrowserWidget(display,windows,"Select",color_name);
9831                 if (*color_name == '\0')
9832                   break;
9833               }
9834             /*
9835               Set border color.
9836             */
9837             (void) XParseColor(display,windows->map_info->colormap,
9838               resource_info->pen_colors[pen_number],&border_color);
9839             break;
9840           }
9841           case MatteEditFuzzCommand:
9842           {
9843             static char
9844               fuzz[MaxTextExtent];
9845
9846             static const char
9847               *FuzzMenu[] =
9848               {
9849                 "0%",
9850                 "2%",
9851                 "5%",
9852                 "10%",
9853                 "15%",
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],FuzzMenu,
9862               command);
9863             if (entry < 0)
9864               break;
9865             if (entry != 5)
9866               {
9867                 (*image)->fuzz=StringToDoubleInterval(FuzzMenu[entry],(double)
9868                   QuantumRange+1.0);
9869                 break;
9870               }
9871             (void) CopyMagickString(fuzz,"20%",MaxTextExtent);
9872             (void) XDialogWidget(display,windows,"Ok",
9873               "Enter fuzz factor (0.0 - 99.9%):",fuzz);
9874             if (*fuzz == '\0')
9875               break;
9876             (void) ConcatenateMagickString(fuzz,"%",MaxTextExtent);
9877             (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+
9878               1.0);
9879             break;
9880           }
9881           case MatteEditValueCommand:
9882           {
9883             static char
9884               message[MaxTextExtent];
9885
9886             static const char
9887               *MatteMenu[] =
9888               {
9889                 "Opaque",
9890                 "Transparent",
9891                 "Dialog...",
9892                 (char *) NULL,
9893               };
9894
9895             /*
9896               Select a command from the pop-up menu.
9897             */
9898             entry=XMenuWidget(display,windows,MatteEditMenu[id],MatteMenu,
9899               command);
9900             if (entry < 0)
9901               break;
9902             if (entry != 2)
9903               {
9904                 (void) FormatLocaleString(matte,MaxTextExtent,QuantumFormat,
9905                   OpaqueAlpha);
9906                 if (LocaleCompare(MatteMenu[entry],"Transparent") == 0)
9907                   (void) FormatLocaleString(matte,MaxTextExtent,QuantumFormat,
9908                     (Quantum) TransparentAlpha);
9909                 break;
9910               }
9911             (void) FormatLocaleString(message,MaxTextExtent,
9912               "Enter matte value (0 - " QuantumFormat "):",(Quantum)
9913               QuantumRange);
9914             (void) XDialogWidget(display,windows,"Matte",message,matte);
9915             if (*matte == '\0')
9916               break;
9917             break;
9918           }
9919           case MatteEditUndoCommand:
9920           {
9921             (void) XMagickCommand(display,resource_info,windows,UndoCommand,
9922               image,exception);
9923             break;
9924           }
9925           case MatteEditHelpCommand:
9926           {
9927             XTextViewWidget(display,resource_info,windows,MagickFalse,
9928               "Help Viewer - Matte Edit",ImageMatteEditHelp);
9929             break;
9930           }
9931           case MatteEditDismissCommand:
9932           {
9933             /*
9934               Prematurely exit.
9935             */
9936             state|=EscapeState;
9937             state|=ExitState;
9938             break;
9939           }
9940           default:
9941             break;
9942         }
9943         (void) XCheckDefineCursor(display,windows->image.id,cursor);
9944         continue;
9945       }
9946     switch (event.type)
9947     {
9948       case ButtonPress:
9949       {
9950         if (event.xbutton.button != Button1)
9951           break;
9952         if ((event.xbutton.window != windows->image.id) &&
9953             (event.xbutton.window != windows->magnify.id))
9954           break;
9955         /*
9956           Update matte data.
9957         */
9958         x=event.xbutton.x;
9959         y=event.xbutton.y;
9960         (void) XMagickCommand(display,resource_info,windows,
9961           SaveToUndoBufferCommand,image,exception);
9962         state|=UpdateConfigurationState;
9963         break;
9964       }
9965       case ButtonRelease:
9966       {
9967         if (event.xbutton.button != Button1)
9968           break;
9969         if ((event.xbutton.window != windows->image.id) &&
9970             (event.xbutton.window != windows->magnify.id))
9971           break;
9972         /*
9973           Update colormap information.
9974         */
9975         x=event.xbutton.x;
9976         y=event.xbutton.y;
9977         XConfigureImageColormap(display,resource_info,windows,*image,exception);
9978         (void) XConfigureImage(display,resource_info,windows,*image,exception);
9979         XInfoWidget(display,windows,text);
9980         (void) XCheckDefineCursor(display,windows->image.id,cursor);
9981         state&=(~UpdateConfigurationState);
9982         break;
9983       }
9984       case Expose:
9985         break;
9986       case KeyPress:
9987       {
9988         char
9989           command[MaxTextExtent];
9990
9991         KeySym
9992           key_symbol;
9993
9994         if (event.xkey.window == windows->magnify.id)
9995           {
9996             Window
9997               window;
9998
9999             window=windows->magnify.id;
10000             while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
10001           }
10002         if (event.xkey.window != windows->image.id)
10003           break;
10004         /*
10005           Respond to a user key press.
10006         */
10007         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
10008           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
10009         switch ((int) key_symbol)
10010         {
10011           case XK_Escape:
10012           case XK_F20:
10013           {
10014             /*
10015               Prematurely exit.
10016             */
10017             state|=ExitState;
10018             break;
10019           }
10020           case XK_F1:
10021           case XK_Help:
10022           {
10023             XTextViewWidget(display,resource_info,windows,MagickFalse,
10024               "Help Viewer - Matte Edit",ImageMatteEditHelp);
10025             break;
10026           }
10027           default:
10028           {
10029             (void) XBell(display,0);
10030             break;
10031           }
10032         }
10033         break;
10034       }
10035       case MotionNotify:
10036       {
10037         /*
10038           Map and unmap Info widget as cursor crosses its boundaries.
10039         */
10040         x=event.xmotion.x;
10041         y=event.xmotion.y;
10042         if( IfMagickTrue(windows->info.mapped) )
10043           {
10044             if ((x < (int) (windows->info.x+windows->info.width)) &&
10045                 (y < (int) (windows->info.y+windows->info.height)))
10046               (void) XWithdrawWindow(display,windows->info.id,
10047                 windows->info.screen);
10048           }
10049         else
10050           if ((x > (int) (windows->info.x+windows->info.width)) ||
10051               (y > (int) (windows->info.y+windows->info.height)))
10052             (void) XMapWindow(display,windows->info.id);
10053         break;
10054       }
10055       default:
10056         break;
10057     }
10058     if (event.xany.window == windows->magnify.id)
10059       {
10060         x=windows->magnify.x-windows->image.x;
10061         y=windows->magnify.y-windows->image.y;
10062       }
10063     x_offset=x;
10064     y_offset=y;
10065     if ((state & UpdateConfigurationState) != 0)
10066       {
10067         CacheView
10068           *image_view;
10069
10070         int
10071           x,
10072           y;
10073
10074         /*
10075           Matte edit is relative to image configuration.
10076         */
10077         (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
10078           MagickTrue);
10079         XPutPixel(windows->image.ximage,x_offset,y_offset,
10080           windows->pixel_info->background_color.pixel);
10081         width=(unsigned int) (*image)->columns;
10082         height=(unsigned int) (*image)->rows;
10083         x=0;
10084         y=0;
10085         if (windows->image.crop_geometry != (char *) NULL)
10086           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,
10087             &height);
10088         x_offset=(int) (width*(windows->image.x+x_offset)/
10089           windows->image.ximage->width+x);
10090         y_offset=(int) (height*(windows->image.y+y_offset)/
10091           windows->image.ximage->height+y);
10092         if ((x_offset < 0) || (y_offset < 0))
10093           continue;
10094         if ((x_offset >= (int) (*image)->columns) ||
10095             (y_offset >= (int) (*image)->rows))
10096           continue;
10097         if( IfMagickFalse(SetImageStorageClass(*image,DirectClass,exception)) )
10098           return(MagickFalse);
10099         if ((*image)->alpha_trait != BlendPixelTrait)
10100           (void) SetImageAlphaChannel(*image,OpaqueAlphaChannel,exception);
10101         image_view=AcquireAuthenticCacheView(*image,exception);
10102         switch (method)
10103         {
10104           case PointMethod:
10105           default:
10106           {
10107             /*
10108               Update matte information using point algorithm.
10109             */
10110             q=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,
10111               (ssize_t) y_offset,1,1,exception);
10112             if (q == (Quantum *) NULL)
10113               break;
10114             SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10115             (void) SyncCacheViewAuthenticPixels(image_view,exception);
10116             break;
10117           }
10118           case ReplaceMethod:
10119           {
10120             PixelInfo
10121               pixel,
10122               target;
10123
10124             /*
10125               Update matte information using replace algorithm.
10126             */
10127             (void) GetOneCacheViewVirtualPixelInfo(image_view,(ssize_t)
10128               x_offset,(ssize_t) y_offset,&target,exception);
10129             for (y=0; y < (int) (*image)->rows; y++)
10130             {
10131               q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10132                 (*image)->columns,1,exception);
10133               if (q == (Quantum *) NULL)
10134                 break;
10135               for (x=0; x < (int) (*image)->columns; x++)
10136               {
10137                 GetPixelInfoPixel(*image,q,&pixel);
10138                 if (IsFuzzyEquivalencePixelInfo(&pixel,&target))
10139                   SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10140                 q+=GetPixelChannels(*image);
10141               }
10142               if( IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
10143                 break;
10144             }
10145             break;
10146           }
10147           case FloodfillMethod:
10148           case FillToBorderMethod:
10149           {
10150             ChannelType
10151               channel_mask;
10152
10153             DrawInfo
10154               *draw_info;
10155
10156             PixelInfo
10157               target;
10158
10159             /*
10160               Update matte information using floodfill algorithm.
10161             */
10162             (void) GetOneVirtualPixelInfo(*image,
10163               GetPixelCacheVirtualMethod(*image),(ssize_t) x_offset,(ssize_t)
10164               y_offset,&target,exception);
10165             if (method == FillToBorderMethod)
10166               {
10167                 target.red=(double) ScaleShortToQuantum(
10168                   border_color.red);
10169                 target.green=(double) ScaleShortToQuantum(
10170                   border_color.green);
10171                 target.blue=(double) ScaleShortToQuantum(
10172                   border_color.blue);
10173               }
10174             draw_info=CloneDrawInfo(resource_info->image_info,
10175               (DrawInfo *) NULL);
10176             draw_info->fill.alpha=(double) ClampToQuantum(
10177               StringToDouble(matte,(char **) NULL));
10178             channel_mask=SetImageChannelMask(*image,AlphaChannel);
10179             (void) FloodfillPaintImage(*image,draw_info,&target,(ssize_t)
10180               x_offset,(ssize_t) y_offset,
10181               IsMagickFalse(method == FloodfillMethod),exception);
10182             (void) SetPixelChannelMask(*image,channel_mask);
10183             draw_info=DestroyDrawInfo(draw_info);
10184             break;
10185           }
10186           case ResetMethod:
10187           {
10188             /*
10189               Update matte information using reset algorithm.
10190             */
10191             if( IfMagickFalse(SetImageStorageClass(*image,DirectClass,exception)) )
10192               return(MagickFalse);
10193             for (y=0; y < (int) (*image)->rows; y++)
10194             {
10195               q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10196                 (*image)->columns,1,exception);
10197               if (q == (Quantum *) NULL)
10198                 break;
10199               for (x=0; x < (int) (*image)->columns; x++)
10200               {
10201                 SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10202                 q+=GetPixelChannels(*image);
10203               }
10204               if( IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
10205                 break;
10206             }
10207             if (StringToLong(matte) == (long) OpaqueAlpha)
10208               (*image)->alpha_trait=UndefinedPixelTrait;
10209             break;
10210           }
10211         }
10212         image_view=DestroyCacheView(image_view);
10213         state&=(~UpdateConfigurationState);
10214       }
10215   } while ((state & ExitState) == 0);
10216   (void) XSelectInput(display,windows->image.id,
10217     windows->image.attributes.event_mask);
10218   XSetCursorState(display,windows,MagickFalse);
10219   (void) XFreeCursor(display,cursor);
10220   return(MagickTrue);
10221 }
10222 \f
10223 /*
10224 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10225 %                                                                             %
10226 %                                                                             %
10227 %                                                                             %
10228 +   X O p e n I m a g e                                                       %
10229 %                                                                             %
10230 %                                                                             %
10231 %                                                                             %
10232 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10233 %
10234 %  XOpenImage() loads an image from a file.
10235 %
10236 %  The format of the XOpenImage method is:
10237 %
10238 %     Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10239 %       XWindows *windows,const unsigned int command)
10240 %
10241 %  A description of each parameter follows:
10242 %
10243 %    o display: Specifies a connection to an X server; returned from
10244 %      XOpenDisplay.
10245 %
10246 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10247 %
10248 %    o windows: Specifies a pointer to a XWindows structure.
10249 %
10250 %    o command: A value other than zero indicates that the file is selected
10251 %      from the command line argument list.
10252 %
10253 */
10254 static Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10255   XWindows *windows,const MagickBooleanType command)
10256 {
10257   const MagickInfo
10258     *magick_info;
10259
10260   ExceptionInfo
10261     *exception;
10262
10263   Image
10264     *nexus;
10265
10266   ImageInfo
10267     *image_info;
10268
10269   static char
10270     filename[MaxTextExtent] = "\0";
10271
10272   /*
10273     Request file name from user.
10274   */
10275   if( IfMagickFalse(command) )
10276     XFileBrowserWidget(display,windows,"Open",filename);
10277   else
10278     {
10279       char
10280         **filelist,
10281         **files;
10282
10283       int
10284         count,
10285         status;
10286
10287       register int
10288         i,
10289         j;
10290
10291       /*
10292         Select next image from the command line.
10293       */
10294       status=XGetCommand(display,windows->image.id,&files,&count);
10295       if (status == 0)
10296         {
10297           ThrowXWindowFatalException(XServerError,"UnableToGetProperty","...");
10298           return((Image *) NULL);
10299         }
10300       filelist=(char **) AcquireQuantumMemory((size_t) count,sizeof(*filelist));
10301       if (filelist == (char **) NULL)
10302         {
10303           ThrowXWindowFatalException(ResourceLimitError,
10304             "MemoryAllocationFailed","...");
10305           (void) XFreeStringList(files);
10306           return((Image *) NULL);
10307         }
10308       j=0;
10309       for (i=1; i < count; i++)
10310         if (*files[i] != '-')
10311           filelist[j++]=files[i];
10312       filelist[j]=(char *) NULL;
10313       XListBrowserWidget(display,windows,&windows->widget,
10314         (const char **) filelist,"Load","Select Image to Load:",filename);
10315       filelist=(char **) RelinquishMagickMemory(filelist);
10316       (void) XFreeStringList(files);
10317     }
10318   if (*filename == '\0')
10319     return((Image *) NULL);
10320   image_info=CloneImageInfo(resource_info->image_info);
10321   (void) SetImageInfoProgressMonitor(image_info,(MagickProgressMonitor) NULL,
10322     (void *) NULL);
10323   (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
10324   exception=AcquireExceptionInfo();
10325   (void) SetImageInfo(image_info,0,exception);
10326   if (LocaleCompare(image_info->magick,"X") == 0)
10327     {
10328       char
10329         seconds[MaxTextExtent];
10330
10331       /*
10332         User may want to delay the X server screen grab.
10333       */
10334       (void) CopyMagickString(seconds,"0",MaxTextExtent);
10335       (void) XDialogWidget(display,windows,"Grab","Enter any delay in seconds:",
10336         seconds);
10337       if (*seconds == '\0')
10338         return((Image *) NULL);
10339       XDelay(display,(size_t) (1000*StringToLong(seconds)));
10340     }
10341   magick_info=GetMagickInfo(image_info->magick,exception);
10342   if ((magick_info != (const MagickInfo *) NULL) &&
10343       IfMagickTrue(magick_info->raw))
10344     {
10345       char
10346         geometry[MaxTextExtent];
10347
10348       /*
10349         Request image size from the user.
10350       */
10351       (void) CopyMagickString(geometry,"512x512",MaxTextExtent);
10352       if (image_info->size != (char *) NULL)
10353         (void) CopyMagickString(geometry,image_info->size,MaxTextExtent);
10354       (void) XDialogWidget(display,windows,"Load","Enter the image geometry:",
10355         geometry);
10356       (void) CloneString(&image_info->size,geometry);
10357     }
10358   /*
10359     Load the image.
10360   */
10361   XSetCursorState(display,windows,MagickTrue);
10362   XCheckRefreshWindows(display,windows);
10363   (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
10364   nexus=ReadImage(image_info,exception);
10365   CatchException(exception);
10366   XSetCursorState(display,windows,MagickFalse);
10367   if (nexus != (Image *) NULL)
10368     XClientMessage(display,windows->image.id,windows->im_protocols,
10369       windows->im_next_image,CurrentTime);
10370   else
10371     {
10372       char
10373         *text,
10374         **textlist;
10375
10376       /*
10377         Unknown image format.
10378       */
10379       text=FileToString(filename,~0,exception);
10380       if (text == (char *) NULL)
10381         return((Image *) NULL);
10382       textlist=StringToList(text);
10383       if (textlist != (char **) NULL)
10384         {
10385           char
10386             title[MaxTextExtent];
10387
10388           register int
10389             i;
10390
10391           (void) FormatLocaleString(title,MaxTextExtent,
10392             "Unknown format: %s",filename);
10393           XTextViewWidget(display,resource_info,windows,MagickTrue,title,
10394             (const char **) textlist);
10395           for (i=0; textlist[i] != (char *) NULL; i++)
10396             textlist[i]=DestroyString(textlist[i]);
10397           textlist=(char **) RelinquishMagickMemory(textlist);
10398         }
10399       text=DestroyString(text);
10400     }
10401   exception=DestroyExceptionInfo(exception);
10402   image_info=DestroyImageInfo(image_info);
10403   return(nexus);
10404 }
10405 \f
10406 /*
10407 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10408 %                                                                             %
10409 %                                                                             %
10410 %                                                                             %
10411 +   X P a n I m a g e                                                         %
10412 %                                                                             %
10413 %                                                                             %
10414 %                                                                             %
10415 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10416 %
10417 %  XPanImage() pans the image until the mouse button is released.
10418 %
10419 %  The format of the XPanImage method is:
10420 %
10421 %      void XPanImage(Display *display,XWindows *windows,XEvent *event,
10422 %        ExceptionInfo *exception)
10423 %
10424 %  A description of each parameter follows:
10425 %
10426 %    o display: Specifies a connection to an X server;  returned from
10427 %      XOpenDisplay.
10428 %
10429 %    o windows: Specifies a pointer to a XWindows structure.
10430 %
10431 %    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
10432 %      the entire image is refreshed.
10433 %
10434 %    o exception: return any errors or warnings in this structure.
10435 %
10436 */
10437 static void XPanImage(Display *display,XWindows *windows,XEvent *event,
10438   ExceptionInfo *exception)
10439 {
10440   char
10441     text[MaxTextExtent];
10442
10443   Cursor
10444     cursor;
10445
10446   double
10447     x_factor,
10448     y_factor;
10449
10450   RectangleInfo
10451     pan_info;
10452
10453   size_t
10454     state;
10455
10456   /*
10457     Define cursor.
10458   */
10459   if ((windows->image.ximage->width > (int) windows->image.width) &&
10460       (windows->image.ximage->height > (int) windows->image.height))
10461     cursor=XCreateFontCursor(display,XC_fleur);
10462   else
10463     if (windows->image.ximage->width > (int) windows->image.width)
10464       cursor=XCreateFontCursor(display,XC_sb_h_double_arrow);
10465     else
10466       if (windows->image.ximage->height > (int) windows->image.height)
10467         cursor=XCreateFontCursor(display,XC_sb_v_double_arrow);
10468       else
10469         cursor=XCreateFontCursor(display,XC_arrow);
10470   (void) XCheckDefineCursor(display,windows->pan.id,cursor);
10471   /*
10472     Pan image as pointer moves until the mouse button is released.
10473   */
10474   x_factor=(double) windows->image.ximage->width/windows->pan.width;
10475   y_factor=(double) windows->image.ximage->height/windows->pan.height;
10476   pan_info.width=windows->pan.width*windows->image.width/
10477     windows->image.ximage->width;
10478   pan_info.height=windows->pan.height*windows->image.height/
10479     windows->image.ximage->height;
10480   pan_info.x=0;
10481   pan_info.y=0;
10482   state=UpdateConfigurationState;
10483   do
10484   {
10485     switch (event->type)
10486     {
10487       case ButtonPress:
10488       {
10489         /*
10490           User choose an initial pan location.
10491         */
10492         pan_info.x=(ssize_t) event->xbutton.x;
10493         pan_info.y=(ssize_t) event->xbutton.y;
10494         state|=UpdateConfigurationState;
10495         break;
10496       }
10497       case ButtonRelease:
10498       {
10499         /*
10500           User has finished panning the image.
10501         */
10502         pan_info.x=(ssize_t) event->xbutton.x;
10503         pan_info.y=(ssize_t) event->xbutton.y;
10504         state|=UpdateConfigurationState | ExitState;
10505         break;
10506       }
10507       case MotionNotify:
10508       {
10509         pan_info.x=(ssize_t) event->xmotion.x;
10510         pan_info.y=(ssize_t) event->xmotion.y;
10511         state|=UpdateConfigurationState;
10512       }
10513       default:
10514         break;
10515     }
10516     if ((state & UpdateConfigurationState) != 0)
10517       {
10518         /*
10519           Check boundary conditions.
10520         */
10521         if (pan_info.x < (ssize_t) (pan_info.width/2))
10522           pan_info.x=0;
10523         else
10524           pan_info.x=(ssize_t) (x_factor*(pan_info.x-(pan_info.width/2)));
10525         if (pan_info.x < 0)
10526           pan_info.x=0;
10527         else
10528           if ((int) (pan_info.x+windows->image.width) >
10529               windows->image.ximage->width)
10530             pan_info.x=(ssize_t)
10531               (windows->image.ximage->width-windows->image.width);
10532         if (pan_info.y < (ssize_t) (pan_info.height/2))
10533           pan_info.y=0;
10534         else
10535           pan_info.y=(ssize_t) (y_factor*(pan_info.y-(pan_info.height/2)));
10536         if (pan_info.y < 0)
10537           pan_info.y=0;
10538         else
10539           if ((int) (pan_info.y+windows->image.height) >
10540               windows->image.ximage->height)
10541             pan_info.y=(ssize_t)
10542               (windows->image.ximage->height-windows->image.height);
10543         if ((windows->image.x != (int) pan_info.x) ||
10544             (windows->image.y != (int) pan_info.y))
10545           {
10546             /*
10547               Display image pan offset.
10548             */
10549             windows->image.x=(int) pan_info.x;
10550             windows->image.y=(int) pan_info.y;
10551             (void) FormatLocaleString(text,MaxTextExtent," %ux%u%+d%+d ",
10552               windows->image.width,windows->image.height,windows->image.x,
10553               windows->image.y);
10554             XInfoWidget(display,windows,text);
10555             /*
10556               Refresh Image window.
10557             */
10558             XDrawPanRectangle(display,windows);
10559             XRefreshWindow(display,&windows->image,(XEvent *) NULL);
10560           }
10561         state&=(~UpdateConfigurationState);
10562       }
10563     /*
10564       Wait for next event.
10565     */
10566     if ((state & ExitState) == 0)
10567       XScreenEvent(display,windows,event,exception);
10568   } while ((state & ExitState) == 0);
10569   /*
10570     Restore cursor.
10571   */
10572   (void) XCheckDefineCursor(display,windows->pan.id,windows->pan.cursor);
10573   (void) XFreeCursor(display,cursor);
10574   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
10575 }
10576 \f
10577 /*
10578 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10579 %                                                                             %
10580 %                                                                             %
10581 %                                                                             %
10582 +   X P a s t e I m a g e                                                     %
10583 %                                                                             %
10584 %                                                                             %
10585 %                                                                             %
10586 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10587 %
10588 %  XPasteImage() pastes an image previously saved with XCropImage in the X
10589 %  window image at a location the user chooses with the pointer.
10590 %
10591 %  The format of the XPasteImage method is:
10592 %
10593 %      MagickBooleanType XPasteImage(Display *display,
10594 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
10595 %        ExceptionInfo *exception)
10596 %
10597 %  A description of each parameter follows:
10598 %
10599 %    o display: Specifies a connection to an X server;  returned from
10600 %      XOpenDisplay.
10601 %
10602 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10603 %
10604 %    o windows: Specifies a pointer to a XWindows structure.
10605 %
10606 %    o image: the image; returned from ReadImage.
10607 %
10608 %    o exception: return any errors or warnings in this structure.
10609 %
10610 */
10611 static MagickBooleanType XPasteImage(Display *display,
10612   XResourceInfo *resource_info,XWindows *windows,Image *image,
10613   ExceptionInfo *exception)
10614 {
10615   static const char
10616     *PasteMenu[] =
10617     {
10618       "Operator",
10619       "Help",
10620       "Dismiss",
10621       (char *) NULL
10622     };
10623
10624   static const ModeType
10625     PasteCommands[] =
10626     {
10627       PasteOperatorsCommand,
10628       PasteHelpCommand,
10629       PasteDismissCommand
10630     };
10631
10632   static CompositeOperator
10633     compose = CopyCompositeOp;
10634
10635   char
10636     text[MaxTextExtent];
10637
10638   Cursor
10639     cursor;
10640
10641   Image
10642     *paste_image;
10643
10644   int
10645     entry,
10646     id,
10647     x,
10648     y;
10649
10650   double
10651     scale_factor;
10652
10653   RectangleInfo
10654     highlight_info,
10655     paste_info;
10656
10657   unsigned int
10658     height,
10659     width;
10660
10661   size_t
10662     state;
10663
10664   XEvent
10665     event;
10666
10667   /*
10668     Copy image.
10669   */
10670   if (resource_info->copy_image == (Image *) NULL)
10671     return(MagickFalse);
10672   paste_image=CloneImage(resource_info->copy_image,0,0,MagickTrue,exception);
10673   /*
10674     Map Command widget.
10675   */
10676   (void) CloneString(&windows->command.name,"Paste");
10677   windows->command.data=1;
10678   (void) XCommandWidget(display,windows,PasteMenu,(XEvent *) NULL);
10679   (void) XMapRaised(display,windows->command.id);
10680   XClientMessage(display,windows->image.id,windows->im_protocols,
10681     windows->im_update_widget,CurrentTime);
10682   /*
10683     Track pointer until button 1 is pressed.
10684   */
10685   XSetCursorState(display,windows,MagickFalse);
10686   XQueryPosition(display,windows->image.id,&x,&y);
10687   (void) XSelectInput(display,windows->image.id,
10688     windows->image.attributes.event_mask | PointerMotionMask);
10689   paste_info.x=(ssize_t) windows->image.x+x;
10690   paste_info.y=(ssize_t) windows->image.y+y;
10691   paste_info.width=0;
10692   paste_info.height=0;
10693   cursor=XCreateFontCursor(display,XC_ul_angle);
10694   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
10695   state=DefaultState;
10696   do
10697   {
10698     if( IfMagickTrue(windows->info.mapped) )
10699       {
10700         /*
10701           Display pointer position.
10702         */
10703         (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
10704           (long) paste_info.x,(long) paste_info.y);
10705         XInfoWidget(display,windows,text);
10706       }
10707     highlight_info=paste_info;
10708     highlight_info.x=paste_info.x-windows->image.x;
10709     highlight_info.y=paste_info.y-windows->image.y;
10710     XHighlightRectangle(display,windows->image.id,
10711       windows->image.highlight_context,&highlight_info);
10712     /*
10713       Wait for next event.
10714     */
10715     XScreenEvent(display,windows,&event,exception);
10716     XHighlightRectangle(display,windows->image.id,
10717       windows->image.highlight_context,&highlight_info);
10718     if (event.xany.window == windows->command.id)
10719       {
10720         /*
10721           Select a command from the Command widget.
10722         */
10723         id=XCommandWidget(display,windows,PasteMenu,&event);
10724         if (id < 0)
10725           continue;
10726         switch (PasteCommands[id])
10727         {
10728           case PasteOperatorsCommand:
10729           {
10730             char
10731               command[MaxTextExtent],
10732               **operators;
10733
10734             /*
10735               Select a command from the pop-up menu.
10736             */
10737             operators=GetCommandOptions(MagickComposeOptions);
10738             if (operators == (char **) NULL)
10739               break;
10740             entry=XMenuWidget(display,windows,PasteMenu[id],
10741               (const char **) operators,command);
10742             if (entry >= 0)
10743               compose=(CompositeOperator) ParseCommandOption(
10744                 MagickComposeOptions,MagickFalse,operators[entry]);
10745             operators=DestroyStringList(operators);
10746             break;
10747           }
10748           case PasteHelpCommand:
10749           {
10750             XTextViewWidget(display,resource_info,windows,MagickFalse,
10751               "Help Viewer - Image Composite",ImagePasteHelp);
10752             break;
10753           }
10754           case PasteDismissCommand:
10755           {
10756             /*
10757               Prematurely exit.
10758             */
10759             state|=EscapeState;
10760             state|=ExitState;
10761             break;
10762           }
10763           default:
10764             break;
10765         }
10766         continue;
10767       }
10768     switch (event.type)
10769     {
10770       case ButtonPress:
10771       {
10772         if( IfMagickTrue(image->debug) )
10773           (void) LogMagickEvent(X11Event,GetMagickModule(),
10774             "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
10775             event.xbutton.button,event.xbutton.x,event.xbutton.y);
10776         if (event.xbutton.button != Button1)
10777           break;
10778         if (event.xbutton.window != windows->image.id)
10779           break;
10780         /*
10781           Paste rectangle is relative to image configuration.
10782         */
10783         width=(unsigned int) image->columns;
10784         height=(unsigned int) image->rows;
10785         x=0;
10786         y=0;
10787         if (windows->image.crop_geometry != (char *) NULL)
10788           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
10789             &width,&height);
10790         scale_factor=(double) windows->image.ximage->width/width;
10791         paste_info.width=(unsigned int) (scale_factor*paste_image->columns+0.5);
10792         scale_factor=(double) windows->image.ximage->height/height;
10793         paste_info.height=(unsigned int) (scale_factor*paste_image->rows+0.5);
10794         (void) XCheckDefineCursor(display,windows->image.id,cursor);
10795         paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10796         paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10797         break;
10798       }
10799       case ButtonRelease:
10800       {
10801         if( IfMagickTrue(image->debug) )
10802           (void) LogMagickEvent(X11Event,GetMagickModule(),
10803             "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
10804             event.xbutton.button,event.xbutton.x,event.xbutton.y);
10805         if (event.xbutton.button != Button1)
10806           break;
10807         if (event.xbutton.window != windows->image.id)
10808           break;
10809         if ((paste_info.width != 0) && (paste_info.height != 0))
10810           {
10811             /*
10812               User has selected the location of the paste image.
10813             */
10814             paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10815             paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10816             state|=ExitState;
10817           }
10818         break;
10819       }
10820       case Expose:
10821         break;
10822       case KeyPress:
10823       {
10824         char
10825           command[MaxTextExtent];
10826
10827         KeySym
10828           key_symbol;
10829
10830         int
10831           length;
10832
10833         if (event.xkey.window != windows->image.id)
10834           break;
10835         /*
10836           Respond to a user key press.
10837         */
10838         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
10839           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
10840         *(command+length)='\0';
10841         if( IfMagickTrue(image->debug) )
10842           (void) LogMagickEvent(X11Event,GetMagickModule(),
10843             "Key press: 0x%lx (%s)",(long) key_symbol,command);
10844         switch ((int) key_symbol)
10845         {
10846           case XK_Escape:
10847           case XK_F20:
10848           {
10849             /*
10850               Prematurely exit.
10851             */
10852             paste_image=DestroyImage(paste_image);
10853             state|=EscapeState;
10854             state|=ExitState;
10855             break;
10856           }
10857           case XK_F1:
10858           case XK_Help:
10859           {
10860             (void) XSetFunction(display,windows->image.highlight_context,
10861               GXcopy);
10862             XTextViewWidget(display,resource_info,windows,MagickFalse,
10863               "Help Viewer - Image Composite",ImagePasteHelp);
10864             (void) XSetFunction(display,windows->image.highlight_context,
10865               GXinvert);
10866             break;
10867           }
10868           default:
10869           {
10870             (void) XBell(display,0);
10871             break;
10872           }
10873         }
10874         break;
10875       }
10876       case MotionNotify:
10877       {
10878         /*
10879           Map and unmap Info widget as text cursor crosses its boundaries.
10880         */
10881         x=event.xmotion.x;
10882         y=event.xmotion.y;
10883         if( IfMagickTrue(windows->info.mapped) )
10884           {
10885             if ((x < (int) (windows->info.x+windows->info.width)) &&
10886                 (y < (int) (windows->info.y+windows->info.height)))
10887               (void) XWithdrawWindow(display,windows->info.id,
10888                 windows->info.screen);
10889           }
10890         else
10891           if ((x > (int) (windows->info.x+windows->info.width)) ||
10892               (y > (int) (windows->info.y+windows->info.height)))
10893             (void) XMapWindow(display,windows->info.id);
10894         paste_info.x=(ssize_t) windows->image.x+x;
10895         paste_info.y=(ssize_t) windows->image.y+y;
10896         break;
10897       }
10898       default:
10899       {
10900         if( IfMagickTrue(image->debug) )
10901           (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
10902             event.type);
10903         break;
10904       }
10905     }
10906   } while ((state & ExitState) == 0);
10907   (void) XSelectInput(display,windows->image.id,
10908     windows->image.attributes.event_mask);
10909   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
10910   XSetCursorState(display,windows,MagickFalse);
10911   (void) XFreeCursor(display,cursor);
10912   if ((state & EscapeState) != 0)
10913     return(MagickTrue);
10914   /*
10915     Image pasting is relative to image configuration.
10916   */
10917   XSetCursorState(display,windows,MagickTrue);
10918   XCheckRefreshWindows(display,windows);
10919   width=(unsigned int) image->columns;
10920   height=(unsigned int) image->rows;
10921   x=0;
10922   y=0;
10923   if (windows->image.crop_geometry != (char *) NULL)
10924     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
10925   scale_factor=(double) width/windows->image.ximage->width;
10926   paste_info.x+=x;
10927   paste_info.x=(ssize_t) (scale_factor*paste_info.x+0.5);
10928   paste_info.width=(unsigned int) (scale_factor*paste_info.width+0.5);
10929   scale_factor=(double) height/windows->image.ximage->height;
10930   paste_info.y+=y;
10931   paste_info.y=(ssize_t) (scale_factor*paste_info.y*scale_factor+0.5);
10932   paste_info.height=(unsigned int) (scale_factor*paste_info.height+0.5);
10933   /*
10934     Paste image with X Image window.
10935   */
10936   (void) CompositeImage(image,paste_image,compose,MagickTrue,paste_info.x,
10937     paste_info.y,exception);
10938   paste_image=DestroyImage(paste_image);
10939   XSetCursorState(display,windows,MagickFalse);
10940   /*
10941     Update image colormap.
10942   */
10943   XConfigureImageColormap(display,resource_info,windows,image,exception);
10944   (void) XConfigureImage(display,resource_info,windows,image,exception);
10945   return(MagickTrue);
10946 }
10947 \f
10948 /*
10949 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10950 %                                                                             %
10951 %                                                                             %
10952 %                                                                             %
10953 +   X P r i n t I m a g e                                                     %
10954 %                                                                             %
10955 %                                                                             %
10956 %                                                                             %
10957 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10958 %
10959 %  XPrintImage() prints an image to a Postscript printer.
10960 %
10961 %  The format of the XPrintImage method is:
10962 %
10963 %      MagickBooleanType XPrintImage(Display *display,
10964 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
10965 %        ExceptionInfo *exception)
10966 %
10967 %  A description of each parameter follows:
10968 %
10969 %    o display: Specifies a connection to an X server; returned from
10970 %      XOpenDisplay.
10971 %
10972 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10973 %
10974 %    o windows: Specifies a pointer to a XWindows structure.
10975 %
10976 %    o image: the image.
10977 %
10978 %    o exception: return any errors or warnings in this structure.
10979 %
10980 */
10981 static MagickBooleanType XPrintImage(Display *display,
10982   XResourceInfo *resource_info,XWindows *windows,Image *image,
10983   ExceptionInfo *exception)
10984 {
10985   char
10986     filename[MaxTextExtent],
10987     geometry[MaxTextExtent];
10988
10989   Image
10990     *print_image;
10991
10992   ImageInfo
10993     *image_info;
10994
10995   MagickStatusType
10996     status;
10997
10998   /*
10999     Request Postscript page geometry from user.
11000   */
11001   image_info=CloneImageInfo(resource_info->image_info);
11002   (void) FormatLocaleString(geometry,MaxTextExtent,"Letter");
11003   if (image_info->page != (char *) NULL)
11004     (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
11005   XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
11006     "Select Postscript Page Geometry:",geometry);
11007   if (*geometry == '\0')
11008     return(MagickTrue);
11009   image_info->page=GetPageGeometry(geometry);
11010   /*
11011     Apply image transforms.
11012   */
11013   XSetCursorState(display,windows,MagickTrue);
11014   XCheckRefreshWindows(display,windows);
11015   print_image=CloneImage(image,0,0,MagickTrue,exception);
11016   if (print_image == (Image *) NULL)
11017     return(MagickFalse);
11018   (void) FormatLocaleString(geometry,MaxTextExtent,"%dx%d!",
11019     windows->image.ximage->width,windows->image.ximage->height);
11020   (void) TransformImage(&print_image,windows->image.crop_geometry,geometry,
11021     exception);
11022   /*
11023     Print image.
11024   */
11025   (void) AcquireUniqueFilename(filename);
11026   (void) FormatLocaleString(print_image->filename,MaxTextExtent,"print:%s",
11027     filename);
11028   status=WriteImage(image_info,print_image,exception);
11029   (void) RelinquishUniqueFileResource(filename);
11030   print_image=DestroyImage(print_image);
11031   image_info=DestroyImageInfo(image_info);
11032   XSetCursorState(display,windows,MagickFalse);
11033   return(IsMagickTrue(status));
11034 }
11035 \f
11036 /*
11037 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11038 %                                                                             %
11039 %                                                                             %
11040 %                                                                             %
11041 +   X R O I I m a g e                                                         %
11042 %                                                                             %
11043 %                                                                             %
11044 %                                                                             %
11045 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11046 %
11047 %  XROIImage() applies an image processing technique to a region of interest.
11048 %
11049 %  The format of the XROIImage method is:
11050 %
11051 %      MagickBooleanType XROIImage(Display *display,
11052 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
11053 %        ExceptionInfo *exception)
11054 %
11055 %  A description of each parameter follows:
11056 %
11057 %    o display: Specifies a connection to an X server; returned from
11058 %      XOpenDisplay.
11059 %
11060 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
11061 %
11062 %    o windows: Specifies a pointer to a XWindows structure.
11063 %
11064 %    o image: the image; returned from ReadImage.
11065 %
11066 %    o exception: return any errors or warnings in this structure.
11067 %
11068 */
11069 static MagickBooleanType XROIImage(Display *display,
11070   XResourceInfo *resource_info,XWindows *windows,Image **image,
11071   ExceptionInfo *exception)
11072 {
11073 #define ApplyMenus  7
11074
11075   static const char
11076     *ROIMenu[] =
11077     {
11078       "Help",
11079       "Dismiss",
11080       (char *) NULL
11081     },
11082     *ApplyMenu[] =
11083     {
11084       "File",
11085       "Edit",
11086       "Transform",
11087       "Enhance",
11088       "Effects",
11089       "F/X",
11090       "Miscellany",
11091       "Help",
11092       "Dismiss",
11093       (char *) NULL
11094     },
11095     *FileMenu[] =
11096     {
11097       "Save...",
11098       "Print...",
11099       (char *) NULL
11100     },
11101     *EditMenu[] =
11102     {
11103       "Undo",
11104       "Redo",
11105       (char *) NULL
11106     },
11107     *TransformMenu[] =
11108     {
11109       "Flop",
11110       "Flip",
11111       "Rotate Right",
11112       "Rotate Left",
11113       (char *) NULL
11114     },
11115     *EnhanceMenu[] =
11116     {
11117       "Hue...",
11118       "Saturation...",
11119       "Brightness...",
11120       "Gamma...",
11121       "Spiff",
11122       "Dull",
11123       "Contrast Stretch...",
11124       "Sigmoidal Contrast...",
11125       "Normalize",
11126       "Equalize",
11127       "Negate",
11128       "Grayscale",
11129       "Map...",
11130       "Quantize...",
11131       (char *) NULL
11132     },
11133     *EffectsMenu[] =
11134     {
11135       "Despeckle",
11136       "Emboss",
11137       "Reduce Noise",
11138       "Add Noise",
11139       "Sharpen...",
11140       "Blur...",
11141       "Threshold...",
11142       "Edge Detect...",
11143       "Spread...",
11144       "Shade...",
11145       "Raise...",
11146       "Segment...",
11147       (char *) NULL
11148     },
11149     *FXMenu[] =
11150     {
11151       "Solarize...",
11152       "Sepia Tone...",
11153       "Swirl...",
11154       "Implode...",
11155       "Vignette...",
11156       "Wave...",
11157       "Oil Paint...",
11158       "Charcoal Draw...",
11159       (char *) NULL
11160     },
11161     *MiscellanyMenu[] =
11162     {
11163       "Image Info",
11164       "Zoom Image",
11165       "Show Preview...",
11166       "Show Histogram",
11167       "Show Matte",
11168       (char *) NULL
11169     };
11170
11171   static const char
11172     **Menus[ApplyMenus] =
11173     {
11174       FileMenu,
11175       EditMenu,
11176       TransformMenu,
11177       EnhanceMenu,
11178       EffectsMenu,
11179       FXMenu,
11180       MiscellanyMenu
11181     };
11182
11183   static const CommandType
11184     ApplyCommands[] =
11185     {
11186       NullCommand,
11187       NullCommand,
11188       NullCommand,
11189       NullCommand,
11190       NullCommand,
11191       NullCommand,
11192       NullCommand,
11193       HelpCommand,
11194       QuitCommand
11195     },
11196     FileCommands[] =
11197     {
11198       SaveCommand,
11199       PrintCommand
11200     },
11201     EditCommands[] =
11202     {
11203       UndoCommand,
11204       RedoCommand
11205     },
11206     TransformCommands[] =
11207     {
11208       FlopCommand,
11209       FlipCommand,
11210       RotateRightCommand,
11211       RotateLeftCommand
11212     },
11213     EnhanceCommands[] =
11214     {
11215       HueCommand,
11216       SaturationCommand,
11217       BrightnessCommand,
11218       GammaCommand,
11219       SpiffCommand,
11220       DullCommand,
11221       ContrastStretchCommand,
11222       SigmoidalContrastCommand,
11223       NormalizeCommand,
11224       EqualizeCommand,
11225       NegateCommand,
11226       GrayscaleCommand,
11227       MapCommand,
11228       QuantizeCommand
11229     },
11230     EffectsCommands[] =
11231     {
11232       DespeckleCommand,
11233       EmbossCommand,
11234       ReduceNoiseCommand,
11235       AddNoiseCommand,
11236       SharpenCommand,
11237       BlurCommand,
11238       EdgeDetectCommand,
11239       SpreadCommand,
11240       ShadeCommand,
11241       RaiseCommand,
11242       SegmentCommand
11243     },
11244     FXCommands[] =
11245     {
11246       SolarizeCommand,
11247       SepiaToneCommand,
11248       SwirlCommand,
11249       ImplodeCommand,
11250       VignetteCommand,
11251       WaveCommand,
11252       OilPaintCommand,
11253       CharcoalDrawCommand
11254     },
11255     MiscellanyCommands[] =
11256     {
11257       InfoCommand,
11258       ZoomCommand,
11259       ShowPreviewCommand,
11260       ShowHistogramCommand,
11261       ShowMatteCommand
11262     },
11263     ROICommands[] =
11264     {
11265       ROIHelpCommand,
11266       ROIDismissCommand
11267     };
11268
11269   static const CommandType
11270     *Commands[ApplyMenus] =
11271     {
11272       FileCommands,
11273       EditCommands,
11274       TransformCommands,
11275       EnhanceCommands,
11276       EffectsCommands,
11277       FXCommands,
11278       MiscellanyCommands
11279     };
11280
11281   char
11282     command[MaxTextExtent],
11283     text[MaxTextExtent];
11284
11285   CommandType
11286     command_type;
11287
11288   Cursor
11289     cursor;
11290
11291   Image
11292     *roi_image;
11293
11294   int
11295     entry,
11296     id,
11297     x,
11298     y;
11299
11300   double
11301     scale_factor;
11302
11303   MagickProgressMonitor
11304     progress_monitor;
11305
11306   RectangleInfo
11307     crop_info,
11308     highlight_info,
11309     roi_info;
11310
11311   unsigned int
11312     height,
11313     width;
11314
11315   size_t
11316     state;
11317
11318   XEvent
11319     event;
11320
11321   /*
11322     Map Command widget.
11323   */
11324   (void) CloneString(&windows->command.name,"ROI");
11325   windows->command.data=0;
11326   (void) XCommandWidget(display,windows,ROIMenu,(XEvent *) NULL);
11327   (void) XMapRaised(display,windows->command.id);
11328   XClientMessage(display,windows->image.id,windows->im_protocols,
11329     windows->im_update_widget,CurrentTime);
11330   /*
11331     Track pointer until button 1 is pressed.
11332   */
11333   XQueryPosition(display,windows->image.id,&x,&y);
11334   (void) XSelectInput(display,windows->image.id,
11335     windows->image.attributes.event_mask | PointerMotionMask);
11336   roi_info.x=(ssize_t) windows->image.x+x;
11337   roi_info.y=(ssize_t) windows->image.y+y;
11338   roi_info.width=0;
11339   roi_info.height=0;
11340   cursor=XCreateFontCursor(display,XC_fleur);
11341   state=DefaultState;
11342   do
11343   {
11344     if( IfMagickTrue(windows->info.mapped) )
11345       {
11346         /*
11347           Display pointer position.
11348         */
11349         (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
11350           (long) roi_info.x,(long) roi_info.y);
11351         XInfoWidget(display,windows,text);
11352       }
11353     /*
11354       Wait for next event.
11355     */
11356     XScreenEvent(display,windows,&event,exception);
11357     if (event.xany.window == windows->command.id)
11358       {
11359         /*
11360           Select a command from the Command widget.
11361         */
11362         id=XCommandWidget(display,windows,ROIMenu,&event);
11363         if (id < 0)
11364           continue;
11365         switch (ROICommands[id])
11366         {
11367           case ROIHelpCommand:
11368           {
11369             XTextViewWidget(display,resource_info,windows,MagickFalse,
11370               "Help Viewer - Region of Interest",ImageROIHelp);
11371             break;
11372           }
11373           case ROIDismissCommand:
11374           {
11375             /*
11376               Prematurely exit.
11377             */
11378             state|=EscapeState;
11379             state|=ExitState;
11380             break;
11381           }
11382           default:
11383             break;
11384         }
11385         continue;
11386       }
11387     switch (event.type)
11388     {
11389       case ButtonPress:
11390       {
11391         if (event.xbutton.button != Button1)
11392           break;
11393         if (event.xbutton.window != windows->image.id)
11394           break;
11395         /*
11396           Note first corner of region of interest rectangle-- exit loop.
11397         */
11398         (void) XCheckDefineCursor(display,windows->image.id,cursor);
11399         roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11400         roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11401         state|=ExitState;
11402         break;
11403       }
11404       case ButtonRelease:
11405         break;
11406       case Expose:
11407         break;
11408       case KeyPress:
11409       {
11410         KeySym
11411           key_symbol;
11412
11413         if (event.xkey.window != windows->image.id)
11414           break;
11415         /*
11416           Respond to a user key press.
11417         */
11418         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11419           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11420         switch ((int) key_symbol)
11421         {
11422           case XK_Escape:
11423           case XK_F20:
11424           {
11425             /*
11426               Prematurely exit.
11427             */
11428             state|=EscapeState;
11429             state|=ExitState;
11430             break;
11431           }
11432           case XK_F1:
11433           case XK_Help:
11434           {
11435             XTextViewWidget(display,resource_info,windows,MagickFalse,
11436               "Help Viewer - Region of Interest",ImageROIHelp);
11437             break;
11438           }
11439           default:
11440           {
11441             (void) XBell(display,0);
11442             break;
11443           }
11444         }
11445         break;
11446       }
11447       case MotionNotify:
11448       {
11449         /*
11450           Map and unmap Info widget as text cursor crosses its boundaries.
11451         */
11452         x=event.xmotion.x;
11453         y=event.xmotion.y;
11454         if( IfMagickTrue(windows->info.mapped) )
11455           {
11456             if ((x < (int) (windows->info.x+windows->info.width)) &&
11457                 (y < (int) (windows->info.y+windows->info.height)))
11458               (void) XWithdrawWindow(display,windows->info.id,
11459                 windows->info.screen);
11460           }
11461         else
11462           if ((x > (int) (windows->info.x+windows->info.width)) ||
11463               (y > (int) (windows->info.y+windows->info.height)))
11464             (void) XMapWindow(display,windows->info.id);
11465         roi_info.x=(ssize_t) windows->image.x+x;
11466         roi_info.y=(ssize_t) windows->image.y+y;
11467         break;
11468       }
11469       default:
11470         break;
11471     }
11472   } while ((state & ExitState) == 0);
11473   (void) XSelectInput(display,windows->image.id,
11474     windows->image.attributes.event_mask);
11475   if ((state & EscapeState) != 0)
11476     {
11477       /*
11478         User want to exit without region of interest.
11479       */
11480       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11481       (void) XFreeCursor(display,cursor);
11482       return(MagickTrue);
11483     }
11484   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
11485   do
11486   {
11487     /*
11488       Size rectangle as pointer moves until the mouse button is released.
11489     */
11490     x=(int) roi_info.x;
11491     y=(int) roi_info.y;
11492     roi_info.width=0;
11493     roi_info.height=0;
11494     state=DefaultState;
11495     do
11496     {
11497       highlight_info=roi_info;
11498       highlight_info.x=roi_info.x-windows->image.x;
11499       highlight_info.y=roi_info.y-windows->image.y;
11500       if ((highlight_info.width > 3) && (highlight_info.height > 3))
11501         {
11502           /*
11503             Display info and draw region of interest rectangle.
11504           */
11505           if( IfMagickFalse(windows->info.mapped) )
11506             (void) XMapWindow(display,windows->info.id);
11507           (void) FormatLocaleString(text,MaxTextExtent,
11508             " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11509             roi_info.height,(double) roi_info.x,(double) roi_info.y);
11510           XInfoWidget(display,windows,text);
11511           XHighlightRectangle(display,windows->image.id,
11512             windows->image.highlight_context,&highlight_info);
11513         }
11514       else
11515         if( IfMagickTrue(windows->info.mapped) )
11516           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11517       /*
11518         Wait for next event.
11519       */
11520       XScreenEvent(display,windows,&event,exception);
11521       if ((highlight_info.width > 3) && (highlight_info.height > 3))
11522         XHighlightRectangle(display,windows->image.id,
11523           windows->image.highlight_context,&highlight_info);
11524       switch (event.type)
11525       {
11526         case ButtonPress:
11527         {
11528           roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11529           roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11530           break;
11531         }
11532         case ButtonRelease:
11533         {
11534           /*
11535             User has committed to region of interest rectangle.
11536           */
11537           roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11538           roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11539           XSetCursorState(display,windows,MagickFalse);
11540           state|=ExitState;
11541           if (LocaleCompare(windows->command.name,"Apply") == 0)
11542             break;
11543           (void) CloneString(&windows->command.name,"Apply");
11544           windows->command.data=ApplyMenus;
11545           (void) XCommandWidget(display,windows,ApplyMenu,(XEvent *) NULL);
11546           break;
11547         }
11548         case Expose:
11549           break;
11550         case MotionNotify:
11551         {
11552           roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11553           roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11554         }
11555         default:
11556           break;
11557       }
11558       if ((((int) roi_info.x != x) && ((int) roi_info.y != y)) ||
11559           ((state & ExitState) != 0))
11560         {
11561           /*
11562             Check boundary conditions.
11563           */
11564           if (roi_info.x < 0)
11565             roi_info.x=0;
11566           else
11567             if (roi_info.x > (ssize_t) windows->image.ximage->width)
11568               roi_info.x=(ssize_t) windows->image.ximage->width;
11569           if ((int) roi_info.x < x)
11570             roi_info.width=(unsigned int) (x-roi_info.x);
11571           else
11572             {
11573               roi_info.width=(unsigned int) (roi_info.x-x);
11574               roi_info.x=(ssize_t) x;
11575             }
11576           if (roi_info.y < 0)
11577             roi_info.y=0;
11578           else
11579             if (roi_info.y > (ssize_t) windows->image.ximage->height)
11580               roi_info.y=(ssize_t) windows->image.ximage->height;
11581           if ((int) roi_info.y < y)
11582             roi_info.height=(unsigned int) (y-roi_info.y);
11583           else
11584             {
11585               roi_info.height=(unsigned int) (roi_info.y-y);
11586               roi_info.y=(ssize_t) y;
11587             }
11588         }
11589     } while ((state & ExitState) == 0);
11590     /*
11591       Wait for user to grab a corner of the rectangle or press return.
11592     */
11593     state=DefaultState;
11594     command_type=NullCommand;
11595     (void) XMapWindow(display,windows->info.id);
11596     do
11597     {
11598       if( IfMagickTrue(windows->info.mapped) )
11599         {
11600           /*
11601             Display pointer position.
11602           */
11603           (void) FormatLocaleString(text,MaxTextExtent,
11604             " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11605             roi_info.height,(double) roi_info.x,(double) roi_info.y);
11606           XInfoWidget(display,windows,text);
11607         }
11608       highlight_info=roi_info;
11609       highlight_info.x=roi_info.x-windows->image.x;
11610       highlight_info.y=roi_info.y-windows->image.y;
11611       if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
11612         {
11613           state|=EscapeState;
11614           state|=ExitState;
11615           break;
11616         }
11617       if ((state & UpdateRegionState) != 0)
11618         {
11619           (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11620           switch (command_type)
11621           {
11622             case UndoCommand:
11623             case RedoCommand:
11624             {
11625               (void) XMagickCommand(display,resource_info,windows,command_type,
11626                 image,exception);
11627               break;
11628             }
11629             default:
11630             {
11631               /*
11632                 Region of interest is relative to image configuration.
11633               */
11634               progress_monitor=SetImageProgressMonitor(*image,
11635                 (MagickProgressMonitor) NULL,(*image)->client_data);
11636               crop_info=roi_info;
11637               width=(unsigned int) (*image)->columns;
11638               height=(unsigned int) (*image)->rows;
11639               x=0;
11640               y=0;
11641               if (windows->image.crop_geometry != (char *) NULL)
11642                 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
11643                   &width,&height);
11644               scale_factor=(double) width/windows->image.ximage->width;
11645               crop_info.x+=x;
11646               crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
11647               crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
11648               scale_factor=(double)
11649                 height/windows->image.ximage->height;
11650               crop_info.y+=y;
11651               crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
11652               crop_info.height=(unsigned int)
11653                 (scale_factor*crop_info.height+0.5);
11654               roi_image=CropImage(*image,&crop_info,exception);
11655               (void) SetImageProgressMonitor(*image,progress_monitor,
11656                 (*image)->client_data);
11657               if (roi_image == (Image *) NULL)
11658                 continue;
11659               /*
11660                 Apply image processing technique to the region of interest.
11661               */
11662               windows->image.orphan=MagickTrue;
11663               (void) XMagickCommand(display,resource_info,windows,command_type,
11664                 &roi_image,exception);
11665               progress_monitor=SetImageProgressMonitor(*image,
11666                 (MagickProgressMonitor) NULL,(*image)->client_data);
11667               (void) XMagickCommand(display,resource_info,windows,
11668                 SaveToUndoBufferCommand,image,exception);
11669               windows->image.orphan=MagickFalse;
11670               (void) CompositeImage(*image,roi_image,CopyCompositeOp,
11671                 MagickTrue,crop_info.x,crop_info.y,exception);
11672               roi_image=DestroyImage(roi_image);
11673               (void) SetImageProgressMonitor(*image,progress_monitor,
11674                 (*image)->client_data);
11675               break;
11676             }
11677           }
11678           if (command_type != InfoCommand)
11679             {
11680               XConfigureImageColormap(display,resource_info,windows,*image,
11681                 exception);
11682               (void) XConfigureImage(display,resource_info,windows,*image,
11683                 exception);
11684             }
11685           XCheckRefreshWindows(display,windows);
11686           XInfoWidget(display,windows,text);
11687           (void) XSetFunction(display,windows->image.highlight_context,
11688             GXinvert);
11689           state&=(~UpdateRegionState);
11690         }
11691       XHighlightRectangle(display,windows->image.id,
11692         windows->image.highlight_context,&highlight_info);
11693       XScreenEvent(display,windows,&event,exception);
11694       if (event.xany.window == windows->command.id)
11695         {
11696           /*
11697             Select a command from the Command widget.
11698           */
11699           (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11700           command_type=NullCommand;
11701           id=XCommandWidget(display,windows,ApplyMenu,&event);
11702           if (id >= 0)
11703             {
11704               (void) CopyMagickString(command,ApplyMenu[id],MaxTextExtent);
11705               command_type=ApplyCommands[id];
11706               if (id < ApplyMenus)
11707                 {
11708                   /*
11709                     Select a command from a pop-up menu.
11710                   */
11711                   entry=XMenuWidget(display,windows,ApplyMenu[id],
11712                     (const char **) Menus[id],command);
11713                   if (entry >= 0)
11714                     {
11715                       (void) CopyMagickString(command,Menus[id][entry],
11716                         MaxTextExtent);
11717                       command_type=Commands[id][entry];
11718                     }
11719                 }
11720             }
11721           (void) XSetFunction(display,windows->image.highlight_context,
11722             GXinvert);
11723           XHighlightRectangle(display,windows->image.id,
11724             windows->image.highlight_context,&highlight_info);
11725           if (command_type == HelpCommand)
11726             {
11727               (void) XSetFunction(display,windows->image.highlight_context,
11728                 GXcopy);
11729               XTextViewWidget(display,resource_info,windows,MagickFalse,
11730                 "Help Viewer - Region of Interest",ImageROIHelp);
11731               (void) XSetFunction(display,windows->image.highlight_context,
11732                 GXinvert);
11733               continue;
11734             }
11735           if (command_type == QuitCommand)
11736             {
11737               /*
11738                 exit.
11739               */
11740               state|=EscapeState;
11741               state|=ExitState;
11742               continue;
11743             }
11744           if (command_type != NullCommand)
11745             state|=UpdateRegionState;
11746           continue;
11747         }
11748       XHighlightRectangle(display,windows->image.id,
11749         windows->image.highlight_context,&highlight_info);
11750       switch (event.type)
11751       {
11752         case ButtonPress:
11753         {
11754           x=windows->image.x;
11755           y=windows->image.y;
11756           if (event.xbutton.button != Button1)
11757             break;
11758           if (event.xbutton.window != windows->image.id)
11759             break;
11760           x=windows->image.x+event.xbutton.x;
11761           y=windows->image.y+event.xbutton.y;
11762           if ((x < (int) (roi_info.x+RoiDelta)) &&
11763               (x > (int) (roi_info.x-RoiDelta)) &&
11764               (y < (int) (roi_info.y+RoiDelta)) &&
11765               (y > (int) (roi_info.y-RoiDelta)))
11766             {
11767               roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11768               roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11769               state|=UpdateConfigurationState;
11770               break;
11771             }
11772           if ((x < (int) (roi_info.x+RoiDelta)) &&
11773               (x > (int) (roi_info.x-RoiDelta)) &&
11774               (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11775               (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11776             {
11777               roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11778               state|=UpdateConfigurationState;
11779               break;
11780             }
11781           if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11782               (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11783               (y < (int) (roi_info.y+RoiDelta)) &&
11784               (y > (int) (roi_info.y-RoiDelta)))
11785             {
11786               roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11787               state|=UpdateConfigurationState;
11788               break;
11789             }
11790           if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11791               (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11792               (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11793               (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11794             {
11795               state|=UpdateConfigurationState;
11796               break;
11797             }
11798         }
11799         case ButtonRelease:
11800         {
11801           if (event.xbutton.window == windows->pan.id)
11802             if ((highlight_info.x != crop_info.x-windows->image.x) ||
11803                 (highlight_info.y != crop_info.y-windows->image.y))
11804               XHighlightRectangle(display,windows->image.id,
11805                 windows->image.highlight_context,&highlight_info);
11806           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11807             event.xbutton.time);
11808           break;
11809         }
11810         case Expose:
11811         {
11812           if (event.xexpose.window == windows->image.id)
11813             if (event.xexpose.count == 0)
11814               {
11815                 event.xexpose.x=(int) highlight_info.x;
11816                 event.xexpose.y=(int) highlight_info.y;
11817                 event.xexpose.width=(int) highlight_info.width;
11818                 event.xexpose.height=(int) highlight_info.height;
11819                 XRefreshWindow(display,&windows->image,&event);
11820               }
11821           if (event.xexpose.window == windows->info.id)
11822             if (event.xexpose.count == 0)
11823               XInfoWidget(display,windows,text);
11824           break;
11825         }
11826         case KeyPress:
11827         {
11828           KeySym
11829             key_symbol;
11830
11831           if (event.xkey.window != windows->image.id)
11832             break;
11833           /*
11834             Respond to a user key press.
11835           */
11836           (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11837             sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11838           switch ((int) key_symbol)
11839           {
11840             case XK_Shift_L:
11841             case XK_Shift_R:
11842               break;
11843             case XK_Escape:
11844             case XK_F20:
11845               state|=EscapeState;
11846             case XK_Return:
11847             {
11848               state|=ExitState;
11849               break;
11850             }
11851             case XK_Home:
11852             case XK_KP_Home:
11853             {
11854               roi_info.x=(ssize_t) (windows->image.width/2L-roi_info.width/2L);
11855               roi_info.y=(ssize_t) (windows->image.height/2L-
11856                 roi_info.height/2L);
11857               break;
11858             }
11859             case XK_Left:
11860             case XK_KP_Left:
11861             {
11862               roi_info.x--;
11863               break;
11864             }
11865             case XK_Up:
11866             case XK_KP_Up:
11867             case XK_Next:
11868             {
11869               roi_info.y--;
11870               break;
11871             }
11872             case XK_Right:
11873             case XK_KP_Right:
11874             {
11875               roi_info.x++;
11876               break;
11877             }
11878             case XK_Prior:
11879             case XK_Down:
11880             case XK_KP_Down:
11881             {
11882               roi_info.y++;
11883               break;
11884             }
11885             case XK_F1:
11886             case XK_Help:
11887             {
11888               (void) XSetFunction(display,windows->image.highlight_context,
11889                 GXcopy);
11890               XTextViewWidget(display,resource_info,windows,MagickFalse,
11891                 "Help Viewer - Region of Interest",ImageROIHelp);
11892               (void) XSetFunction(display,windows->image.highlight_context,
11893                 GXinvert);
11894               break;
11895             }
11896             default:
11897             {
11898               command_type=XImageWindowCommand(display,resource_info,windows,
11899                 event.xkey.state,key_symbol,image,exception);
11900               if (command_type != NullCommand)
11901                 state|=UpdateRegionState;
11902               break;
11903             }
11904           }
11905           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11906             event.xkey.time);
11907           break;
11908         }
11909         case KeyRelease:
11910           break;
11911         case MotionNotify:
11912         {
11913           if (event.xbutton.window != windows->image.id)
11914             break;
11915           /*
11916             Map and unmap Info widget as text cursor crosses its boundaries.
11917           */
11918           x=event.xmotion.x;
11919           y=event.xmotion.y;
11920           if( IfMagickTrue(windows->info.mapped) )
11921             {
11922               if ((x < (int) (windows->info.x+windows->info.width)) &&
11923                   (y < (int) (windows->info.y+windows->info.height)))
11924                 (void) XWithdrawWindow(display,windows->info.id,
11925                   windows->info.screen);
11926             }
11927           else
11928             if ((x > (int) (windows->info.x+windows->info.width)) ||
11929                 (y > (int) (windows->info.y+windows->info.height)))
11930               (void) XMapWindow(display,windows->info.id);
11931           roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11932           roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11933           break;
11934         }
11935         case SelectionRequest:
11936         {
11937           XSelectionEvent
11938             notify;
11939
11940           XSelectionRequestEvent
11941             *request;
11942
11943           /*
11944             Set primary selection.
11945           */
11946           (void) FormatLocaleString(text,MaxTextExtent,
11947             "%.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11948             roi_info.height,(double) roi_info.x,(double) roi_info.y);
11949           request=(&(event.xselectionrequest));
11950           (void) XChangeProperty(request->display,request->requestor,
11951             request->property,request->target,8,PropModeReplace,
11952             (unsigned char *) text,(int) strlen(text));
11953           notify.type=SelectionNotify;
11954           notify.display=request->display;
11955           notify.requestor=request->requestor;
11956           notify.selection=request->selection;
11957           notify.target=request->target;
11958           notify.time=request->time;
11959           if (request->property == None)
11960             notify.property=request->target;
11961           else
11962             notify.property=request->property;
11963           (void) XSendEvent(request->display,request->requestor,False,0,
11964             (XEvent *) &notify);
11965         }
11966         default:
11967           break;
11968       }
11969       if ((state & UpdateConfigurationState) != 0)
11970         {
11971           (void) XPutBackEvent(display,&event);
11972           (void) XCheckDefineCursor(display,windows->image.id,cursor);
11973           break;
11974         }
11975     } while ((state & ExitState) == 0);
11976   } while ((state & ExitState) == 0);
11977   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11978   XSetCursorState(display,windows,MagickFalse);
11979   if ((state & EscapeState) != 0)
11980     return(MagickTrue);
11981   return(MagickTrue);
11982 }
11983 \f
11984 /*
11985 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11986 %                                                                             %
11987 %                                                                             %
11988 %                                                                             %
11989 +   X R o t a t e I m a g e                                                   %
11990 %                                                                             %
11991 %                                                                             %
11992 %                                                                             %
11993 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11994 %
11995 %  XRotateImage() rotates the X image.  If the degrees parameter if zero, the
11996 %  rotation angle is computed from the slope of a line drawn by the user.
11997 %
11998 %  The format of the XRotateImage method is:
11999 %
12000 %      MagickBooleanType XRotateImage(Display *display,
12001 %        XResourceInfo *resource_info,XWindows *windows,double degrees,
12002 %        Image **image,ExceptionInfo *exception)
12003 %
12004 %  A description of each parameter follows:
12005 %
12006 %    o display: Specifies a connection to an X server; returned from
12007 %      XOpenDisplay.
12008 %
12009 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12010 %
12011 %    o windows: Specifies a pointer to a XWindows structure.
12012 %
12013 %    o degrees: Specifies the number of degrees to rotate the image.
12014 %
12015 %    o image: the image.
12016 %
12017 %    o exception: return any errors or warnings in this structure.
12018 %
12019 */
12020 static MagickBooleanType XRotateImage(Display *display,
12021   XResourceInfo *resource_info,XWindows *windows,double degrees,Image **image,
12022   ExceptionInfo *exception)
12023 {
12024   static const char
12025     *RotateMenu[] =
12026     {
12027       "Pixel Color",
12028       "Direction",
12029       "Help",
12030       "Dismiss",
12031       (char *) NULL
12032     };
12033
12034   static ModeType
12035     direction = HorizontalRotateCommand;
12036
12037   static const ModeType
12038     DirectionCommands[] =
12039     {
12040       HorizontalRotateCommand,
12041       VerticalRotateCommand
12042     },
12043     RotateCommands[] =
12044     {
12045       RotateColorCommand,
12046       RotateDirectionCommand,
12047       RotateHelpCommand,
12048       RotateDismissCommand
12049     };
12050
12051   static unsigned int
12052     pen_id = 0;
12053
12054   char
12055     command[MaxTextExtent],
12056     text[MaxTextExtent];
12057
12058   Image
12059     *rotate_image;
12060
12061   int
12062     id,
12063     x,
12064     y;
12065
12066   double
12067     normalized_degrees;
12068
12069   register int
12070     i;
12071
12072   unsigned int
12073     height,
12074     rotations,
12075     width;
12076
12077   if (degrees == 0.0)
12078     {
12079       unsigned int
12080         distance;
12081
12082       size_t
12083         state;
12084
12085       XEvent
12086         event;
12087
12088       XSegment
12089         rotate_info;
12090
12091       /*
12092         Map Command widget.
12093       */
12094       (void) CloneString(&windows->command.name,"Rotate");
12095       windows->command.data=2;
12096       (void) XCommandWidget(display,windows,RotateMenu,(XEvent *) NULL);
12097       (void) XMapRaised(display,windows->command.id);
12098       XClientMessage(display,windows->image.id,windows->im_protocols,
12099         windows->im_update_widget,CurrentTime);
12100       /*
12101         Wait for first button press.
12102       */
12103       (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12104       XQueryPosition(display,windows->image.id,&x,&y);
12105       rotate_info.x1=x;
12106       rotate_info.y1=y;
12107       rotate_info.x2=x;
12108       rotate_info.y2=y;
12109       state=DefaultState;
12110       do
12111       {
12112         XHighlightLine(display,windows->image.id,
12113           windows->image.highlight_context,&rotate_info);
12114         /*
12115           Wait for next event.
12116         */
12117         XScreenEvent(display,windows,&event,exception);
12118         XHighlightLine(display,windows->image.id,
12119           windows->image.highlight_context,&rotate_info);
12120         if (event.xany.window == windows->command.id)
12121           {
12122             /*
12123               Select a command from the Command widget.
12124             */
12125             id=XCommandWidget(display,windows,RotateMenu,&event);
12126             if (id < 0)
12127               continue;
12128             (void) XSetFunction(display,windows->image.highlight_context,
12129               GXcopy);
12130             switch (RotateCommands[id])
12131             {
12132               case RotateColorCommand:
12133               {
12134                 const char
12135                   *ColorMenu[MaxNumberPens];
12136
12137                 int
12138                   pen_number;
12139
12140                 XColor
12141                   color;
12142
12143                 /*
12144                   Initialize menu selections.
12145                 */
12146                 for (i=0; i < (int) (MaxNumberPens-2); i++)
12147                   ColorMenu[i]=resource_info->pen_colors[i];
12148                 ColorMenu[MaxNumberPens-2]="Browser...";
12149                 ColorMenu[MaxNumberPens-1]=(const char *) NULL;
12150                 /*
12151                   Select a pen color from the pop-up menu.
12152                 */
12153                 pen_number=XMenuWidget(display,windows,RotateMenu[id],
12154                   (const char **) ColorMenu,command);
12155                 if (pen_number < 0)
12156                   break;
12157                 if (pen_number == (MaxNumberPens-2))
12158                   {
12159                     static char
12160                       color_name[MaxTextExtent] = "gray";
12161
12162                     /*
12163                       Select a pen color from a dialog.
12164                     */
12165                     resource_info->pen_colors[pen_number]=color_name;
12166                     XColorBrowserWidget(display,windows,"Select",color_name);
12167                     if (*color_name == '\0')
12168                       break;
12169                   }
12170                 /*
12171                   Set pen color.
12172                 */
12173                 (void) XParseColor(display,windows->map_info->colormap,
12174                   resource_info->pen_colors[pen_number],&color);
12175                 XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
12176                   (unsigned int) MaxColors,&color);
12177                 windows->pixel_info->pen_colors[pen_number]=color;
12178                 pen_id=(unsigned int) pen_number;
12179                 break;
12180               }
12181               case RotateDirectionCommand:
12182               {
12183                 static const char
12184                   *Directions[] =
12185                   {
12186                     "horizontal",
12187                     "vertical",
12188                     (char *) NULL,
12189                   };
12190
12191                 /*
12192                   Select a command from the pop-up menu.
12193                 */
12194                 id=XMenuWidget(display,windows,RotateMenu[id],
12195                   Directions,command);
12196                 if (id >= 0)
12197                   direction=DirectionCommands[id];
12198                 break;
12199               }
12200               case RotateHelpCommand:
12201               {
12202                 XTextViewWidget(display,resource_info,windows,MagickFalse,
12203                   "Help Viewer - Image Rotation",ImageRotateHelp);
12204                 break;
12205               }
12206               case RotateDismissCommand:
12207               {
12208                 /*
12209                   Prematurely exit.
12210                 */
12211                 state|=EscapeState;
12212                 state|=ExitState;
12213                 break;
12214               }
12215               default:
12216                 break;
12217             }
12218             (void) XSetFunction(display,windows->image.highlight_context,
12219               GXinvert);
12220             continue;
12221           }
12222         switch (event.type)
12223         {
12224           case ButtonPress:
12225           {
12226             if (event.xbutton.button != Button1)
12227               break;
12228             if (event.xbutton.window != windows->image.id)
12229               break;
12230             /*
12231               exit loop.
12232             */
12233             (void) XSetFunction(display,windows->image.highlight_context,
12234               GXcopy);
12235             rotate_info.x1=event.xbutton.x;
12236             rotate_info.y1=event.xbutton.y;
12237             state|=ExitState;
12238             break;
12239           }
12240           case ButtonRelease:
12241             break;
12242           case Expose:
12243             break;
12244           case KeyPress:
12245           {
12246             char
12247               command[MaxTextExtent];
12248
12249             KeySym
12250               key_symbol;
12251
12252             if (event.xkey.window != windows->image.id)
12253               break;
12254             /*
12255               Respond to a user key press.
12256             */
12257             (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
12258               sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12259             switch ((int) key_symbol)
12260             {
12261               case XK_Escape:
12262               case XK_F20:
12263               {
12264                 /*
12265                   Prematurely exit.
12266                 */
12267                 state|=EscapeState;
12268                 state|=ExitState;
12269                 break;
12270               }
12271               case XK_F1:
12272               case XK_Help:
12273               {
12274                 (void) XSetFunction(display,windows->image.highlight_context,
12275                   GXcopy);
12276                 XTextViewWidget(display,resource_info,windows,MagickFalse,
12277                   "Help Viewer - Image Rotation",ImageRotateHelp);
12278                 (void) XSetFunction(display,windows->image.highlight_context,
12279                   GXinvert);
12280                 break;
12281               }
12282               default:
12283               {
12284                 (void) XBell(display,0);
12285                 break;
12286               }
12287             }
12288             break;
12289           }
12290           case MotionNotify:
12291           {
12292             rotate_info.x1=event.xmotion.x;
12293             rotate_info.y1=event.xmotion.y;
12294           }
12295         }
12296         rotate_info.x2=rotate_info.x1;
12297         rotate_info.y2=rotate_info.y1;
12298         if (direction == HorizontalRotateCommand)
12299           rotate_info.x2+=32;
12300         else
12301           rotate_info.y2-=32;
12302       } while ((state & ExitState) == 0);
12303       (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12304       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12305       if ((state & EscapeState) != 0)
12306         return(MagickTrue);
12307       /*
12308         Draw line as pointer moves until the mouse button is released.
12309       */
12310       distance=0;
12311       (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12312       state=DefaultState;
12313       do
12314       {
12315         if (distance > 9)
12316           {
12317             /*
12318               Display info and draw rotation line.
12319             */
12320             if( IfMagickFalse(windows->info.mapped) )
12321               (void) XMapWindow(display,windows->info.id);
12322             (void) FormatLocaleString(text,MaxTextExtent," %g",
12323               direction == VerticalRotateCommand ? degrees-90.0 : degrees);
12324             XInfoWidget(display,windows,text);
12325             XHighlightLine(display,windows->image.id,
12326               windows->image.highlight_context,&rotate_info);
12327           }
12328         else
12329           if( IfMagickTrue(windows->info.mapped) )
12330             (void) XWithdrawWindow(display,windows->info.id,
12331               windows->info.screen);
12332         /*
12333           Wait for next event.
12334         */
12335         XScreenEvent(display,windows,&event,exception);
12336         if (distance > 9)
12337           XHighlightLine(display,windows->image.id,
12338             windows->image.highlight_context,&rotate_info);
12339         switch (event.type)
12340         {
12341           case ButtonPress:
12342             break;
12343           case ButtonRelease:
12344           {
12345             /*
12346               User has committed to rotation line.
12347             */
12348             rotate_info.x2=event.xbutton.x;
12349             rotate_info.y2=event.xbutton.y;
12350             state|=ExitState;
12351             break;
12352           }
12353           case Expose:
12354             break;
12355           case MotionNotify:
12356           {
12357             rotate_info.x2=event.xmotion.x;
12358             rotate_info.y2=event.xmotion.y;
12359           }
12360           default:
12361             break;
12362         }
12363         /*
12364           Check boundary conditions.
12365         */
12366         if (rotate_info.x2 < 0)
12367           rotate_info.x2=0;
12368         else
12369           if (rotate_info.x2 > (int) windows->image.width)
12370             rotate_info.x2=(short) windows->image.width;
12371         if (rotate_info.y2 < 0)
12372           rotate_info.y2=0;
12373         else
12374           if (rotate_info.y2 > (int) windows->image.height)
12375             rotate_info.y2=(short) windows->image.height;
12376         /*
12377           Compute rotation angle from the slope of the line.
12378         */
12379         degrees=0.0;
12380         distance=(unsigned int)
12381           ((rotate_info.x2-rotate_info.x1+1)*(rotate_info.x2-rotate_info.x1+1))+
12382           ((rotate_info.y2-rotate_info.y1+1)*(rotate_info.y2-rotate_info.y1+1));
12383         if (distance > 9)
12384           degrees=RadiansToDegrees(-atan2((double) (rotate_info.y2-
12385             rotate_info.y1),(double) (rotate_info.x2-rotate_info.x1)));
12386       } while ((state & ExitState) == 0);
12387       (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12388       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12389       if (distance <= 9)
12390         return(MagickTrue);
12391     }
12392   if (direction == VerticalRotateCommand)
12393     degrees-=90.0;
12394   if (degrees == 0.0)
12395     return(MagickTrue);
12396   /*
12397     Rotate image.
12398   */
12399   normalized_degrees=degrees;
12400   while (normalized_degrees < -45.0)
12401     normalized_degrees+=360.0;
12402   for (rotations=0; normalized_degrees > 45.0; rotations++)
12403     normalized_degrees-=90.0;
12404   if (normalized_degrees != 0.0)
12405     (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
12406       exception);
12407   XSetCursorState(display,windows,MagickTrue);
12408   XCheckRefreshWindows(display,windows);
12409   (*image)->background_color.red=(double) ScaleShortToQuantum(
12410     windows->pixel_info->pen_colors[pen_id].red);
12411   (*image)->background_color.green=(double) ScaleShortToQuantum(
12412     windows->pixel_info->pen_colors[pen_id].green);
12413   (*image)->background_color.blue=(double) ScaleShortToQuantum(
12414     windows->pixel_info->pen_colors[pen_id].blue);
12415   rotate_image=RotateImage(*image,degrees,exception);
12416   XSetCursorState(display,windows,MagickFalse);
12417   if (rotate_image == (Image *) NULL)
12418     return(MagickFalse);
12419   *image=DestroyImage(*image);
12420   *image=rotate_image;
12421   if (windows->image.crop_geometry != (char *) NULL)
12422     {
12423       /*
12424         Rotate crop geometry.
12425       */
12426       width=(unsigned int) (*image)->columns;
12427       height=(unsigned int) (*image)->rows;
12428       (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12429       switch (rotations % 4)
12430       {
12431         default:
12432         case 0:
12433           break;
12434         case 1:
12435         {
12436           /*
12437             Rotate 90 degrees.
12438           */
12439           (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12440             "%ux%u%+d%+d",height,width,(int) (*image)->columns-
12441             (int) height-y,x);
12442           break;
12443         }
12444         case 2:
12445         {
12446           /*
12447             Rotate 180 degrees.
12448           */
12449           (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12450             "%ux%u%+d%+d",width,height,(int) width-x,(int) height-y);
12451           break;
12452         }
12453         case 3:
12454         {
12455           /*
12456             Rotate 270 degrees.
12457           */
12458           (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12459             "%ux%u%+d%+d",height,width,y,(int) (*image)->rows-(int) width-x);
12460           break;
12461         }
12462       }
12463     }
12464   if( IfMagickTrue(windows->image.orphan) )
12465     return(MagickTrue);
12466   if (normalized_degrees != 0.0)
12467     {
12468       /*
12469         Update image colormap.
12470       */
12471       windows->image.window_changes.width=(int) (*image)->columns;
12472       windows->image.window_changes.height=(int) (*image)->rows;
12473       if (windows->image.crop_geometry != (char *) NULL)
12474         {
12475           /*
12476             Obtain dimensions of image from crop geometry.
12477           */
12478           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
12479             &width,&height);
12480           windows->image.window_changes.width=(int) width;
12481           windows->image.window_changes.height=(int) height;
12482         }
12483       XConfigureImageColormap(display,resource_info,windows,*image,exception);
12484     }
12485   else
12486     if (((rotations % 4) == 1) || ((rotations % 4) == 3))
12487       {
12488         windows->image.window_changes.width=windows->image.ximage->height;
12489         windows->image.window_changes.height=windows->image.ximage->width;
12490       }
12491   /*
12492     Update image configuration.
12493   */
12494   (void) XConfigureImage(display,resource_info,windows,*image,exception);
12495   return(MagickTrue);
12496 }
12497 \f
12498 /*
12499 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12500 %                                                                             %
12501 %                                                                             %
12502 %                                                                             %
12503 +   X S a v e I m a g e                                                       %
12504 %                                                                             %
12505 %                                                                             %
12506 %                                                                             %
12507 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12508 %
12509 %  XSaveImage() saves an image to a file.
12510 %
12511 %  The format of the XSaveImage method is:
12512 %
12513 %      MagickBooleanType XSaveImage(Display *display,
12514 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
12515 %        ExceptionInfo *exception)
12516 %
12517 %  A description of each parameter follows:
12518 %
12519 %    o display: Specifies a connection to an X server; returned from
12520 %      XOpenDisplay.
12521 %
12522 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12523 %
12524 %    o windows: Specifies a pointer to a XWindows structure.
12525 %
12526 %    o image: the image.
12527 %
12528 %    o exception: return any errors or warnings in this structure.
12529 %
12530 */
12531 static MagickBooleanType XSaveImage(Display *display,
12532   XResourceInfo *resource_info,XWindows *windows,Image *image,
12533   ExceptionInfo *exception)
12534 {
12535   char
12536     filename[MaxTextExtent],
12537     geometry[MaxTextExtent];
12538
12539   Image
12540     *save_image;
12541
12542   ImageInfo
12543     *image_info;
12544
12545   MagickStatusType
12546     status;
12547
12548   /*
12549     Request file name from user.
12550   */
12551   if (resource_info->write_filename != (char *) NULL)
12552     (void) CopyMagickString(filename,resource_info->write_filename,
12553       MaxTextExtent);
12554   else
12555     {
12556       char
12557         path[MaxTextExtent];
12558
12559       int
12560         status;
12561
12562       GetPathComponent(image->filename,HeadPath,path);
12563       GetPathComponent(image->filename,TailPath,filename);
12564       if (*path != '\0')
12565         {
12566           status=chdir(path);
12567           if (status == -1)
12568             (void) ThrowMagickException(exception,GetMagickModule(),
12569               FileOpenError,"UnableToOpenFile","%s",path);
12570         }
12571     }
12572   XFileBrowserWidget(display,windows,"Save",filename);
12573   if (*filename == '\0')
12574     return(MagickTrue);
12575   if( IfMagickTrue(IsPathAccessible(filename)) )
12576     {
12577       int
12578         status;
12579
12580       /*
12581         File exists-- seek user's permission before overwriting.
12582       */
12583       status=XConfirmWidget(display,windows,"Overwrite",filename);
12584       if (status <= 0)
12585         return(MagickTrue);
12586     }
12587   image_info=CloneImageInfo(resource_info->image_info);
12588   (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
12589   (void) SetImageInfo(image_info,1,exception);
12590   if ((LocaleCompare(image_info->magick,"JPEG") == 0) ||
12591       (LocaleCompare(image_info->magick,"JPG") == 0))
12592     {
12593       char
12594         quality[MaxTextExtent];
12595
12596       int
12597         status;
12598
12599       /*
12600         Request JPEG quality from user.
12601       */
12602       (void) FormatLocaleString(quality,MaxTextExtent,"%.20g",(double)
12603         image->quality);
12604       status=XDialogWidget(display,windows,"Save","Enter JPEG quality:",
12605         quality);
12606       if (*quality == '\0')
12607         return(MagickTrue);
12608       image->quality=StringToUnsignedLong(quality);
12609       image_info->interlace=status != 0 ? NoInterlace : PlaneInterlace;
12610     }
12611   if ((LocaleCompare(image_info->magick,"EPS") == 0) ||
12612       (LocaleCompare(image_info->magick,"PDF") == 0) ||
12613       (LocaleCompare(image_info->magick,"PS") == 0) ||
12614       (LocaleCompare(image_info->magick,"PS2") == 0))
12615     {
12616       char
12617         geometry[MaxTextExtent];
12618
12619       /*
12620         Request page geometry from user.
12621       */
12622       (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
12623       if (LocaleCompare(image_info->magick,"PDF") == 0)
12624         (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
12625       if (image_info->page != (char *) NULL)
12626         (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
12627       XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
12628         "Select page geometry:",geometry);
12629       if (*geometry != '\0')
12630         image_info->page=GetPageGeometry(geometry);
12631     }
12632   /*
12633     Apply image transforms.
12634   */
12635   XSetCursorState(display,windows,MagickTrue);
12636   XCheckRefreshWindows(display,windows);
12637   save_image=CloneImage(image,0,0,MagickTrue,exception);
12638   if (save_image == (Image *) NULL)
12639     return(MagickFalse);
12640   (void) FormatLocaleString(geometry,MaxTextExtent,"%dx%d!",
12641     windows->image.ximage->width,windows->image.ximage->height);
12642   (void) TransformImage(&save_image,windows->image.crop_geometry,geometry,
12643     exception);
12644   /*
12645     Write image.
12646   */
12647   (void) CopyMagickString(save_image->filename,filename,MaxTextExtent);
12648   status=WriteImage(image_info,save_image,exception);
12649   if( IfMagickTrue(status) )
12650     image->taint=MagickFalse;
12651   save_image=DestroyImage(save_image);
12652   image_info=DestroyImageInfo(image_info);
12653   XSetCursorState(display,windows,MagickFalse);
12654   return(IsMagickTrue(status));
12655 }
12656 \f
12657 /*
12658 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12659 %                                                                             %
12660 %                                                                             %
12661 %                                                                             %
12662 +   X S c r e e n E v e n t                                                   %
12663 %                                                                             %
12664 %                                                                             %
12665 %                                                                             %
12666 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12667 %
12668 %  XScreenEvent() handles global events associated with the Pan and Magnify
12669 %  windows.
12670 %
12671 %  The format of the XScreenEvent function is:
12672 %
12673 %      void XScreenEvent(Display *display,XWindows *windows,XEvent *event,
12674 %        ExceptionInfo *exception)
12675 %
12676 %  A description of each parameter follows:
12677 %
12678 %    o display: Specifies a pointer to the Display structure;  returned from
12679 %      XOpenDisplay.
12680 %
12681 %    o windows: Specifies a pointer to a XWindows structure.
12682 %
12683 %    o event: Specifies a pointer to a X11 XEvent structure.
12684 %
12685 %    o exception: return any errors or warnings in this structure.
12686 %
12687 */
12688
12689 #if defined(__cplusplus) || defined(c_plusplus)
12690 extern "C" {
12691 #endif
12692
12693 static int XPredicate(Display *magick_unused(display),XEvent *event,char *data)
12694 {
12695   register XWindows
12696     *windows;
12697
12698   windows=(XWindows *) data;
12699   if ((event->type == ClientMessage) &&
12700       (event->xclient.window == windows->image.id))
12701     return(MagickFalse);
12702   return(MagickTrue);
12703 }
12704
12705 #if defined(__cplusplus) || defined(c_plusplus)
12706 }
12707 #endif
12708
12709 static void XScreenEvent(Display *display,XWindows *windows,XEvent *event,
12710   ExceptionInfo *exception)
12711 {
12712   register int
12713     x,
12714     y;
12715
12716   (void) XIfEvent(display,event,XPredicate,(char *) windows);
12717   if (event->xany.window == windows->command.id)
12718     return;
12719   switch (event->type)
12720   {
12721     case ButtonPress:
12722     case ButtonRelease:
12723     {
12724       if ((event->xbutton.button == Button3) &&
12725           (event->xbutton.state & Mod1Mask))
12726         {
12727           /*
12728             Convert Alt-Button3 to Button2.
12729           */
12730           event->xbutton.button=Button2;
12731           event->xbutton.state&=(~Mod1Mask);
12732         }
12733       if (event->xbutton.window == windows->backdrop.id)
12734         {
12735           (void) XSetInputFocus(display,event->xbutton.window,RevertToParent,
12736             event->xbutton.time);
12737           break;
12738         }
12739       if (event->xbutton.window == windows->pan.id)
12740         {
12741           XPanImage(display,windows,event,exception);
12742           break;
12743         }
12744       if (event->xbutton.window == windows->image.id)
12745         if (event->xbutton.button == Button2)
12746           {
12747             /*
12748               Update magnified image.
12749             */
12750             x=event->xbutton.x;
12751             y=event->xbutton.y;
12752             if (x < 0)
12753               x=0;
12754             else
12755               if (x >= (int) windows->image.width)
12756                 x=(int) (windows->image.width-1);
12757             windows->magnify.x=(int) windows->image.x+x;
12758             if (y < 0)
12759               y=0;
12760             else
12761              if (y >= (int) windows->image.height)
12762                y=(int) (windows->image.height-1);
12763             windows->magnify.y=windows->image.y+y;
12764             if( IfMagickFalse(windows->magnify.mapped) )
12765               (void) XMapRaised(display,windows->magnify.id);
12766             XMakeMagnifyImage(display,windows,exception);
12767             if (event->type == ButtonRelease)
12768               (void) XWithdrawWindow(display,windows->info.id,
12769                 windows->info.screen);
12770             break;
12771           }
12772       break;
12773     }
12774     case ClientMessage:
12775     {
12776       /*
12777         If client window delete message, exit.
12778       */
12779       if (event->xclient.message_type != windows->wm_protocols)
12780         break;
12781       if (*event->xclient.data.l != (long) windows->wm_delete_window)
12782         break;
12783       if (event->xclient.window == windows->magnify.id)
12784         {
12785           (void) XWithdrawWindow(display,windows->magnify.id,
12786             windows->magnify.screen);
12787           break;
12788         }
12789       break;
12790     }
12791     case ConfigureNotify:
12792     {
12793       if (event->xconfigure.window == windows->magnify.id)
12794         {
12795           unsigned int
12796             magnify;
12797
12798           /*
12799             Magnify window has a new configuration.
12800           */
12801           windows->magnify.width=(unsigned int) event->xconfigure.width;
12802           windows->magnify.height=(unsigned int) event->xconfigure.height;
12803           if( IfMagickFalse(windows->magnify.mapped) )
12804             break;
12805           magnify=1;
12806           while ((int) magnify <= event->xconfigure.width)
12807             magnify<<=1;
12808           while ((int) magnify <= event->xconfigure.height)
12809             magnify<<=1;
12810           magnify>>=1;
12811           if (((int) magnify != event->xconfigure.width) ||
12812               ((int) magnify != event->xconfigure.height))
12813             {
12814               XWindowChanges
12815                 window_changes;
12816
12817               window_changes.width=(int) magnify;
12818               window_changes.height=(int) magnify;
12819               (void) XReconfigureWMWindow(display,windows->magnify.id,
12820                 windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
12821                 &window_changes);
12822               break;
12823             }
12824           XMakeMagnifyImage(display,windows,exception);
12825           break;
12826         }
12827       break;
12828     }
12829     case Expose:
12830     {
12831       if (event->xexpose.window == windows->image.id)
12832         {
12833           XRefreshWindow(display,&windows->image,event);
12834           break;
12835         }
12836       if (event->xexpose.window == windows->pan.id)
12837         if (event->xexpose.count == 0)
12838           {
12839             XDrawPanRectangle(display,windows);
12840             break;
12841           }
12842       if (event->xexpose.window == windows->magnify.id)
12843         if (event->xexpose.count == 0)
12844           {
12845             XMakeMagnifyImage(display,windows,exception);
12846             break;
12847           }
12848       break;
12849     }
12850     case KeyPress:
12851     {
12852       char
12853         command[MaxTextExtent];
12854
12855       KeySym
12856         key_symbol;
12857
12858       if (event->xkey.window != windows->magnify.id)
12859         break;
12860       /*
12861         Respond to a user key press.
12862       */
12863       (void) XLookupString((XKeyEvent *) &event->xkey,command,(int)
12864         sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12865       XMagnifyWindowCommand(display,windows,event->xkey.state,key_symbol,
12866         exception);
12867       break;
12868     }
12869     case MapNotify:
12870     {
12871       if (event->xmap.window == windows->magnify.id)
12872         {
12873           windows->magnify.mapped=MagickTrue;
12874           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12875           break;
12876         }
12877       if (event->xmap.window == windows->info.id)
12878         {
12879           windows->info.mapped=MagickTrue;
12880           break;
12881         }
12882       break;
12883     }
12884     case MotionNotify:
12885     {
12886       while (XCheckMaskEvent(display,ButtonMotionMask,event)) ;
12887       if (event->xmotion.window == windows->image.id)
12888         if( IfMagickTrue(windows->magnify.mapped) )
12889           {
12890             /*
12891               Update magnified image.
12892             */
12893             x=event->xmotion.x;
12894             y=event->xmotion.y;
12895             if (x < 0)
12896               x=0;
12897             else
12898               if (x >= (int) windows->image.width)
12899                 x=(int) (windows->image.width-1);
12900             windows->magnify.x=(int) windows->image.x+x;
12901             if (y < 0)
12902               y=0;
12903             else
12904              if (y >= (int) windows->image.height)
12905                y=(int) (windows->image.height-1);
12906             windows->magnify.y=windows->image.y+y;
12907             XMakeMagnifyImage(display,windows,exception);
12908           }
12909       break;
12910     }
12911     case UnmapNotify:
12912     {
12913       if (event->xunmap.window == windows->magnify.id)
12914         {
12915           windows->magnify.mapped=MagickFalse;
12916           break;
12917         }
12918       if (event->xunmap.window == windows->info.id)
12919         {
12920           windows->info.mapped=MagickFalse;
12921           break;
12922         }
12923       break;
12924     }
12925     default:
12926       break;
12927   }
12928 }
12929 \f
12930 /*
12931 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12932 %                                                                             %
12933 %                                                                             %
12934 %                                                                             %
12935 +   X S e t C r o p G e o m e t r y                                           %
12936 %                                                                             %
12937 %                                                                             %
12938 %                                                                             %
12939 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12940 %
12941 %  XSetCropGeometry() accepts a cropping geometry relative to the Image window
12942 %  and translates it to a cropping geometry relative to the image.
12943 %
12944 %  The format of the XSetCropGeometry method is:
12945 %
12946 %      void XSetCropGeometry(Display *display,XWindows *windows,
12947 %        RectangleInfo *crop_info,Image *image)
12948 %
12949 %  A description of each parameter follows:
12950 %
12951 %    o display: Specifies a connection to an X server; returned from
12952 %      XOpenDisplay.
12953 %
12954 %    o windows: Specifies a pointer to a XWindows structure.
12955 %
12956 %    o crop_info:  A pointer to a RectangleInfo that defines a region of the
12957 %      Image window to crop.
12958 %
12959 %    o image: the image.
12960 %
12961 */
12962 static void XSetCropGeometry(Display *display,XWindows *windows,
12963   RectangleInfo *crop_info,Image *image)
12964 {
12965   char
12966     text[MaxTextExtent];
12967
12968   int
12969     x,
12970     y;
12971
12972   double
12973     scale_factor;
12974
12975   unsigned int
12976     height,
12977     width;
12978
12979   if( IfMagickTrue(windows->info.mapped) )
12980     {
12981       /*
12982         Display info on cropping rectangle.
12983       */
12984       (void) FormatLocaleString(text,MaxTextExtent," %.20gx%.20g%+.20g%+.20g",
12985         (double) crop_info->width,(double) crop_info->height,(double)
12986         crop_info->x,(double) crop_info->y);
12987       XInfoWidget(display,windows,text);
12988     }
12989   /*
12990     Cropping geometry is relative to any previous crop geometry.
12991   */
12992   x=0;
12993   y=0;
12994   width=(unsigned int) image->columns;
12995   height=(unsigned int) image->rows;
12996   if (windows->image.crop_geometry != (char *) NULL)
12997     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12998   else
12999     windows->image.crop_geometry=AcquireString((char *) NULL);
13000   /*
13001     Define the crop geometry string from the cropping rectangle.
13002   */
13003   scale_factor=(double) width/windows->image.ximage->width;
13004   if (crop_info->x > 0)
13005     x+=(int) (scale_factor*crop_info->x+0.5);
13006   width=(unsigned int) (scale_factor*crop_info->width+0.5);
13007   if (width == 0)
13008     width=1;
13009   scale_factor=(double) height/windows->image.ximage->height;
13010   if (crop_info->y > 0)
13011     y+=(int) (scale_factor*crop_info->y+0.5);
13012   height=(unsigned int) (scale_factor*crop_info->height+0.5);
13013   if (height == 0)
13014     height=1;
13015   (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
13016     "%ux%u%+d%+d",width,height,x,y);
13017 }
13018 \f
13019 /*
13020 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13021 %                                                                             %
13022 %                                                                             %
13023 %                                                                             %
13024 +   X T i l e I m a g e                                                       %
13025 %                                                                             %
13026 %                                                                             %
13027 %                                                                             %
13028 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13029 %
13030 %  XTileImage() loads or deletes a selected tile from a visual image directory.
13031 %  The load or delete command is chosen from a menu.
13032 %
13033 %  The format of the XTileImage method is:
13034 %
13035 %      Image *XTileImage(Display *display,XResourceInfo *resource_info,
13036 %        XWindows *windows,Image *image,XEvent *event,ExceptionInfo *exception)
13037 %
13038 %  A description of each parameter follows:
13039 %
13040 %    o tile_image:  XTileImage reads or deletes the tile image
13041 %      and returns it.  A null image is returned if an error occurs.
13042 %
13043 %    o display: Specifies a connection to an X server;  returned from
13044 %      XOpenDisplay.
13045 %
13046 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13047 %
13048 %    o windows: Specifies a pointer to a XWindows structure.
13049 %
13050 %    o image: the image; returned from ReadImage.
13051 %
13052 %    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
13053 %      the entire image is refreshed.
13054 %
13055 %    o exception: return any errors or warnings in this structure.
13056 %
13057 */
13058 static Image *XTileImage(Display *display,XResourceInfo *resource_info,
13059   XWindows *windows,Image *image,XEvent *event,ExceptionInfo *exception)
13060 {
13061   static const char
13062     *VerbMenu[] =
13063     {
13064       "Load",
13065       "Next",
13066       "Former",
13067       "Delete",
13068       "Update",
13069       (char *) NULL,
13070     };
13071
13072   static const ModeType
13073     TileCommands[] =
13074     {
13075       TileLoadCommand,
13076       TileNextCommand,
13077       TileFormerCommand,
13078       TileDeleteCommand,
13079       TileUpdateCommand
13080     };
13081
13082   char
13083     command[MaxTextExtent],
13084     filename[MaxTextExtent];
13085
13086   Image
13087     *tile_image;
13088
13089   int
13090     id,
13091     status,
13092     tile,
13093     x,
13094     y;
13095
13096   double
13097     scale_factor;
13098
13099   register char
13100     *p,
13101     *q;
13102
13103   register int
13104     i;
13105
13106   unsigned int
13107     height,
13108     width;
13109
13110   /*
13111     Tile image is relative to montage image configuration.
13112   */
13113   x=0;
13114   y=0;
13115   width=(unsigned int) image->columns;
13116   height=(unsigned int) image->rows;
13117   if (windows->image.crop_geometry != (char *) NULL)
13118     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
13119   scale_factor=(double) width/windows->image.ximage->width;
13120   event->xbutton.x+=windows->image.x;
13121   event->xbutton.x=(int) (scale_factor*event->xbutton.x+x+0.5);
13122   scale_factor=(double) height/windows->image.ximage->height;
13123   event->xbutton.y+=windows->image.y;
13124   event->xbutton.y=(int) (scale_factor*event->xbutton.y+y+0.5);
13125   /*
13126     Determine size and location of each tile in the visual image directory.
13127   */
13128   width=(unsigned int) image->columns;
13129   height=(unsigned int) image->rows;
13130   x=0;
13131   y=0;
13132   (void) XParseGeometry(image->montage,&x,&y,&width,&height);
13133   tile=((event->xbutton.y-y)/height)*(((int) image->columns-x)/width)+
13134     (event->xbutton.x-x)/width;
13135   if (tile < 0)
13136     {
13137       /*
13138         Button press is outside any tile.
13139       */
13140       (void) XBell(display,0);
13141       return((Image *) NULL);
13142     }
13143   /*
13144     Determine file name from the tile directory.
13145   */
13146   p=image->directory;
13147   for (i=tile; (i != 0) && (*p != '\0'); )
13148   {
13149     if (*p == '\n')
13150       i--;
13151     p++;
13152   }
13153   if (*p == '\0')
13154     {
13155       /*
13156         Button press is outside any tile.
13157       */
13158       (void) XBell(display,0);
13159       return((Image *) NULL);
13160     }
13161   /*
13162     Select a command from the pop-up menu.
13163   */
13164   id=XMenuWidget(display,windows,"Tile Verb",VerbMenu,command);
13165   if (id < 0)
13166     return((Image *) NULL);
13167   q=p;
13168   while ((*q != '\n') && (*q != '\0'))
13169     q++;
13170   (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13171   /*
13172     Perform command for the selected tile.
13173   */
13174   XSetCursorState(display,windows,MagickTrue);
13175   XCheckRefreshWindows(display,windows);
13176   tile_image=NewImageList();
13177   switch (TileCommands[id])
13178   {
13179     case TileLoadCommand:
13180     {
13181       /*
13182         Load tile image.
13183       */
13184       XCheckRefreshWindows(display,windows);
13185       (void) CopyMagickString(resource_info->image_info->magick,"MIFF",
13186         MaxTextExtent);
13187       (void) CopyMagickString(resource_info->image_info->filename,filename,
13188         MaxTextExtent);
13189       tile_image=ReadImage(resource_info->image_info,exception);
13190       CatchException(exception);
13191       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13192       break;
13193     }
13194     case TileNextCommand:
13195     {
13196       /*
13197         Display next image.
13198       */
13199       XClientMessage(display,windows->image.id,windows->im_protocols,
13200         windows->im_next_image,CurrentTime);
13201       break;
13202     }
13203     case TileFormerCommand:
13204     {
13205       /*
13206         Display former image.
13207       */
13208       XClientMessage(display,windows->image.id,windows->im_protocols,
13209         windows->im_former_image,CurrentTime);
13210       break;
13211     }
13212     case TileDeleteCommand:
13213     {
13214       /*
13215         Delete tile image.
13216       */
13217       if( IfMagickFalse(IsPathAccessible(filename)) )
13218         {
13219           XNoticeWidget(display,windows,"Image file does not exist:",filename);
13220           break;
13221         }
13222       status=XConfirmWidget(display,windows,"Really delete tile",filename);
13223       if (status <= 0)
13224         break;
13225       status=IsMagickTrue(remove_utf8(filename));
13226       if( IfMagickTrue(status) )
13227         {
13228           XNoticeWidget(display,windows,"Unable to delete image file:",
13229             filename);
13230           break;
13231         }
13232     }
13233     case TileUpdateCommand:
13234     {
13235       int
13236         x_offset,
13237         y_offset;
13238
13239       PixelInfo
13240         pixel;
13241
13242       register int
13243         j;
13244
13245       register Quantum
13246         *s;
13247
13248       /*
13249         Ensure all the images exist.
13250       */
13251       tile=0;
13252       GetPixelInfo(image,&pixel);
13253       for (p=image->directory; *p != '\0'; p++)
13254       {
13255         CacheView
13256           *image_view;
13257
13258         q=p;
13259         while ((*q != '\n') && (*q != '\0'))
13260           q++;
13261         (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13262         p=q;
13263         if( IfMagickTrue(IsPathAccessible(filename)) )
13264           {
13265             tile++;
13266             continue;
13267           }
13268         /*
13269           Overwrite tile with background color.
13270         */
13271         x_offset=(int) (width*(tile % (((int) image->columns-x)/width))+x);
13272         y_offset=(int) (height*(tile/(((int) image->columns-x)/width))+y);
13273         image_view=AcquireAuthenticCacheView(image,exception);
13274         (void) GetOneCacheViewVirtualPixelInfo(image_view,0,0,&pixel,exception);
13275         for (i=0; i < (int) height; i++)
13276         {
13277           s=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,(ssize_t)
13278             y_offset+i,width,1,exception);
13279           if (s == (Quantum *) NULL)
13280             break;
13281           for (j=0; j < (int) width; j++)
13282           {
13283             SetPixelInfoPixel(image,&pixel,s);
13284             s+=GetPixelChannels(image);
13285           }
13286           if( IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
13287             break;
13288         }
13289         image_view=DestroyCacheView(image_view);
13290         tile++;
13291       }
13292       windows->image.window_changes.width=(int) image->columns;
13293       windows->image.window_changes.height=(int) image->rows;
13294       XConfigureImageColormap(display,resource_info,windows,image,exception);
13295       (void) XConfigureImage(display,resource_info,windows,image,exception);
13296       break;
13297     }
13298     default:
13299       break;
13300   }
13301   XSetCursorState(display,windows,MagickFalse);
13302   return(tile_image);
13303 }
13304 \f
13305 /*
13306 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13307 %                                                                             %
13308 %                                                                             %
13309 %                                                                             %
13310 +   X T r a n s l a t e I m a g e                                             %
13311 %                                                                             %
13312 %                                                                             %
13313 %                                                                             %
13314 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13315 %
13316 %  XTranslateImage() translates the image within an Image window by one pixel
13317 %  as specified by the key symbol.  If the image has a montage string the
13318 %  translation is respect to the width and height contained within the string.
13319 %
13320 %  The format of the XTranslateImage method is:
13321 %
13322 %      void XTranslateImage(Display *display,XWindows *windows,
13323 %        Image *image,const KeySym key_symbol)
13324 %
13325 %  A description of each parameter follows:
13326 %
13327 %    o display: Specifies a connection to an X server; returned from
13328 %      XOpenDisplay.
13329 %
13330 %    o windows: Specifies a pointer to a XWindows structure.
13331 %
13332 %    o image: the image.
13333 %
13334 %    o key_symbol: Specifies a KeySym which indicates which side of the image
13335 %      to trim.
13336 %
13337 */
13338 static void XTranslateImage(Display *display,XWindows *windows,
13339   Image *image,const KeySym key_symbol)
13340 {
13341   char
13342     text[MaxTextExtent];
13343
13344   int
13345     x,
13346     y;
13347
13348   unsigned int
13349     x_offset,
13350     y_offset;
13351
13352   /*
13353     User specified a pan position offset.
13354   */
13355   x_offset=windows->image.width;
13356   y_offset=windows->image.height;
13357   if (image->montage != (char *) NULL)
13358     (void) XParseGeometry(image->montage,&x,&y,&x_offset,&y_offset);
13359   switch ((int) key_symbol)
13360   {
13361     case XK_Home:
13362     case XK_KP_Home:
13363     {
13364       windows->image.x=(int) windows->image.width/2;
13365       windows->image.y=(int) windows->image.height/2;
13366       break;
13367     }
13368     case XK_Left:
13369     case XK_KP_Left:
13370     {
13371       windows->image.x-=x_offset;
13372       break;
13373     }
13374     case XK_Next:
13375     case XK_Up:
13376     case XK_KP_Up:
13377     {
13378       windows->image.y-=y_offset;
13379       break;
13380     }
13381     case XK_Right:
13382     case XK_KP_Right:
13383     {
13384       windows->image.x+=x_offset;
13385       break;
13386     }
13387     case XK_Prior:
13388     case XK_Down:
13389     case XK_KP_Down:
13390     {
13391       windows->image.y+=y_offset;
13392       break;
13393     }
13394     default:
13395       return;
13396   }
13397   /*
13398     Check boundary conditions.
13399   */
13400   if (windows->image.x < 0)
13401     windows->image.x=0;
13402   else
13403     if ((int) (windows->image.x+windows->image.width) >
13404         windows->image.ximage->width)
13405       windows->image.x=(int) windows->image.ximage->width-windows->image.width;
13406   if (windows->image.y < 0)
13407     windows->image.y=0;
13408   else
13409     if ((int) (windows->image.y+windows->image.height) >
13410         windows->image.ximage->height)
13411       windows->image.y=(int) windows->image.ximage->height-windows->image.height;
13412   /*
13413     Refresh Image window.
13414   */
13415   (void) FormatLocaleString(text,MaxTextExtent," %ux%u%+d%+d ",
13416     windows->image.width,windows->image.height,windows->image.x,
13417     windows->image.y);
13418   XInfoWidget(display,windows,text);
13419   XCheckRefreshWindows(display,windows);
13420   XDrawPanRectangle(display,windows);
13421   XRefreshWindow(display,&windows->image,(XEvent *) NULL);
13422   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13423 }
13424 \f
13425 /*
13426 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13427 %                                                                             %
13428 %                                                                             %
13429 %                                                                             %
13430 +   X T r i m I m a g e                                                       %
13431 %                                                                             %
13432 %                                                                             %
13433 %                                                                             %
13434 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13435 %
13436 %  XTrimImage() trims the edges from the Image window.
13437 %
13438 %  The format of the XTrimImage method is:
13439 %
13440 %      MagickBooleanType XTrimImage(Display *display,
13441 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
13442 %        ExceptionInfo *exception)
13443 %
13444 %  A description of each parameter follows:
13445 %
13446 %    o display: Specifies a connection to an X server; returned from
13447 %      XOpenDisplay.
13448 %
13449 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13450 %
13451 %    o windows: Specifies a pointer to a XWindows structure.
13452 %
13453 %    o image: the image.
13454 %
13455 %    o exception: return any errors or warnings in this structure.
13456 %
13457 */
13458 static MagickBooleanType XTrimImage(Display *display,
13459   XResourceInfo *resource_info,XWindows *windows,Image *image,
13460   ExceptionInfo *exception)
13461 {
13462   RectangleInfo
13463     trim_info;
13464
13465   register int
13466     x,
13467     y;
13468
13469   size_t
13470     background,
13471     pixel;
13472
13473   /*
13474     Trim edges from image.
13475   */
13476   XSetCursorState(display,windows,MagickTrue);
13477   XCheckRefreshWindows(display,windows);
13478   /*
13479     Crop the left edge.
13480   */
13481   background=XGetPixel(windows->image.ximage,0,0);
13482   trim_info.width=(size_t) windows->image.ximage->width;
13483   for (x=0; x < windows->image.ximage->width; x++)
13484   {
13485     for (y=0; y < windows->image.ximage->height; y++)
13486     {
13487       pixel=XGetPixel(windows->image.ximage,x,y);
13488       if (pixel != background)
13489         break;
13490     }
13491     if (y < windows->image.ximage->height)
13492       break;
13493   }
13494   trim_info.x=(ssize_t) x;
13495   if (trim_info.x == (ssize_t) windows->image.ximage->width)
13496     {
13497       XSetCursorState(display,windows,MagickFalse);
13498       return(MagickFalse);
13499     }
13500   /*
13501     Crop the right edge.
13502   */
13503   background=XGetPixel(windows->image.ximage,windows->image.ximage->width-1,0);
13504   for (x=windows->image.ximage->width-1; x != 0; x--)
13505   {
13506     for (y=0; y < windows->image.ximage->height; y++)
13507     {
13508       pixel=XGetPixel(windows->image.ximage,x,y);
13509       if (pixel != background)
13510         break;
13511     }
13512     if (y < windows->image.ximage->height)
13513       break;
13514   }
13515   trim_info.width=(size_t) (x-trim_info.x+1);
13516   /*
13517     Crop the top edge.
13518   */
13519   background=XGetPixel(windows->image.ximage,0,0);
13520   trim_info.height=(size_t) windows->image.ximage->height;
13521   for (y=0; y < windows->image.ximage->height; y++)
13522   {
13523     for (x=0; x < windows->image.ximage->width; x++)
13524     {
13525       pixel=XGetPixel(windows->image.ximage,x,y);
13526       if (pixel != background)
13527         break;
13528     }
13529     if (x < windows->image.ximage->width)
13530       break;
13531   }
13532   trim_info.y=(ssize_t) y;
13533   /*
13534     Crop the bottom edge.
13535   */
13536   background=XGetPixel(windows->image.ximage,0,windows->image.ximage->height-1);
13537   for (y=windows->image.ximage->height-1; y != 0; y--)
13538   {
13539     for (x=0; x < windows->image.ximage->width; x++)
13540     {
13541       pixel=XGetPixel(windows->image.ximage,x,y);
13542       if (pixel != background)
13543         break;
13544     }
13545     if (x < windows->image.ximage->width)
13546       break;
13547   }
13548   trim_info.height=(size_t) y-trim_info.y+1;
13549   if (((unsigned int) trim_info.width != windows->image.width) ||
13550       ((unsigned int) trim_info.height != windows->image.height))
13551     {
13552       /*
13553         Reconfigure Image window as defined by the trimming rectangle.
13554       */
13555       XSetCropGeometry(display,windows,&trim_info,image);
13556       windows->image.window_changes.width=(int) trim_info.width;
13557       windows->image.window_changes.height=(int) trim_info.height;
13558       (void) XConfigureImage(display,resource_info,windows,image,exception);
13559     }
13560   XSetCursorState(display,windows,MagickFalse);
13561   return(MagickTrue);
13562 }
13563 \f
13564 /*
13565 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13566 %                                                                             %
13567 %                                                                             %
13568 %                                                                             %
13569 +   X V i s u a l D i r e c t o r y I m a g e                                 %
13570 %                                                                             %
13571 %                                                                             %
13572 %                                                                             %
13573 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13574 %
13575 %  XVisualDirectoryImage() creates a Visual Image Directory.
13576 %
13577 %  The format of the XVisualDirectoryImage method is:
13578 %
13579 %      Image *XVisualDirectoryImage(Display *display,
13580 %        XResourceInfo *resource_info,XWindows *windows,
13581 %        ExceptionInfo *exception)
13582 %
13583 %  A description of each parameter follows:
13584 %
13585 %    o display: Specifies a connection to an X server; returned from
13586 %      XOpenDisplay.
13587 %
13588 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13589 %
13590 %    o windows: Specifies a pointer to a XWindows structure.
13591 %
13592 %    o exception: return any errors or warnings in this structure.
13593 %
13594 */
13595 static Image *XVisualDirectoryImage(Display *display,
13596   XResourceInfo *resource_info,XWindows *windows,ExceptionInfo *exception)
13597 {
13598 #define TileImageTag  "Scale/Image"
13599 #define XClientName  "montage"
13600
13601   char
13602     **filelist;
13603
13604   Image
13605     *images,
13606     *montage_image,
13607     *next_image,
13608     *thumbnail_image;
13609
13610   ImageInfo
13611     *read_info;
13612
13613   int
13614     number_files;
13615
13616   MagickBooleanType
13617     backdrop;
13618
13619   MagickStatusType
13620     status;
13621
13622   MontageInfo
13623     *montage_info;
13624
13625   RectangleInfo
13626     geometry;
13627
13628   register int
13629     i;
13630
13631   static char
13632     filename[MaxTextExtent] = "\0",
13633     filenames[MaxTextExtent] = "*";
13634
13635   XResourceInfo
13636     background_resources;
13637
13638   /*
13639     Request file name from user.
13640   */
13641   XFileBrowserWidget(display,windows,"Directory",filenames);
13642   if (*filenames == '\0')
13643     return((Image *) NULL);
13644   /*
13645     Expand the filenames.
13646   */
13647   filelist=(char **) AcquireMagickMemory(sizeof(*filelist));
13648   if (filelist == (char **) NULL)
13649     {
13650       ThrowXWindowFatalException(ResourceLimitError,"MemoryAllocationFailed",
13651         filenames);
13652       return((Image *) NULL);
13653     }
13654   number_files=1;
13655   filelist[0]=filenames;
13656   status=ExpandFilenames(&number_files,&filelist);
13657   if( IfMagickFalse(status) || (number_files == 0))
13658     {
13659       if (number_files == 0)
13660         ThrowXWindowFatalException(ImageError,"NoImagesWereFound",filenames)
13661       else
13662         ThrowXWindowFatalException(ResourceLimitError,"MemoryAllocationFailed",
13663           filenames);
13664       return((Image *) NULL);
13665     }
13666   /*
13667     Set image background resources.
13668   */
13669   background_resources=(*resource_info);
13670   background_resources.window_id=AcquireString("");
13671   (void) FormatLocaleString(background_resources.window_id,MaxTextExtent,
13672     "0x%lx",windows->image.id);
13673   background_resources.backdrop=MagickTrue;
13674   /*
13675     Read each image and convert them to a tile.
13676   */
13677   backdrop=IsMagickTrue( (windows->visual_info->klass == TrueColor) ||
13678     (windows->visual_info->klass == DirectColor) );
13679   read_info=CloneImageInfo(resource_info->image_info);
13680   (void) SetImageOption(read_info,"jpeg:size","120x120");
13681   (void) CloneString(&read_info->size,DefaultTileGeometry);
13682   (void) SetImageInfoProgressMonitor(read_info,(MagickProgressMonitor) NULL,
13683     (void *) NULL);
13684   images=NewImageList();
13685   XSetCursorState(display,windows,MagickTrue);
13686   XCheckRefreshWindows(display,windows);
13687   for (i=0; i < (int) number_files; i++)
13688   {
13689     (void) CopyMagickString(read_info->filename,filelist[i],MaxTextExtent);
13690     filelist[i]=DestroyString(filelist[i]);
13691     *read_info->magick='\0';
13692     next_image=ReadImage(read_info,exception);
13693     CatchException(exception);
13694     if (next_image != (Image *) NULL)
13695       {
13696         (void) DeleteImageProperty(next_image,"label");
13697         (void) SetImageProperty(next_image,"label",InterpretImageProperties(
13698           read_info,next_image,DefaultTileLabel,exception),exception);
13699         (void) ParseRegionGeometry(next_image,read_info->size,&geometry,
13700           exception);
13701         thumbnail_image=ThumbnailImage(next_image,geometry.width,
13702           geometry.height,exception);
13703         if (thumbnail_image != (Image *) NULL)
13704           {
13705             next_image=DestroyImage(next_image);
13706             next_image=thumbnail_image;
13707           }
13708         if (backdrop)
13709           {
13710             (void) XDisplayBackgroundImage(display,&background_resources,
13711               next_image,exception);
13712             XSetCursorState(display,windows,MagickTrue);
13713           }
13714         AppendImageToList(&images,next_image);
13715         if (images->progress_monitor != (MagickProgressMonitor) NULL)
13716           {
13717             MagickBooleanType
13718               proceed;
13719
13720             proceed=SetImageProgress(images,LoadImageTag,(MagickOffsetType) i,
13721               (MagickSizeType) number_files);
13722             if( IfMagickFalse(proceed) )
13723               break;
13724           }
13725       }
13726   }
13727   filelist=(char **) RelinquishMagickMemory(filelist);
13728   if (images == (Image *) NULL)
13729     {
13730       read_info=DestroyImageInfo(read_info);
13731       XSetCursorState(display,windows,MagickFalse);
13732       ThrowXWindowFatalException(ImageError,"NoImagesWereLoaded",filenames);
13733       return((Image *) NULL);
13734     }
13735   /*
13736     Create the Visual Image Directory.
13737   */
13738   montage_info=CloneMontageInfo(read_info,(MontageInfo *) NULL);
13739   montage_info->pointsize=10;
13740   if (resource_info->font != (char *) NULL)
13741     (void) CloneString(&montage_info->font,resource_info->font);
13742   (void) CopyMagickString(montage_info->filename,filename,MaxTextExtent);
13743   montage_image=MontageImageList(read_info,montage_info,GetFirstImageInList(
13744     images),exception);
13745   images=DestroyImageList(images);
13746   montage_info=DestroyMontageInfo(montage_info);
13747   read_info=DestroyImageInfo(read_info);
13748   XSetCursorState(display,windows,MagickFalse);
13749   if (montage_image == (Image *) NULL)
13750     return(montage_image);
13751   XClientMessage(display,windows->image.id,windows->im_protocols,
13752     windows->im_next_image,CurrentTime);
13753   return(montage_image);
13754 }
13755 \f
13756 /*
13757 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13758 %                                                                             %
13759 %                                                                             %
13760 %                                                                             %
13761 %   X D i s p l a y B a c k g r o u n d I m a g e                             %
13762 %                                                                             %
13763 %                                                                             %
13764 %                                                                             %
13765 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13766 %
13767 %  XDisplayBackgroundImage() displays an image in the background of a window.
13768 %
13769 %  The format of the XDisplayBackgroundImage method is:
13770 %
13771 %      MagickBooleanType XDisplayBackgroundImage(Display *display,
13772 %        XResourceInfo *resource_info,Image *image,ExceptionInfo *exception)
13773 %
13774 %  A description of each parameter follows:
13775 %
13776 %    o display: Specifies a connection to an X server;  returned from
13777 %      XOpenDisplay.
13778 %
13779 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13780 %
13781 %    o image: the image.
13782 %
13783 %    o exception: return any errors or warnings in this structure.
13784 %
13785 */
13786 MagickExport MagickBooleanType XDisplayBackgroundImage(Display *display,
13787   XResourceInfo *resource_info,Image *image,ExceptionInfo *exception)
13788 {
13789   char
13790     geometry[MaxTextExtent],
13791     visual_type[MaxTextExtent];
13792
13793   int
13794     height,
13795     status,
13796     width;
13797
13798   RectangleInfo
13799     geometry_info;
13800
13801   static XPixelInfo
13802     pixel;
13803
13804   static XStandardColormap
13805     *map_info;
13806
13807   static XVisualInfo
13808     *visual_info = (XVisualInfo *) NULL;
13809
13810   static XWindowInfo
13811     window_info;
13812
13813   size_t
13814     delay;
13815
13816   Window
13817     root_window;
13818
13819   XGCValues
13820     context_values;
13821
13822   XResourceInfo
13823     resources;
13824
13825   XWindowAttributes
13826     window_attributes;
13827
13828   /*
13829     Determine target window.
13830   */
13831   assert(image != (Image *) NULL);
13832   assert(image->signature == MagickSignature);
13833   if( IfMagickTrue(image->debug) )
13834     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
13835   resources=(*resource_info);
13836   window_info.id=(Window) NULL;
13837   root_window=XRootWindow(display,XDefaultScreen(display));
13838   if (LocaleCompare(resources.window_id,"root") == 0)
13839     window_info.id=root_window;
13840   else
13841     {
13842       if (isdigit((unsigned char) *resources.window_id) != 0)
13843         window_info.id=XWindowByID(display,root_window,
13844           (Window) strtol((char *) resources.window_id,(char **) NULL,0));
13845       if (window_info.id == (Window) NULL)
13846         window_info.id=XWindowByName(display,root_window,resources.window_id);
13847     }
13848   if (window_info.id == (Window) NULL)
13849     {
13850       ThrowXWindowFatalException(XServerError,"NoWindowWithSpecifiedIDExists",
13851         resources.window_id);
13852       return(MagickFalse);
13853     }
13854   /*
13855     Determine window visual id.
13856   */
13857   window_attributes.width=XDisplayWidth(display,XDefaultScreen(display));
13858   window_attributes.height=XDisplayHeight(display,XDefaultScreen(display));
13859   (void) CopyMagickString(visual_type,"default",MaxTextExtent);
13860   status=XGetWindowAttributes(display,window_info.id,&window_attributes);
13861   if (status != 0)
13862     (void) FormatLocaleString(visual_type,MaxTextExtent,"0x%lx",
13863       XVisualIDFromVisual(window_attributes.visual));
13864   if (visual_info == (XVisualInfo *) NULL)
13865     {
13866       /*
13867         Allocate standard colormap.
13868       */
13869       map_info=XAllocStandardColormap();
13870       if (map_info == (XStandardColormap *) NULL)
13871         ThrowXWindowFatalException(XServerFatalError,"MemoryAllocationFailed",
13872           image->filename);
13873       map_info->colormap=(Colormap) NULL;
13874       pixel.pixels=(unsigned long *) NULL;
13875       /*
13876         Initialize visual info.
13877       */
13878       resources.map_type=(char *) NULL;
13879       resources.visual_type=visual_type;
13880       visual_info=XBestVisualInfo(display,map_info,&resources);
13881       if (visual_info == (XVisualInfo *) NULL)
13882         ThrowXWindowFatalException(XServerFatalError,"UnableToGetVisual",
13883           resources.visual_type);
13884       /*
13885         Initialize window info.
13886       */
13887       window_info.ximage=(XImage *) NULL;
13888       window_info.matte_image=(XImage *) NULL;
13889       window_info.pixmap=(Pixmap) NULL;
13890       window_info.matte_pixmap=(Pixmap) NULL;
13891     }
13892   /*
13893     Free previous root colors.
13894   */
13895   if (window_info.id == root_window)
13896     (void) XDestroyWindowColors(display,root_window);
13897   /*
13898     Initialize Standard Colormap.
13899   */
13900   resources.colormap=SharedColormap;
13901   XMakeStandardColormap(display,visual_info,&resources,image,map_info,&pixel,
13902     exception);
13903   /*
13904     Graphic context superclass.
13905   */
13906   context_values.background=pixel.background_color.pixel;
13907   context_values.foreground=pixel.foreground_color.pixel;
13908   pixel.annotate_context=XCreateGC(display,window_info.id,
13909     (size_t) (GCBackground | GCForeground),&context_values);
13910   if (pixel.annotate_context == (GC) NULL)
13911     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
13912       image->filename);
13913   /*
13914     Initialize Image window attributes.
13915   */
13916   window_info.name=AcquireString("\0");
13917   window_info.icon_name=AcquireString("\0");
13918   XGetWindowInfo(display,visual_info,map_info,&pixel,(XFontStruct *) NULL,
13919     &resources,&window_info);
13920   /*
13921     Create the X image.
13922   */
13923   window_info.width=(unsigned int) image->columns;
13924   window_info.height=(unsigned int) image->rows;
13925   if ((image->columns != window_info.width) ||
13926       (image->rows != window_info.height))
13927     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13928       image->filename);
13929   (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>",
13930     window_attributes.width,window_attributes.height);
13931   geometry_info.width=window_info.width;
13932   geometry_info.height=window_info.height;
13933   geometry_info.x=(ssize_t) window_info.x;
13934   geometry_info.y=(ssize_t) window_info.y;
13935   (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
13936     &geometry_info.width,&geometry_info.height);
13937   window_info.width=(unsigned int) geometry_info.width;
13938   window_info.height=(unsigned int) geometry_info.height;
13939   window_info.x=(int) geometry_info.x;
13940   window_info.y=(int) geometry_info.y;
13941   status=XMakeImage(display,&resources,&window_info,image,window_info.width,
13942     window_info.height,exception);
13943   if( IfMagickFalse(status) )
13944     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13945       image->filename);
13946   window_info.x=0;
13947   window_info.y=0;
13948   if( IfMagickTrue(image->debug) )
13949     {
13950       (void) LogMagickEvent(X11Event,GetMagickModule(),
13951         "Image: %s[%.20g] %.20gx%.20g ",image->filename,(double) image->scene,
13952         (double) image->columns,(double) image->rows);
13953       if (image->colors != 0)
13954         (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
13955           image->colors);
13956       (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",image->magick);
13957     }
13958   /*
13959     Adjust image dimensions as specified by backdrop or geometry options.
13960   */
13961   width=(int) window_info.width;
13962   height=(int) window_info.height;
13963   if( IfMagickTrue(resources.backdrop) )
13964     {
13965       /*
13966         Center image on window.
13967       */
13968       window_info.x=(window_attributes.width/2)-
13969         (window_info.ximage->width/2);
13970       window_info.y=(window_attributes.height/2)-
13971         (window_info.ximage->height/2);
13972       width=window_attributes.width;
13973       height=window_attributes.height;
13974     }
13975   if ((resources.image_geometry != (char *) NULL) &&
13976       (*resources.image_geometry != '\0'))
13977     {
13978       char
13979         default_geometry[MaxTextExtent];
13980
13981       int
13982         flags,
13983         gravity;
13984
13985       XSizeHints
13986         *size_hints;
13987
13988       /*
13989         User specified geometry.
13990       */
13991       size_hints=XAllocSizeHints();
13992       if (size_hints == (XSizeHints *) NULL)
13993         ThrowXWindowFatalException(ResourceLimitFatalError,
13994           "MemoryAllocationFailed",image->filename);
13995       size_hints->flags=0L;
13996       (void) FormatLocaleString(default_geometry,MaxTextExtent,"%dx%d",
13997         width,height);
13998       flags=XWMGeometry(display,visual_info->screen,resources.image_geometry,
13999         default_geometry,window_info.border_width,size_hints,&window_info.x,
14000         &window_info.y,&width,&height,&gravity);
14001       if (flags & (XValue | YValue))
14002         {
14003           width=window_attributes.width;
14004           height=window_attributes.height;
14005         }
14006       (void) XFree((void *) size_hints);
14007     }
14008   /*
14009     Create the X pixmap.
14010   */
14011   window_info.pixmap=XCreatePixmap(display,window_info.id,(unsigned int) width,
14012     (unsigned int) height,window_info.depth);
14013   if (window_info.pixmap == (Pixmap) NULL)
14014     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXPixmap",
14015       image->filename);
14016   /*
14017     Display pixmap on the window.
14018   */
14019   if (((unsigned int) width > window_info.width) ||
14020       ((unsigned int) height > window_info.height))
14021     (void) XFillRectangle(display,window_info.pixmap,
14022       window_info.annotate_context,0,0,(unsigned int) width,
14023       (unsigned int) height);
14024   (void) XPutImage(display,window_info.pixmap,window_info.annotate_context,
14025     window_info.ximage,0,0,window_info.x,window_info.y,(unsigned int)
14026     window_info.width,(unsigned int) window_info.height);
14027   (void) XSetWindowBackgroundPixmap(display,window_info.id,window_info.pixmap);
14028   (void) XClearWindow(display,window_info.id);
14029   delay=1000*image->delay/MagickMax(image->ticks_per_second,1L);
14030   XDelay(display,delay == 0UL ? 10UL : delay);
14031   (void) XSync(display,MagickFalse);
14032   return(IsMagickTrue(window_info.id == root_window));
14033 }
14034 \f
14035 /*
14036 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
14037 %                                                                             %
14038 %                                                                             %
14039 %                                                                             %
14040 +   X D i s p l a y I m a g e                                                 %
14041 %                                                                             %
14042 %                                                                             %
14043 %                                                                             %
14044 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
14045 %
14046 %  XDisplayImage() displays an image via X11.  A new image is created and
14047 %  returned if the user interactively transforms the displayed image.
14048 %
14049 %  The format of the XDisplayImage method is:
14050 %
14051 %      Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
14052 %        char **argv,int argc,Image **image,size_t *state,
14053 %        ExceptionInfo *exception)
14054 %
14055 %  A description of each parameter follows:
14056 %
14057 %    o nexus:  Method XDisplayImage returns an image when the
14058 %      user chooses 'Open Image' from the command menu or picks a tile
14059 %      from the image directory.  Otherwise a null image is returned.
14060 %
14061 %    o display: Specifies a connection to an X server;  returned from
14062 %      XOpenDisplay.
14063 %
14064 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
14065 %
14066 %    o argv: Specifies the application's argument list.
14067 %
14068 %    o argc: Specifies the number of arguments.
14069 %
14070 %    o image: Specifies an address to an address of an Image structure;
14071 %
14072 %    o exception: return any errors or warnings in this structure.
14073 %
14074 */
14075 MagickExport Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
14076   char **argv,int argc,Image **image,size_t *state,ExceptionInfo *exception)
14077 {
14078 #define MagnifySize  256  /* must be a power of 2 */
14079 #define MagickMenus  10
14080 #define MagickTitle  "Commands"
14081
14082   static const char
14083     *CommandMenu[] =
14084     {
14085       "File",
14086       "Edit",
14087       "View",
14088       "Transform",
14089       "Enhance",
14090       "Effects",
14091       "F/X",
14092       "Image Edit",
14093       "Miscellany",
14094       "Help",
14095       (char *) NULL
14096     },
14097     *FileMenu[] =
14098     {
14099       "Open...",
14100       "Next",
14101       "Former",
14102       "Select...",
14103       "Save...",
14104       "Print...",
14105       "Delete...",
14106       "New...",
14107       "Visual Directory...",
14108       "Quit",
14109       (char *) NULL
14110     },
14111     *EditMenu[] =
14112     {
14113       "Undo",
14114       "Redo",
14115       "Cut",
14116       "Copy",
14117       "Paste",
14118       (char *) NULL
14119     },
14120     *ViewMenu[] =
14121     {
14122       "Half Size",
14123       "Original Size",
14124       "Double Size",
14125       "Resize...",
14126       "Apply",
14127       "Refresh",
14128       "Restore",
14129       (char *) NULL
14130     },
14131     *TransformMenu[] =
14132     {
14133       "Crop",
14134       "Chop",
14135       "Flop",
14136       "Flip",
14137       "Rotate Right",
14138       "Rotate Left",
14139       "Rotate...",
14140       "Shear...",
14141       "Roll...",
14142       "Trim Edges",
14143       (char *) NULL
14144     },
14145     *EnhanceMenu[] =
14146     {
14147       "Hue...",
14148       "Saturation...",
14149       "Brightness...",
14150       "Gamma...",
14151       "Spiff",
14152       "Dull",
14153       "Contrast Stretch...",
14154       "Sigmoidal Contrast...",
14155       "Normalize",
14156       "Equalize",
14157       "Negate",
14158       "Grayscale",
14159       "Map...",
14160       "Quantize...",
14161       (char *) NULL
14162     },
14163     *EffectsMenu[] =
14164     {
14165       "Despeckle",
14166       "Emboss",
14167       "Reduce Noise",
14168       "Add Noise...",
14169       "Sharpen...",
14170       "Blur...",
14171       "Threshold...",
14172       "Edge Detect...",
14173       "Spread...",
14174       "Shade...",
14175       "Raise...",
14176       "Segment...",
14177       (char *) NULL
14178     },
14179     *FXMenu[] =
14180     {
14181       "Solarize...",
14182       "Sepia Tone...",
14183       "Swirl...",
14184       "Implode...",
14185       "Vignette...",
14186       "Wave...",
14187       "Oil Paint...",
14188       "Charcoal Draw...",
14189       (char *) NULL
14190     },
14191     *ImageEditMenu[] =
14192     {
14193       "Annotate...",
14194       "Draw...",
14195       "Color...",
14196       "Matte...",
14197       "Composite...",
14198       "Add Border...",
14199       "Add Frame...",
14200       "Comment...",
14201       "Launch...",
14202       "Region of Interest...",
14203       (char *) NULL
14204     },
14205     *MiscellanyMenu[] =
14206     {
14207       "Image Info",
14208       "Zoom Image",
14209       "Show Preview...",
14210       "Show Histogram",
14211       "Show Matte",
14212       "Background...",
14213       "Slide Show...",
14214       "Preferences...",
14215       (char *) NULL
14216     },
14217     *HelpMenu[] =
14218     {
14219       "Overview",
14220       "Browse Documentation",
14221       "About Display",
14222       (char *) NULL
14223     },
14224     *ShortCutsMenu[] =
14225     {
14226       "Next",
14227       "Former",
14228       "Open...",
14229       "Save...",
14230       "Print...",
14231       "Undo",
14232       "Restore",
14233       "Image Info",
14234       "Quit",
14235       (char *) NULL
14236     },
14237     *VirtualMenu[] =
14238     {
14239       "Image Info",
14240       "Print",
14241       "Next",
14242       "Quit",
14243       (char *) NULL
14244     };
14245
14246   static const char
14247     **Menus[MagickMenus] =
14248     {
14249       FileMenu,
14250       EditMenu,
14251       ViewMenu,
14252       TransformMenu,
14253       EnhanceMenu,
14254       EffectsMenu,
14255       FXMenu,
14256       ImageEditMenu,
14257       MiscellanyMenu,
14258       HelpMenu
14259     };
14260
14261   static CommandType
14262     CommandMenus[] =
14263     {
14264       NullCommand,
14265       NullCommand,
14266       NullCommand,
14267       NullCommand,
14268       NullCommand,
14269       NullCommand,
14270       NullCommand,
14271       NullCommand,
14272       NullCommand,
14273       NullCommand,
14274     },
14275     FileCommands[] =
14276     {
14277       OpenCommand,
14278       NextCommand,
14279       FormerCommand,
14280       SelectCommand,
14281       SaveCommand,
14282       PrintCommand,
14283       DeleteCommand,
14284       NewCommand,
14285       VisualDirectoryCommand,
14286       QuitCommand
14287     },
14288     EditCommands[] =
14289     {
14290       UndoCommand,
14291       RedoCommand,
14292       CutCommand,
14293       CopyCommand,
14294       PasteCommand
14295     },
14296     ViewCommands[] =
14297     {
14298       HalfSizeCommand,
14299       OriginalSizeCommand,
14300       DoubleSizeCommand,
14301       ResizeCommand,
14302       ApplyCommand,
14303       RefreshCommand,
14304       RestoreCommand
14305     },
14306     TransformCommands[] =
14307     {
14308       CropCommand,
14309       ChopCommand,
14310       FlopCommand,
14311       FlipCommand,
14312       RotateRightCommand,
14313       RotateLeftCommand,
14314       RotateCommand,
14315       ShearCommand,
14316       RollCommand,
14317       TrimCommand
14318     },
14319     EnhanceCommands[] =
14320     {
14321       HueCommand,
14322       SaturationCommand,
14323       BrightnessCommand,
14324       GammaCommand,
14325       SpiffCommand,
14326       DullCommand,
14327       ContrastStretchCommand,
14328       SigmoidalContrastCommand,
14329       NormalizeCommand,
14330       EqualizeCommand,
14331       NegateCommand,
14332       GrayscaleCommand,
14333       MapCommand,
14334       QuantizeCommand
14335     },
14336     EffectsCommands[] =
14337     {
14338       DespeckleCommand,
14339       EmbossCommand,
14340       ReduceNoiseCommand,
14341       AddNoiseCommand,
14342       SharpenCommand,
14343       BlurCommand,
14344       ThresholdCommand,
14345       EdgeDetectCommand,
14346       SpreadCommand,
14347       ShadeCommand,
14348       RaiseCommand,
14349       SegmentCommand
14350     },
14351     FXCommands[] =
14352     {
14353       SolarizeCommand,
14354       SepiaToneCommand,
14355       SwirlCommand,
14356       ImplodeCommand,
14357       VignetteCommand,
14358       WaveCommand,
14359       OilPaintCommand,
14360       CharcoalDrawCommand
14361     },
14362     ImageEditCommands[] =
14363     {
14364       AnnotateCommand,
14365       DrawCommand,
14366       ColorCommand,
14367       MatteCommand,
14368       CompositeCommand,
14369       AddBorderCommand,
14370       AddFrameCommand,
14371       CommentCommand,
14372       LaunchCommand,
14373       RegionofInterestCommand
14374     },
14375     MiscellanyCommands[] =
14376     {
14377       InfoCommand,
14378       ZoomCommand,
14379       ShowPreviewCommand,
14380       ShowHistogramCommand,
14381       ShowMatteCommand,
14382       BackgroundCommand,
14383       SlideShowCommand,
14384       PreferencesCommand
14385     },
14386     HelpCommands[] =
14387     {
14388       HelpCommand,
14389       BrowseDocumentationCommand,
14390       VersionCommand
14391     },
14392     ShortCutsCommands[] =
14393     {
14394       NextCommand,
14395       FormerCommand,
14396       OpenCommand,
14397       SaveCommand,
14398       PrintCommand,
14399       UndoCommand,
14400       RestoreCommand,
14401       InfoCommand,
14402       QuitCommand
14403     },
14404     VirtualCommands[] =
14405     {
14406       InfoCommand,
14407       PrintCommand,
14408       NextCommand,
14409       QuitCommand
14410     };
14411
14412   static CommandType
14413     *Commands[MagickMenus] =
14414     {
14415       FileCommands,
14416       EditCommands,
14417       ViewCommands,
14418       TransformCommands,
14419       EnhanceCommands,
14420       EffectsCommands,
14421       FXCommands,
14422       ImageEditCommands,
14423       MiscellanyCommands,
14424       HelpCommands
14425     };
14426
14427   char
14428     command[MaxTextExtent],
14429     *directory,
14430     geometry[MaxTextExtent],
14431     resource_name[MaxTextExtent];
14432
14433   CommandType
14434     command_type;
14435
14436   Image
14437     *display_image,
14438     *nexus;
14439
14440   int
14441     entry,
14442     id;
14443
14444   KeySym
14445     key_symbol;
14446
14447   MagickStatusType
14448     context_mask,
14449     status;
14450
14451   RectangleInfo
14452     geometry_info;
14453
14454   register int
14455     i;
14456
14457   static char
14458     working_directory[MaxTextExtent];
14459
14460   static XPoint
14461     vid_info;
14462
14463   static XWindowInfo
14464     *magick_windows[MaxXWindows];
14465
14466   static unsigned int
14467     number_windows;
14468
14469   struct stat
14470     attributes;
14471
14472   time_t
14473     timer,
14474     timestamp,
14475     update_time;
14476
14477   unsigned int
14478     height,
14479     width;
14480
14481   size_t
14482     delay;
14483
14484   WarningHandler
14485     warning_handler;
14486
14487   Window
14488     root_window;
14489
14490   XClassHint
14491     *class_hints;
14492
14493   XEvent
14494     event;
14495
14496   XFontStruct
14497     *font_info;
14498
14499   XGCValues
14500     context_values;
14501
14502   XPixelInfo
14503     *icon_pixel,
14504     *pixel;
14505
14506   XResourceInfo
14507     *icon_resources;
14508
14509   XStandardColormap
14510     *icon_map,
14511     *map_info;
14512
14513   XVisualInfo
14514     *icon_visual,
14515     *visual_info;
14516
14517   XWindowChanges
14518     window_changes;
14519
14520   XWindows
14521     *windows;
14522
14523   XWMHints
14524     *manager_hints;
14525
14526   assert(image != (Image **) NULL);
14527   assert((*image)->signature == MagickSignature);
14528   if( IfMagickTrue((*image)->debug) )
14529     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*image)->filename);
14530   display_image=(*image);
14531   warning_handler=(WarningHandler) NULL;
14532   windows=XSetWindows((XWindows *) ~0);
14533   if (windows != (XWindows *) NULL)
14534     {
14535       int
14536         status;
14537
14538       if (*working_directory == '\0')
14539         (void) CopyMagickString(working_directory,".",MaxTextExtent);
14540       status=chdir(working_directory);
14541       if (status == -1)
14542         (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
14543           "UnableToOpenFile","%s",working_directory);
14544       warning_handler=resource_info->display_warnings ?
14545         SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
14546       warning_handler=resource_info->display_warnings ?
14547         SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
14548     }
14549   else
14550     {
14551       /*
14552         Allocate windows structure.
14553       */
14554       resource_info->colors=display_image->colors;
14555       windows=XSetWindows(XInitializeWindows(display,resource_info));
14556       if (windows == (XWindows *) NULL)
14557         ThrowXWindowFatalException(XServerFatalError,"UnableToCreateWindow",
14558           (*image)->filename);
14559       /*
14560         Initialize window id's.
14561       */
14562       number_windows=0;
14563       magick_windows[number_windows++]=(&windows->icon);
14564       magick_windows[number_windows++]=(&windows->backdrop);
14565       magick_windows[number_windows++]=(&windows->image);
14566       magick_windows[number_windows++]=(&windows->info);
14567       magick_windows[number_windows++]=(&windows->command);
14568       magick_windows[number_windows++]=(&windows->widget);
14569       magick_windows[number_windows++]=(&windows->popup);
14570       magick_windows[number_windows++]=(&windows->magnify);
14571       magick_windows[number_windows++]=(&windows->pan);
14572       for (i=0; i < (int) number_windows; i++)
14573         magick_windows[i]->id=(Window) NULL;
14574       vid_info.x=0;
14575       vid_info.y=0;
14576     }
14577   /*
14578     Initialize font info.
14579   */
14580   if (windows->font_info != (XFontStruct *) NULL)
14581     (void) XFreeFont(display,windows->font_info);
14582   windows->font_info=XBestFont(display,resource_info,MagickFalse);
14583   if (windows->font_info == (XFontStruct *) NULL)
14584     ThrowXWindowFatalException(XServerFatalError,"UnableToLoadFont",
14585       resource_info->font);
14586   /*
14587     Initialize Standard Colormap.
14588   */
14589   map_info=windows->map_info;
14590   icon_map=windows->icon_map;
14591   visual_info=windows->visual_info;
14592   icon_visual=windows->icon_visual;
14593   pixel=windows->pixel_info;
14594   icon_pixel=windows->icon_pixel;
14595   font_info=windows->font_info;
14596   icon_resources=windows->icon_resources;
14597   class_hints=windows->class_hints;
14598   manager_hints=windows->manager_hints;
14599   root_window=XRootWindow(display,visual_info->screen);
14600   nexus=NewImageList();
14601   if( IfMagickTrue(display_image->debug) )
14602     {
14603       (void) LogMagickEvent(X11Event,GetMagickModule(),
14604         "Image: %s[%.20g] %.20gx%.20g ",display_image->filename,
14605         (double) display_image->scene,(double) display_image->columns,
14606         (double) display_image->rows);
14607       if (display_image->colors != 0)
14608         (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
14609           display_image->colors);
14610       (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",
14611         display_image->magick);
14612     }
14613   XMakeStandardColormap(display,visual_info,resource_info,display_image,
14614     map_info,pixel,exception);
14615   display_image->taint=MagickFalse;
14616   /*
14617     Initialize graphic context.
14618   */
14619   windows->context.id=(Window) NULL;
14620   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14621     resource_info,&windows->context);
14622   (void) CloneString(&class_hints->res_name,resource_info->client_name);
14623   (void) CloneString(&class_hints->res_class,resource_info->client_name);
14624   class_hints->res_class[0]=(char) toupper((int) class_hints->res_class[0]);
14625   manager_hints->flags=InputHint | StateHint;
14626   manager_hints->input=MagickFalse;
14627   manager_hints->initial_state=WithdrawnState;
14628   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14629     &windows->context);
14630   if( IfMagickTrue(display_image->debug) )
14631     (void) LogMagickEvent(X11Event,GetMagickModule(),
14632       "Window id: 0x%lx (context)",windows->context.id);
14633   context_values.background=pixel->background_color.pixel;
14634   context_values.font=font_info->fid;
14635   context_values.foreground=pixel->foreground_color.pixel;
14636   context_values.graphics_exposures=MagickFalse;
14637   context_mask=(MagickStatusType)
14638     (GCBackground | GCFont | GCForeground | GCGraphicsExposures);
14639   if (pixel->annotate_context != (GC) NULL)
14640     (void) XFreeGC(display,pixel->annotate_context);
14641   pixel->annotate_context=XCreateGC(display,windows->context.id,
14642     context_mask,&context_values);
14643   if (pixel->annotate_context == (GC) NULL)
14644     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14645       display_image->filename);
14646   context_values.background=pixel->depth_color.pixel;
14647   if (pixel->widget_context != (GC) NULL)
14648     (void) XFreeGC(display,pixel->widget_context);
14649   pixel->widget_context=XCreateGC(display,windows->context.id,context_mask,
14650     &context_values);
14651   if (pixel->widget_context == (GC) NULL)
14652     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14653       display_image->filename);
14654   context_values.background=pixel->foreground_color.pixel;
14655   context_values.foreground=pixel->background_color.pixel;
14656   context_values.plane_mask=context_values.background ^
14657     context_values.foreground;
14658   if (pixel->highlight_context != (GC) NULL)
14659     (void) XFreeGC(display,pixel->highlight_context);
14660   pixel->highlight_context=XCreateGC(display,windows->context.id,
14661     (size_t) (context_mask | GCPlaneMask),&context_values);
14662   if (pixel->highlight_context == (GC) NULL)
14663     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14664       display_image->filename);
14665   (void) XDestroyWindow(display,windows->context.id);
14666   /*
14667     Initialize icon window.
14668   */
14669   XGetWindowInfo(display,icon_visual,icon_map,icon_pixel,(XFontStruct *) NULL,
14670     icon_resources,&windows->icon);
14671   windows->icon.geometry=resource_info->icon_geometry;
14672   XBestIconSize(display,&windows->icon,display_image);
14673   windows->icon.attributes.colormap=XDefaultColormap(display,
14674     icon_visual->screen);
14675   windows->icon.attributes.event_mask=ExposureMask | StructureNotifyMask;
14676   manager_hints->flags=InputHint | StateHint;
14677   manager_hints->input=MagickFalse;
14678   manager_hints->initial_state=IconicState;
14679   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14680     &windows->icon);
14681   if( IfMagickTrue(display_image->debug) )
14682     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (icon)",
14683       windows->icon.id);
14684   /*
14685     Initialize graphic context for icon window.
14686   */
14687   if (icon_pixel->annotate_context != (GC) NULL)
14688     (void) XFreeGC(display,icon_pixel->annotate_context);
14689   context_values.background=icon_pixel->background_color.pixel;
14690   context_values.foreground=icon_pixel->foreground_color.pixel;
14691   icon_pixel->annotate_context=XCreateGC(display,windows->icon.id,
14692     (size_t) (GCBackground | GCForeground),&context_values);
14693   if (icon_pixel->annotate_context == (GC) NULL)
14694     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14695       display_image->filename);
14696   windows->icon.annotate_context=icon_pixel->annotate_context;
14697   /*
14698     Initialize Image window.
14699   */
14700   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14701     &windows->image);
14702   windows->image.shape=MagickTrue;  /* non-rectangular shape hint */
14703   if( IfMagickFalse(resource_info->use_shared_memory) )
14704     windows->image.shared_memory=MagickFalse;
14705   if ((resource_info->title != (char *) NULL) && !(*state & MontageImageState))
14706     {
14707       char
14708         *title;
14709
14710       title=InterpretImageProperties(resource_info->image_info,display_image,
14711         resource_info->title,exception);
14712       (void) CopyMagickString(windows->image.name,title,MaxTextExtent);
14713       (void) CopyMagickString(windows->image.icon_name,title,MaxTextExtent);
14714       title=DestroyString(title);
14715     }
14716   else
14717     {
14718       char
14719         filename[MaxTextExtent];
14720
14721       /*
14722         Window name is the base of the filename.
14723       */
14724       GetPathComponent(display_image->magick_filename,TailPath,filename);
14725       if (display_image->scene == 0)
14726         (void) FormatLocaleString(windows->image.name,MaxTextExtent,
14727           "%s: %s",MagickPackageName,filename);
14728       else
14729         (void) FormatLocaleString(windows->image.name,MaxTextExtent,
14730           "%s: %s[scene: %.20g frames: %.20g]",MagickPackageName,filename,
14731           (double) display_image->scene,(double) GetImageListLength(
14732           display_image));
14733       (void) CopyMagickString(windows->image.icon_name,filename,MaxTextExtent);
14734     }
14735   if (resource_info->immutable)
14736     windows->image.immutable=MagickTrue;
14737   windows->image.use_pixmap=resource_info->use_pixmap;
14738   windows->image.geometry=resource_info->image_geometry;
14739   (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>!",
14740     XDisplayWidth(display,visual_info->screen),
14741     XDisplayHeight(display,visual_info->screen));
14742   geometry_info.width=display_image->columns;
14743   geometry_info.height=display_image->rows;
14744   geometry_info.x=0;
14745   geometry_info.y=0;
14746   (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
14747     &geometry_info.width,&geometry_info.height);
14748   windows->image.width=(unsigned int) geometry_info.width;
14749   windows->image.height=(unsigned int) geometry_info.height;
14750   windows->image.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14751     ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14752     KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14753     PropertyChangeMask | StructureNotifyMask | SubstructureNotifyMask;
14754   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14755     resource_info,&windows->backdrop);
14756   if ((resource_info->backdrop) || (windows->backdrop.id != (Window) NULL))
14757     {
14758       /*
14759         Initialize backdrop window.
14760       */
14761       windows->backdrop.x=0;
14762       windows->backdrop.y=0;
14763       (void) CloneString(&windows->backdrop.name,"Backdrop");
14764       windows->backdrop.flags=(size_t) (USSize | USPosition);
14765       windows->backdrop.width=(unsigned int)
14766         XDisplayWidth(display,visual_info->screen);
14767       windows->backdrop.height=(unsigned int)
14768         XDisplayHeight(display,visual_info->screen);
14769       windows->backdrop.border_width=0;
14770       windows->backdrop.immutable=MagickTrue;
14771       windows->backdrop.attributes.do_not_propagate_mask=ButtonPressMask |
14772         ButtonReleaseMask;
14773       windows->backdrop.attributes.event_mask=ButtonPressMask | KeyPressMask |
14774         StructureNotifyMask;
14775       manager_hints->flags=IconWindowHint | InputHint | StateHint;
14776       manager_hints->icon_window=windows->icon.id;
14777       manager_hints->input=MagickTrue;
14778       manager_hints->initial_state=resource_info->iconic ? IconicState :
14779         NormalState;
14780       XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14781         &windows->backdrop);
14782       if( IfMagickTrue(display_image->debug) )
14783         (void) LogMagickEvent(X11Event,GetMagickModule(),
14784           "Window id: 0x%lx (backdrop)",windows->backdrop.id);
14785       (void) XMapWindow(display,windows->backdrop.id);
14786       (void) XClearWindow(display,windows->backdrop.id);
14787       if (windows->image.id != (Window) NULL)
14788         {
14789           (void) XDestroyWindow(display,windows->image.id);
14790           windows->image.id=(Window) NULL;
14791         }
14792       /*
14793         Position image in the center the backdrop.
14794       */
14795       windows->image.flags|=USPosition;
14796       windows->image.x=(XDisplayWidth(display,visual_info->screen)/2)-
14797         (windows->image.width/2);
14798       windows->image.y=(XDisplayHeight(display,visual_info->screen)/2)-
14799         (windows->image.height/2);
14800     }
14801   manager_hints->flags=IconWindowHint | InputHint | StateHint;
14802   manager_hints->icon_window=windows->icon.id;
14803   manager_hints->input=MagickTrue;
14804   manager_hints->initial_state=resource_info->iconic ? IconicState :
14805     NormalState;
14806   if (windows->group_leader.id != (Window) NULL)
14807     {
14808       /*
14809         Follow the leader.
14810       */
14811       manager_hints->flags|=WindowGroupHint;
14812       manager_hints->window_group=windows->group_leader.id;
14813       (void) XSelectInput(display,windows->group_leader.id,StructureNotifyMask);
14814       if( IfMagickTrue(display_image->debug) )
14815         (void) LogMagickEvent(X11Event,GetMagickModule(),
14816           "Window id: 0x%lx (group leader)",windows->group_leader.id);
14817     }
14818   XMakeWindow(display,
14819     (Window) (resource_info->backdrop ? windows->backdrop.id : root_window),
14820     argv,argc,class_hints,manager_hints,&windows->image);
14821   (void) XChangeProperty(display,windows->image.id,windows->im_protocols,
14822     XA_STRING,8,PropModeReplace,(unsigned char *) NULL,0);
14823   if (windows->group_leader.id != (Window) NULL)
14824     (void) XSetTransientForHint(display,windows->image.id,
14825       windows->group_leader.id);
14826   if( IfMagickTrue(display_image->debug) )
14827     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (image)",
14828       windows->image.id);
14829   /*
14830     Initialize Info widget.
14831   */
14832   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14833     &windows->info);
14834   (void) CloneString(&windows->info.name,"Info");
14835   (void) CloneString(&windows->info.icon_name,"Info");
14836   windows->info.border_width=1;
14837   windows->info.x=2;
14838   windows->info.y=2;
14839   windows->info.flags|=PPosition;
14840   windows->info.attributes.win_gravity=UnmapGravity;
14841   windows->info.attributes.event_mask=ButtonPressMask | ExposureMask |
14842     StructureNotifyMask;
14843   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14844   manager_hints->input=MagickFalse;
14845   manager_hints->initial_state=NormalState;
14846   manager_hints->window_group=windows->image.id;
14847   XMakeWindow(display,windows->image.id,argv,argc,class_hints,manager_hints,
14848     &windows->info);
14849   windows->info.highlight_stipple=XCreateBitmapFromData(display,
14850     windows->info.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14851   windows->info.shadow_stipple=XCreateBitmapFromData(display,
14852     windows->info.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14853   (void) XSetTransientForHint(display,windows->info.id,windows->image.id);
14854   if( IfMagickTrue(windows->image.mapped) )
14855     (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14856   if( IfMagickTrue(display_image->debug) )
14857     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (info)",
14858       windows->info.id);
14859   /*
14860     Initialize Command widget.
14861   */
14862   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14863     resource_info,&windows->command);
14864   windows->command.data=MagickMenus;
14865   (void) XCommandWidget(display,windows,CommandMenu,(XEvent *) NULL);
14866   (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.command",
14867     resource_info->client_name);
14868   windows->command.geometry=XGetResourceClass(resource_info->resource_database,
14869     resource_name,"geometry",(char *) NULL);
14870   (void) CloneString(&windows->command.name,MagickTitle);
14871   windows->command.border_width=0;
14872   windows->command.flags|=PPosition;
14873   windows->command.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14874     ButtonReleaseMask | EnterWindowMask | ExposureMask | LeaveWindowMask |
14875     OwnerGrabButtonMask | 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->command);
14882   windows->command.highlight_stipple=XCreateBitmapFromData(display,
14883     windows->command.id,(char *) HighlightBitmap,HighlightWidth,
14884     HighlightHeight);
14885   windows->command.shadow_stipple=XCreateBitmapFromData(display,
14886     windows->command.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14887   (void) XSetTransientForHint(display,windows->command.id,windows->image.id);
14888   if( IfMagickTrue(windows->command.mapped) )
14889     (void) XMapRaised(display,windows->command.id);
14890   if( IfMagickTrue(display_image->debug) )
14891     (void) LogMagickEvent(X11Event,GetMagickModule(),
14892       "Window id: 0x%lx (command)",windows->command.id);
14893   /*
14894     Initialize Widget window.
14895   */
14896   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14897     resource_info,&windows->widget);
14898   (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.widget",
14899     resource_info->client_name);
14900   windows->widget.geometry=XGetResourceClass(resource_info->resource_database,
14901     resource_name,"geometry",(char *) NULL);
14902   windows->widget.border_width=0;
14903   windows->widget.flags|=PPosition;
14904   windows->widget.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14905     ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14906     KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14907     StructureNotifyMask;
14908   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14909   manager_hints->input=MagickTrue;
14910   manager_hints->initial_state=NormalState;
14911   manager_hints->window_group=windows->image.id;
14912   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14913     &windows->widget);
14914   windows->widget.highlight_stipple=XCreateBitmapFromData(display,
14915     windows->widget.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14916   windows->widget.shadow_stipple=XCreateBitmapFromData(display,
14917     windows->widget.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14918   (void) XSetTransientForHint(display,windows->widget.id,windows->image.id);
14919   if( IfMagickTrue(display_image->debug) )
14920     (void) LogMagickEvent(X11Event,GetMagickModule(),
14921       "Window id: 0x%lx (widget)",windows->widget.id);
14922   /*
14923     Initialize popup window.
14924   */
14925   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14926     resource_info,&windows->popup);
14927   windows->popup.border_width=0;
14928   windows->popup.flags|=PPosition;
14929   windows->popup.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14930     ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14931     KeyReleaseMask | LeaveWindowMask | StructureNotifyMask;
14932   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14933   manager_hints->input=MagickTrue;
14934   manager_hints->initial_state=NormalState;
14935   manager_hints->window_group=windows->image.id;
14936   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14937     &windows->popup);
14938   windows->popup.highlight_stipple=XCreateBitmapFromData(display,
14939     windows->popup.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14940   windows->popup.shadow_stipple=XCreateBitmapFromData(display,
14941     windows->popup.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14942   (void) XSetTransientForHint(display,windows->popup.id,windows->image.id);
14943   if( IfMagickTrue(display_image->debug) )
14944     (void) LogMagickEvent(X11Event,GetMagickModule(),
14945       "Window id: 0x%lx (pop up)",windows->popup.id);
14946   /*
14947     Initialize Magnify window and cursor.
14948   */
14949   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14950     resource_info,&windows->magnify);
14951   if( IfMagickFalse(resource_info->use_shared_memory) )
14952     windows->magnify.shared_memory=MagickFalse;
14953   (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.magnify",
14954     resource_info->client_name);
14955   windows->magnify.geometry=XGetResourceClass(resource_info->resource_database,
14956     resource_name,"geometry",(char *) NULL);
14957   (void) FormatLocaleString(windows->magnify.name,MaxTextExtent,"Magnify %uX",
14958     resource_info->magnify);
14959   if (windows->magnify.cursor != (Cursor) NULL)
14960     (void) XFreeCursor(display,windows->magnify.cursor);
14961   windows->magnify.cursor=XMakeCursor(display,windows->image.id,
14962     map_info->colormap,resource_info->background_color,
14963     resource_info->foreground_color);
14964   if (windows->magnify.cursor == (Cursor) NULL)
14965     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateCursor",
14966       display_image->filename);
14967   windows->magnify.width=MagnifySize;
14968   windows->magnify.height=MagnifySize;
14969   windows->magnify.flags|=PPosition;
14970   windows->magnify.min_width=MagnifySize;
14971   windows->magnify.min_height=MagnifySize;
14972   windows->magnify.width_inc=MagnifySize;
14973   windows->magnify.height_inc=MagnifySize;
14974   windows->magnify.data=resource_info->magnify;
14975   windows->magnify.attributes.cursor=windows->magnify.cursor;
14976   windows->magnify.attributes.event_mask=ButtonPressMask | ButtonReleaseMask |
14977     ExposureMask | KeyPressMask | KeyReleaseMask | OwnerGrabButtonMask |
14978     StructureNotifyMask;
14979   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14980   manager_hints->input=MagickTrue;
14981   manager_hints->initial_state=NormalState;
14982   manager_hints->window_group=windows->image.id;
14983   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14984     &windows->magnify);
14985   if( IfMagickTrue(display_image->debug) )
14986     (void) LogMagickEvent(X11Event,GetMagickModule(),
14987       "Window id: 0x%lx (magnify)",windows->magnify.id);
14988   (void) XSetTransientForHint(display,windows->magnify.id,windows->image.id);
14989   /*
14990     Initialize panning window.
14991   */
14992   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14993     resource_info,&windows->pan);
14994   (void) CloneString(&windows->pan.name,"Pan Icon");
14995   windows->pan.width=windows->icon.width;
14996   windows->pan.height=windows->icon.height;
14997   (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.pan",
14998     resource_info->client_name);
14999   windows->pan.geometry=XGetResourceClass(resource_info->resource_database,
15000     resource_name,"geometry",(char *) NULL);
15001   (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
15002     &windows->pan.width,&windows->pan.height);
15003   windows->pan.flags|=PPosition;
15004   windows->pan.immutable=MagickTrue;
15005   windows->pan.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
15006     ButtonReleaseMask | ExposureMask | KeyPressMask | KeyReleaseMask |
15007     StructureNotifyMask;
15008   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
15009   manager_hints->input=MagickFalse;
15010   manager_hints->initial_state=NormalState;
15011   manager_hints->window_group=windows->image.id;
15012   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
15013     &windows->pan);
15014   if( IfMagickTrue(display_image->debug) )
15015     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (pan)",
15016       windows->pan.id);
15017   (void) XSetTransientForHint(display,windows->pan.id,windows->image.id);
15018   if( IfMagickTrue(windows->info.mapped) )
15019     (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15020   if( IfMagickFalse(windows->image.mapped) ||
15021       (windows->backdrop.id != (Window) NULL))
15022     (void) XMapWindow(display,windows->image.id);
15023   /*
15024     Set our progress monitor and warning handlers.
15025   */
15026   if (warning_handler == (WarningHandler) NULL)
15027     {
15028       warning_handler=resource_info->display_warnings ?
15029         SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
15030       warning_handler=resource_info->display_warnings ?
15031         SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
15032     }
15033   /*
15034     Initialize Image and Magnify X images.
15035   */
15036   windows->image.x=0;
15037   windows->image.y=0;
15038   windows->magnify.shape=MagickFalse;
15039   width=(unsigned int) display_image->columns;
15040   height=(unsigned int) display_image->rows;
15041   if ((display_image->columns != width) || (display_image->rows != height))
15042     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15043       display_image->filename);
15044   status=XMakeImage(display,resource_info,&windows->image,display_image,
15045     width,height,exception);
15046   if( IfMagickFalse(status) )
15047     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15048       display_image->filename);
15049   status=XMakeImage(display,resource_info,&windows->magnify,(Image *) NULL,
15050     windows->magnify.width,windows->magnify.height,exception);
15051   if( IfMagickFalse(status) )
15052     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15053       display_image->filename);
15054   if( IfMagickTrue(windows->magnify.mapped) )
15055     (void) XMapRaised(display,windows->magnify.id);
15056   if( IfMagickTrue(windows->pan.mapped) )
15057     (void) XMapRaised(display,windows->pan.id);
15058   windows->image.window_changes.width=(int) display_image->columns;
15059   windows->image.window_changes.height=(int) display_image->rows;
15060   (void) XConfigureImage(display,resource_info,windows,display_image,exception);
15061   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15062   (void) XSync(display,MagickFalse);
15063   /*
15064     Respond to events.
15065   */
15066   delay=display_image->delay/MagickMax(display_image->ticks_per_second,1L);
15067   timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15068   update_time=0;
15069   if( IfMagickTrue(resource_info->update) )
15070     {
15071       MagickBooleanType
15072         status;
15073
15074       /*
15075         Determine when file data was last modified.
15076       */
15077       status=GetPathAttributes(display_image->filename,&attributes);
15078       if( IfMagickTrue(status) )
15079         update_time=attributes.st_mtime;
15080     }
15081   *state&=(~FormerImageState);
15082   *state&=(~MontageImageState);
15083   *state&=(~NextImageState);
15084   do
15085   {
15086     /*
15087       Handle a window event.
15088     */
15089     if( IfMagickTrue(windows->image.mapped) )
15090       if ((display_image->delay != 0) || (resource_info->update != 0))
15091         {
15092           if (timer < time((time_t *) NULL))
15093             {
15094               if( IfMagickFalse(resource_info->update) )
15095                 *state|=NextImageState | ExitState;
15096               else
15097                 {
15098                   MagickBooleanType
15099                     status;
15100
15101                   /*
15102                     Determine if image file was modified.
15103                   */
15104                   status=GetPathAttributes(display_image->filename,&attributes);
15105                   if( IfMagickTrue(status) )
15106                     if (update_time != attributes.st_mtime)
15107                       {
15108                         /*
15109                           Redisplay image.
15110                         */
15111                         (void) FormatLocaleString(
15112                           resource_info->image_info->filename,MaxTextExtent,
15113                           "%s:%s",display_image->magick,
15114                           display_image->filename);
15115                         nexus=ReadImage(resource_info->image_info,exception);
15116                         if (nexus != (Image *) NULL)
15117                           {
15118                             nexus=DestroyImage(nexus);
15119                             *state|=NextImageState | ExitState;
15120                           }
15121                       }
15122                   delay=display_image->delay/MagickMax(
15123                     display_image->ticks_per_second,1L);
15124                   timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15125                 }
15126             }
15127           if (XEventsQueued(display,QueuedAfterFlush) == 0)
15128             {
15129               /*
15130                 Do not block if delay > 0.
15131               */
15132               XDelay(display,SuspendTime << 2);
15133               continue;
15134             }
15135         }
15136     timestamp=time((time_t *) NULL);
15137     (void) XNextEvent(display,&event);
15138     if( IfMagickFalse(windows->image.stasis) )
15139       windows->image.stasis=IsMagickTrue((time((time_t *) NULL)-timestamp) > 0);
15140     if( IfMagickFalse(windows->magnify.stasis) )
15141       windows->magnify.stasis=IsMagickTrue((time((time_t *) NULL)-timestamp) > 0);
15142     if (event.xany.window == windows->command.id)
15143       {
15144         /*
15145           Select a command from the Command widget.
15146         */
15147         id=XCommandWidget(display,windows,CommandMenu,&event);
15148         if (id < 0)
15149           continue;
15150         (void) CopyMagickString(command,CommandMenu[id],MaxTextExtent);
15151         command_type=CommandMenus[id];
15152         if (id < MagickMenus)
15153           {
15154             /*
15155               Select a command from a pop-up menu.
15156             */
15157             entry=XMenuWidget(display,windows,CommandMenu[id],Menus[id],
15158               command);
15159             if (entry < 0)
15160               continue;
15161             (void) CopyMagickString(command,Menus[id][entry],MaxTextExtent);
15162             command_type=Commands[id][entry];
15163           }
15164         if (command_type != NullCommand)
15165           nexus=XMagickCommand(display,resource_info,windows,command_type,
15166             &display_image,exception);
15167         continue;
15168       }
15169     switch (event.type)
15170     {
15171       case ButtonPress:
15172       {
15173         if( IfMagickTrue(display_image->debug) )
15174           (void) LogMagickEvent(X11Event,GetMagickModule(),
15175             "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
15176             event.xbutton.button,event.xbutton.x,event.xbutton.y);
15177         if ((event.xbutton.button == Button3) &&
15178             (event.xbutton.state & Mod1Mask))
15179           {
15180             /*
15181               Convert Alt-Button3 to Button2.
15182             */
15183             event.xbutton.button=Button2;
15184             event.xbutton.state&=(~Mod1Mask);
15185           }
15186         if (event.xbutton.window == windows->backdrop.id)
15187           {
15188             (void) XSetInputFocus(display,event.xbutton.window,RevertToParent,
15189               event.xbutton.time);
15190             break;
15191           }
15192         if (event.xbutton.window == windows->image.id)
15193           {
15194             switch (event.xbutton.button)
15195             {
15196               case Button1:
15197               {
15198                 if (resource_info->immutable)
15199                   {
15200                     /*
15201                       Select a command from the Virtual menu.
15202                     */
15203                     entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15204                       command);
15205                     if (entry >= 0)
15206                       nexus=XMagickCommand(display,resource_info,windows,
15207                         VirtualCommands[entry],&display_image,exception);
15208                     break;
15209                   }
15210                 /*
15211                   Map/unmap Command widget.
15212                 */
15213                 if( IfMagickTrue(windows->command.mapped) )
15214                   (void) XWithdrawWindow(display,windows->command.id,
15215                     windows->command.screen);
15216                 else
15217                   {
15218                     (void) XCommandWidget(display,windows,CommandMenu,
15219                       (XEvent *) NULL);
15220                     (void) XMapRaised(display,windows->command.id);
15221                   }
15222                 break;
15223               }
15224               case Button2:
15225               {
15226                 /*
15227                   User pressed the image magnify button.
15228                 */
15229                 (void) XMagickCommand(display,resource_info,windows,ZoomCommand,
15230                   &display_image,exception);
15231                 XMagnifyImage(display,windows,&event,exception);
15232                 break;
15233               }
15234               case Button3:
15235               {
15236                 if (resource_info->immutable)
15237                   {
15238                     /*
15239                       Select a command from the Virtual menu.
15240                     */
15241                     entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15242                       command);
15243                     if (entry >= 0)
15244                       nexus=XMagickCommand(display,resource_info,windows,
15245                         VirtualCommands[entry],&display_image,exception);
15246                     break;
15247                   }
15248                 if (display_image->montage != (char *) NULL)
15249                   {
15250                     /*
15251                       Open or delete a tile from a visual image directory.
15252                     */
15253                     nexus=XTileImage(display,resource_info,windows,
15254                       display_image,&event,exception);
15255                     if (nexus != (Image *) NULL)
15256                       *state|=MontageImageState | NextImageState | ExitState;
15257                     vid_info.x=(short int) windows->image.x;
15258                     vid_info.y=(short int) windows->image.y;
15259                     break;
15260                   }
15261                 /*
15262                   Select a command from the Short Cuts menu.
15263                 */
15264                 entry=XMenuWidget(display,windows,"Short Cuts",ShortCutsMenu,
15265                   command);
15266                 if (entry >= 0)
15267                   nexus=XMagickCommand(display,resource_info,windows,
15268                     ShortCutsCommands[entry],&display_image,exception);
15269                 break;
15270               }
15271               case Button4:
15272               {
15273                 /*
15274                   Wheel up.
15275                 */
15276                 XTranslateImage(display,windows,*image,XK_Up);
15277                 break;
15278               }
15279               case Button5:
15280               {
15281                 /*
15282                   Wheel down.
15283                 */
15284                 XTranslateImage(display,windows,*image,XK_Down);
15285                 break;
15286               }
15287               default:
15288                 break;
15289             }
15290             break;
15291           }
15292         if (event.xbutton.window == windows->magnify.id)
15293           {
15294             int
15295               factor;
15296
15297             static const char
15298               *MagnifyMenu[] =
15299               {
15300                 "2",
15301                 "4",
15302                 "5",
15303                 "6",
15304                 "7",
15305                 "8",
15306                 "9",
15307                 "3",
15308                 (char *) NULL,
15309               };
15310
15311             static KeySym
15312               MagnifyCommands[] =
15313               {
15314                 XK_2,
15315                 XK_4,
15316                 XK_5,
15317                 XK_6,
15318                 XK_7,
15319                 XK_8,
15320                 XK_9,
15321                 XK_3
15322               };
15323
15324             /*
15325               Select a magnify factor from the pop-up menu.
15326             */
15327             factor=XMenuWidget(display,windows,"Magnify",MagnifyMenu,command);
15328             if (factor >= 0)
15329               XMagnifyWindowCommand(display,windows,0,MagnifyCommands[factor],
15330                 exception);
15331             break;
15332           }
15333         if (event.xbutton.window == windows->pan.id)
15334           {
15335             switch (event.xbutton.button)
15336             {
15337               case Button4:
15338               {
15339                 /*
15340                   Wheel up.
15341                 */
15342                 XTranslateImage(display,windows,*image,XK_Up);
15343                 break;
15344               }
15345               case Button5:
15346               {
15347                 /*
15348                   Wheel down.
15349                 */
15350                 XTranslateImage(display,windows,*image,XK_Down);
15351                 break;
15352               }
15353               default:
15354               {
15355                 XPanImage(display,windows,&event,exception);
15356                 break;
15357               }
15358             }
15359             break;
15360           }
15361         delay=display_image->delay/MagickMax(display_image->ticks_per_second,
15362           1L);
15363         timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15364         break;
15365       }
15366       case ButtonRelease:
15367       {
15368         if( IfMagickTrue(display_image->debug) )
15369           (void) LogMagickEvent(X11Event,GetMagickModule(),
15370             "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
15371             event.xbutton.button,event.xbutton.x,event.xbutton.y);
15372         break;
15373       }
15374       case ClientMessage:
15375       {
15376         if( IfMagickTrue(display_image->debug) )
15377           (void) LogMagickEvent(X11Event,GetMagickModule(),
15378             "Client Message: 0x%lx 0x%lx %d 0x%lx",event.xclient.window,
15379             event.xclient.message_type,event.xclient.format,(unsigned long)
15380             event.xclient.data.l[0]);
15381         if (event.xclient.message_type == windows->im_protocols)
15382           {
15383             if (*event.xclient.data.l == (long) windows->im_update_widget)
15384               {
15385                 (void) CloneString(&windows->command.name,MagickTitle);
15386                 windows->command.data=MagickMenus;
15387                 (void) XCommandWidget(display,windows,CommandMenu,
15388                   (XEvent *) NULL);
15389                 break;
15390               }
15391             if (*event.xclient.data.l == (long) windows->im_update_colormap)
15392               {
15393                 /*
15394                   Update graphic context and window colormap.
15395                 */
15396                 for (i=0; i < (int) number_windows; i++)
15397                 {
15398                   if (magick_windows[i]->id == windows->icon.id)
15399                     continue;
15400                   context_values.background=pixel->background_color.pixel;
15401                   context_values.foreground=pixel->foreground_color.pixel;
15402                   (void) XChangeGC(display,magick_windows[i]->annotate_context,
15403                     context_mask,&context_values);
15404                   (void) XChangeGC(display,magick_windows[i]->widget_context,
15405                     context_mask,&context_values);
15406                   context_values.background=pixel->foreground_color.pixel;
15407                   context_values.foreground=pixel->background_color.pixel;
15408                   context_values.plane_mask=context_values.background ^
15409                     context_values.foreground;
15410                   (void) XChangeGC(display,magick_windows[i]->highlight_context,
15411                     (size_t) (context_mask | GCPlaneMask),
15412                     &context_values);
15413                   magick_windows[i]->attributes.background_pixel=
15414                     pixel->background_color.pixel;
15415                   magick_windows[i]->attributes.border_pixel=
15416                     pixel->border_color.pixel;
15417                   magick_windows[i]->attributes.colormap=map_info->colormap;
15418                   (void) XChangeWindowAttributes(display,magick_windows[i]->id,
15419                     (unsigned long) magick_windows[i]->mask,
15420                     &magick_windows[i]->attributes);
15421                 }
15422                 if( IfMagickTrue(windows->pan.mapped) )
15423                   {
15424                     (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
15425                       windows->pan.pixmap);
15426                     (void) XClearWindow(display,windows->pan.id);
15427                     XDrawPanRectangle(display,windows);
15428                   }
15429                 if (windows->backdrop.id != (Window) NULL)
15430                   (void) XInstallColormap(display,map_info->colormap);
15431                 break;
15432               }
15433             if (*event.xclient.data.l == (long) windows->im_former_image)
15434               {
15435                 *state|=FormerImageState | ExitState;
15436                 break;
15437               }
15438             if (*event.xclient.data.l == (long) windows->im_next_image)
15439               {
15440                 *state|=NextImageState | ExitState;
15441                 break;
15442               }
15443             if (*event.xclient.data.l == (long) windows->im_retain_colors)
15444               {
15445                 *state|=RetainColorsState;
15446                 break;
15447               }
15448             if (*event.xclient.data.l == (long) windows->im_exit)
15449               {
15450                 *state|=ExitState;
15451                 break;
15452               }
15453             break;
15454           }
15455         if (event.xclient.message_type == windows->dnd_protocols)
15456           {
15457             Atom
15458               selection,
15459               type;
15460
15461             int
15462               format,
15463               status;
15464
15465             unsigned char
15466               *data;
15467
15468             unsigned long
15469               after,
15470               length;
15471
15472             /*
15473               Display image named by the Drag-and-Drop selection.
15474             */
15475             if ((*event.xclient.data.l != 2) && (*event.xclient.data.l != 128))
15476               break;
15477             selection=XInternAtom(display,"DndSelection",MagickFalse);
15478             status=XGetWindowProperty(display,root_window,selection,0L,(long)
15479               MaxTextExtent,MagickFalse,(Atom) AnyPropertyType,&type,&format,
15480               &length,&after,&data);
15481             if ((status != Success) || (length == 0))
15482               break;
15483             if (*event.xclient.data.l == 2)
15484               {
15485                 /*
15486                   Offix DND.
15487                 */
15488                 (void) CopyMagickString(resource_info->image_info->filename,
15489                   (char *) data,MaxTextExtent);
15490               }
15491             else
15492               {
15493                 /*
15494                   XDND.
15495                 */
15496                 if (strncmp((char *) data, "file:", 5) != 0)
15497                   {
15498                     (void) XFree((void *) data);
15499                     break;
15500                   }
15501                 (void) CopyMagickString(resource_info->image_info->filename,
15502                   ((char *) data)+5,MaxTextExtent);
15503               }
15504             nexus=ReadImage(resource_info->image_info,exception);
15505             CatchException(exception);
15506             if (nexus != (Image *) NULL)
15507               *state|=NextImageState | ExitState;
15508             (void) XFree((void *) data);
15509             break;
15510           }
15511         /*
15512           If client window delete message, exit.
15513         */
15514         if (event.xclient.message_type != windows->wm_protocols)
15515           break;
15516         if (*event.xclient.data.l != (long) windows->wm_delete_window)
15517           break;
15518         (void) XWithdrawWindow(display,event.xclient.window,
15519           visual_info->screen);
15520         if (event.xclient.window == windows->image.id)
15521           {
15522             *state|=ExitState;
15523             break;
15524           }
15525         if (event.xclient.window == windows->pan.id)
15526           {
15527             /*
15528               Restore original image size when pan window is deleted.
15529             */
15530             windows->image.window_changes.width=windows->image.ximage->width;
15531             windows->image.window_changes.height=windows->image.ximage->height;
15532             (void) XConfigureImage(display,resource_info,windows,
15533               display_image,exception);
15534           }
15535         break;
15536       }
15537       case ConfigureNotify:
15538       {
15539         if( IfMagickTrue(display_image->debug) )
15540           (void) LogMagickEvent(X11Event,GetMagickModule(),
15541             "Configure Notify: 0x%lx %dx%d+%d+%d %d",event.xconfigure.window,
15542             event.xconfigure.width,event.xconfigure.height,event.xconfigure.x,
15543             event.xconfigure.y,event.xconfigure.send_event);
15544         if (event.xconfigure.window == windows->image.id)
15545           {
15546             /*
15547               Image window has a new configuration.
15548             */
15549             if (event.xconfigure.send_event != 0)
15550               {
15551                 XWindowChanges
15552                   window_changes;
15553
15554                 /*
15555                   Position the transient windows relative of the Image window.
15556                 */
15557                 if (windows->command.geometry == (char *) NULL)
15558                   if( IfMagickFalse(windows->command.mapped) )
15559                     {
15560                       windows->command.x=event.xconfigure.x-
15561                         windows->command.width-25;
15562                       windows->command.y=event.xconfigure.y;
15563                       XConstrainWindowPosition(display,&windows->command);
15564                       window_changes.x=windows->command.x;
15565                       window_changes.y=windows->command.y;
15566                       (void) XReconfigureWMWindow(display,windows->command.id,
15567                         windows->command.screen,(unsigned int) (CWX | CWY),
15568                         &window_changes);
15569                     }
15570                 if (windows->widget.geometry == (char *) NULL)
15571                   if( IfMagickFalse(windows->widget.mapped) )
15572                     {
15573                       windows->widget.x=event.xconfigure.x+
15574                         event.xconfigure.width/10;
15575                       windows->widget.y=event.xconfigure.y+
15576                         event.xconfigure.height/10;
15577                       XConstrainWindowPosition(display,&windows->widget);
15578                       window_changes.x=windows->widget.x;
15579                       window_changes.y=windows->widget.y;
15580                       (void) XReconfigureWMWindow(display,windows->widget.id,
15581                         windows->widget.screen,(unsigned int) (CWX | CWY),
15582                         &window_changes);
15583                     }
15584                 if (windows->magnify.geometry == (char *) NULL)
15585                   if( IfMagickFalse(windows->magnify.mapped) )
15586                     {
15587                       windows->magnify.x=event.xconfigure.x+
15588                         event.xconfigure.width+25;
15589                       windows->magnify.y=event.xconfigure.y;
15590                       XConstrainWindowPosition(display,&windows->magnify);
15591                       window_changes.x=windows->magnify.x;
15592                       window_changes.y=windows->magnify.y;
15593                       (void) XReconfigureWMWindow(display,windows->magnify.id,
15594                         windows->magnify.screen,(unsigned int) (CWX | CWY),
15595                         &window_changes);
15596                     }
15597                 if (windows->pan.geometry == (char *) NULL)
15598                   if( IfMagickFalse(windows->pan.mapped) )
15599                     {
15600                       windows->pan.x=event.xconfigure.x+
15601                         event.xconfigure.width+25;
15602                       windows->pan.y=event.xconfigure.y+
15603                         windows->magnify.height+50;
15604                       XConstrainWindowPosition(display,&windows->pan);
15605                       window_changes.x=windows->pan.x;
15606                       window_changes.y=windows->pan.y;
15607                       (void) XReconfigureWMWindow(display,windows->pan.id,
15608                         windows->pan.screen,(unsigned int) (CWX | CWY),
15609                         &window_changes);
15610                     }
15611               }
15612             if ((event.xconfigure.width == (int) windows->image.width) &&
15613                 (event.xconfigure.height == (int) windows->image.height))
15614               break;
15615             windows->image.width=(unsigned int) event.xconfigure.width;
15616             windows->image.height=(unsigned int) event.xconfigure.height;
15617             windows->image.x=0;
15618             windows->image.y=0;
15619             if (display_image->montage != (char *) NULL)
15620               {
15621                 windows->image.x=vid_info.x;
15622                 windows->image.y=vid_info.y;
15623               }
15624             if( IfMagickTrue(windows->image.mapped) &&
15625                 IfMagickTrue(windows->image.stasis) )
15626               {
15627                 /*
15628                   Update image window configuration.
15629                 */
15630                 windows->image.window_changes.width=event.xconfigure.width;
15631                 windows->image.window_changes.height=event.xconfigure.height;
15632                 (void) XConfigureImage(display,resource_info,windows,
15633                   display_image,exception);
15634               }
15635             /*
15636               Update pan window configuration.
15637             */
15638             if ((event.xconfigure.width < windows->image.ximage->width) ||
15639                 (event.xconfigure.height < windows->image.ximage->height))
15640               {
15641                 (void) XMapRaised(display,windows->pan.id);
15642                 XDrawPanRectangle(display,windows);
15643               }
15644             else
15645               if( IfMagickTrue(windows->pan.mapped) )
15646                 (void) XWithdrawWindow(display,windows->pan.id,
15647                   windows->pan.screen);
15648             break;
15649           }
15650         if (event.xconfigure.window == windows->magnify.id)
15651           {
15652             unsigned int
15653               magnify;
15654
15655             /*
15656               Magnify window has a new configuration.
15657             */
15658             windows->magnify.width=(unsigned int) event.xconfigure.width;
15659             windows->magnify.height=(unsigned int) event.xconfigure.height;
15660             if( IfMagickFalse(windows->magnify.mapped) )
15661               break;
15662             magnify=1;
15663             while ((int) magnify <= event.xconfigure.width)
15664               magnify<<=1;
15665             while ((int) magnify <= event.xconfigure.height)
15666               magnify<<=1;
15667             magnify>>=1;
15668             if (((int) magnify != event.xconfigure.width) ||
15669                 ((int) magnify != event.xconfigure.height))
15670               {
15671                 window_changes.width=(int) magnify;
15672                 window_changes.height=(int) magnify;
15673                 (void) XReconfigureWMWindow(display,windows->magnify.id,
15674                   windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
15675                   &window_changes);
15676                 break;
15677               }
15678             if( IfMagickTrue(windows->magnify.mapped) &&
15679                 IfMagickTrue(windows->magnify.stasis) )
15680               {
15681                 status=XMakeImage(display,resource_info,&windows->magnify,
15682                   display_image,windows->magnify.width,windows->magnify.height,
15683                   exception);
15684                 XMakeMagnifyImage(display,windows,exception);
15685               }
15686             break;
15687           }
15688         if( IfMagickTrue(windows->magnify.mapped) &&
15689             (event.xconfigure.window == windows->pan.id))
15690           {
15691             /*
15692               Pan icon window has a new configuration.
15693             */
15694             if (event.xconfigure.send_event != 0)
15695               {
15696                 windows->pan.x=event.xconfigure.x;
15697                 windows->pan.y=event.xconfigure.y;
15698               }
15699             windows->pan.width=(unsigned int) event.xconfigure.width;
15700             windows->pan.height=(unsigned int) event.xconfigure.height;
15701             break;
15702           }
15703         if (event.xconfigure.window == windows->icon.id)
15704           {
15705             /*
15706               Icon window has a new configuration.
15707             */
15708             windows->icon.width=(unsigned int) event.xconfigure.width;
15709             windows->icon.height=(unsigned int) event.xconfigure.height;
15710             break;
15711           }
15712         break;
15713       }
15714       case DestroyNotify:
15715       {
15716         /*
15717           Group leader has exited.
15718         */
15719         if( IfMagickTrue(display_image->debug) )
15720           (void) LogMagickEvent(X11Event,GetMagickModule(),
15721             "Destroy Notify: 0x%lx",event.xdestroywindow.window);
15722         if (event.xdestroywindow.window == windows->group_leader.id)
15723           {
15724             *state|=ExitState;
15725             break;
15726           }
15727         break;
15728       }
15729       case EnterNotify:
15730       {
15731         /*
15732           Selectively install colormap.
15733         */
15734         if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15735           if (event.xcrossing.mode != NotifyUngrab)
15736             XInstallColormap(display,map_info->colormap);
15737         break;
15738       }
15739       case Expose:
15740       {
15741         if( IfMagickTrue(display_image->debug) )
15742           (void) LogMagickEvent(X11Event,GetMagickModule(),
15743             "Expose: 0x%lx %dx%d+%d+%d",event.xexpose.window,
15744             event.xexpose.width,event.xexpose.height,event.xexpose.x,
15745             event.xexpose.y);
15746         /*
15747           Refresh windows that are now exposed.
15748         */
15749         if ((event.xexpose.window == windows->image.id) &&
15750             IfMagickTrue(windows->image.mapped) )
15751           {
15752             XRefreshWindow(display,&windows->image,&event);
15753             delay=display_image->delay/MagickMax(
15754               display_image->ticks_per_second,1L);
15755             timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15756             break;
15757           }
15758         if ((event.xexpose.window == windows->magnify.id) &&
15759             IfMagickTrue(windows->magnify.mapped))
15760           {
15761             XMakeMagnifyImage(display,windows,exception);
15762             break;
15763           }
15764         if (event.xexpose.window == windows->pan.id)
15765           {
15766             XDrawPanRectangle(display,windows);
15767             break;
15768           }
15769         if (event.xexpose.window == windows->icon.id)
15770           {
15771             XRefreshWindow(display,&windows->icon,&event);
15772             break;
15773           }
15774         break;
15775       }
15776       case KeyPress:
15777       {
15778         int
15779           length;
15780
15781         /*
15782           Respond to a user key press.
15783         */
15784         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
15785           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15786         *(command+length)='\0';
15787         if( IfMagickTrue(display_image->debug) )
15788           (void) LogMagickEvent(X11Event,GetMagickModule(),
15789             "Key press: %d 0x%lx (%s)",event.xkey.state,(unsigned long)
15790             key_symbol,command);
15791         if (event.xkey.window == windows->image.id)
15792           {
15793             command_type=XImageWindowCommand(display,resource_info,windows,
15794               event.xkey.state,key_symbol,&display_image,exception);
15795             if (command_type != NullCommand)
15796               nexus=XMagickCommand(display,resource_info,windows,command_type,
15797                 &display_image,exception);
15798           }
15799         if (event.xkey.window == windows->magnify.id)
15800           XMagnifyWindowCommand(display,windows,event.xkey.state,key_symbol,
15801             exception);
15802         if (event.xkey.window == windows->pan.id)
15803           {
15804             if ((key_symbol == XK_q) || (key_symbol == XK_Escape))
15805               (void) XWithdrawWindow(display,windows->pan.id,
15806                 windows->pan.screen);
15807             else
15808               if ((key_symbol == XK_F1) || (key_symbol == XK_Help))
15809                 XTextViewWidget(display,resource_info,windows,MagickFalse,
15810                   "Help Viewer - Image Pan",ImagePanHelp);
15811               else
15812                 XTranslateImage(display,windows,*image,key_symbol);
15813           }
15814         delay=display_image->delay/MagickMax(
15815           display_image->ticks_per_second,1L);
15816         timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15817         break;
15818       }
15819       case KeyRelease:
15820       {
15821         /*
15822           Respond to a user key release.
15823         */
15824         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
15825           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15826         if( IfMagickTrue(display_image->debug) )
15827           (void) LogMagickEvent(X11Event,GetMagickModule(),
15828             "Key release: 0x%lx (%c)",(unsigned long) key_symbol,*command);
15829         break;
15830       }
15831       case LeaveNotify:
15832       {
15833         /*
15834           Selectively uninstall colormap.
15835         */
15836         if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15837           if (event.xcrossing.mode != NotifyUngrab)
15838             XUninstallColormap(display,map_info->colormap);
15839         break;
15840       }
15841       case MapNotify:
15842       {
15843         if( IfMagickTrue(display_image->debug) )
15844           (void) LogMagickEvent(X11Event,GetMagickModule(),"Map Notify: 0x%lx",
15845             event.xmap.window);
15846         if (event.xmap.window == windows->backdrop.id)
15847           {
15848             (void) XSetInputFocus(display,event.xmap.window,RevertToParent,
15849               CurrentTime);
15850             windows->backdrop.mapped=MagickTrue;
15851             break;
15852           }
15853         if (event.xmap.window == windows->image.id)
15854           {
15855             if (windows->backdrop.id != (Window) NULL)
15856               (void) XInstallColormap(display,map_info->colormap);
15857             if (LocaleCompare(display_image->magick,"LOGO") == 0)
15858               {
15859                 if (LocaleCompare(display_image->filename,"LOGO") == 0)
15860                   nexus=XOpenImage(display,resource_info,windows,MagickFalse);
15861               }
15862             if (((int) windows->image.width < windows->image.ximage->width) ||
15863                 ((int) windows->image.height < windows->image.ximage->height))
15864               (void) XMapRaised(display,windows->pan.id);
15865             windows->image.mapped=MagickTrue;
15866             break;
15867           }
15868         if (event.xmap.window == windows->magnify.id)
15869           {
15870             XMakeMagnifyImage(display,windows,exception);
15871             windows->magnify.mapped=MagickTrue;
15872             (void) XWithdrawWindow(display,windows->info.id,
15873               windows->info.screen);
15874             break;
15875           }
15876         if (event.xmap.window == windows->pan.id)
15877           {
15878             XMakePanImage(display,resource_info,windows,display_image,
15879               exception);
15880             windows->pan.mapped=MagickTrue;
15881             break;
15882           }
15883         if (event.xmap.window == windows->info.id)
15884           {
15885             windows->info.mapped=MagickTrue;
15886             break;
15887           }
15888         if (event.xmap.window == windows->icon.id)
15889           {
15890             MagickBooleanType
15891               taint;
15892
15893             /*
15894               Create an icon image.
15895             */
15896             taint=display_image->taint;
15897             XMakeStandardColormap(display,icon_visual,icon_resources,
15898               display_image,icon_map,icon_pixel,exception);
15899             (void) XMakeImage(display,icon_resources,&windows->icon,
15900               display_image,windows->icon.width,windows->icon.height,
15901               exception);
15902             display_image->taint=taint;
15903             (void) XSetWindowBackgroundPixmap(display,windows->icon.id,
15904               windows->icon.pixmap);
15905             (void) XClearWindow(display,windows->icon.id);
15906             (void) XWithdrawWindow(display,windows->info.id,
15907               windows->info.screen);
15908             windows->icon.mapped=MagickTrue;
15909             break;
15910           }
15911         if (event.xmap.window == windows->command.id)
15912           {
15913             windows->command.mapped=MagickTrue;
15914             break;
15915           }
15916         if (event.xmap.window == windows->popup.id)
15917           {
15918             windows->popup.mapped=MagickTrue;
15919             break;
15920           }
15921         if (event.xmap.window == windows->widget.id)
15922           {
15923             windows->widget.mapped=MagickTrue;
15924             break;
15925           }
15926         break;
15927       }
15928       case MappingNotify:
15929       {
15930         (void) XRefreshKeyboardMapping(&event.xmapping);
15931         break;
15932       }
15933       case NoExpose:
15934         break;
15935       case PropertyNotify:
15936       {
15937         Atom
15938           type;
15939
15940         int
15941           format,
15942           status;
15943
15944         unsigned char
15945           *data;
15946
15947         unsigned long
15948           after,
15949           length;
15950
15951         if( IfMagickTrue(display_image->debug) )
15952           (void) LogMagickEvent(X11Event,GetMagickModule(),
15953             "Property Notify: 0x%lx 0x%lx %d",event.xproperty.window,
15954             event.xproperty.atom,event.xproperty.state);
15955         if (event.xproperty.atom != windows->im_remote_command)
15956           break;
15957         /*
15958           Display image named by the remote command protocol.
15959         */
15960         status=XGetWindowProperty(display,event.xproperty.window,
15961           event.xproperty.atom,0L,(long) MaxTextExtent,MagickFalse,(Atom)
15962           AnyPropertyType,&type,&format,&length,&after,&data);
15963         if ((status != Success) || (length == 0))
15964           break;
15965         if (LocaleCompare((char *) data,"-quit") == 0)
15966           {
15967             XClientMessage(display,windows->image.id,windows->im_protocols,
15968               windows->im_exit,CurrentTime);
15969             (void) XFree((void *) data);
15970             break;
15971           }
15972         (void) CopyMagickString(resource_info->image_info->filename,
15973           (char *) data,MaxTextExtent);
15974         (void) XFree((void *) data);
15975         nexus=ReadImage(resource_info->image_info,exception);
15976         CatchException(exception);
15977         if (nexus != (Image *) NULL)
15978           *state|=NextImageState | ExitState;
15979         break;
15980       }
15981       case ReparentNotify:
15982       {
15983         if( IfMagickTrue(display_image->debug) )
15984           (void) LogMagickEvent(X11Event,GetMagickModule(),
15985             "Reparent Notify: 0x%lx=>0x%lx",event.xreparent.parent,
15986             event.xreparent.window);
15987         break;
15988       }
15989       case UnmapNotify:
15990       {
15991         if( IfMagickTrue(display_image->debug) )
15992           (void) LogMagickEvent(X11Event,GetMagickModule(),
15993             "Unmap Notify: 0x%lx",event.xunmap.window);
15994         if (event.xunmap.window == windows->backdrop.id)
15995           {
15996             windows->backdrop.mapped=MagickFalse;
15997             break;
15998           }
15999         if (event.xunmap.window == windows->image.id)
16000           {
16001             windows->image.mapped=MagickFalse;
16002             break;
16003           }
16004         if (event.xunmap.window == windows->magnify.id)
16005           {
16006             windows->magnify.mapped=MagickFalse;
16007             break;
16008           }
16009         if (event.xunmap.window == windows->pan.id)
16010           {
16011             windows->pan.mapped=MagickFalse;
16012             break;
16013           }
16014         if (event.xunmap.window == windows->info.id)
16015           {
16016             windows->info.mapped=MagickFalse;
16017             break;
16018           }
16019         if (event.xunmap.window == windows->icon.id)
16020           {
16021             if (map_info->colormap == icon_map->colormap)
16022               XConfigureImageColormap(display,resource_info,windows,
16023                 display_image,exception);
16024             (void) XFreeStandardColormap(display,icon_visual,icon_map,
16025               icon_pixel);
16026             windows->icon.mapped=MagickFalse;
16027             break;
16028           }
16029         if (event.xunmap.window == windows->command.id)
16030           {
16031             windows->command.mapped=MagickFalse;
16032             break;
16033           }
16034         if (event.xunmap.window == windows->popup.id)
16035           {
16036             if (windows->backdrop.id != (Window) NULL)
16037               (void) XSetInputFocus(display,windows->image.id,RevertToParent,
16038                 CurrentTime);
16039             windows->popup.mapped=MagickFalse;
16040             break;
16041           }
16042         if (event.xunmap.window == windows->widget.id)
16043           {
16044             if (windows->backdrop.id != (Window) NULL)
16045               (void) XSetInputFocus(display,windows->image.id,RevertToParent,
16046                 CurrentTime);
16047             windows->widget.mapped=MagickFalse;
16048             break;
16049           }
16050         break;
16051       }
16052       default:
16053       {
16054         if( IfMagickTrue(display_image->debug) )
16055           (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
16056             event.type);
16057         break;
16058       }
16059     }
16060   } while (!(*state & ExitState));
16061   if ((*state & ExitState) == 0)
16062     (void) XMagickCommand(display,resource_info,windows,FreeBuffersCommand,
16063       &display_image,exception);
16064   else
16065     if( IfMagickTrue(resource_info->confirm_edit) )
16066       {
16067         /*
16068           Query user if image has changed.
16069         */
16070         if( IfMagickFalse(resource_info->immutable) &&
16071             IfMagickTrue(display_image->taint))
16072           {
16073             int
16074               status;
16075
16076             status=XConfirmWidget(display,windows,"Your image changed.",
16077               "Do you want to save it");
16078             if (status == 0)
16079               *state&=(~ExitState);
16080             else
16081               if (status > 0)
16082                 (void) XMagickCommand(display,resource_info,windows,SaveCommand,
16083                   &display_image,exception);
16084           }
16085       }
16086   if ((windows->visual_info->klass == GrayScale) ||
16087       (windows->visual_info->klass == PseudoColor) ||
16088       (windows->visual_info->klass == DirectColor))
16089     {
16090       /*
16091         Withdraw pan and Magnify window.
16092       */
16093       if( IfMagickTrue(windows->info.mapped) )
16094         (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
16095       if( IfMagickTrue(windows->magnify.mapped) )
16096         (void) XWithdrawWindow(display,windows->magnify.id,
16097           windows->magnify.screen);
16098       if( IfMagickTrue(windows->command.mapped) )
16099         (void) XWithdrawWindow(display,windows->command.id,
16100           windows->command.screen);
16101     }
16102   if( IfMagickTrue(windows->pan.mapped) )
16103     (void) XWithdrawWindow(display,windows->pan.id,windows->pan.screen);
16104   if( IfMagickFalse(resource_info->backdrop) )
16105     if (windows->backdrop.mapped)
16106       {
16107         (void) XWithdrawWindow(display,windows->backdrop.id,
16108           windows->backdrop.screen);
16109         (void) XDestroyWindow(display,windows->backdrop.id);
16110         windows->backdrop.id=(Window) NULL;
16111         (void) XWithdrawWindow(display,windows->image.id,
16112           windows->image.screen);
16113         (void) XDestroyWindow(display,windows->image.id);
16114         windows->image.id=(Window) NULL;
16115       }
16116   XSetCursorState(display,windows,MagickTrue);
16117   XCheckRefreshWindows(display,windows);
16118   if (((*state & FormerImageState) != 0) || ((*state & NextImageState) != 0))
16119     *state&=(~ExitState);
16120   if (*state & ExitState)
16121     {
16122       /*
16123         Free Standard Colormap.
16124       */
16125       (void) XFreeStandardColormap(display,icon_visual,icon_map,icon_pixel);
16126       if (resource_info->map_type == (char *) NULL)
16127         (void) XFreeStandardColormap(display,visual_info,map_info,pixel);
16128       /*
16129         Free X resources.
16130       */
16131       if (resource_info->copy_image != (Image *) NULL)
16132         {
16133           resource_info->copy_image=DestroyImage(resource_info->copy_image);
16134           resource_info->copy_image=NewImageList();
16135         }
16136       DestroyXResources();
16137     }
16138   (void) XSync(display,MagickFalse);
16139   /*
16140     Restore our progress monitor and warning handlers.
16141   */
16142   (void) SetErrorHandler(warning_handler);
16143   (void) SetWarningHandler(warning_handler);
16144   /*
16145     Change to home directory.
16146   */
16147   directory=getcwd(working_directory,MaxTextExtent);
16148   (void) directory;
16149   {
16150     int
16151       status;
16152
16153     if (*resource_info->home_directory == '\0')
16154       (void) CopyMagickString(resource_info->home_directory,".",MaxTextExtent);
16155     status=chdir(resource_info->home_directory);
16156     if (status == -1)
16157       (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
16158         "UnableToOpenFile","%s",resource_info->home_directory);
16159   }
16160   *image=display_image;
16161   return(nexus);
16162 }
16163 #else
16164 \f
16165 /*
16166 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16167 %                                                                             %
16168 %                                                                             %
16169 %                                                                             %
16170 +   D i s p l a y I m a g e s                                                 %
16171 %                                                                             %
16172 %                                                                             %
16173 %                                                                             %
16174 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16175 %
16176 %  DisplayImages() displays an image sequence to any X window screen.  It
16177 %  returns a value other than 0 if successful.  Check the exception member
16178 %  of image to determine the reason for any failure.
16179 %
16180 %  The format of the DisplayImages method is:
16181 %
16182 %      MagickBooleanType DisplayImages(const ImageInfo *image_info,
16183 %        Image *images,ExceptionInfo *exception)
16184 %
16185 %  A description of each parameter follows:
16186 %
16187 %    o image_info: the image info.
16188 %
16189 %    o image: the image.
16190 %
16191 %    o exception: return any errors or warnings in this structure.
16192 %
16193 */
16194 MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
16195   Image *image,ExceptionInfo *exception)
16196 {
16197   assert(image_info != (const ImageInfo *) NULL);
16198   assert(image_info->signature == MagickSignature);
16199   assert(image != (Image *) NULL);
16200   assert(image->signature == MagickSignature);
16201   if( IfMagickTrue(image->debug) )
16202     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
16203   (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16204     "DelegateLibrarySupportNotBuiltIn","'%s' (X11)",image->filename);
16205   return(MagickFalse);
16206 }
16207 \f
16208 /*
16209 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16210 %                                                                             %
16211 %                                                                             %
16212 %                                                                             %
16213 +   R e m o t e D i s p l a y C o m m a n d                                   %
16214 %                                                                             %
16215 %                                                                             %
16216 %                                                                             %
16217 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16218 %
16219 %  RemoteDisplayCommand() encourages a remote display program to display the
16220 %  specified image filename.
16221 %
16222 %  The format of the RemoteDisplayCommand method is:
16223 %
16224 %      MagickBooleanType RemoteDisplayCommand(const ImageInfo *image,
16225 %        const char *window,const char *filename,ExceptionInfo *exception)
16226 %
16227 %  A description of each parameter follows:
16228 %
16229 %    o image_info: the image info.
16230 %
16231 %    o window: Specifies the name or id of an X window.
16232 %
16233 %    o filename: the name of the image filename to display.
16234 %
16235 %    o exception: return any errors or warnings in this structure.
16236 %
16237 */
16238 MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
16239   const char *window,const char *filename,ExceptionInfo *exception)
16240 {
16241   assert(image_info != (const ImageInfo *) NULL);
16242   assert(image_info->signature == MagickSignature);
16243   assert(filename != (char *) NULL);
16244   (void) window;
16245   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
16246   (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16247     "DelegateLibrarySupportNotBuiltIn","'%s' (X11)",image_info->filename);
16248   return(MagickFalse);
16249 }
16250 #endif