]> granicus.if.org Git - imagemagick/blob - MagickCore/display.c
Make MagickMax()/MagickMin() a macro rather than an inline.
[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 %                                  Cristy                                     %
17 %                                July 1992                                    %
18 %                                                                             %
19 %                                                                             %
20 %  Copyright 1999-2015 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/channel.h"
49 #include "MagickCore/client.h"
50 #include "MagickCore/color.h"
51 #include "MagickCore/colorspace.h"
52 #include "MagickCore/composite.h"
53 #include "MagickCore/constitute.h"
54 #include "MagickCore/decorate.h"
55 #include "MagickCore/delegate.h"
56 #include "MagickCore/display.h"
57 #include "MagickCore/display-private.h"
58 #include "MagickCore/distort.h"
59 #include "MagickCore/draw.h"
60 #include "MagickCore/effect.h"
61 #include "MagickCore/enhance.h"
62 #include "MagickCore/exception.h"
63 #include "MagickCore/exception-private.h"
64 #include "MagickCore/fx.h"
65 #include "MagickCore/geometry.h"
66 #include "MagickCore/image.h"
67 #include "MagickCore/image-private.h"
68 #include "MagickCore/list.h"
69 #include "MagickCore/log.h"
70 #include "MagickCore/magick.h"
71 #include "MagickCore/memory_.h"
72 #include "MagickCore/monitor.h"
73 #include "MagickCore/monitor-private.h"
74 #include "MagickCore/montage.h"
75 #include "MagickCore/nt-base-private.h"
76 #include "MagickCore/option.h"
77 #include "MagickCore/paint.h"
78 #include "MagickCore/pixel.h"
79 #include "MagickCore/pixel-accessor.h"
80 #include "MagickCore/PreRvIcccm.h"
81 #include "MagickCore/property.h"
82 #include "MagickCore/quantum.h"
83 #include "MagickCore/quantum-private.h"
84 #include "MagickCore/resize.h"
85 #include "MagickCore/resource_.h"
86 #include "MagickCore/shear.h"
87 #include "MagickCore/segment.h"
88 #include "MagickCore/statistic.h"
89 #include "MagickCore/string_.h"
90 #include "MagickCore/string-private.h"
91 #include "MagickCore/transform.h"
92 #include "MagickCore/threshold.h"
93 #include "MagickCore/utility.h"
94 #include "MagickCore/utility-private.h"
95 #include "MagickCore/version.h"
96 #include "MagickCore/widget.h"
97 #include "MagickCore/widget-private.h"
98 #include "MagickCore/xwindow.h"
99 #include "MagickCore/xwindow-private.h"
100 \f
101 #if defined(MAGICKCORE_X11_DELEGATE)
102 /*
103   Define declarations.
104 */
105 #define MaxColors  MagickMin((ssize_t) windows->visual_info->colormap_size,256L)
106 \f
107 /*
108   Constant declarations.
109 */
110 static const unsigned char
111   HighlightBitmap[8] =
112   {
113     0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55
114   },
115   OpaqueBitmap[8] =
116   {
117     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
118   },
119   ShadowBitmap[8] =
120   {
121     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
122   };
123
124 static const char
125   *PageSizes[] =
126   {
127     "Letter",
128     "Tabloid",
129     "Ledger",
130     "Legal",
131     "Statement",
132     "Executive",
133     "A3",
134     "A4",
135     "A5",
136     "B4",
137     "B5",
138     "Folio",
139     "Quarto",
140     "10x14",
141     (char *) NULL
142   };
143 \f
144 /*
145   Help widget declarations.
146 */
147 static const char
148   *ImageAnnotateHelp[] =
149   {
150     "In annotate mode, the Command widget has these options:",
151     "",
152     "    Font Name",
153     "      fixed",
154     "      variable",
155     "      5x8",
156     "      6x10",
157     "      7x13bold",
158     "      8x13bold",
159     "      9x15bold",
160     "      10x20",
161     "      12x24",
162     "      Browser...",
163     "    Font Color",
164     "      black",
165     "      blue",
166     "      cyan",
167     "      green",
168     "      gray",
169     "      red",
170     "      magenta",
171     "      yellow",
172     "      white",
173     "      transparent",
174     "      Browser...",
175     "    Font Color",
176     "      black",
177     "      blue",
178     "      cyan",
179     "      green",
180     "      gray",
181     "      red",
182     "      magenta",
183     "      yellow",
184     "      white",
185     "      transparent",
186     "      Browser...",
187     "    Rotate Text",
188     "      -90",
189     "      -45",
190     "      -30",
191     "      0",
192     "      30",
193     "      45",
194     "      90",
195     "      180",
196     "      Dialog...",
197     "    Help",
198     "    Dismiss",
199     "",
200     "Choose a font name from the Font Name sub-menu.  Additional",
201     "font names can be specified with the font browser.  You can",
202     "change the menu names by setting the X resources font1",
203     "through font9.",
204     "",
205     "Choose a font color from the Font Color sub-menu.",
206     "Additional font colors can be specified with the color",
207     "browser.  You can change the menu colors by setting the X",
208     "resources pen1 through pen9.",
209     "",
210     "If you select the color browser and press Grab, you can",
211     "choose the font color by moving the pointer to the desired",
212     "color on the screen and press any button.",
213     "",
214     "If you choose to rotate the text, choose Rotate Text from the",
215     "menu and select an angle.  Typically you will only want to",
216     "rotate one line of text at a time.  Depending on the angle you",
217     "choose, subsequent lines may end up overwriting each other.",
218     "",
219     "Choosing a font and its color is optional.  The default font",
220     "is fixed and the default color is black.  However, you must",
221     "choose a location to begin entering text and press button 1.",
222     "An underscore character will appear at the location of the",
223     "pointer.  The cursor changes to a pencil to indicate you are",
224     "in text mode.  To exit immediately, press Dismiss.",
225     "",
226     "In text mode, any key presses will display the character at",
227     "the location of the underscore and advance the underscore",
228     "cursor.  Enter your text and once completed press Apply to",
229     "finish your image annotation.  To correct errors press BACK",
230     "SPACE.  To delete an entire line of text, press DELETE.  Any",
231     "text that exceeds the boundaries of the image window is",
232     "automagically continued onto the next line.",
233     "",
234     "The actual color you request for the font is saved in the",
235     "image.  However, the color that appears in your image window",
236     "may be different.  For example, on a monochrome screen the",
237     "text will appear black or white even if you choose the color",
238     "red as the font color.  However, the image saved to a file",
239     "with -write is written with red lettering.  To assure the",
240     "correct color text in the final image, any PseudoClass image",
241     "is promoted to DirectClass (see miff(5)).  To force a",
242     "PseudoClass image to remain PseudoClass, use -colors.",
243     (char *) NULL,
244   },
245   *ImageChopHelp[] =
246   {
247     "In chop mode, the Command widget has these options:",
248     "",
249     "    Direction",
250     "      horizontal",
251     "      vertical",
252     "    Help",
253     "    Dismiss",
254     "",
255     "If the you choose the horizontal direction (this the",
256     "default), the area of the image between the two horizontal",
257     "endpoints of the chop line is removed.  Otherwise, the area",
258     "of the image between the two vertical endpoints of the chop",
259     "line is removed.",
260     "",
261     "Select a location within the image window to begin your chop,",
262     "press and hold any button.  Next, move the pointer to",
263     "another location in the image.  As you move a line will",
264     "connect the initial location and the pointer.  When you",
265     "release the button, the area within the image to chop is",
266     "determined by which direction you choose from the Command",
267     "widget.",
268     "",
269     "To cancel the image chopping, move the pointer back to the",
270     "starting point of the line and release the button.",
271     (char *) NULL,
272   },
273   *ImageColorEditHelp[] =
274   {
275     "In color edit mode, the Command widget has these options:",
276     "",
277     "    Method",
278     "      point",
279     "      replace",
280     "      floodfill",
281     "      filltoborder",
282     "      reset",
283     "    Pixel Color",
284     "      black",
285     "      blue",
286     "      cyan",
287     "      green",
288     "      gray",
289     "      red",
290     "      magenta",
291     "      yellow",
292     "      white",
293     "      Browser...",
294     "    Border Color",
295     "      black",
296     "      blue",
297     "      cyan",
298     "      green",
299     "      gray",
300     "      red",
301     "      magenta",
302     "      yellow",
303     "      white",
304     "      Browser...",
305     "    Fuzz",
306     "      0%",
307     "      2%",
308     "      5%",
309     "      10%",
310     "      15%",
311     "      Dialog...",
312     "    Undo",
313     "    Help",
314     "    Dismiss",
315     "",
316     "Choose a color editing method from the Method sub-menu",
317     "of the Command widget.  The point method recolors any pixel",
318     "selected with the pointer until the button is released.  The",
319     "replace method recolors any pixel that matches the color of",
320     "the pixel you select with a button press.  Floodfill recolors",
321     "any pixel that matches the color of the pixel you select with",
322     "a button press and is a neighbor.  Whereas filltoborder recolors",
323     "any neighbor pixel that is not the border color.  Finally reset",
324     "changes the entire image to the designated color.",
325     "",
326     "Next, choose a pixel color from the Pixel Color sub-menu.",
327     "Additional pixel colors can be specified with the color",
328     "browser.  You can change the menu colors by setting the X",
329     "resources pen1 through pen9.",
330     "",
331     "Now press button 1 to select a pixel within the image window",
332     "to change its color.  Additional pixels may be recolored as",
333     "prescribed by the method you choose.",
334     "",
335     "If the Magnify widget is mapped, it can be helpful in positioning",
336     "your pointer within the image (refer to button 2).",
337     "",
338     "The actual color you request for the pixels is saved in the",
339     "image.  However, the color that appears in your image window",
340     "may be different.  For example, on a monochrome screen the",
341     "pixel will appear black or white even if you choose the",
342     "color red as the pixel color.  However, the image saved to a",
343     "file with -write is written with red pixels.  To assure the",
344     "correct color text in the final image, any PseudoClass image",
345     "is promoted to DirectClass (see miff(5)).  To force a",
346     "PseudoClass image to remain PseudoClass, use -colors.",
347     (char *) NULL,
348   },
349   *ImageCompositeHelp[] =
350   {
351     "First a widget window is displayed requesting you to enter an",
352     "image name. Press Composite, Grab or type a file name.",
353     "Press Cancel if you choose not to create a composite image.",
354     "When you choose Grab, move the pointer to the desired window",
355     "and press any button.",
356     "",
357     "If the Composite image does not have any matte information,",
358     "you are informed and the file browser is displayed again.",
359     "Enter the name of a mask image.  The image is typically",
360     "grayscale and the same size as the composite image.  If the",
361     "image is not grayscale, it is converted to grayscale and the",
362     "resulting intensities are used as matte information.",
363     "",
364     "A small window appears showing the location of the cursor in",
365     "the image window. You are now in composite mode.  To exit",
366     "immediately, press Dismiss.  In composite mode, the Command",
367     "widget has these options:",
368     "",
369     "    Operators",
370     "      Over",
371     "      In",
372     "      Out",
373     "      Atop",
374     "      Xor",
375     "      Plus",
376     "      Minus",
377     "      Add",
378     "      Subtract",
379     "      Difference",
380     "      Multiply",
381     "      Bumpmap",
382     "      Copy",
383     "      CopyRed",
384     "      CopyGreen",
385     "      CopyBlue",
386     "      CopyOpacity",
387     "      Clear",
388     "    Dissolve",
389     "    Displace",
390     "    Help",
391     "    Dismiss",
392     "",
393     "Choose a composite operation from the Operators sub-menu of",
394     "the Command widget.  How each operator behaves is described",
395     "below.  Image window is the image currently displayed on",
396     "your X server and image is the image obtained with the File",
397     "Browser widget.",
398     "",
399     "Over     The result is the union of the two image shapes,",
400     "         with image obscuring image window in the region of",
401     "         overlap.",
402     "",
403     "In       The result is simply image cut by the shape of",
404     "         image window.  None of the image data of image",
405     "         window is in the result.",
406     "",
407     "Out      The resulting image is image with the shape of",
408     "         image window cut out.",
409     "",
410     "Atop     The result is the same shape as image image window,",
411     "         with image obscuring image window where the image",
412     "         shapes overlap.  Note this differs from over",
413     "         because the portion of image outside image window's",
414     "         shape does not appear in the result.",
415     "",
416     "Xor      The result is the image data from both image and",
417     "         image window that is outside the overlap region.",
418     "         The overlap region is blank.",
419     "",
420     "Plus     The result is just the sum of the image data.",
421     "         Output values are cropped to QuantumRange (no overflow).",
422     "",
423     "Minus    The result of image - image window, with underflow",
424     "         cropped to zero.",
425     "",
426     "Add      The result of image + image window, with overflow",
427     "         wrapping around (mod 256).",
428     "",
429     "Subtract The result of image - image window, with underflow",
430     "         wrapping around (mod 256).  The add and subtract",
431     "         operators can be used to perform reversible",
432     "         transformations.",
433     "",
434     "Difference",
435     "         The result of abs(image - image window).  This",
436     "         useful for comparing two very similar images.",
437     "",
438     "Multiply",
439     "         The result of image * image window.  This",
440     "         useful for the creation of drop-shadows.",
441     "",
442     "Bumpmap  The result of surface normals from image * image",
443     "         window.",
444     "",
445     "Copy     The resulting image is image window replaced with",
446     "         image.  Here the matte information is ignored.",
447     "",
448     "CopyRed  The red layer of the image window is replace with",
449     "         the red layer of the image.  The other layers are",
450     "         untouched.",
451     "",
452     "CopyGreen",
453     "         The green layer of the image window is replace with",
454     "         the green layer of the image.  The other layers are",
455     "         untouched.",
456     "",
457     "CopyBlue The blue layer of the image window is replace with",
458     "         the blue layer of the image.  The other layers are",
459     "         untouched.",
460     "",
461     "CopyOpacity",
462     "         The matte layer of the image window is replace with",
463     "         the matte layer of the image.  The other layers are",
464     "         untouched.",
465     "",
466     "The image compositor requires a matte, or alpha channel in",
467     "the image for some operations.  This extra channel usually",
468     "defines a mask which represents a sort of a cookie-cutter",
469     "for the image.  This the case when matte is opaque (full",
470     "coverage) for pixels inside the shape, zero outside, and",
471     "between 0 and QuantumRange on the boundary.  If image does not",
472     "have a matte channel, it is initialized with 0 for any pixel",
473     "matching in color to pixel location (0,0), otherwise QuantumRange.",
474     "",
475     "If you choose Dissolve, the composite operator becomes Over.  The",
476     "image matte channel percent transparency is initialized to factor.",
477     "The image window is initialized to (100-factor). Where factor is the",
478     "value you specify in the Dialog widget.",
479     "",
480     "Displace shifts the image pixels as defined by a displacement",
481     "map.  With this option, image is used as a displacement map.",
482     "Black, within the displacement map, is a maximum positive",
483     "displacement.  White is a maximum negative displacement and",
484     "middle gray is neutral.  The displacement is scaled to determine",
485     "the pixel shift.  By default, the displacement applies in both the",
486     "horizontal and vertical directions.  However, if you specify a mask,",
487     "image is the horizontal X displacement and mask the vertical Y",
488     "displacement.",
489     "",
490     "Note that matte information for image window is not retained",
491     "for colormapped X server visuals (e.g. StaticColor,",
492     "StaticColor, GrayScale, PseudoColor).  Correct compositing",
493     "behavior may require a TrueColor or DirectColor visual or a",
494     "Standard Colormap.",
495     "",
496     "Choosing a composite operator is optional.  The default",
497     "operator is replace.  However, you must choose a location to",
498     "composite your image and press button 1.  Press and hold the",
499     "button before releasing and an outline of the image will",
500     "appear to help you identify your location.",
501     "",
502     "The actual colors of the composite image is saved.  However,",
503     "the color that appears in image window may be different.",
504     "For example, on a monochrome screen image window will appear",
505     "black or white even though your composited image may have",
506     "many colors.  If the image is saved to a file it is written",
507     "with the correct colors.  To assure the correct colors are",
508     "saved in the final image, any PseudoClass image is promoted",
509     "to DirectClass (see miff(5)).  To force a PseudoClass image",
510     "to remain PseudoClass, use -colors.",
511     (char *) NULL,
512   },
513   *ImageCutHelp[] =
514   {
515     "In cut mode, the Command widget has these options:",
516     "",
517     "    Help",
518     "    Dismiss",
519     "",
520     "To define a cut region, press button 1 and drag.  The",
521     "cut region is defined by a highlighted rectangle that",
522     "expands or contracts as it follows the pointer.  Once you",
523     "are satisfied with the cut region, release the button.",
524     "You are now in rectify mode.  In rectify mode, the Command",
525     "widget has these options:",
526     "",
527     "    Cut",
528     "    Help",
529     "    Dismiss",
530     "",
531     "You can make adjustments by moving the pointer to one of the",
532     "cut rectangle corners, pressing a button, and dragging.",
533     "Finally, press Cut to commit your copy region.  To",
534     "exit without cutting the image, press Dismiss.",
535     (char *) NULL,
536   },
537   *ImageCopyHelp[] =
538   {
539     "In copy mode, the Command widget has these options:",
540     "",
541     "    Help",
542     "    Dismiss",
543     "",
544     "To define a copy region, press button 1 and drag.  The",
545     "copy region is defined by a highlighted rectangle that",
546     "expands or contracts as it follows the pointer.  Once you",
547     "are satisfied with the copy region, release the button.",
548     "You are now in rectify mode.  In rectify mode, the Command",
549     "widget has these options:",
550     "",
551     "    Copy",
552     "    Help",
553     "    Dismiss",
554     "",
555     "You can make adjustments by moving the pointer to one of the",
556     "copy rectangle corners, pressing a button, and dragging.",
557     "Finally, press Copy to commit your copy region.  To",
558     "exit without copying the image, press Dismiss.",
559     (char *) NULL,
560   },
561   *ImageCropHelp[] =
562   {
563     "In crop mode, the Command widget has these options:",
564     "",
565     "    Help",
566     "    Dismiss",
567     "",
568     "To define a cropping region, press button 1 and drag.  The",
569     "cropping region is defined by a highlighted rectangle that",
570     "expands or contracts as it follows the pointer.  Once you",
571     "are satisfied with the cropping region, release the button.",
572     "You are now in rectify mode.  In rectify mode, the Command",
573     "widget has these options:",
574     "",
575     "    Crop",
576     "    Help",
577     "    Dismiss",
578     "",
579     "You can make adjustments by moving the pointer to one of the",
580     "cropping rectangle corners, pressing a button, and dragging.",
581     "Finally, press Crop to commit your cropping region.  To",
582     "exit without cropping the image, press Dismiss.",
583     (char *) NULL,
584   },
585   *ImageDrawHelp[] =
586   {
587     "The cursor changes to a crosshair to indicate you are in",
588     "draw mode.  To exit immediately, press Dismiss.  In draw mode,",
589     "the Command widget has these options:",
590     "",
591     "    Element",
592     "      point",
593     "      line",
594     "      rectangle",
595     "      fill rectangle",
596     "      circle",
597     "      fill circle",
598     "      ellipse",
599     "      fill ellipse",
600     "      polygon",
601     "      fill polygon",
602     "    Color",
603     "      black",
604     "      blue",
605     "      cyan",
606     "      green",
607     "      gray",
608     "      red",
609     "      magenta",
610     "      yellow",
611     "      white",
612     "      transparent",
613     "      Browser...",
614     "    Stipple",
615     "      Brick",
616     "      Diagonal",
617     "      Scales",
618     "      Vertical",
619     "      Wavy",
620     "      Translucent",
621     "      Opaque",
622     "      Open...",
623     "    Width",
624     "      1",
625     "      2",
626     "      4",
627     "      8",
628     "      16",
629     "      Dialog...",
630     "    Undo",
631     "    Help",
632     "    Dismiss",
633     "",
634     "Choose a drawing primitive from the Element sub-menu.",
635     "",
636     "Choose a color from the Color sub-menu.  Additional",
637     "colors can be specified with the color browser.",
638     "",
639     "If you choose the color browser and press Grab, you can",
640     "select the color by moving the pointer to the desired",
641     "color on the screen and press any button.  The transparent",
642     "color updates the image matte channel and is useful for",
643     "image compositing.",
644     "",
645     "Choose a stipple, if appropriate, from the Stipple sub-menu.",
646     "Additional stipples can be specified with the file browser.",
647     "Stipples obtained from the file browser must be on disk in the",
648     "X11 bitmap format.",
649     "",
650     "Choose a width, if appropriate, from the Width sub-menu.  To",
651     "choose a specific width select the Dialog widget.",
652     "",
653     "Choose a point in the Image window and press button 1 and",
654     "hold.  Next, move the pointer to another location in the",
655     "image.  As you move, a line connects the initial location and",
656     "the pointer.  When you release the button, the image is",
657     "updated with the primitive you just drew.  For polygons, the",
658     "image is updated when you press and release the button without",
659     "moving the pointer.",
660     "",
661     "To cancel image drawing, move the pointer back to the",
662     "starting point of the line and release the button.",
663     (char *) NULL,
664   },
665   *DisplayHelp[] =
666   {
667     "BUTTONS",
668     "  The effects of each button press is described below.  Three",
669     "  buttons are required.  If you have a two button mouse,",
670     "  button 1 and 3 are returned.  Press ALT and button 3 to",
671     "  simulate button 2.",
672     "",
673     "  1    Press this button to map or unmap the Command widget.",
674     "",
675     "  2    Press and drag to define a region of the image to",
676     "       magnify.",
677     "",
678     "  3    Press and drag to choose from a select set of commands.",
679     "       This button behaves differently if the image being",
680     "       displayed is a visual image directory.  Here, choose a",
681     "       particular tile of the directory and press this button and",
682     "       drag to select a command from a pop-up menu.  Choose from",
683     "       these menu items:",
684     "",
685     "           Open",
686     "           Next",
687     "           Former",
688     "           Delete",
689     "           Update",
690     "",
691     "       If you choose Open, the image represented by the tile is",
692     "       displayed.  To return to the visual image directory, choose",
693     "       Next from the Command widget.  Next and Former moves to the",
694     "       next or former image respectively.  Choose Delete to delete",
695     "       a particular image tile.  Finally, choose Update to",
696     "       synchronize all the image tiles with their respective",
697     "       images.",
698     "",
699     "COMMAND WIDGET",
700     "  The Command widget lists a number of sub-menus and commands.",
701     "  They are",
702     "",
703     "      File",
704     "        Open...",
705     "        Next",
706     "        Former",
707     "        Select...",
708     "        Save...",
709     "        Print...",
710     "        Delete...",
711     "        New...",
712     "        Visual Directory...",
713     "        Quit",
714     "      Edit",
715     "        Undo",
716     "        Redo",
717     "        Cut",
718     "        Copy",
719     "        Paste",
720     "      View",
721     "        Half Size",
722     "        Original Size",
723     "        Double Size",
724     "        Resize...",
725     "        Apply",
726     "        Refresh",
727     "        Restore",
728     "      Transform",
729     "        Crop",
730     "        Chop",
731     "        Flop",
732     "        Flip",
733     "        Rotate Right",
734     "        Rotate Left",
735     "        Rotate...",
736     "        Shear...",
737     "        Roll...",
738     "        Trim Edges",
739     "      Enhance",
740     "        Brightness...",
741     "        Saturation...",
742     "        Hue...",
743     "        Gamma...",
744     "        Sharpen...",
745     "        Dull",
746     "        Contrast Stretch...",
747     "        Sigmoidal Contrast...",
748     "        Normalize",
749     "        Equalize",
750     "        Negate",
751     "        Grayscale",
752     "        Map...",
753     "        Quantize...",
754     "      Effects",
755     "        Despeckle",
756     "        Emboss",
757     "        Reduce Noise",
758     "        Add Noise",
759     "        Sharpen...",
760     "        Blur...",
761     "        Threshold...",
762     "        Edge Detect...",
763     "        Spread...",
764     "        Shade...",
765     "        Painting...",
766     "        Segment...",
767     "      F/X",
768     "        Solarize...",
769     "        Sepia Tone...",
770     "        Swirl...",
771     "        Implode...",
772     "        Vignette...",
773     "        Wave...",
774     "        Oil Painting...",
775     "        Charcoal Drawing...",
776     "      Image Edit",
777     "        Annotate...",
778     "        Draw...",
779     "        Color...",
780     "        Matte...",
781     "        Composite...",
782     "        Add Border...",
783     "        Add Frame...",
784     "        Comment...",
785     "        Launch...",
786     "        Region of Interest...",
787     "      Miscellany",
788     "        Image Info",
789     "        Zoom Image",
790     "        Show Preview...",
791     "        Show Histogram",
792     "        Show Matte",
793     "        Background...",
794     "        Slide Show",
795     "        Preferences...",
796     "      Help",
797     "        Overview",
798     "        Browse Documentation",
799     "        About Display",
800     "",
801     "  Menu items with a indented triangle have a sub-menu.  They",
802     "  are represented above as the indented items.  To access a",
803     "  sub-menu item, move the pointer to the appropriate menu and",
804     "  press a button and drag.  When you find the desired sub-menu",
805     "  item, release the button and the command is executed.  Move",
806     "  the pointer away from the sub-menu if you decide not to",
807     "  execute a particular command.",
808     "",
809     "KEYBOARD ACCELERATORS",
810     "  Accelerators are one or two key presses that effect a",
811     "  particular command.  The keyboard accelerators that",
812     "  display(1) understands is:",
813     "",
814     "  Ctl+O     Press to open an image from a file.",
815     "",
816     "  space     Press to display the next image.",
817     "",
818     "            If the image is a multi-paged document such as a Postscript",
819     "            document, you can skip ahead several pages by preceding",
820     "            this command with a number.  For example to display the",
821     "            third page beyond the current page, press 3<space>.",
822     "",
823     "  backspace Press to display the former image.",
824     "",
825     "            If the image is a multi-paged document such as a Postscript",
826     "            document, you can skip behind several pages by preceding",
827     "            this command with a number.  For example to display the",
828     "            third page preceding the current page, press 3<backspace>.",
829     "",
830     "  Ctl+S     Press to write the image to a file.",
831     "",
832     "  Ctl+P     Press to print the image to a Postscript printer.",
833     "",
834     "  Ctl+D     Press to delete an image file.",
835     "",
836     "  Ctl+N     Press to create a blank canvas.",
837     "",
838     "  Ctl+Q     Press to discard all images and exit program.",
839     "",
840     "  Ctl+Z     Press to undo last image transformation.",
841     "",
842     "  Ctl+R     Press to redo last image transformation.",
843     "",
844     "  Ctl+X     Press to cut a region of the image.",
845     "",
846     "  Ctl+C     Press to copy a region of the image.",
847     "",
848     "  Ctl+V     Press to paste a region to the image.",
849     "",
850     "  <         Press to half the image size.",
851     "",
852     "  -         Press to return to the original image size.",
853     "",
854     "  >         Press to double the image size.",
855     "",
856     "  %         Press to resize the image to a width and height you",
857     "            specify.",
858     "",
859     "Cmd-A       Press to make any image transformations permanent."
860     "",
861     "            By default, any image size transformations are applied",
862     "            to the original image to create the image displayed on",
863     "            the X server.  However, the transformations are not",
864     "            permanent (i.e. the original image does not change",
865     "            size only the X image does).  For example, if you",
866     "            press > the X image will appear to double in size,",
867     "            but the original image will in fact remain the same size.",
868     "            To force the original image to double in size, press >",
869     "            followed by Cmd-A.",
870     "",
871     "  @         Press to refresh the image window.",
872     "",
873     "  C         Press to cut out a rectangular region of the image.",
874     "",
875     "  [         Press to chop the image.",
876     "",
877     "  H         Press to flop image in the horizontal direction.",
878     "",
879     "  V         Press to flip image in the vertical direction.",
880     "",
881     "  /         Press to rotate the image 90 degrees clockwise.",
882     "",
883     " \\         Press to rotate the image 90 degrees counter-clockwise.",
884     "",
885     "  *         Press to rotate the image the number of degrees you",
886     "            specify.",
887     "",
888     "  S         Press to shear the image the number of degrees you",
889     "            specify.",
890     "",
891     "  R         Press to roll the image.",
892     "",
893     "  T         Press to trim the image edges.",
894     "",
895     "  Shft-H    Press to vary the image hue.",
896     "",
897     "  Shft-S    Press to vary the color saturation.",
898     "",
899     "  Shft-L    Press to vary the color brightness.",
900     "",
901     "  Shft-G    Press to gamma correct the image.",
902     "",
903     "  Shft-C    Press to sharpen the image contrast.",
904     "",
905     "  Shft-Z    Press to dull the image contrast.",
906     "",
907     "  =         Press to perform histogram equalization on the image.",
908     "",
909     "  Shft-N    Press to perform histogram normalization on the image.",
910     "",
911     "  Shft-~    Press to negate the colors of the image.",
912     "",
913     "  .         Press to convert the image colors to gray.",
914     "",
915     "  Shft-#    Press to set the maximum number of unique colors in the",
916     "            image.",
917     "",
918     "  F2        Press to reduce the speckles in an image.",
919     "",
920     "  F3        Press to eliminate peak noise from an image.",
921     "",
922     "  F4        Press to add noise to an image.",
923     "",
924     "  F5        Press to sharpen an image.",
925     "",
926     "  F6        Press to delete an image file.",
927     "",
928     "  F7        Press to threshold the image.",
929     "",
930     "  F8        Press to detect edges within an image.",
931     "",
932     "  F9        Press to emboss an image.",
933     "",
934     "  F10       Press to displace pixels by a random amount.",
935     "",
936     "  F11       Press to negate all pixels above the threshold level.",
937     "",
938     "  F12       Press to shade the image using a distant light source.",
939     "",
940     "  F13       Press to lighten or darken image edges to create a 3-D effect.",
941     "",
942     "  F14       Press to segment the image by color.",
943     "",
944     "  Meta-S    Press to swirl image pixels about the center.",
945     "",
946     "  Meta-I    Press to implode image pixels about the center.",
947     "",
948     "  Meta-W    Press to alter an image along a sine wave.",
949     "",
950     "  Meta-P    Press to simulate an oil painting.",
951     "",
952     "  Meta-C    Press to simulate a charcoal drawing.",
953     "",
954     "  Alt-A     Press to annotate the image with text.",
955     "",
956     "  Alt-D     Press to draw on an image.",
957     "",
958     "  Alt-P     Press to edit an image pixel color.",
959     "",
960     "  Alt-M     Press to edit the image matte information.",
961     "",
962     "  Alt-V     Press to composite the image with another.",
963     "",
964     "  Alt-B     Press to add a border to the image.",
965     "",
966     "  Alt-F     Press to add an ornamental border to the image.",
967     "",
968     "  Alt-Shft-!",
969     "            Press to add an image comment.",
970     "",
971     "  Ctl-A     Press to apply image processing techniques to a region",
972     "            of interest.",
973     "",
974     "  Shft-?    Press to display information about the image.",
975     "",
976     "  Shft-+    Press to map the zoom image window.",
977     "",
978     "  Shft-P    Press to preview an image enhancement, effect, or f/x.",
979     "",
980     "  F1        Press to display helpful information about display(1).",
981     "",
982     "  Find      Press to browse documentation about ImageMagick.",
983     "",
984     "  1-9       Press to change the level of magnification.",
985     "",
986     "  Use the arrow keys to move the image one pixel up, down,",
987     "  left, or right within the magnify window.  Be sure to first",
988     "  map the magnify window by pressing button 2.",
989     "",
990     "  Press ALT and one of the arrow keys to trim off one pixel",
991     "  from any side of the image.",
992     (char *) NULL,
993   },
994   *ImageMatteEditHelp[] =
995   {
996     "Matte information within an image is useful for some",
997     "operations such as image compositing (See IMAGE",
998     "COMPOSITING).  This extra channel usually defines a mask",
999     "which represents a sort of a cookie-cutter for the image.",
1000     "This the case when matte is opaque (full coverage) for",
1001     "pixels inside the shape, zero outside, and between 0 and",
1002     "QuantumRange on the boundary.",
1003     "",
1004     "A small window appears showing the location of the cursor in",
1005     "the image window. You are now in matte edit mode.  To exit",
1006     "immediately, press Dismiss.  In matte edit mode, the Command",
1007     "widget has these options:",
1008     "",
1009     "    Method",
1010     "      point",
1011     "      replace",
1012     "      floodfill",
1013     "      filltoborder",
1014     "      reset",
1015     "    Border Color",
1016     "      black",
1017     "      blue",
1018     "      cyan",
1019     "      green",
1020     "      gray",
1021     "      red",
1022     "      magenta",
1023     "      yellow",
1024     "      white",
1025     "      Browser...",
1026     "    Fuzz",
1027     "      0%",
1028     "      2%",
1029     "      5%",
1030     "      10%",
1031     "      15%",
1032     "      Dialog...",
1033     "    Matte",
1034     "      Opaque",
1035     "      Transparent",
1036     "      Dialog...",
1037     "    Undo",
1038     "    Help",
1039     "    Dismiss",
1040     "",
1041     "Choose a matte editing method from the Method sub-menu of",
1042     "the Command widget.  The point method changes the matte value",
1043     "of any pixel selected with the pointer until the button is",
1044     "is released.  The replace method changes the matte value of",
1045     "any pixel that matches the color of the pixel you select with",
1046     "a button press.  Floodfill changes the matte value of any pixel",
1047     "that matches the color of the pixel you select with a button",
1048     "press and is a neighbor.  Whereas filltoborder changes the matte",
1049     "value any neighbor pixel that is not the border color.  Finally",
1050     "reset changes the entire image to the designated matte value.",
1051     "",
1052     "Choose Matte Value and pick Opaque or Transarent.  For other values",
1053     "select the Dialog entry.  Here a dialog appears requesting a matte",
1054     "value.  The value you select is assigned as the opacity value of the",
1055     "selected pixel or pixels.",
1056     "",
1057     "Now, press any button to select a pixel within the image",
1058     "window to change its matte value.",
1059     "",
1060     "If the Magnify widget is mapped, it can be helpful in positioning",
1061     "your pointer within the image (refer to button 2).",
1062     "",
1063     "Matte information is only valid in a DirectClass image.",
1064     "Therefore, any PseudoClass image is promoted to DirectClass",
1065     "(see miff(5)).  Note that matte information for PseudoClass",
1066     "is not retained for colormapped X server visuals (e.g.",
1067     "StaticColor, StaticColor, GrayScale, PseudoColor) unless you",
1068     "immediately save your image to a file (refer to Write).",
1069     "Correct matte editing behavior may require a TrueColor or",
1070     "DirectColor visual or a Standard Colormap.",
1071     (char *) NULL,
1072   },
1073   *ImagePanHelp[] =
1074   {
1075     "When an image exceeds the width or height of the X server",
1076     "screen, display maps a small panning icon.  The rectangle",
1077     "within the panning icon shows the area that is currently",
1078     "displayed in the image window.  To pan about the image,",
1079     "press any button and drag the pointer within the panning",
1080     "icon.  The pan rectangle moves with the pointer and the",
1081     "image window is updated to reflect the location of the",
1082     "rectangle within the panning icon.  When you have selected",
1083     "the area of the image you wish to view, release the button.",
1084     "",
1085     "Use the arrow keys to pan the image one pixel up, down,",
1086     "left, or right within the image window.",
1087     "",
1088     "The panning icon is withdrawn if the image becomes smaller",
1089     "than the dimensions of the X server screen.",
1090     (char *) NULL,
1091   },
1092   *ImagePasteHelp[] =
1093   {
1094     "A small window appears showing the location of the cursor in",
1095     "the image window. You are now in paste mode.  To exit",
1096     "immediately, press Dismiss.  In paste mode, the Command",
1097     "widget has these options:",
1098     "",
1099     "    Operators",
1100     "      over",
1101     "      in",
1102     "      out",
1103     "      atop",
1104     "      xor",
1105     "      plus",
1106     "      minus",
1107     "      add",
1108     "      subtract",
1109     "      difference",
1110     "      replace",
1111     "    Help",
1112     "    Dismiss",
1113     "",
1114     "Choose a composite operation from the Operators sub-menu of",
1115     "the Command widget.  How each operator behaves is described",
1116     "below.  Image window is the image currently displayed on",
1117     "your X server and image is the image obtained with the File",
1118     "Browser widget.",
1119     "",
1120     "Over     The result is the union of the two image shapes,",
1121     "         with image obscuring image window in the region of",
1122     "         overlap.",
1123     "",
1124     "In       The result is simply image cut by the shape of",
1125     "         image window.  None of the image data of image",
1126     "         window is in the result.",
1127     "",
1128     "Out      The resulting image is image with the shape of",
1129     "         image window cut out.",
1130     "",
1131     "Atop     The result is the same shape as image image window,",
1132     "         with image obscuring image window where the image",
1133     "         shapes overlap.  Note this differs from over",
1134     "         because the portion of image outside image window's",
1135     "         shape does not appear in the result.",
1136     "",
1137     "Xor      The result is the image data from both image and",
1138     "         image window that is outside the overlap region.",
1139     "         The overlap region is blank.",
1140     "",
1141     "Plus     The result is just the sum of the image data.",
1142     "         Output values are cropped to QuantumRange (no overflow).",
1143     "         This operation is independent of the matte",
1144     "         channels.",
1145     "",
1146     "Minus    The result of image - image window, with underflow",
1147     "         cropped to zero.",
1148     "",
1149     "Add      The result of image + image window, with overflow",
1150     "         wrapping around (mod 256).",
1151     "",
1152     "Subtract The result of image - image window, with underflow",
1153     "         wrapping around (mod 256).  The add and subtract",
1154     "         operators can be used to perform reversible",
1155     "         transformations.",
1156     "",
1157     "Difference",
1158     "         The result of abs(image - image window).  This",
1159     "         useful for comparing two very similar images.",
1160     "",
1161     "Copy     The resulting image is image window replaced with",
1162     "         image.  Here the matte information is ignored.",
1163     "",
1164     "CopyRed  The red layer of the image window is replace with",
1165     "         the red layer of the image.  The other layers are",
1166     "         untouched.",
1167     "",
1168     "CopyGreen",
1169     "         The green layer of the image window is replace with",
1170     "         the green layer of the image.  The other layers are",
1171     "         untouched.",
1172     "",
1173     "CopyBlue The blue layer of the image window is replace with",
1174     "         the blue layer of the image.  The other layers are",
1175     "         untouched.",
1176     "",
1177     "CopyOpacity",
1178     "         The matte layer of the image window is replace with",
1179     "         the matte layer of the image.  The other layers are",
1180     "         untouched.",
1181     "",
1182     "The image compositor requires a matte, or alpha channel in",
1183     "the image for some operations.  This extra channel usually",
1184     "defines a mask which represents a sort of a cookie-cutter",
1185     "for the image.  This the case when matte is opaque (full",
1186     "coverage) for pixels inside the shape, zero outside, and",
1187     "between 0 and QuantumRange on the boundary.  If image does not",
1188     "have a matte channel, it is initialized with 0 for any pixel",
1189     "matching in color to pixel location (0,0), otherwise QuantumRange.",
1190     "",
1191     "Note that matte information for image window is not retained",
1192     "for colormapped X server visuals (e.g. StaticColor,",
1193     "StaticColor, GrayScale, PseudoColor).  Correct compositing",
1194     "behavior may require a TrueColor or DirectColor visual or a",
1195     "Standard Colormap.",
1196     "",
1197     "Choosing a composite operator is optional.  The default",
1198     "operator is replace.  However, you must choose a location to",
1199     "paste your image and press button 1.  Press and hold the",
1200     "button before releasing and an outline of the image will",
1201     "appear to help you identify your location.",
1202     "",
1203     "The actual colors of the pasted image is saved.  However,",
1204     "the color that appears in image window may be different.",
1205     "For example, on a monochrome screen image window will appear",
1206     "black or white even though your pasted image may have",
1207     "many colors.  If the image is saved to a file it is written",
1208     "with the correct colors.  To assure the correct colors are",
1209     "saved in the final image, any PseudoClass image is promoted",
1210     "to DirectClass (see miff(5)).  To force a PseudoClass image",
1211     "to remain PseudoClass, use -colors.",
1212     (char *) NULL,
1213   },
1214   *ImageROIHelp[] =
1215   {
1216     "In region of interest mode, the Command widget has these",
1217     "options:",
1218     "",
1219     "    Help",
1220     "    Dismiss",
1221     "",
1222     "To define a region of interest, press button 1 and drag.",
1223     "The region of interest is defined by a highlighted rectangle",
1224     "that expands or contracts as it follows the pointer.  Once",
1225     "you are satisfied with the region of interest, release the",
1226     "button.  You are now in apply mode.  In apply mode the",
1227     "Command widget has these options:",
1228     "",
1229     "      File",
1230     "        Save...",
1231     "        Print...",
1232     "      Edit",
1233     "        Undo",
1234     "        Redo",
1235     "      Transform",
1236     "        Flop",
1237     "        Flip",
1238     "        Rotate Right",
1239     "        Rotate Left",
1240     "      Enhance",
1241     "        Hue...",
1242     "        Saturation...",
1243     "        Brightness...",
1244     "        Gamma...",
1245     "        Spiff",
1246     "        Dull",
1247     "        Contrast Stretch",
1248     "        Sigmoidal Contrast...",
1249     "        Normalize",
1250     "        Equalize",
1251     "        Negate",
1252     "        Grayscale",
1253     "        Map...",
1254     "        Quantize...",
1255     "      Effects",
1256     "        Despeckle",
1257     "        Emboss",
1258     "        Reduce Noise",
1259     "        Sharpen...",
1260     "        Blur...",
1261     "        Threshold...",
1262     "        Edge Detect...",
1263     "        Spread...",
1264     "        Shade...",
1265     "        Raise...",
1266     "        Segment...",
1267     "      F/X",
1268     "        Solarize...",
1269     "        Sepia Tone...",
1270     "        Swirl...",
1271     "        Implode...",
1272     "        Vignette...",
1273     "        Wave...",
1274     "        Oil Painting...",
1275     "        Charcoal Drawing...",
1276     "      Miscellany",
1277     "        Image Info",
1278     "        Zoom Image",
1279     "        Show Preview...",
1280     "        Show Histogram",
1281     "        Show Matte",
1282     "      Help",
1283     "      Dismiss",
1284     "",
1285     "You can make adjustments to the region of interest by moving",
1286     "the pointer to one of the rectangle corners, pressing a",
1287     "button, and dragging.  Finally, choose an image processing",
1288     "technique from the Command widget.  You can choose more than",
1289     "one image processing technique to apply to an area.",
1290     "Alternatively, you can move the region of interest before",
1291     "applying another image processing technique.  To exit, press",
1292     "Dismiss.",
1293     (char *) NULL,
1294   },
1295   *ImageRotateHelp[] =
1296   {
1297     "In rotate mode, the Command widget has these options:",
1298     "",
1299     "    Pixel Color",
1300     "      black",
1301     "      blue",
1302     "      cyan",
1303     "      green",
1304     "      gray",
1305     "      red",
1306     "      magenta",
1307     "      yellow",
1308     "      white",
1309     "      Browser...",
1310     "    Direction",
1311     "      horizontal",
1312     "      vertical",
1313     "    Help",
1314     "    Dismiss",
1315     "",
1316     "Choose a background color from the Pixel Color sub-menu.",
1317     "Additional background colors can be specified with the color",
1318     "browser.  You can change the menu colors by setting the X",
1319     "resources pen1 through pen9.",
1320     "",
1321     "If you choose the color browser and press Grab, you can",
1322     "select the background color by moving the pointer to the",
1323     "desired color on the screen and press any button.",
1324     "",
1325     "Choose a point in the image window and press this button and",
1326     "hold.  Next, move the pointer to another location in the",
1327     "image.  As you move a line connects the initial location and",
1328     "the pointer.  When you release the button, the degree of",
1329     "image rotation is determined by the slope of the line you",
1330     "just drew.  The slope is relative to the direction you",
1331     "choose from the Direction sub-menu of the Command widget.",
1332     "",
1333     "To cancel the image rotation, move the pointer back to the",
1334     "starting point of the line and release the button.",
1335     (char *) NULL,
1336   };
1337 \f
1338 /*
1339   Enumeration declarations.
1340 */
1341 typedef enum
1342 {
1343   CopyMode,
1344   CropMode,
1345   CutMode
1346 } ClipboardMode;
1347
1348 typedef enum
1349 {
1350   OpenCommand,
1351   NextCommand,
1352   FormerCommand,
1353   SelectCommand,
1354   SaveCommand,
1355   PrintCommand,
1356   DeleteCommand,
1357   NewCommand,
1358   VisualDirectoryCommand,
1359   QuitCommand,
1360   UndoCommand,
1361   RedoCommand,
1362   CutCommand,
1363   CopyCommand,
1364   PasteCommand,
1365   HalfSizeCommand,
1366   OriginalSizeCommand,
1367   DoubleSizeCommand,
1368   ResizeCommand,
1369   ApplyCommand,
1370   RefreshCommand,
1371   RestoreCommand,
1372   CropCommand,
1373   ChopCommand,
1374   FlopCommand,
1375   FlipCommand,
1376   RotateRightCommand,
1377   RotateLeftCommand,
1378   RotateCommand,
1379   ShearCommand,
1380   RollCommand,
1381   TrimCommand,
1382   HueCommand,
1383   SaturationCommand,
1384   BrightnessCommand,
1385   GammaCommand,
1386   SpiffCommand,
1387   DullCommand,
1388   ContrastStretchCommand,
1389   SigmoidalContrastCommand,
1390   NormalizeCommand,
1391   EqualizeCommand,
1392   NegateCommand,
1393   GrayscaleCommand,
1394   MapCommand,
1395   QuantizeCommand,
1396   DespeckleCommand,
1397   EmbossCommand,
1398   ReduceNoiseCommand,
1399   AddNoiseCommand,
1400   SharpenCommand,
1401   BlurCommand,
1402   ThresholdCommand,
1403   EdgeDetectCommand,
1404   SpreadCommand,
1405   ShadeCommand,
1406   RaiseCommand,
1407   SegmentCommand,
1408   SolarizeCommand,
1409   SepiaToneCommand,
1410   SwirlCommand,
1411   ImplodeCommand,
1412   VignetteCommand,
1413   WaveCommand,
1414   OilPaintCommand,
1415   CharcoalDrawCommand,
1416   AnnotateCommand,
1417   DrawCommand,
1418   ColorCommand,
1419   MatteCommand,
1420   CompositeCommand,
1421   AddBorderCommand,
1422   AddFrameCommand,
1423   CommentCommand,
1424   LaunchCommand,
1425   RegionofInterestCommand,
1426   ROIHelpCommand,
1427   ROIDismissCommand,
1428   InfoCommand,
1429   ZoomCommand,
1430   ShowPreviewCommand,
1431   ShowHistogramCommand,
1432   ShowMatteCommand,
1433   BackgroundCommand,
1434   SlideShowCommand,
1435   PreferencesCommand,
1436   HelpCommand,
1437   BrowseDocumentationCommand,
1438   VersionCommand,
1439   SaveToUndoBufferCommand,
1440   FreeBuffersCommand,
1441   NullCommand
1442 } CommandType;
1443
1444 typedef enum
1445 {
1446   AnnotateNameCommand,
1447   AnnotateFontColorCommand,
1448   AnnotateBackgroundColorCommand,
1449   AnnotateRotateCommand,
1450   AnnotateHelpCommand,
1451   AnnotateDismissCommand,
1452   TextHelpCommand,
1453   TextApplyCommand,
1454   ChopDirectionCommand,
1455   ChopHelpCommand,
1456   ChopDismissCommand,
1457   HorizontalChopCommand,
1458   VerticalChopCommand,
1459   ColorEditMethodCommand,
1460   ColorEditColorCommand,
1461   ColorEditBorderCommand,
1462   ColorEditFuzzCommand,
1463   ColorEditUndoCommand,
1464   ColorEditHelpCommand,
1465   ColorEditDismissCommand,
1466   CompositeOperatorsCommand,
1467   CompositeDissolveCommand,
1468   CompositeDisplaceCommand,
1469   CompositeHelpCommand,
1470   CompositeDismissCommand,
1471   CropHelpCommand,
1472   CropDismissCommand,
1473   RectifyCopyCommand,
1474   RectifyHelpCommand,
1475   RectifyDismissCommand,
1476   DrawElementCommand,
1477   DrawColorCommand,
1478   DrawStippleCommand,
1479   DrawWidthCommand,
1480   DrawUndoCommand,
1481   DrawHelpCommand,
1482   DrawDismissCommand,
1483   MatteEditMethod,
1484   MatteEditBorderCommand,
1485   MatteEditFuzzCommand,
1486   MatteEditValueCommand,
1487   MatteEditUndoCommand,
1488   MatteEditHelpCommand,
1489   MatteEditDismissCommand,
1490   PasteOperatorsCommand,
1491   PasteHelpCommand,
1492   PasteDismissCommand,
1493   RotateColorCommand,
1494   RotateDirectionCommand,
1495   RotateCropCommand,
1496   RotateSharpenCommand,
1497   RotateHelpCommand,
1498   RotateDismissCommand,
1499   HorizontalRotateCommand,
1500   VerticalRotateCommand,
1501   TileLoadCommand,
1502   TileNextCommand,
1503   TileFormerCommand,
1504   TileDeleteCommand,
1505   TileUpdateCommand
1506 } ModeType;
1507 \f
1508 /*
1509   Stipples.
1510 */
1511 #define BricksWidth  20
1512 #define BricksHeight  20
1513 #define DiagonalWidth  16
1514 #define DiagonalHeight  16
1515 #define HighlightWidth  8
1516 #define HighlightHeight  8
1517 #define OpaqueWidth  8
1518 #define OpaqueHeight  8
1519 #define ScalesWidth  16
1520 #define ScalesHeight  16
1521 #define ShadowWidth  8
1522 #define ShadowHeight  8
1523 #define VerticalWidth  16
1524 #define VerticalHeight  16
1525 #define WavyWidth  16
1526 #define WavyHeight  16
1527 \f
1528 /*
1529   Constant declaration.
1530 */
1531 static const int
1532   RoiDelta = 8;
1533
1534 static const unsigned char
1535   BricksBitmap[] =
1536   {
1537     0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00,
1538     0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01,
1539     0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00,
1540     0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f,
1541     0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01
1542   },
1543   DiagonalBitmap[] =
1544   {
1545     0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88,
1546     0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22,
1547     0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22
1548   },
1549   ScalesBitmap[] =
1550   {
1551     0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3, 0x80, 0x80, 0x80, 0x80,
1552     0x41, 0x41, 0x3e, 0x3e, 0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3,
1553     0x80, 0x80, 0x80, 0x80, 0x41, 0x41, 0x3e, 0x3e
1554   },
1555   VerticalBitmap[] =
1556   {
1557     0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1558     0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1559     0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11
1560   },
1561   WavyBitmap[] =
1562   {
1563     0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfb, 0xff,
1564     0xe7, 0xff, 0x1f, 0xff, 0xff, 0xf8, 0xff, 0xe7, 0xff, 0xdf, 0xff, 0xbf,
1565     0xff, 0xbf, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f
1566   };
1567 \f
1568 /*
1569   Function prototypes.
1570 */
1571 static CommandType
1572   XImageWindowCommand(Display *,XResourceInfo *,XWindows *,
1573     const MagickStatusType,KeySym,Image **,ExceptionInfo *);
1574
1575 static Image
1576   *XMagickCommand(Display *,XResourceInfo *,XWindows *,const CommandType,
1577     Image **,ExceptionInfo *),
1578   *XOpenImage(Display *,XResourceInfo *,XWindows *,const MagickBooleanType),
1579   *XTileImage(Display *,XResourceInfo *,XWindows *,Image *,XEvent *,
1580     ExceptionInfo *),
1581   *XVisualDirectoryImage(Display *,XResourceInfo *,XWindows *,
1582     ExceptionInfo *);
1583
1584 static MagickBooleanType
1585   XAnnotateEditImage(Display *,XResourceInfo *,XWindows *,Image *,
1586     ExceptionInfo *),
1587   XBackgroundImage(Display *,XResourceInfo *,XWindows *,Image **,
1588     ExceptionInfo *),
1589   XChopImage(Display *,XResourceInfo *,XWindows *,Image **,
1590     ExceptionInfo *),
1591   XCropImage(Display *,XResourceInfo *,XWindows *,Image *,const ClipboardMode,
1592     ExceptionInfo *),
1593   XColorEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1594     ExceptionInfo *),
1595   XCompositeImage(Display *,XResourceInfo *,XWindows *,Image *,
1596     ExceptionInfo *),
1597   XConfigureImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1598   XDrawEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1599     ExceptionInfo *),
1600   XMatteEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1601     ExceptionInfo *),
1602   XPasteImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1603   XPrintImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1604   XRotateImage(Display *,XResourceInfo *,XWindows *,double,Image **,
1605     ExceptionInfo *),
1606   XROIImage(Display *,XResourceInfo *,XWindows *,Image **,ExceptionInfo *),
1607   XSaveImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1608   XTrimImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *);
1609
1610 static void
1611   XDrawPanRectangle(Display *,XWindows *),
1612   XImageCache(Display *,XResourceInfo *,XWindows *,const CommandType,Image **,
1613     ExceptionInfo *),
1614   XMagnifyImage(Display *,XWindows *,XEvent *,ExceptionInfo *),
1615   XMakePanImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1616   XPanImage(Display *,XWindows *,XEvent *,ExceptionInfo *),
1617   XMagnifyWindowCommand(Display *,XWindows *,const MagickStatusType,
1618     const KeySym,ExceptionInfo *),
1619   XSetCropGeometry(Display *,XWindows *,RectangleInfo *,Image *),
1620   XScreenEvent(Display *,XWindows *,XEvent *,ExceptionInfo *),
1621   XTranslateImage(Display *,XWindows *,Image *,const KeySym);
1622 \f
1623 /*
1624 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1625 %                                                                             %
1626 %                                                                             %
1627 %                                                                             %
1628 %   D i s p l a y I m a g e s                                                 %
1629 %                                                                             %
1630 %                                                                             %
1631 %                                                                             %
1632 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1633 %
1634 %  DisplayImages() displays an image sequence to any X window screen.  It
1635 %  returns a value other than 0 if successful.  Check the exception member
1636 %  of image to determine the reason for any failure.
1637 %
1638 %  The format of the DisplayImages method is:
1639 %
1640 %      MagickBooleanType DisplayImages(const ImageInfo *image_info,
1641 %        Image *images,ExceptionInfo *exception)
1642 %
1643 %  A description of each parameter follows:
1644 %
1645 %    o image_info: the image info.
1646 %
1647 %    o image: the image.
1648 %
1649 %    o exception: return any errors or warnings in this structure.
1650 %
1651 */
1652 MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
1653   Image *images,ExceptionInfo *exception)
1654 {
1655   char
1656     *argv[1];
1657
1658   Display
1659     *display;
1660
1661   Image
1662     *image;
1663
1664   register ssize_t
1665     i;
1666
1667   size_t
1668     state;
1669
1670   XrmDatabase
1671     resource_database;
1672
1673   XResourceInfo
1674     resource_info;
1675
1676   assert(image_info != (const ImageInfo *) NULL);
1677   assert(image_info->signature == MagickSignature);
1678   assert(images != (Image *) NULL);
1679   assert(images->signature == MagickSignature);
1680   if (IfMagickTrue(images->debug) )
1681     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
1682   display=XOpenDisplay(image_info->server_name);
1683   if (display == (Display *) NULL)
1684     {
1685       (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
1686         "UnableToOpenXServer","`%s'",XDisplayName(image_info->server_name));
1687       return(MagickFalse);
1688     }
1689   if (exception->severity != UndefinedException)
1690     CatchException(exception);
1691   (void) XSetErrorHandler(XError);
1692   resource_database=XGetResourceDatabase(display,GetClientName());
1693   (void) ResetMagickMemory(&resource_info,0,sizeof(resource_info));
1694   XGetResourceInfo(image_info,resource_database,GetClientName(),&resource_info);
1695   if (image_info->page != (char *) NULL)
1696     resource_info.image_geometry=AcquireString(image_info->page);
1697   resource_info.immutable=MagickTrue;
1698   argv[0]=AcquireString(GetClientName());
1699   state=DefaultState;
1700   for (i=0; (state & ExitState) == 0; i++)
1701   {
1702     if ((images->iterations != 0) && (i >= (ssize_t) images->iterations))
1703       break;
1704     image=GetImageFromList(images,i % GetImageListLength(images));
1705     (void) XDisplayImage(display,&resource_info,argv,1,&image,&state,exception);
1706   }
1707   (void) SetErrorHandler((ErrorHandler) NULL);
1708   (void) SetWarningHandler((WarningHandler) NULL);
1709   argv[0]=DestroyString(argv[0]);
1710   (void) XCloseDisplay(display);
1711   XDestroyResourceInfo(&resource_info);
1712   if (exception->severity != UndefinedException)
1713     return(MagickFalse);
1714   return(MagickTrue);
1715 }
1716 \f
1717 /*
1718 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1719 %                                                                             %
1720 %                                                                             %
1721 %                                                                             %
1722 %   R e m o t e D i s p l a y C o m m a n d                                   %
1723 %                                                                             %
1724 %                                                                             %
1725 %                                                                             %
1726 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1727 %
1728 %  RemoteDisplayCommand() encourages a remote display program to display the
1729 %  specified image filename.
1730 %
1731 %  The format of the RemoteDisplayCommand method is:
1732 %
1733 %      MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1734 %        const char *window,const char *filename,ExceptionInfo *exception)
1735 %
1736 %  A description of each parameter follows:
1737 %
1738 %    o image_info: the image info.
1739 %
1740 %    o window: Specifies the name or id of an X window.
1741 %
1742 %    o filename: the name of the image filename to display.
1743 %
1744 %    o exception: return any errors or warnings in this structure.
1745 %
1746 */
1747 MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1748   const char *window,const char *filename,ExceptionInfo *exception)
1749 {
1750   Display
1751     *display;
1752
1753   MagickStatusType
1754     status;
1755
1756   assert(image_info != (const ImageInfo *) NULL);
1757   assert(image_info->signature == MagickSignature);
1758   assert(filename != (char *) NULL);
1759   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
1760   display=XOpenDisplay(image_info->server_name);
1761   if (display == (Display *) NULL)
1762     {
1763       (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
1764         "UnableToOpenXServer","`%s'",XDisplayName(image_info->server_name));
1765       return(MagickFalse);
1766     }
1767   (void) XSetErrorHandler(XError);
1768   status=XRemoteCommand(display,window,filename);
1769   (void) XCloseDisplay(display);
1770   return(IsMagickTrue(status));
1771 }
1772 \f
1773 /*
1774 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1775 %                                                                             %
1776 %                                                                             %
1777 %                                                                             %
1778 +   X A n n o t a t e E d i t I m a g e                                       %
1779 %                                                                             %
1780 %                                                                             %
1781 %                                                                             %
1782 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1783 %
1784 %  XAnnotateEditImage() annotates the image with text.
1785 %
1786 %  The format of the XAnnotateEditImage method is:
1787 %
1788 %      MagickBooleanType XAnnotateEditImage(Display *display,
1789 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
1790 %        ExceptionInfo *exception)
1791 %
1792 %  A description of each parameter follows:
1793 %
1794 %    o display: Specifies a connection to an X server;  returned from
1795 %      XOpenDisplay.
1796 %
1797 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
1798 %
1799 %    o windows: Specifies a pointer to a XWindows structure.
1800 %
1801 %    o image: the image; returned from ReadImage.
1802 %
1803 */
1804
1805 static MagickBooleanType XAnnotateEditImage(Display *display,
1806   XResourceInfo *resource_info,XWindows *windows,Image *image,
1807   ExceptionInfo *exception)
1808 {
1809   static const char
1810     *AnnotateMenu[] =
1811     {
1812       "Font Name",
1813       "Font Color",
1814       "Box Color",
1815       "Rotate Text",
1816       "Help",
1817       "Dismiss",
1818       (char *) NULL
1819     },
1820     *TextMenu[] =
1821     {
1822       "Help",
1823       "Apply",
1824       (char *) NULL
1825     };
1826
1827   static const ModeType
1828     AnnotateCommands[] =
1829     {
1830       AnnotateNameCommand,
1831       AnnotateFontColorCommand,
1832       AnnotateBackgroundColorCommand,
1833       AnnotateRotateCommand,
1834       AnnotateHelpCommand,
1835       AnnotateDismissCommand
1836     },
1837     TextCommands[] =
1838     {
1839       TextHelpCommand,
1840       TextApplyCommand
1841     };
1842
1843   static MagickBooleanType
1844     transparent_box = MagickTrue,
1845     transparent_pen = MagickFalse;
1846
1847   static double
1848     degrees = 0.0;
1849
1850   static unsigned int
1851     box_id = MaxNumberPens-2,
1852     font_id = 0,
1853     pen_id = 0;
1854
1855   char
1856     command[MaxTextExtent],
1857     text[MaxTextExtent];
1858
1859   const char
1860     *ColorMenu[MaxNumberPens+1];
1861
1862   Cursor
1863     cursor;
1864
1865   GC
1866     annotate_context;
1867
1868   int
1869     id,
1870     pen_number,
1871     status,
1872     x,
1873     y;
1874
1875   KeySym
1876     key_symbol;
1877
1878   register char
1879     *p;
1880
1881   register ssize_t
1882     i;
1883
1884   unsigned int
1885     height,
1886     width;
1887
1888   size_t
1889     state;
1890
1891   XAnnotateInfo
1892     *annotate_info,
1893     *previous_info;
1894
1895   XColor
1896     color;
1897
1898   XFontStruct
1899     *font_info;
1900
1901   XEvent
1902     event,
1903     text_event;
1904
1905   /*
1906     Map Command widget.
1907   */
1908   (void) CloneString(&windows->command.name,"Annotate");
1909   windows->command.data=4;
1910   (void) XCommandWidget(display,windows,AnnotateMenu,(XEvent *) NULL);
1911   (void) XMapRaised(display,windows->command.id);
1912   XClientMessage(display,windows->image.id,windows->im_protocols,
1913     windows->im_update_widget,CurrentTime);
1914   /*
1915     Track pointer until button 1 is pressed.
1916   */
1917   XQueryPosition(display,windows->image.id,&x,&y);
1918   (void) XSelectInput(display,windows->image.id,
1919     windows->image.attributes.event_mask | PointerMotionMask);
1920   cursor=XCreateFontCursor(display,XC_left_side);
1921   (void) XCheckDefineCursor(display,windows->image.id,cursor);
1922   state=DefaultState;
1923   do
1924   {
1925     if (IfMagickTrue(windows->info.mapped) )
1926       {
1927         /*
1928           Display pointer position.
1929         */
1930         (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
1931           x+windows->image.x,y+windows->image.y);
1932         XInfoWidget(display,windows,text);
1933       }
1934     /*
1935       Wait for next event.
1936     */
1937     XScreenEvent(display,windows,&event,exception);
1938     if (event.xany.window == windows->command.id)
1939       {
1940         /*
1941           Select a command from the Command widget.
1942         */
1943         id=XCommandWidget(display,windows,AnnotateMenu,&event);
1944         (void) XCheckDefineCursor(display,windows->image.id,cursor);
1945         if (id < 0)
1946           continue;
1947         switch (AnnotateCommands[id])
1948         {
1949           case AnnotateNameCommand:
1950           {
1951             const char
1952               *FontMenu[MaxNumberFonts];
1953
1954             int
1955               font_number;
1956
1957             /*
1958               Initialize menu selections.
1959             */
1960             for (i=0; i < MaxNumberFonts; i++)
1961               FontMenu[i]=resource_info->font_name[i];
1962             FontMenu[MaxNumberFonts-2]="Browser...";
1963             FontMenu[MaxNumberFonts-1]=(const char *) NULL;
1964             /*
1965               Select a font name from the pop-up menu.
1966             */
1967             font_number=XMenuWidget(display,windows,AnnotateMenu[id],
1968               (const char **) FontMenu,command);
1969             if (font_number < 0)
1970               break;
1971             if (font_number == (MaxNumberFonts-2))
1972               {
1973                 static char
1974                   font_name[MaxTextExtent] = "fixed";
1975
1976                 /*
1977                   Select a font name from a browser.
1978                 */
1979                 resource_info->font_name[font_number]=font_name;
1980                 XFontBrowserWidget(display,windows,"Select",font_name);
1981                 if (*font_name == '\0')
1982                   break;
1983               }
1984             /*
1985               Initialize font info.
1986             */
1987             font_info=XLoadQueryFont(display,resource_info->font_name[
1988               font_number]);
1989             if (font_info == (XFontStruct *) NULL)
1990               {
1991                 XNoticeWidget(display,windows,"Unable to load font:",
1992                   resource_info->font_name[font_number]);
1993                 break;
1994               }
1995             font_id=(unsigned int) font_number;
1996             (void) XFreeFont(display,font_info);
1997             break;
1998           }
1999           case AnnotateFontColorCommand:
2000           {
2001             /*
2002               Initialize menu selections.
2003             */
2004             for (i=0; i < (int) (MaxNumberPens-2); i++)
2005               ColorMenu[i]=resource_info->pen_colors[i];
2006             ColorMenu[MaxNumberPens-2]="transparent";
2007             ColorMenu[MaxNumberPens-1]="Browser...";
2008             ColorMenu[MaxNumberPens]=(const char *) NULL;
2009             /*
2010               Select a pen color from the pop-up menu.
2011             */
2012             pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
2013               (const char **) ColorMenu,command);
2014             if (pen_number < 0)
2015               break;
2016             transparent_pen=pen_number == (MaxNumberPens-2) ? MagickTrue :
2017               MagickFalse;
2018             if (IfMagickTrue(transparent_pen) )
2019               break;
2020             if (pen_number == (MaxNumberPens-1))
2021               {
2022                 static char
2023                   color_name[MaxTextExtent] = "gray";
2024
2025                 /*
2026                   Select a pen color from a dialog.
2027                 */
2028                 resource_info->pen_colors[pen_number]=color_name;
2029                 XColorBrowserWidget(display,windows,"Select",color_name);
2030                 if (*color_name == '\0')
2031                   break;
2032               }
2033             /*
2034               Set pen color.
2035             */
2036             (void) XParseColor(display,windows->map_info->colormap,
2037               resource_info->pen_colors[pen_number],&color);
2038             XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
2039               (unsigned int) MaxColors,&color);
2040             windows->pixel_info->pen_colors[pen_number]=color;
2041             pen_id=(unsigned int) pen_number;
2042             break;
2043           }
2044           case AnnotateBackgroundColorCommand:
2045           {
2046             /*
2047               Initialize menu selections.
2048             */
2049             for (i=0; i < (int) (MaxNumberPens-2); i++)
2050               ColorMenu[i]=resource_info->pen_colors[i];
2051             ColorMenu[MaxNumberPens-2]="transparent";
2052             ColorMenu[MaxNumberPens-1]="Browser...";
2053             ColorMenu[MaxNumberPens]=(const char *) NULL;
2054             /*
2055               Select a pen color from the pop-up menu.
2056             */
2057             pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
2058               (const char **) ColorMenu,command);
2059             if (pen_number < 0)
2060               break;
2061             transparent_box=pen_number == (MaxNumberPens-2) ? MagickTrue :
2062               MagickFalse;
2063             if (IfMagickTrue(transparent_box) )
2064               break;
2065             if (pen_number == (MaxNumberPens-1))
2066               {
2067                 static char
2068                   color_name[MaxTextExtent] = "gray";
2069
2070                 /*
2071                   Select a pen color from a dialog.
2072                 */
2073                 resource_info->pen_colors[pen_number]=color_name;
2074                 XColorBrowserWidget(display,windows,"Select",color_name);
2075                 if (*color_name == '\0')
2076                   break;
2077               }
2078             /*
2079               Set pen color.
2080             */
2081             (void) XParseColor(display,windows->map_info->colormap,
2082               resource_info->pen_colors[pen_number],&color);
2083             XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
2084               (unsigned int) MaxColors,&color);
2085             windows->pixel_info->pen_colors[pen_number]=color;
2086             box_id=(unsigned int) pen_number;
2087             break;
2088           }
2089           case AnnotateRotateCommand:
2090           {
2091             int
2092               entry;
2093
2094             static char
2095               angle[MaxTextExtent] = "30.0";
2096
2097             static const char
2098               *RotateMenu[] =
2099               {
2100                 "-90",
2101                 "-45",
2102                 "-30",
2103                 "0",
2104                 "30",
2105                 "45",
2106                 "90",
2107                 "180",
2108                 "Dialog...",
2109                 (char *) NULL,
2110               };
2111
2112             /*
2113               Select a command from the pop-up menu.
2114             */
2115             entry=XMenuWidget(display,windows,AnnotateMenu[id],RotateMenu,
2116               command);
2117             if (entry < 0)
2118               break;
2119             if (entry != 8)
2120               {
2121                 degrees=StringToDouble(RotateMenu[entry],(char **) NULL);
2122                 break;
2123               }
2124             (void) XDialogWidget(display,windows,"OK","Enter rotation angle:",
2125               angle);
2126             if (*angle == '\0')
2127               break;
2128             degrees=StringToDouble(angle,(char **) NULL);
2129             break;
2130           }
2131           case AnnotateHelpCommand:
2132           {
2133             XTextViewWidget(display,resource_info,windows,MagickFalse,
2134               "Help Viewer - Image Annotation",ImageAnnotateHelp);
2135             break;
2136           }
2137           case AnnotateDismissCommand:
2138           {
2139             /*
2140               Prematurely exit.
2141             */
2142             state|=EscapeState;
2143             state|=ExitState;
2144             break;
2145           }
2146           default:
2147             break;
2148         }
2149         continue;
2150       }
2151     switch (event.type)
2152     {
2153       case ButtonPress:
2154       {
2155         if (event.xbutton.button != Button1)
2156           break;
2157         if (event.xbutton.window != windows->image.id)
2158           break;
2159         /*
2160           Change to text entering mode.
2161         */
2162         x=event.xbutton.x;
2163         y=event.xbutton.y;
2164         state|=ExitState;
2165         break;
2166       }
2167       case ButtonRelease:
2168         break;
2169       case Expose:
2170         break;
2171       case KeyPress:
2172       {
2173         if (event.xkey.window != windows->image.id)
2174           break;
2175         /*
2176           Respond to a user key press.
2177         */
2178         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2179           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2180         switch ((int) key_symbol)
2181         {
2182           case XK_Escape:
2183           case XK_F20:
2184           {
2185             /*
2186               Prematurely exit.
2187             */
2188             state|=EscapeState;
2189             state|=ExitState;
2190             break;
2191           }
2192           case XK_F1:
2193           case XK_Help:
2194           {
2195             XTextViewWidget(display,resource_info,windows,MagickFalse,
2196               "Help Viewer - Image Annotation",ImageAnnotateHelp);
2197             break;
2198           }
2199           default:
2200           {
2201             (void) XBell(display,0);
2202             break;
2203           }
2204         }
2205         break;
2206       }
2207       case MotionNotify:
2208       {
2209         /*
2210           Map and unmap Info widget as cursor crosses its boundaries.
2211         */
2212         x=event.xmotion.x;
2213         y=event.xmotion.y;
2214         if (IfMagickTrue(windows->info.mapped) )
2215           {
2216             if ((x < (int) (windows->info.x+windows->info.width)) &&
2217                 (y < (int) (windows->info.y+windows->info.height)))
2218               (void) XWithdrawWindow(display,windows->info.id,
2219                 windows->info.screen);
2220           }
2221         else
2222           if ((x > (int) (windows->info.x+windows->info.width)) ||
2223               (y > (int) (windows->info.y+windows->info.height)))
2224             (void) XMapWindow(display,windows->info.id);
2225         break;
2226       }
2227       default:
2228         break;
2229     }
2230   } while ((state & ExitState) == 0);
2231   (void) XSelectInput(display,windows->image.id,
2232     windows->image.attributes.event_mask);
2233   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
2234   if ((state & EscapeState) != 0)
2235     return(MagickTrue);
2236   /*
2237     Set font info and check boundary conditions.
2238   */
2239   font_info=XLoadQueryFont(display,resource_info->font_name[font_id]);
2240   if (font_info == (XFontStruct *) NULL)
2241     {
2242       XNoticeWidget(display,windows,"Unable to load font:",
2243         resource_info->font_name[font_id]);
2244       font_info=windows->font_info;
2245     }
2246   if ((x+font_info->max_bounds.width) >= (int) windows->image.width)
2247     x=(int) windows->image.width-font_info->max_bounds.width;
2248   if (y < (int) (font_info->ascent+font_info->descent))
2249     y=(int) font_info->ascent+font_info->descent;
2250   if (((int) font_info->max_bounds.width > (int) windows->image.width) ||
2251       ((font_info->ascent+font_info->descent) >= (int) windows->image.height))
2252     return(MagickFalse);
2253   /*
2254     Initialize annotate structure.
2255   */
2256   annotate_info=(XAnnotateInfo *) AcquireMagickMemory(sizeof(*annotate_info));
2257   if (annotate_info == (XAnnotateInfo *) NULL)
2258     return(MagickFalse);
2259   XGetAnnotateInfo(annotate_info);
2260   annotate_info->x=x;
2261   annotate_info->y=y;
2262   if (IfMagickFalse(transparent_box) && IfMagickFalse(transparent_pen))
2263     annotate_info->stencil=OpaqueStencil;
2264   else
2265     if (IfMagickFalse(transparent_box) )
2266       annotate_info->stencil=BackgroundStencil;
2267     else
2268       annotate_info->stencil=ForegroundStencil;
2269   annotate_info->height=(unsigned int) font_info->ascent+font_info->descent;
2270   annotate_info->degrees=degrees;
2271   annotate_info->font_info=font_info;
2272   annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2273     windows->image.width/MagickMax((ssize_t) font_info->min_bounds.width,1)+2UL,
2274     sizeof(*annotate_info->text));
2275   if (annotate_info->text == (char *) NULL)
2276     return(MagickFalse);
2277   /*
2278     Create cursor and set graphic context.
2279   */
2280   cursor=XCreateFontCursor(display,XC_pencil);
2281   (void) XCheckDefineCursor(display,windows->image.id,cursor);
2282   annotate_context=windows->image.annotate_context;
2283   (void) XSetFont(display,annotate_context,font_info->fid);
2284   (void) XSetBackground(display,annotate_context,
2285     windows->pixel_info->pen_colors[box_id].pixel);
2286   (void) XSetForeground(display,annotate_context,
2287     windows->pixel_info->pen_colors[pen_id].pixel);
2288   /*
2289     Begin annotating the image with text.
2290   */
2291   (void) CloneString(&windows->command.name,"Text");
2292   windows->command.data=0;
2293   (void) XCommandWidget(display,windows,TextMenu,(XEvent *) NULL);
2294   state=DefaultState;
2295   (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2296   text_event.xexpose.width=(int) font_info->max_bounds.width;
2297   text_event.xexpose.height=font_info->max_bounds.ascent+
2298     font_info->max_bounds.descent;
2299   p=annotate_info->text;
2300   do
2301   {
2302     /*
2303       Display text cursor.
2304     */
2305     *p='\0';
2306     (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2307     /*
2308       Wait for next event.
2309     */
2310     XScreenEvent(display,windows,&event,exception);
2311     if (event.xany.window == windows->command.id)
2312       {
2313         /*
2314           Select a command from the Command widget.
2315         */
2316         (void) XSetBackground(display,annotate_context,
2317           windows->pixel_info->background_color.pixel);
2318         (void) XSetForeground(display,annotate_context,
2319           windows->pixel_info->foreground_color.pixel);
2320         id=XCommandWidget(display,windows,AnnotateMenu,&event);
2321         (void) XSetBackground(display,annotate_context,
2322           windows->pixel_info->pen_colors[box_id].pixel);
2323         (void) XSetForeground(display,annotate_context,
2324           windows->pixel_info->pen_colors[pen_id].pixel);
2325         if (id < 0)
2326           continue;
2327         switch (TextCommands[id])
2328         {
2329           case TextHelpCommand:
2330           {
2331             XTextViewWidget(display,resource_info,windows,MagickFalse,
2332               "Help Viewer - Image Annotation",ImageAnnotateHelp);
2333             (void) XCheckDefineCursor(display,windows->image.id,cursor);
2334             break;
2335           }
2336           case TextApplyCommand:
2337           {
2338             /*
2339               Finished annotating.
2340             */
2341             annotate_info->width=(unsigned int) XTextWidth(font_info,
2342               annotate_info->text,(int) strlen(annotate_info->text));
2343             XRefreshWindow(display,&windows->image,&text_event);
2344             state|=ExitState;
2345             break;
2346           }
2347           default:
2348             break;
2349         }
2350         continue;
2351       }
2352     /*
2353       Erase text cursor.
2354     */
2355     text_event.xexpose.x=x;
2356     text_event.xexpose.y=y-font_info->max_bounds.ascent;
2357     (void) XClearArea(display,windows->image.id,x,text_event.xexpose.y,
2358       (unsigned int) text_event.xexpose.width,(unsigned int)
2359       text_event.xexpose.height,MagickFalse);
2360     XRefreshWindow(display,&windows->image,&text_event);
2361     switch (event.type)
2362     {
2363       case ButtonPress:
2364       {
2365         if (event.xbutton.window != windows->image.id)
2366           break;
2367         if (event.xbutton.button == Button2)
2368           {
2369             /*
2370               Request primary selection.
2371             */
2372             (void) XConvertSelection(display,XA_PRIMARY,XA_STRING,XA_STRING,
2373               windows->image.id,CurrentTime);
2374             break;
2375           }
2376         break;
2377       }
2378       case Expose:
2379       {
2380         if (event.xexpose.count == 0)
2381           {
2382             XAnnotateInfo
2383               *text_info;
2384
2385             /*
2386               Refresh Image window.
2387             */
2388             XRefreshWindow(display,&windows->image,(XEvent *) NULL);
2389             text_info=annotate_info;
2390             while (text_info != (XAnnotateInfo *) NULL)
2391             {
2392               if (annotate_info->stencil == ForegroundStencil)
2393                 (void) XDrawString(display,windows->image.id,annotate_context,
2394                   text_info->x,text_info->y,text_info->text,
2395                   (int) strlen(text_info->text));
2396               else
2397                 (void) XDrawImageString(display,windows->image.id,
2398                   annotate_context,text_info->x,text_info->y,text_info->text,
2399                   (int) strlen(text_info->text));
2400               text_info=text_info->previous;
2401             }
2402             (void) XDrawString(display,windows->image.id,annotate_context,
2403               x,y,"_",1);
2404           }
2405         break;
2406       }
2407       case KeyPress:
2408       {
2409         int
2410           length;
2411
2412         if (event.xkey.window != windows->image.id)
2413           break;
2414         /*
2415           Respond to a user key press.
2416         */
2417         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
2418           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2419         *(command+length)='\0';
2420         if (((event.xkey.state & ControlMask) != 0) ||
2421             ((event.xkey.state & Mod1Mask) != 0))
2422           state|=ModifierState;
2423         if ((state & ModifierState) != 0)
2424           switch ((int) key_symbol)
2425           {
2426             case XK_u:
2427             case XK_U:
2428             {
2429               key_symbol=DeleteCommand;
2430               break;
2431             }
2432             default:
2433               break;
2434           }
2435         switch ((int) key_symbol)
2436         {
2437           case XK_BackSpace:
2438           {
2439             /*
2440               Erase one character.
2441             */
2442             if (p == annotate_info->text)
2443               {
2444                 if (annotate_info->previous == (XAnnotateInfo *) NULL)
2445                   break;
2446                 else
2447                   {
2448                     /*
2449                       Go to end of the previous line of text.
2450                     */
2451                     annotate_info=annotate_info->previous;
2452                     p=annotate_info->text;
2453                     x=annotate_info->x+annotate_info->width;
2454                     y=annotate_info->y;
2455                     if (annotate_info->width != 0)
2456                       p+=strlen(annotate_info->text);
2457                     break;
2458                   }
2459               }
2460             p--;
2461             x-=XTextWidth(font_info,p,1);
2462             text_event.xexpose.x=x;
2463             text_event.xexpose.y=y-font_info->max_bounds.ascent;
2464             XRefreshWindow(display,&windows->image,&text_event);
2465             break;
2466           }
2467           case XK_bracketleft:
2468           {
2469             key_symbol=XK_Escape;
2470             break;
2471           }
2472           case DeleteCommand:
2473           {
2474             /*
2475               Erase the entire line of text.
2476             */
2477             while (p != annotate_info->text)
2478             {
2479               p--;
2480               x-=XTextWidth(font_info,p,1);
2481               text_event.xexpose.x=x;
2482               XRefreshWindow(display,&windows->image,&text_event);
2483             }
2484             break;
2485           }
2486           case XK_Escape:
2487           case XK_F20:
2488           {
2489             /*
2490               Finished annotating.
2491             */
2492             annotate_info->width=(unsigned int) XTextWidth(font_info,
2493               annotate_info->text,(int) strlen(annotate_info->text));
2494             XRefreshWindow(display,&windows->image,&text_event);
2495             state|=ExitState;
2496             break;
2497           }
2498           default:
2499           {
2500             /*
2501               Draw a single character on the Image window.
2502             */
2503             if ((state & ModifierState) != 0)
2504               break;
2505             if (*command == '\0')
2506               break;
2507             *p=(*command);
2508             if (annotate_info->stencil == ForegroundStencil)
2509               (void) XDrawString(display,windows->image.id,annotate_context,
2510                 x,y,p,1);
2511             else
2512               (void) XDrawImageString(display,windows->image.id,
2513                 annotate_context,x,y,p,1);
2514             x+=XTextWidth(font_info,p,1);
2515             p++;
2516             if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2517               break;
2518           }
2519           case XK_Return:
2520           case XK_KP_Enter:
2521           {
2522             /*
2523               Advance to the next line of text.
2524             */
2525             *p='\0';
2526             annotate_info->width=(unsigned int) XTextWidth(font_info,
2527               annotate_info->text,(int) strlen(annotate_info->text));
2528             if (annotate_info->next != (XAnnotateInfo *) NULL)
2529               {
2530                 /*
2531                   Line of text already exists.
2532                 */
2533                 annotate_info=annotate_info->next;
2534                 x=annotate_info->x;
2535                 y=annotate_info->y;
2536                 p=annotate_info->text;
2537                 break;
2538               }
2539             annotate_info->next=(XAnnotateInfo *) AcquireMagickMemory(
2540               sizeof(*annotate_info->next));
2541             if (annotate_info->next == (XAnnotateInfo *) NULL)
2542               return(MagickFalse);
2543             *annotate_info->next=(*annotate_info);
2544             annotate_info->next->previous=annotate_info;
2545             annotate_info=annotate_info->next;
2546             annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2547               windows->image.width/MagickMax((ssize_t)
2548               font_info->min_bounds.width,1)+2UL,sizeof(*annotate_info->text));
2549             if (annotate_info->text == (char *) NULL)
2550               return(MagickFalse);
2551             annotate_info->y+=annotate_info->height;
2552             if (annotate_info->y > (int) windows->image.height)
2553               annotate_info->y=(int) annotate_info->height;
2554             annotate_info->next=(XAnnotateInfo *) NULL;
2555             x=annotate_info->x;
2556             y=annotate_info->y;
2557             p=annotate_info->text;
2558             break;
2559           }
2560         }
2561         break;
2562       }
2563       case KeyRelease:
2564       {
2565         /*
2566           Respond to a user key release.
2567         */
2568         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2569           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2570         state&=(~ModifierState);
2571         break;
2572       }
2573       case SelectionNotify:
2574       {
2575         Atom
2576           type;
2577
2578         int
2579           format;
2580
2581         unsigned char
2582           *data;
2583
2584         unsigned long
2585           after,
2586           length;
2587
2588         /*
2589           Obtain response from primary selection.
2590         */
2591         if (event.xselection.property == (Atom) None)
2592           break;
2593         status=XGetWindowProperty(display,event.xselection.requestor,
2594           event.xselection.property,0L,(long) MaxTextExtent,True,XA_STRING,
2595           &type,&format,&length,&after,&data);
2596         if ((status != Success) || (type != XA_STRING) || (format == 32) ||
2597             (length == 0))
2598           break;
2599         /*
2600           Annotate Image window with primary selection.
2601         */
2602         for (i=0; i < (ssize_t) length; i++)
2603         {
2604           if ((char) data[i] != '\n')
2605             {
2606               /*
2607                 Draw a single character on the Image window.
2608               */
2609               *p=(char) data[i];
2610               (void) XDrawString(display,windows->image.id,annotate_context,
2611                 x,y,p,1);
2612               x+=XTextWidth(font_info,p,1);
2613               p++;
2614               if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2615                 continue;
2616             }
2617           /*
2618             Advance to the next line of text.
2619           */
2620           *p='\0';
2621           annotate_info->width=(unsigned int) XTextWidth(font_info,
2622             annotate_info->text,(int) strlen(annotate_info->text));
2623           if (annotate_info->next != (XAnnotateInfo *) NULL)
2624             {
2625               /*
2626                 Line of text already exists.
2627               */
2628               annotate_info=annotate_info->next;
2629               x=annotate_info->x;
2630               y=annotate_info->y;
2631               p=annotate_info->text;
2632               continue;
2633             }
2634           annotate_info->next=(XAnnotateInfo *) AcquireMagickMemory(
2635             sizeof(*annotate_info->next));
2636           if (annotate_info->next == (XAnnotateInfo *) NULL)
2637             return(MagickFalse);
2638           *annotate_info->next=(*annotate_info);
2639           annotate_info->next->previous=annotate_info;
2640           annotate_info=annotate_info->next;
2641           annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2642             windows->image.width/MagickMax((ssize_t)
2643             font_info->min_bounds.width,1)+2UL,sizeof(*annotate_info->text));
2644           if (annotate_info->text == (char *) NULL)
2645             return(MagickFalse);
2646           annotate_info->y+=annotate_info->height;
2647           if (annotate_info->y > (int) windows->image.height)
2648             annotate_info->y=(int) annotate_info->height;
2649           annotate_info->next=(XAnnotateInfo *) NULL;
2650           x=annotate_info->x;
2651           y=annotate_info->y;
2652           p=annotate_info->text;
2653         }
2654         (void) XFree((void *) data);
2655         break;
2656       }
2657       default:
2658         break;
2659     }
2660   } while ((state & ExitState) == 0);
2661   (void) XFreeCursor(display,cursor);
2662   /*
2663     Annotation is relative to image configuration.
2664   */
2665   width=(unsigned int) image->columns;
2666   height=(unsigned int) image->rows;
2667   x=0;
2668   y=0;
2669   if (windows->image.crop_geometry != (char *) NULL)
2670     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
2671   /*
2672     Initialize annotated image.
2673   */
2674   XSetCursorState(display,windows,MagickTrue);
2675   XCheckRefreshWindows(display,windows);
2676   while (annotate_info != (XAnnotateInfo *) NULL)
2677   {
2678     if (annotate_info->width == 0)
2679       {
2680         /*
2681           No text on this line--  go to the next line of text.
2682         */
2683         previous_info=annotate_info->previous;
2684         annotate_info->text=(char *)
2685           RelinquishMagickMemory(annotate_info->text);
2686         annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2687         annotate_info=previous_info;
2688         continue;
2689       }
2690     /*
2691       Determine pixel index for box and pen color.
2692     */
2693     windows->pixel_info->box_color=windows->pixel_info->pen_colors[box_id];
2694     if (windows->pixel_info->colors != 0)
2695       for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
2696         if (windows->pixel_info->pixels[i] ==
2697             windows->pixel_info->pen_colors[box_id].pixel)
2698           {
2699             windows->pixel_info->box_index=(unsigned short) i;
2700             break;
2701           }
2702     windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
2703     if (windows->pixel_info->colors != 0)
2704       for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
2705         if (windows->pixel_info->pixels[i] ==
2706             windows->pixel_info->pen_colors[pen_id].pixel)
2707           {
2708             windows->pixel_info->pen_index=(unsigned short) i;
2709             break;
2710           }
2711     /*
2712       Define the annotate geometry string.
2713     */
2714     annotate_info->x=(int)
2715       width*(annotate_info->x+windows->image.x)/windows->image.ximage->width;
2716     annotate_info->y=(int) height*(annotate_info->y-font_info->ascent+
2717       windows->image.y)/windows->image.ximage->height;
2718     (void) FormatLocaleString(annotate_info->geometry,MaxTextExtent,
2719       "%ux%u%+d%+d",width*annotate_info->width/windows->image.ximage->width,
2720       height*annotate_info->height/windows->image.ximage->height,
2721       annotate_info->x+x,annotate_info->y+y);
2722     /*
2723       Annotate image with text.
2724     */
2725     status=XAnnotateImage(display,windows->pixel_info,annotate_info,image,
2726       exception);
2727     if (status == 0)
2728       return(MagickFalse);
2729     /*
2730       Free up memory.
2731     */
2732     previous_info=annotate_info->previous;
2733     annotate_info->text=DestroyString(annotate_info->text);
2734     annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2735     annotate_info=previous_info;
2736   }
2737   (void) XSetForeground(display,annotate_context,
2738     windows->pixel_info->foreground_color.pixel);
2739   (void) XSetBackground(display,annotate_context,
2740     windows->pixel_info->background_color.pixel);
2741   (void) XSetFont(display,annotate_context,windows->font_info->fid);
2742   XSetCursorState(display,windows,MagickFalse);
2743   (void) XFreeFont(display,font_info);
2744   /*
2745     Update image configuration.
2746   */
2747   XConfigureImageColormap(display,resource_info,windows,image,exception);
2748   (void) XConfigureImage(display,resource_info,windows,image,exception);
2749   return(MagickTrue);
2750 }
2751 \f
2752 /*
2753 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2754 %                                                                             %
2755 %                                                                             %
2756 %                                                                             %
2757 +   X B a c k g r o u n d I m a g e                                           %
2758 %                                                                             %
2759 %                                                                             %
2760 %                                                                             %
2761 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2762 %
2763 %  XBackgroundImage() displays the image in the background of a window.
2764 %
2765 %  The format of the XBackgroundImage method is:
2766 %
2767 %      MagickBooleanType XBackgroundImage(Display *display,
2768 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
2769 %        ExceptionInfo *exception)
2770 %
2771 %  A description of each parameter follows:
2772 %
2773 %    o display: Specifies a connection to an X server; returned from
2774 %      XOpenDisplay.
2775 %
2776 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2777 %
2778 %    o windows: Specifies a pointer to a XWindows structure.
2779 %
2780 %    o image: the image.
2781 %
2782 %    o exception: return any errors or warnings in this structure.
2783 %
2784 */
2785 static MagickBooleanType XBackgroundImage(Display *display,
2786   XResourceInfo *resource_info,XWindows *windows,Image **image,
2787   ExceptionInfo *exception)
2788 {
2789 #define BackgroundImageTag  "Background/Image"
2790
2791   int
2792     status;
2793
2794   static char
2795     window_id[MaxTextExtent] = "root";
2796
2797   XResourceInfo
2798     background_resources;
2799
2800   /*
2801     Put image in background.
2802   */
2803   status=XDialogWidget(display,windows,"Background",
2804     "Enter window id (id 0x00 selects window with pointer):",window_id);
2805   if (*window_id == '\0')
2806     return(MagickFalse);
2807   (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
2808     exception);
2809   XInfoWidget(display,windows,BackgroundImageTag);
2810   XSetCursorState(display,windows,MagickTrue);
2811   XCheckRefreshWindows(display,windows);
2812   background_resources=(*resource_info);
2813   background_resources.window_id=window_id;
2814   background_resources.backdrop=IsMagickTrue(status);
2815   status=XDisplayBackgroundImage(display,&background_resources,*image,
2816     exception);
2817   if (IfMagickTrue(status))
2818     XClientMessage(display,windows->image.id,windows->im_protocols,
2819       windows->im_retain_colors,CurrentTime);
2820   XSetCursorState(display,windows,MagickFalse);
2821   (void) XMagickCommand(display,resource_info,windows,UndoCommand,image,
2822     exception);
2823   return(MagickTrue);
2824 }
2825 \f
2826 /*
2827 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2828 %                                                                             %
2829 %                                                                             %
2830 %                                                                             %
2831 +   X C h o p I m a g e                                                       %
2832 %                                                                             %
2833 %                                                                             %
2834 %                                                                             %
2835 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2836 %
2837 %  XChopImage() chops the X image.
2838 %
2839 %  The format of the XChopImage method is:
2840 %
2841 %    MagickBooleanType XChopImage(Display *display,XResourceInfo *resource_info,
2842 %      XWindows *windows,Image **image,ExceptionInfo *exception)
2843 %
2844 %  A description of each parameter follows:
2845 %
2846 %    o display: Specifies a connection to an X server; returned from
2847 %      XOpenDisplay.
2848 %
2849 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2850 %
2851 %    o windows: Specifies a pointer to a XWindows structure.
2852 %
2853 %    o image: the image.
2854 %
2855 %    o exception: return any errors or warnings in this structure.
2856 %
2857 */
2858 static MagickBooleanType XChopImage(Display *display,
2859   XResourceInfo *resource_info,XWindows *windows,Image **image,
2860   ExceptionInfo *exception)
2861 {
2862   static const char
2863     *ChopMenu[] =
2864     {
2865       "Direction",
2866       "Help",
2867       "Dismiss",
2868       (char *) NULL
2869     };
2870
2871   static ModeType
2872     direction = HorizontalChopCommand;
2873
2874   static const ModeType
2875     ChopCommands[] =
2876     {
2877       ChopDirectionCommand,
2878       ChopHelpCommand,
2879       ChopDismissCommand
2880     },
2881     DirectionCommands[] =
2882     {
2883       HorizontalChopCommand,
2884       VerticalChopCommand
2885     };
2886
2887   char
2888     text[MaxTextExtent];
2889
2890   Image
2891     *chop_image;
2892
2893   int
2894     id,
2895     x,
2896     y;
2897
2898   double
2899     scale_factor;
2900
2901   RectangleInfo
2902     chop_info;
2903
2904   unsigned int
2905     distance,
2906     height,
2907     width;
2908
2909   size_t
2910     state;
2911
2912   XEvent
2913     event;
2914
2915   XSegment
2916     segment_info;
2917
2918   /*
2919     Map Command widget.
2920   */
2921   (void) CloneString(&windows->command.name,"Chop");
2922   windows->command.data=1;
2923   (void) XCommandWidget(display,windows,ChopMenu,(XEvent *) NULL);
2924   (void) XMapRaised(display,windows->command.id);
2925   XClientMessage(display,windows->image.id,windows->im_protocols,
2926     windows->im_update_widget,CurrentTime);
2927   /*
2928     Track pointer until button 1 is pressed.
2929   */
2930   XQueryPosition(display,windows->image.id,&x,&y);
2931   (void) XSelectInput(display,windows->image.id,
2932     windows->image.attributes.event_mask | PointerMotionMask);
2933   state=DefaultState;
2934   do
2935   {
2936     if (IfMagickTrue(windows->info.mapped) )
2937       {
2938         /*
2939           Display pointer position.
2940         */
2941         (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
2942           x+windows->image.x,y+windows->image.y);
2943         XInfoWidget(display,windows,text);
2944       }
2945     /*
2946       Wait for next event.
2947     */
2948     XScreenEvent(display,windows,&event,exception);
2949     if (event.xany.window == windows->command.id)
2950       {
2951         /*
2952           Select a command from the Command widget.
2953         */
2954         id=XCommandWidget(display,windows,ChopMenu,&event);
2955         if (id < 0)
2956           continue;
2957         switch (ChopCommands[id])
2958         {
2959           case ChopDirectionCommand:
2960           {
2961             char
2962               command[MaxTextExtent];
2963
2964             static const char
2965               *Directions[] =
2966               {
2967                 "horizontal",
2968                 "vertical",
2969                 (char *) NULL,
2970               };
2971
2972             /*
2973               Select a command from the pop-up menu.
2974             */
2975             id=XMenuWidget(display,windows,ChopMenu[id],Directions,command);
2976             if (id >= 0)
2977               direction=DirectionCommands[id];
2978             break;
2979           }
2980           case ChopHelpCommand:
2981           {
2982             XTextViewWidget(display,resource_info,windows,MagickFalse,
2983               "Help Viewer - Image Chop",ImageChopHelp);
2984             break;
2985           }
2986           case ChopDismissCommand:
2987           {
2988             /*
2989               Prematurely exit.
2990             */
2991             state|=EscapeState;
2992             state|=ExitState;
2993             break;
2994           }
2995           default:
2996             break;
2997         }
2998         continue;
2999       }
3000     switch (event.type)
3001     {
3002       case ButtonPress:
3003       {
3004         if (event.xbutton.button != Button1)
3005           break;
3006         if (event.xbutton.window != windows->image.id)
3007           break;
3008         /*
3009           User has committed to start point of chopping line.
3010         */
3011         segment_info.x1=(short int) event.xbutton.x;
3012         segment_info.x2=(short int) event.xbutton.x;
3013         segment_info.y1=(short int) event.xbutton.y;
3014         segment_info.y2=(short int) event.xbutton.y;
3015         state|=ExitState;
3016         break;
3017       }
3018       case ButtonRelease:
3019         break;
3020       case Expose:
3021         break;
3022       case KeyPress:
3023       {
3024         char
3025           command[MaxTextExtent];
3026
3027         KeySym
3028           key_symbol;
3029
3030         if (event.xkey.window != windows->image.id)
3031           break;
3032         /*
3033           Respond to a user key press.
3034         */
3035         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3036           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3037         switch ((int) key_symbol)
3038         {
3039           case XK_Escape:
3040           case XK_F20:
3041           {
3042             /*
3043               Prematurely exit.
3044             */
3045             state|=EscapeState;
3046             state|=ExitState;
3047             break;
3048           }
3049           case XK_F1:
3050           case XK_Help:
3051           {
3052             (void) XSetFunction(display,windows->image.highlight_context,
3053               GXcopy);
3054             XTextViewWidget(display,resource_info,windows,MagickFalse,
3055               "Help Viewer - Image Chop",ImageChopHelp);
3056             (void) XSetFunction(display,windows->image.highlight_context,
3057               GXinvert);
3058             break;
3059           }
3060           default:
3061           {
3062             (void) XBell(display,0);
3063             break;
3064           }
3065         }
3066         break;
3067       }
3068       case MotionNotify:
3069       {
3070         /*
3071           Map and unmap Info widget as text cursor crosses its boundaries.
3072         */
3073         x=event.xmotion.x;
3074         y=event.xmotion.y;
3075         if (IfMagickTrue(windows->info.mapped) )
3076           {
3077             if ((x < (int) (windows->info.x+windows->info.width)) &&
3078                 (y < (int) (windows->info.y+windows->info.height)))
3079               (void) XWithdrawWindow(display,windows->info.id,
3080                 windows->info.screen);
3081           }
3082         else
3083           if ((x > (int) (windows->info.x+windows->info.width)) ||
3084               (y > (int) (windows->info.y+windows->info.height)))
3085             (void) XMapWindow(display,windows->info.id);
3086       }
3087     }
3088   } while ((state & ExitState) == 0);
3089   (void) XSelectInput(display,windows->image.id,
3090     windows->image.attributes.event_mask);
3091   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3092   if ((state & EscapeState) != 0)
3093     return(MagickTrue);
3094   /*
3095     Draw line as pointer moves until the mouse button is released.
3096   */
3097   chop_info.width=0;
3098   chop_info.height=0;
3099   chop_info.x=0;
3100   chop_info.y=0;
3101   distance=0;
3102   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
3103   state=DefaultState;
3104   do
3105   {
3106     if (distance > 9)
3107       {
3108         /*
3109           Display info and draw chopping line.
3110         */
3111         if (IfMagickFalse(windows->info.mapped) )
3112           (void) XMapWindow(display,windows->info.id);
3113         (void) FormatLocaleString(text,MaxTextExtent,
3114           " %.20gx%.20g%+.20g%+.20g",(double) chop_info.width,(double)
3115           chop_info.height,(double) chop_info.x,(double) chop_info.y);
3116         XInfoWidget(display,windows,text);
3117         XHighlightLine(display,windows->image.id,
3118           windows->image.highlight_context,&segment_info);
3119       }
3120     else
3121       if (IfMagickTrue(windows->info.mapped) )
3122         (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3123     /*
3124       Wait for next event.
3125     */
3126     XScreenEvent(display,windows,&event,exception);
3127     if (distance > 9)
3128       XHighlightLine(display,windows->image.id,
3129         windows->image.highlight_context,&segment_info);
3130     switch (event.type)
3131     {
3132       case ButtonPress:
3133       {
3134         segment_info.x2=(short int) event.xmotion.x;
3135         segment_info.y2=(short int) event.xmotion.y;
3136         break;
3137       }
3138       case ButtonRelease:
3139       {
3140         /*
3141           User has committed to chopping line.
3142         */
3143         segment_info.x2=(short int) event.xbutton.x;
3144         segment_info.y2=(short int) event.xbutton.y;
3145         state|=ExitState;
3146         break;
3147       }
3148       case Expose:
3149         break;
3150       case MotionNotify:
3151       {
3152         segment_info.x2=(short int) event.xmotion.x;
3153         segment_info.y2=(short int) event.xmotion.y;
3154       }
3155       default:
3156         break;
3157     }
3158     /*
3159       Check boundary conditions.
3160     */
3161     if (segment_info.x2 < 0)
3162       segment_info.x2=0;
3163     else
3164       if (segment_info.x2 > windows->image.ximage->width)
3165         segment_info.x2=windows->image.ximage->width;
3166     if (segment_info.y2 < 0)
3167       segment_info.y2=0;
3168     else
3169       if (segment_info.y2 > windows->image.ximage->height)
3170         segment_info.y2=windows->image.ximage->height;
3171     distance=(unsigned int)
3172       (((segment_info.x2-segment_info.x1)*(segment_info.x2-segment_info.x1))+
3173        ((segment_info.y2-segment_info.y1)*(segment_info.y2-segment_info.y1)));
3174     /*
3175       Compute chopping geometry.
3176     */
3177     if (direction == HorizontalChopCommand)
3178       {
3179         chop_info.width=(size_t) (segment_info.x2-segment_info.x1+1);
3180         chop_info.x=(ssize_t) windows->image.x+segment_info.x1;
3181         chop_info.height=0;
3182         chop_info.y=0;
3183         if (segment_info.x1 > (int) segment_info.x2)
3184           {
3185             chop_info.width=(size_t) (segment_info.x1-segment_info.x2+1);
3186             chop_info.x=(ssize_t) windows->image.x+segment_info.x2;
3187           }
3188       }
3189     else
3190       {
3191         chop_info.width=0;
3192         chop_info.height=(size_t) (segment_info.y2-segment_info.y1+1);
3193         chop_info.x=0;
3194         chop_info.y=(ssize_t) windows->image.y+segment_info.y1;
3195         if (segment_info.y1 > segment_info.y2)
3196           {
3197             chop_info.height=(size_t) (segment_info.y1-segment_info.y2+1);
3198             chop_info.y=(ssize_t) windows->image.y+segment_info.y2;
3199           }
3200       }
3201   } while ((state & ExitState) == 0);
3202   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
3203   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3204   if (distance <= 9)
3205     return(MagickTrue);
3206   /*
3207     Image chopping is relative to image configuration.
3208   */
3209   (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
3210     exception);
3211   XSetCursorState(display,windows,MagickTrue);
3212   XCheckRefreshWindows(display,windows);
3213   windows->image.window_changes.width=windows->image.ximage->width-
3214     (unsigned int) chop_info.width;
3215   windows->image.window_changes.height=windows->image.ximage->height-
3216     (unsigned int) chop_info.height;
3217   width=(unsigned int) (*image)->columns;
3218   height=(unsigned int) (*image)->rows;
3219   x=0;
3220   y=0;
3221   if (windows->image.crop_geometry != (char *) NULL)
3222     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
3223   scale_factor=(double) width/windows->image.ximage->width;
3224   chop_info.x+=x;
3225   chop_info.x=(ssize_t) (scale_factor*chop_info.x+0.5);
3226   chop_info.width=(unsigned int) (scale_factor*chop_info.width+0.5);
3227   scale_factor=(double) height/windows->image.ximage->height;
3228   chop_info.y+=y;
3229   chop_info.y=(ssize_t) (scale_factor*chop_info.y+0.5);
3230   chop_info.height=(unsigned int) (scale_factor*chop_info.height+0.5);
3231   /*
3232     Chop image.
3233   */
3234   chop_image=ChopImage(*image,&chop_info,exception);
3235   XSetCursorState(display,windows,MagickFalse);
3236   if (chop_image == (Image *) NULL)
3237     return(MagickFalse);
3238   *image=DestroyImage(*image);
3239   *image=chop_image;
3240   /*
3241     Update image configuration.
3242   */
3243   XConfigureImageColormap(display,resource_info,windows,*image,exception);
3244   (void) XConfigureImage(display,resource_info,windows,*image,exception);
3245   return(MagickTrue);
3246 }
3247 \f
3248 /*
3249 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3250 %                                                                             %
3251 %                                                                             %
3252 %                                                                             %
3253 +   X C o l o r E d i t I m a g e                                             %
3254 %                                                                             %
3255 %                                                                             %
3256 %                                                                             %
3257 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3258 %
3259 %  XColorEditImage() allows the user to interactively change the color of one
3260 %  pixel for a DirectColor image or one colormap entry for a PseudoClass image.
3261 %
3262 %  The format of the XColorEditImage method is:
3263 %
3264 %      MagickBooleanType XColorEditImage(Display *display,
3265 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
3266 %          ExceptionInfo *exception)
3267 %
3268 %  A description of each parameter follows:
3269 %
3270 %    o display: Specifies a connection to an X server;  returned from
3271 %      XOpenDisplay.
3272 %
3273 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3274 %
3275 %    o windows: Specifies a pointer to a XWindows structure.
3276 %
3277 %    o image: the image; returned from ReadImage.
3278 %
3279 %    o exception: return any errors or warnings in this structure.
3280 %
3281 */
3282 static MagickBooleanType XColorEditImage(Display *display,
3283   XResourceInfo *resource_info,XWindows *windows,Image **image,
3284   ExceptionInfo *exception)
3285 {
3286   static const char
3287     *ColorEditMenu[] =
3288     {
3289       "Method",
3290       "Pixel Color",
3291       "Border Color",
3292       "Fuzz",
3293       "Undo",
3294       "Help",
3295       "Dismiss",
3296       (char *) NULL
3297     };
3298
3299   static const ModeType
3300     ColorEditCommands[] =
3301     {
3302       ColorEditMethodCommand,
3303       ColorEditColorCommand,
3304       ColorEditBorderCommand,
3305       ColorEditFuzzCommand,
3306       ColorEditUndoCommand,
3307       ColorEditHelpCommand,
3308       ColorEditDismissCommand
3309     };
3310
3311   static PaintMethod
3312     method = PointMethod;
3313
3314   static unsigned int
3315     pen_id = 0;
3316
3317   static XColor
3318     border_color = { 0, 0, 0, 0, 0, 0 };
3319
3320   char
3321     command[MaxTextExtent],
3322     text[MaxTextExtent];
3323
3324   Cursor
3325     cursor;
3326
3327   int
3328     entry,
3329     id,
3330     x,
3331     x_offset,
3332     y,
3333     y_offset;
3334
3335   register Quantum
3336     *q;
3337
3338   register ssize_t
3339     i;
3340
3341   unsigned int
3342     height,
3343     width;
3344
3345   size_t
3346     state;
3347
3348   XColor
3349     color;
3350
3351   XEvent
3352     event;
3353
3354   /*
3355     Map Command widget.
3356   */
3357   (void) CloneString(&windows->command.name,"Color Edit");
3358   windows->command.data=4;
3359   (void) XCommandWidget(display,windows,ColorEditMenu,(XEvent *) NULL);
3360   (void) XMapRaised(display,windows->command.id);
3361   XClientMessage(display,windows->image.id,windows->im_protocols,
3362     windows->im_update_widget,CurrentTime);
3363   /*
3364     Make cursor.
3365   */
3366   cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
3367     resource_info->background_color,resource_info->foreground_color);
3368   (void) XCheckDefineCursor(display,windows->image.id,cursor);
3369   /*
3370     Track pointer until button 1 is pressed.
3371   */
3372   XQueryPosition(display,windows->image.id,&x,&y);
3373   (void) XSelectInput(display,windows->image.id,
3374     windows->image.attributes.event_mask | PointerMotionMask);
3375   state=DefaultState;
3376   do
3377   {
3378     if (IfMagickTrue(windows->info.mapped) )
3379       {
3380         /*
3381           Display pointer position.
3382         */
3383         (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
3384           x+windows->image.x,y+windows->image.y);
3385         XInfoWidget(display,windows,text);
3386       }
3387     /*
3388       Wait for next event.
3389     */
3390     XScreenEvent(display,windows,&event,exception);
3391     if (event.xany.window == windows->command.id)
3392       {
3393         /*
3394           Select a command from the Command widget.
3395         */
3396         id=XCommandWidget(display,windows,ColorEditMenu,&event);
3397         if (id < 0)
3398           {
3399             (void) XCheckDefineCursor(display,windows->image.id,cursor);
3400             continue;
3401           }
3402         switch (ColorEditCommands[id])
3403         {
3404           case ColorEditMethodCommand:
3405           {
3406             char
3407               **methods;
3408
3409             /*
3410               Select a method from the pop-up menu.
3411             */
3412             methods=(char **) GetCommandOptions(MagickMethodOptions);
3413             if (methods == (char **) NULL)
3414               break;
3415             entry=XMenuWidget(display,windows,ColorEditMenu[id],
3416               (const char **) methods,command);
3417             if (entry >= 0)
3418               method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
3419                 MagickFalse,methods[entry]);
3420             methods=DestroyStringList(methods);
3421             break;
3422           }
3423           case ColorEditColorCommand:
3424           {
3425             const char
3426               *ColorMenu[MaxNumberPens];
3427
3428             int
3429               pen_number;
3430
3431             /*
3432               Initialize menu selections.
3433             */
3434             for (i=0; i < (int) (MaxNumberPens-2); i++)
3435               ColorMenu[i]=resource_info->pen_colors[i];
3436             ColorMenu[MaxNumberPens-2]="Browser...";
3437             ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3438             /*
3439               Select a pen color from the pop-up menu.
3440             */
3441             pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3442               (const char **) ColorMenu,command);
3443             if (pen_number < 0)
3444               break;
3445             if (pen_number == (MaxNumberPens-2))
3446               {
3447                 static char
3448                   color_name[MaxTextExtent] = "gray";
3449
3450                 /*
3451                   Select a pen color from a dialog.
3452                 */
3453                 resource_info->pen_colors[pen_number]=color_name;
3454                 XColorBrowserWidget(display,windows,"Select",color_name);
3455                 if (*color_name == '\0')
3456                   break;
3457               }
3458             /*
3459               Set pen color.
3460             */
3461             (void) XParseColor(display,windows->map_info->colormap,
3462               resource_info->pen_colors[pen_number],&color);
3463             XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
3464               (unsigned int) MaxColors,&color);
3465             windows->pixel_info->pen_colors[pen_number]=color;
3466             pen_id=(unsigned int) pen_number;
3467             break;
3468           }
3469           case ColorEditBorderCommand:
3470           {
3471             const char
3472               *ColorMenu[MaxNumberPens];
3473
3474             int
3475               pen_number;
3476
3477             /*
3478               Initialize menu selections.
3479             */
3480             for (i=0; i < (int) (MaxNumberPens-2); i++)
3481               ColorMenu[i]=resource_info->pen_colors[i];
3482             ColorMenu[MaxNumberPens-2]="Browser...";
3483             ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3484             /*
3485               Select a pen color from the pop-up menu.
3486             */
3487             pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3488               (const char **) ColorMenu,command);
3489             if (pen_number < 0)
3490               break;
3491             if (pen_number == (MaxNumberPens-2))
3492               {
3493                 static char
3494                   color_name[MaxTextExtent] = "gray";
3495
3496                 /*
3497                   Select a pen color from a dialog.
3498                 */
3499                 resource_info->pen_colors[pen_number]=color_name;
3500                 XColorBrowserWidget(display,windows,"Select",color_name);
3501                 if (*color_name == '\0')
3502                   break;
3503               }
3504             /*
3505               Set border color.
3506             */
3507             (void) XParseColor(display,windows->map_info->colormap,
3508               resource_info->pen_colors[pen_number],&border_color);
3509             break;
3510           }
3511           case ColorEditFuzzCommand:
3512           {
3513             static char
3514               fuzz[MaxTextExtent];
3515
3516             static const char
3517               *FuzzMenu[] =
3518               {
3519                 "0%",
3520                 "2%",
3521                 "5%",
3522                 "10%",
3523                 "15%",
3524                 "Dialog...",
3525                 (char *) NULL,
3526               };
3527
3528             /*
3529               Select a command from the pop-up menu.
3530             */
3531             entry=XMenuWidget(display,windows,ColorEditMenu[id],FuzzMenu,
3532               command);
3533             if (entry < 0)
3534               break;
3535             if (entry != 5)
3536               {
3537                 (*image)->fuzz=StringToDoubleInterval(FuzzMenu[entry],(double)
3538                   QuantumRange+1.0);
3539                 break;
3540               }
3541             (void) (void) CopyMagickString(fuzz,"20%",MaxTextExtent);
3542             (void) XDialogWidget(display,windows,"Ok",
3543               "Enter fuzz factor (0.0 - 99.9%):",fuzz);
3544             if (*fuzz == '\0')
3545               break;
3546             (void) ConcatenateMagickString(fuzz,"%",MaxTextExtent);
3547             (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+
3548               1.0);
3549             break;
3550           }
3551           case ColorEditUndoCommand:
3552           {
3553             (void) XMagickCommand(display,resource_info,windows,UndoCommand,
3554               image,exception);
3555             break;
3556           }
3557           case ColorEditHelpCommand:
3558           default:
3559           {
3560             XTextViewWidget(display,resource_info,windows,MagickFalse,
3561               "Help Viewer - Image Annotation",ImageColorEditHelp);
3562             break;
3563           }
3564           case ColorEditDismissCommand:
3565           {
3566             /*
3567               Prematurely exit.
3568             */
3569             state|=EscapeState;
3570             state|=ExitState;
3571             break;
3572           }
3573         }
3574         (void) XCheckDefineCursor(display,windows->image.id,cursor);
3575         continue;
3576       }
3577     switch (event.type)
3578     {
3579       case ButtonPress:
3580       {
3581         if (event.xbutton.button != Button1)
3582           break;
3583         if ((event.xbutton.window != windows->image.id) &&
3584             (event.xbutton.window != windows->magnify.id))
3585           break;
3586         /*
3587           exit loop.
3588         */
3589         x=event.xbutton.x;
3590         y=event.xbutton.y;
3591         (void) XMagickCommand(display,resource_info,windows,
3592           SaveToUndoBufferCommand,image,exception);
3593         state|=UpdateConfigurationState;
3594         break;
3595       }
3596       case ButtonRelease:
3597       {
3598         if (event.xbutton.button != Button1)
3599           break;
3600         if ((event.xbutton.window != windows->image.id) &&
3601             (event.xbutton.window != windows->magnify.id))
3602           break;
3603         /*
3604           Update colormap information.
3605         */
3606         x=event.xbutton.x;
3607         y=event.xbutton.y;
3608         XConfigureImageColormap(display,resource_info,windows,*image,exception);
3609         (void) XConfigureImage(display,resource_info,windows,*image,exception);
3610         XInfoWidget(display,windows,text);
3611         (void) XCheckDefineCursor(display,windows->image.id,cursor);
3612         state&=(~UpdateConfigurationState);
3613         break;
3614       }
3615       case Expose:
3616         break;
3617       case KeyPress:
3618       {
3619         KeySym
3620           key_symbol;
3621
3622         if (event.xkey.window == windows->magnify.id)
3623           {
3624             Window
3625               window;
3626
3627             window=windows->magnify.id;
3628             while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
3629           }
3630         if (event.xkey.window != windows->image.id)
3631           break;
3632         /*
3633           Respond to a user key press.
3634         */
3635         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3636           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3637         switch ((int) key_symbol)
3638         {
3639           case XK_Escape:
3640           case XK_F20:
3641           {
3642             /*
3643               Prematurely exit.
3644             */
3645             state|=ExitState;
3646             break;
3647           }
3648           case XK_F1:
3649           case XK_Help:
3650           {
3651             XTextViewWidget(display,resource_info,windows,MagickFalse,
3652               "Help Viewer - Image Annotation",ImageColorEditHelp);
3653             break;
3654           }
3655           default:
3656           {
3657             (void) XBell(display,0);
3658             break;
3659           }
3660         }
3661         break;
3662       }
3663       case MotionNotify:
3664       {
3665         /*
3666           Map and unmap Info widget as cursor crosses its boundaries.
3667         */
3668         x=event.xmotion.x;
3669         y=event.xmotion.y;
3670         if (IfMagickTrue(windows->info.mapped) )
3671           {
3672             if ((x < (int) (windows->info.x+windows->info.width)) &&
3673                 (y < (int) (windows->info.y+windows->info.height)))
3674               (void) XWithdrawWindow(display,windows->info.id,
3675                 windows->info.screen);
3676           }
3677         else
3678           if ((x > (int) (windows->info.x+windows->info.width)) ||
3679               (y > (int) (windows->info.y+windows->info.height)))
3680             (void) XMapWindow(display,windows->info.id);
3681         break;
3682       }
3683       default:
3684         break;
3685     }
3686     if (event.xany.window == windows->magnify.id)
3687       {
3688         x=windows->magnify.x-windows->image.x;
3689         y=windows->magnify.y-windows->image.y;
3690       }
3691     x_offset=x;
3692     y_offset=y;
3693     if ((state & UpdateConfigurationState) != 0)
3694       {
3695         CacheView
3696           *image_view;
3697
3698         int
3699           x,
3700           y;
3701
3702         /*
3703           Pixel edit is relative to image configuration.
3704         */
3705         (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
3706           MagickTrue);
3707         color=windows->pixel_info->pen_colors[pen_id];
3708         XPutPixel(windows->image.ximage,x_offset,y_offset,color.pixel);
3709         width=(unsigned int) (*image)->columns;
3710         height=(unsigned int) (*image)->rows;
3711         x=0;
3712         y=0;
3713         if (windows->image.crop_geometry != (char *) NULL)
3714           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
3715             &width,&height);
3716         x_offset=(int)
3717           (width*(windows->image.x+x_offset)/windows->image.ximage->width+x);
3718         y_offset=(int)
3719           (height*(windows->image.y+y_offset)/windows->image.ximage->height+y);
3720         if ((x_offset < 0) || (y_offset < 0))
3721           continue;
3722         if ((x_offset >= (int) (*image)->columns) ||
3723             (y_offset >= (int) (*image)->rows))
3724           continue;
3725         image_view=AcquireAuthenticCacheView(*image,exception);
3726         switch (method)
3727         {
3728           case PointMethod:
3729           default:
3730           {
3731             /*
3732               Update color information using point algorithm.
3733             */
3734             if (IfMagickFalse(SetImageStorageClass(*image,DirectClass,exception)) )
3735               return(MagickFalse);
3736             q=GetCacheViewAuthenticPixels(image_view,(ssize_t)x_offset,
3737               (ssize_t) y_offset,1,1,exception);
3738             if (q == (Quantum *) NULL)
3739               break;
3740             SetPixelRed(*image,ScaleShortToQuantum(color.red),q);
3741             SetPixelGreen(*image,ScaleShortToQuantum(color.green),q);
3742             SetPixelBlue(*image,ScaleShortToQuantum(color.blue),q);
3743             (void) SyncCacheViewAuthenticPixels(image_view,exception);
3744             break;
3745           }
3746           case ReplaceMethod:
3747           {
3748             PixelInfo
3749               pixel,
3750               target;
3751
3752             /*
3753               Update color information using replace algorithm.
3754             */
3755             (void) GetOneCacheViewVirtualPixelInfo(image_view,(ssize_t)
3756               x_offset,(ssize_t) y_offset,&target,exception);
3757             if ((*image)->storage_class == DirectClass)
3758               {
3759                 for (y=0; y < (int) (*image)->rows; y++)
3760                 {
3761                   q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3762                     (*image)->columns,1,exception);
3763                   if (q == (Quantum *) NULL)
3764                     break;
3765                   for (x=0; x < (int) (*image)->columns; x++)
3766                   {
3767                     GetPixelInfoPixel(*image,q,&pixel);
3768                     if (IsFuzzyEquivalencePixelInfo(&pixel,&target))
3769                       {
3770                         SetPixelRed(*image,ScaleShortToQuantum(
3771                           color.red),q);
3772                         SetPixelGreen(*image,ScaleShortToQuantum(
3773                           color.green),q);
3774                         SetPixelBlue(*image,ScaleShortToQuantum(
3775                           color.blue),q);
3776                       }
3777                     q+=GetPixelChannels(*image);
3778                   }
3779                   if (IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
3780                     break;
3781                 }
3782               }
3783             else
3784               {
3785                 for (i=0; i < (ssize_t) (*image)->colors; i++)
3786                   if (IsFuzzyEquivalencePixelInfo((*image)->colormap+i,&target))
3787                     {
3788                       (*image)->colormap[i].red=(double) ScaleShortToQuantum(
3789                         color.red);
3790                       (*image)->colormap[i].green=(double) ScaleShortToQuantum(
3791                         color.green);
3792                       (*image)->colormap[i].blue=(double) ScaleShortToQuantum(
3793                         color.blue);
3794                     }
3795                 (void) SyncImage(*image,exception);
3796               }
3797             break;
3798           }
3799           case FloodfillMethod:
3800           case FillToBorderMethod:
3801           {
3802             DrawInfo
3803               *draw_info;
3804
3805             PixelInfo
3806               target;
3807
3808             /*
3809               Update color information using floodfill algorithm.
3810             */
3811             (void) GetOneVirtualPixelInfo(*image,
3812               GetPixelCacheVirtualMethod(*image),(ssize_t) x_offset,(ssize_t)
3813               y_offset,&target,exception);
3814             if (method == FillToBorderMethod)
3815               {
3816                 target.red=(double)
3817                   ScaleShortToQuantum(border_color.red);
3818                 target.green=(double)
3819                   ScaleShortToQuantum(border_color.green);
3820                 target.blue=(double)
3821                   ScaleShortToQuantum(border_color.blue);
3822               }
3823             draw_info=CloneDrawInfo(resource_info->image_info,
3824               (DrawInfo *) NULL);
3825             (void) QueryColorCompliance(resource_info->pen_colors[pen_id],
3826               AllCompliance,&draw_info->fill,exception);
3827             (void) FloodfillPaintImage(*image,draw_info,&target,
3828               (ssize_t)x_offset,(ssize_t)y_offset,
3829               IsMagickFalse(method == FloodfillMethod),exception);
3830             draw_info=DestroyDrawInfo(draw_info);
3831             break;
3832           }
3833           case ResetMethod:
3834           {
3835             /*
3836               Update color information using reset algorithm.
3837             */
3838             if (IfMagickFalse(SetImageStorageClass(*image,DirectClass,exception)) )
3839               return(MagickFalse);
3840             for (y=0; y < (int) (*image)->rows; y++)
3841             {
3842               q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3843                 (*image)->columns,1,exception);
3844               if (q == (Quantum *) NULL)
3845                 break;
3846               for (x=0; x < (int) (*image)->columns; x++)
3847               {
3848                 SetPixelRed(*image,ScaleShortToQuantum(color.red),q);
3849                 SetPixelGreen(*image,ScaleShortToQuantum(color.green),q);
3850                 SetPixelBlue(*image,ScaleShortToQuantum(color.blue),q);
3851                 q+=GetPixelChannels(*image);
3852               }
3853               if (IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
3854                 break;
3855             }
3856             break;
3857           }
3858         }
3859         image_view=DestroyCacheView(image_view);
3860         state&=(~UpdateConfigurationState);
3861       }
3862   } while ((state & ExitState) == 0);
3863   (void) XSelectInput(display,windows->image.id,
3864     windows->image.attributes.event_mask);
3865   XSetCursorState(display,windows,MagickFalse);
3866   (void) XFreeCursor(display,cursor);
3867   return(MagickTrue);
3868 }
3869 \f
3870 /*
3871 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3872 %                                                                             %
3873 %                                                                             %
3874 %                                                                             %
3875 +   X C o m p o s i t e I m a g e                                             %
3876 %                                                                             %
3877 %                                                                             %
3878 %                                                                             %
3879 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3880 %
3881 %  XCompositeImage() requests an image name from the user, reads the image and
3882 %  composites it with the X window image at a location the user chooses with
3883 %  the pointer.
3884 %
3885 %  The format of the XCompositeImage method is:
3886 %
3887 %      MagickBooleanType XCompositeImage(Display *display,
3888 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
3889 %        ExceptionInfo *exception)
3890 %
3891 %  A description of each parameter follows:
3892 %
3893 %    o display: Specifies a connection to an X server;  returned from
3894 %      XOpenDisplay.
3895 %
3896 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3897 %
3898 %    o windows: Specifies a pointer to a XWindows structure.
3899 %
3900 %    o image: the image; returned from ReadImage.
3901 %
3902 %    o exception: return any errors or warnings in this structure.
3903 %
3904 */
3905 static MagickBooleanType XCompositeImage(Display *display,
3906   XResourceInfo *resource_info,XWindows *windows,Image *image,
3907   ExceptionInfo *exception)
3908 {
3909   static char
3910     displacement_geometry[MaxTextExtent] = "30x30",
3911     filename[MaxTextExtent] = "\0";
3912
3913   static const char
3914     *CompositeMenu[] =
3915     {
3916       "Operators",
3917       "Dissolve",
3918       "Displace",
3919       "Help",
3920       "Dismiss",
3921       (char *) NULL
3922     };
3923
3924   static CompositeOperator
3925     compose = CopyCompositeOp;
3926
3927   static const ModeType
3928     CompositeCommands[] =
3929     {
3930       CompositeOperatorsCommand,
3931       CompositeDissolveCommand,
3932       CompositeDisplaceCommand,
3933       CompositeHelpCommand,
3934       CompositeDismissCommand
3935     };
3936
3937   char
3938     text[MaxTextExtent];
3939
3940   Cursor
3941     cursor;
3942
3943   Image
3944     *composite_image;
3945
3946   int
3947     entry,
3948     id,
3949     x,
3950     y;
3951
3952   double
3953     blend,
3954     scale_factor;
3955
3956   RectangleInfo
3957     highlight_info,
3958     composite_info;
3959
3960   unsigned int
3961     height,
3962     width;
3963
3964   size_t
3965     state;
3966
3967   XEvent
3968     event;
3969
3970   /*
3971     Request image file name from user.
3972   */
3973   XFileBrowserWidget(display,windows,"Composite",filename);
3974   if (*filename == '\0')
3975     return(MagickTrue);
3976   /*
3977     Read image.
3978   */
3979   XSetCursorState(display,windows,MagickTrue);
3980   XCheckRefreshWindows(display,windows);
3981   (void) CopyMagickString(resource_info->image_info->filename,filename,
3982     MaxTextExtent);
3983   composite_image=ReadImage(resource_info->image_info,exception);
3984   CatchException(exception);
3985   XSetCursorState(display,windows,MagickFalse);
3986   if (composite_image == (Image *) NULL)
3987     return(MagickFalse);
3988   /*
3989     Map Command widget.
3990   */
3991   (void) CloneString(&windows->command.name,"Composite");
3992   windows->command.data=1;
3993   (void) XCommandWidget(display,windows,CompositeMenu,(XEvent *) NULL);
3994   (void) XMapRaised(display,windows->command.id);
3995   XClientMessage(display,windows->image.id,windows->im_protocols,
3996     windows->im_update_widget,CurrentTime);
3997   /*
3998     Track pointer until button 1 is pressed.
3999   */
4000   XQueryPosition(display,windows->image.id,&x,&y);
4001   (void) XSelectInput(display,windows->image.id,
4002     windows->image.attributes.event_mask | PointerMotionMask);
4003   composite_info.x=(ssize_t) windows->image.x+x;
4004   composite_info.y=(ssize_t) windows->image.y+y;
4005   composite_info.width=0;
4006   composite_info.height=0;
4007   cursor=XCreateFontCursor(display,XC_ul_angle);
4008   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
4009   blend=0.0;
4010   state=DefaultState;
4011   do
4012   {
4013     if (IfMagickTrue(windows->info.mapped) )
4014       {
4015         /*
4016           Display pointer position.
4017         */
4018         (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
4019           (long) composite_info.x,(long) composite_info.y);
4020         XInfoWidget(display,windows,text);
4021       }
4022     highlight_info=composite_info;
4023     highlight_info.x=composite_info.x-windows->image.x;
4024     highlight_info.y=composite_info.y-windows->image.y;
4025     XHighlightRectangle(display,windows->image.id,
4026       windows->image.highlight_context,&highlight_info);
4027     /*
4028       Wait for next event.
4029     */
4030     XScreenEvent(display,windows,&event,exception);
4031     XHighlightRectangle(display,windows->image.id,
4032       windows->image.highlight_context,&highlight_info);
4033     if (event.xany.window == windows->command.id)
4034       {
4035         /*
4036           Select a command from the Command widget.
4037         */
4038         id=XCommandWidget(display,windows,CompositeMenu,&event);
4039         if (id < 0)
4040           continue;
4041         switch (CompositeCommands[id])
4042         {
4043           case CompositeOperatorsCommand:
4044           {
4045             char
4046               command[MaxTextExtent],
4047               **operators;
4048
4049             /*
4050               Select a command from the pop-up menu.
4051             */
4052             operators=GetCommandOptions(MagickComposeOptions);
4053             if (operators == (char **) NULL)
4054               break;
4055             entry=XMenuWidget(display,windows,CompositeMenu[id],
4056               (const char **) operators,command);
4057             if (entry >= 0)
4058               compose=(CompositeOperator) ParseCommandOption(
4059                 MagickComposeOptions,MagickFalse,operators[entry]);
4060             operators=DestroyStringList(operators);
4061             break;
4062           }
4063           case CompositeDissolveCommand:
4064           {
4065             static char
4066               factor[MaxTextExtent] = "20.0";
4067
4068             /*
4069               Dissolve the two images a given percent.
4070             */
4071             (void) XSetFunction(display,windows->image.highlight_context,
4072               GXcopy);
4073             (void) XDialogWidget(display,windows,"Dissolve",
4074               "Enter the blend factor (0.0 - 99.9%):",factor);
4075             (void) XSetFunction(display,windows->image.highlight_context,
4076               GXinvert);
4077             if (*factor == '\0')
4078               break;
4079             blend=StringToDouble(factor,(char **) NULL);
4080             compose=DissolveCompositeOp;
4081             break;
4082           }
4083           case CompositeDisplaceCommand:
4084           {
4085             /*
4086               Get horizontal and vertical scale displacement geometry.
4087             */
4088             (void) XSetFunction(display,windows->image.highlight_context,
4089               GXcopy);
4090             (void) XDialogWidget(display,windows,"Displace",
4091               "Enter the horizontal and vertical scale:",displacement_geometry);
4092             (void) XSetFunction(display,windows->image.highlight_context,
4093               GXinvert);
4094             if (*displacement_geometry == '\0')
4095               break;
4096             compose=DisplaceCompositeOp;
4097             break;
4098           }
4099           case CompositeHelpCommand:
4100           {
4101             (void) XSetFunction(display,windows->image.highlight_context,
4102               GXcopy);
4103             XTextViewWidget(display,resource_info,windows,MagickFalse,
4104               "Help Viewer - Image Composite",ImageCompositeHelp);
4105             (void) XSetFunction(display,windows->image.highlight_context,
4106               GXinvert);
4107             break;
4108           }
4109           case CompositeDismissCommand:
4110           {
4111             /*
4112               Prematurely exit.
4113             */
4114             state|=EscapeState;
4115             state|=ExitState;
4116             break;
4117           }
4118           default:
4119             break;
4120         }
4121         continue;
4122       }
4123     switch (event.type)
4124     {
4125       case ButtonPress:
4126       {
4127         if (IfMagickTrue(image->debug) )
4128           (void) LogMagickEvent(X11Event,GetMagickModule(),
4129             "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
4130             event.xbutton.button,event.xbutton.x,event.xbutton.y);
4131         if (event.xbutton.button != Button1)
4132           break;
4133         if (event.xbutton.window != windows->image.id)
4134           break;
4135         /*
4136           Change cursor.
4137         */
4138         composite_info.width=composite_image->columns;
4139         composite_info.height=composite_image->rows;
4140         (void) XCheckDefineCursor(display,windows->image.id,cursor);
4141         composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4142         composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4143         break;
4144       }
4145       case ButtonRelease:
4146       {
4147         if (IfMagickTrue(image->debug) )
4148           (void) LogMagickEvent(X11Event,GetMagickModule(),
4149             "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
4150             event.xbutton.button,event.xbutton.x,event.xbutton.y);
4151         if (event.xbutton.button != Button1)
4152           break;
4153         if (event.xbutton.window != windows->image.id)
4154           break;
4155         if ((composite_info.width != 0) && (composite_info.height != 0))
4156           {
4157             /*
4158               User has selected the location of the composite image.
4159             */
4160             composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4161             composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4162             state|=ExitState;
4163           }
4164         break;
4165       }
4166       case Expose:
4167         break;
4168       case KeyPress:
4169       {
4170         char
4171           command[MaxTextExtent];
4172
4173         KeySym
4174           key_symbol;
4175
4176         int
4177           length;
4178
4179         if (event.xkey.window != windows->image.id)
4180           break;
4181         /*
4182           Respond to a user key press.
4183         */
4184         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
4185           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4186         *(command+length)='\0';
4187         if (IfMagickTrue(image->debug) )
4188           (void) LogMagickEvent(X11Event,GetMagickModule(),
4189             "Key press: 0x%lx (%s)",(unsigned long) key_symbol,command);
4190         switch ((int) key_symbol)
4191         {
4192           case XK_Escape:
4193           case XK_F20:
4194           {
4195             /*
4196               Prematurely exit.
4197             */
4198             composite_image=DestroyImage(composite_image);
4199             state|=EscapeState;
4200             state|=ExitState;
4201             break;
4202           }
4203           case XK_F1:
4204           case XK_Help:
4205           {
4206             (void) XSetFunction(display,windows->image.highlight_context,
4207               GXcopy);
4208             XTextViewWidget(display,resource_info,windows,MagickFalse,
4209               "Help Viewer - Image Composite",ImageCompositeHelp);
4210             (void) XSetFunction(display,windows->image.highlight_context,
4211               GXinvert);
4212             break;
4213           }
4214           default:
4215           {
4216             (void) XBell(display,0);
4217             break;
4218           }
4219         }
4220         break;
4221       }
4222       case MotionNotify:
4223       {
4224         /*
4225           Map and unmap Info widget as text cursor crosses its boundaries.
4226         */
4227         x=event.xmotion.x;
4228         y=event.xmotion.y;
4229         if (IfMagickTrue(windows->info.mapped) )
4230           {
4231             if ((x < (int) (windows->info.x+windows->info.width)) &&
4232                 (y < (int) (windows->info.y+windows->info.height)))
4233               (void) XWithdrawWindow(display,windows->info.id,
4234                 windows->info.screen);
4235           }
4236         else
4237           if ((x > (int) (windows->info.x+windows->info.width)) ||
4238               (y > (int) (windows->info.y+windows->info.height)))
4239             (void) XMapWindow(display,windows->info.id);
4240         composite_info.x=(ssize_t) windows->image.x+x;
4241         composite_info.y=(ssize_t) windows->image.y+y;
4242         break;
4243       }
4244       default:
4245       {
4246         if (IfMagickTrue(image->debug) )
4247           (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
4248             event.type);
4249         break;
4250       }
4251     }
4252   } while ((state & ExitState) == 0);
4253   (void) XSelectInput(display,windows->image.id,
4254     windows->image.attributes.event_mask);
4255   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
4256   XSetCursorState(display,windows,MagickFalse);
4257   (void) XFreeCursor(display,cursor);
4258   if ((state & EscapeState) != 0)
4259     return(MagickTrue);
4260   /*
4261     Image compositing is relative to image configuration.
4262   */
4263   XSetCursorState(display,windows,MagickTrue);
4264   XCheckRefreshWindows(display,windows);
4265   width=(unsigned int) image->columns;
4266   height=(unsigned int) image->rows;
4267   x=0;
4268   y=0;
4269   if (windows->image.crop_geometry != (char *) NULL)
4270     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
4271   scale_factor=(double) width/windows->image.ximage->width;
4272   composite_info.x+=x;
4273   composite_info.x=(ssize_t) (scale_factor*composite_info.x+0.5);
4274   composite_info.width=(unsigned int) (scale_factor*composite_info.width+0.5);
4275   scale_factor=(double) height/windows->image.ximage->height;
4276   composite_info.y+=y;
4277   composite_info.y=(ssize_t) (scale_factor*composite_info.y+0.5);
4278   composite_info.height=(unsigned int) (scale_factor*composite_info.height+0.5);
4279   if ((composite_info.width != composite_image->columns) ||
4280       (composite_info.height != composite_image->rows))
4281     {
4282       Image
4283         *resize_image;
4284
4285       /*
4286         Scale composite image.
4287       */
4288       resize_image=ResizeImage(composite_image,composite_info.width,
4289         composite_info.height,composite_image->filter,exception);
4290       composite_image=DestroyImage(composite_image);
4291       if (resize_image == (Image *) NULL)
4292         {
4293           XSetCursorState(display,windows,MagickFalse);
4294           return(MagickFalse);
4295         }
4296       composite_image=resize_image;
4297     }
4298   if (compose == DisplaceCompositeOp)
4299     (void) SetImageArtifact(composite_image,"compose:args",
4300       displacement_geometry);
4301   if (blend != 0.0)
4302     {
4303       CacheView
4304         *image_view;
4305
4306       int
4307         y;
4308
4309       Quantum
4310         opacity;
4311
4312       register int
4313         x;
4314
4315       register Quantum
4316         *q;
4317
4318       /*
4319         Create mattes for blending.
4320       */
4321       (void) SetImageAlphaChannel(composite_image,OpaqueAlphaChannel,exception);
4322       opacity=(Quantum) (ScaleQuantumToChar(QuantumRange)-
4323         ((ssize_t) ScaleQuantumToChar(QuantumRange)*blend)/100);
4324       if (IfMagickFalse(SetImageStorageClass(image,DirectClass,exception)) )
4325         return(MagickFalse);
4326       image->alpha_trait=BlendPixelTrait;
4327       image_view=AcquireAuthenticCacheView(image,exception);
4328       for (y=0; y < (int) image->rows; y++)
4329       {
4330         q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,image->columns,1,
4331           exception);
4332         if (q == (Quantum *) NULL)
4333           break;
4334         for (x=0; x < (int) image->columns; x++)
4335         {
4336           SetPixelAlpha(image,opacity,q);
4337           q+=GetPixelChannels(image);
4338         }
4339         if (IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
4340           break;
4341       }
4342       image_view=DestroyCacheView(image_view);
4343     }
4344   /*
4345     Composite image with X Image window.
4346   */
4347   (void) CompositeImage(image,composite_image,compose,MagickTrue,
4348     composite_info.x,composite_info.y,exception);
4349   composite_image=DestroyImage(composite_image);
4350   XSetCursorState(display,windows,MagickFalse);
4351   /*
4352     Update image configuration.
4353   */
4354   XConfigureImageColormap(display,resource_info,windows,image,exception);
4355   (void) XConfigureImage(display,resource_info,windows,image,exception);
4356   return(MagickTrue);
4357 }
4358 \f
4359 /*
4360 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4361 %                                                                             %
4362 %                                                                             %
4363 %                                                                             %
4364 +   X C o n f i g u r e I m a g e                                             %
4365 %                                                                             %
4366 %                                                                             %
4367 %                                                                             %
4368 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4369 %
4370 %  XConfigureImage() creates a new X image.  It also notifies the window
4371 %  manager of the new image size and configures the transient widows.
4372 %
4373 %  The format of the XConfigureImage method is:
4374 %
4375 %      MagickBooleanType XConfigureImage(Display *display,
4376 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
4377 %        ExceptionInfo *exception)
4378 %
4379 %  A description of each parameter follows:
4380 %
4381 %    o display: Specifies a connection to an X server; returned from
4382 %      XOpenDisplay.
4383 %
4384 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4385 %
4386 %    o windows: Specifies a pointer to a XWindows structure.
4387 %
4388 %    o image: the image.
4389 %
4390 %    o exception: return any errors or warnings in this structure.
4391 %
4392 %    o exception: return any errors or warnings in this structure.
4393 %
4394 */
4395 static MagickBooleanType XConfigureImage(Display *display,
4396   XResourceInfo *resource_info,XWindows *windows,Image *image,
4397   ExceptionInfo *exception)
4398 {
4399   char
4400     geometry[MaxTextExtent];
4401
4402   MagickStatusType
4403     status;
4404
4405   size_t
4406     mask,
4407     height,
4408     width;
4409
4410   ssize_t
4411     x,
4412     y;
4413
4414   XSizeHints
4415     *size_hints;
4416
4417   XWindowChanges
4418     window_changes;
4419
4420   /*
4421     Dismiss if window dimensions are zero.
4422   */
4423   width=(unsigned int) windows->image.window_changes.width;
4424   height=(unsigned int) windows->image.window_changes.height;
4425   if (IfMagickTrue(image->debug) )
4426     (void) LogMagickEvent(X11Event,GetMagickModule(),
4427       "Configure Image: %dx%d=>%.20gx%.20g",windows->image.ximage->width,
4428       windows->image.ximage->height,(double) width,(double) height);
4429   if ((width*height) == 0)
4430     return(MagickTrue);
4431   x=0;
4432   y=0;
4433   /*
4434     Resize image to fit Image window dimensions.
4435   */
4436   XSetCursorState(display,windows,MagickTrue);
4437   (void) XFlush(display);
4438   if (((int) width != windows->image.ximage->width) ||
4439       ((int) height != windows->image.ximage->height))
4440     image->taint=MagickTrue;
4441   windows->magnify.x=(int)
4442     width*windows->magnify.x/windows->image.ximage->width;
4443   windows->magnify.y=(int)
4444     height*windows->magnify.y/windows->image.ximage->height;
4445   windows->image.x=(int) (width*windows->image.x/windows->image.ximage->width);
4446   windows->image.y=(int)
4447     (height*windows->image.y/windows->image.ximage->height);
4448   status=XMakeImage(display,resource_info,&windows->image,image,
4449     (unsigned int) width,(unsigned int) height,exception);
4450   if (IfMagickFalse(status) )
4451     XNoticeWidget(display,windows,"Unable to configure X image:",
4452       windows->image.name);
4453   /*
4454     Notify window manager of the new configuration.
4455   */
4456   if (resource_info->image_geometry != (char *) NULL)
4457     (void) FormatLocaleString(geometry,MaxTextExtent,"%s>!",
4458       resource_info->image_geometry);
4459   else
4460     (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>!",
4461       XDisplayWidth(display,windows->image.screen),
4462       XDisplayHeight(display,windows->image.screen));
4463   (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
4464   window_changes.width=(int) width;
4465   if (window_changes.width > XDisplayWidth(display,windows->image.screen))
4466     window_changes.width=XDisplayWidth(display,windows->image.screen);
4467   window_changes.height=(int) height;
4468   if (window_changes.height > XDisplayHeight(display,windows->image.screen))
4469     window_changes.height=XDisplayHeight(display,windows->image.screen);
4470   mask=(size_t) (CWWidth | CWHeight);
4471   if (resource_info->backdrop)
4472     {
4473       mask|=CWX | CWY;
4474       window_changes.x=(int)
4475         ((XDisplayWidth(display,windows->image.screen)/2)-(width/2));
4476       window_changes.y=(int)
4477         ((XDisplayHeight(display,windows->image.screen)/2)-(height/2));
4478     }
4479   (void) XReconfigureWMWindow(display,windows->image.id,windows->image.screen,
4480     (unsigned int) mask,&window_changes);
4481   (void) XClearWindow(display,windows->image.id);
4482   XRefreshWindow(display,&windows->image,(XEvent *) NULL);
4483   /*
4484     Update Magnify window configuration.
4485   */
4486   if (IfMagickTrue(windows->magnify.mapped) )
4487     XMakeMagnifyImage(display,windows,exception);
4488   windows->pan.crop_geometry=windows->image.crop_geometry;
4489   XBestIconSize(display,&windows->pan,image);
4490   while (((windows->pan.width << 1) < MaxIconSize) &&
4491          ((windows->pan.height << 1) < MaxIconSize))
4492   {
4493     windows->pan.width<<=1;
4494     windows->pan.height<<=1;
4495   }
4496   if (windows->pan.geometry != (char *) NULL)
4497     (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
4498       &windows->pan.width,&windows->pan.height);
4499   window_changes.width=(int) windows->pan.width;
4500   window_changes.height=(int) windows->pan.height;
4501   size_hints=XAllocSizeHints();
4502   if (size_hints != (XSizeHints *) NULL)
4503     {
4504       /*
4505         Set new size hints.
4506       */
4507       size_hints->flags=PSize | PMinSize | PMaxSize;
4508       size_hints->width=window_changes.width;
4509       size_hints->height=window_changes.height;
4510       size_hints->min_width=size_hints->width;
4511       size_hints->min_height=size_hints->height;
4512       size_hints->max_width=size_hints->width;
4513       size_hints->max_height=size_hints->height;
4514       (void) XSetNormalHints(display,windows->pan.id,size_hints);
4515       (void) XFree((void *) size_hints);
4516     }
4517   (void) XReconfigureWMWindow(display,windows->pan.id,windows->pan.screen,
4518     (unsigned int) (CWWidth | CWHeight),&window_changes);
4519   /*
4520     Update icon window configuration.
4521   */
4522   windows->icon.crop_geometry=windows->image.crop_geometry;
4523   XBestIconSize(display,&windows->icon,image);
4524   window_changes.width=(int) windows->icon.width;
4525   window_changes.height=(int) windows->icon.height;
4526   (void) XReconfigureWMWindow(display,windows->icon.id,windows->icon.screen,
4527     (unsigned int) (CWWidth | CWHeight),&window_changes);
4528   XSetCursorState(display,windows,MagickFalse);
4529   return(IsMagickTrue(status));
4530 }
4531 \f
4532 /*
4533 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4534 %                                                                             %
4535 %                                                                             %
4536 %                                                                             %
4537 +   X C r o p I m a g e                                                       %
4538 %                                                                             %
4539 %                                                                             %
4540 %                                                                             %
4541 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4542 %
4543 %  XCropImage() allows the user to select a region of the image and crop, copy,
4544 %  or cut it.  For copy or cut, the image can subsequently be composited onto
4545 %  the image with XPasteImage.
4546 %
4547 %  The format of the XCropImage method is:
4548 %
4549 %      MagickBooleanType XCropImage(Display *display,
4550 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
4551 %        const ClipboardMode mode,ExceptionInfo *exception)
4552 %
4553 %  A description of each parameter follows:
4554 %
4555 %    o display: Specifies a connection to an X server; returned from
4556 %      XOpenDisplay.
4557 %
4558 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4559 %
4560 %    o windows: Specifies a pointer to a XWindows structure.
4561 %
4562 %    o image: the image; returned from ReadImage.
4563 %
4564 %    o mode: This unsigned value specified whether the image should be
4565 %      cropped, copied, or cut.
4566 %
4567 %    o exception: return any errors or warnings in this structure.
4568 %
4569 */
4570 static MagickBooleanType XCropImage(Display *display,
4571   XResourceInfo *resource_info,XWindows *windows,Image *image,
4572   const ClipboardMode mode,ExceptionInfo *exception)
4573 {
4574   static const char
4575     *CropModeMenu[] =
4576     {
4577       "Help",
4578       "Dismiss",
4579       (char *) NULL
4580     },
4581     *RectifyModeMenu[] =
4582     {
4583       "Crop",
4584       "Help",
4585       "Dismiss",
4586       (char *) NULL
4587     };
4588
4589   static const ModeType
4590     CropCommands[] =
4591     {
4592       CropHelpCommand,
4593       CropDismissCommand
4594     },
4595     RectifyCommands[] =
4596     {
4597       RectifyCopyCommand,
4598       RectifyHelpCommand,
4599       RectifyDismissCommand
4600     };
4601
4602   CacheView
4603     *image_view;
4604
4605   char
4606     command[MaxTextExtent],
4607     text[MaxTextExtent];
4608
4609   Cursor
4610     cursor;
4611
4612   int
4613     id,
4614     x,
4615     y;
4616
4617   KeySym
4618     key_symbol;
4619
4620   Image
4621     *crop_image;
4622
4623   double
4624     scale_factor;
4625
4626   RectangleInfo
4627     crop_info,
4628     highlight_info;
4629
4630   register Quantum
4631     *q;
4632
4633   unsigned int
4634     height,
4635     width;
4636
4637   size_t
4638     state;
4639
4640   XEvent
4641     event;
4642
4643   /*
4644     Map Command widget.
4645   */
4646   switch (mode)
4647   {
4648     case CopyMode:
4649     {
4650       (void) CloneString(&windows->command.name,"Copy");
4651       break;
4652     }
4653     case CropMode:
4654     {
4655       (void) CloneString(&windows->command.name,"Crop");
4656       break;
4657     }
4658     case CutMode:
4659     {
4660       (void) CloneString(&windows->command.name,"Cut");
4661       break;
4662     }
4663   }
4664   RectifyModeMenu[0]=windows->command.name;
4665   windows->command.data=0;
4666   (void) XCommandWidget(display,windows,CropModeMenu,(XEvent *) NULL);
4667   (void) XMapRaised(display,windows->command.id);
4668   XClientMessage(display,windows->image.id,windows->im_protocols,
4669     windows->im_update_widget,CurrentTime);
4670   /*
4671     Track pointer until button 1 is pressed.
4672   */
4673   XQueryPosition(display,windows->image.id,&x,&y);
4674   (void) XSelectInput(display,windows->image.id,
4675     windows->image.attributes.event_mask | PointerMotionMask);
4676   crop_info.x=(ssize_t) windows->image.x+x;
4677   crop_info.y=(ssize_t) windows->image.y+y;
4678   crop_info.width=0;
4679   crop_info.height=0;
4680   cursor=XCreateFontCursor(display,XC_fleur);
4681   state=DefaultState;
4682   do
4683   {
4684     if (IfMagickTrue(windows->info.mapped) )
4685       {
4686         /*
4687           Display pointer position.
4688         */
4689         (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
4690           (long) crop_info.x,(long) crop_info.y);
4691         XInfoWidget(display,windows,text);
4692       }
4693     /*
4694       Wait for next event.
4695     */
4696     XScreenEvent(display,windows,&event,exception);
4697     if (event.xany.window == windows->command.id)
4698       {
4699         /*
4700           Select a command from the Command widget.
4701         */
4702         id=XCommandWidget(display,windows,CropModeMenu,&event);
4703         if (id < 0)
4704           continue;
4705         switch (CropCommands[id])
4706         {
4707           case CropHelpCommand:
4708           {
4709             switch (mode)
4710             {
4711               case CopyMode:
4712               {
4713                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4714                   "Help Viewer - Image Copy",ImageCopyHelp);
4715                 break;
4716               }
4717               case CropMode:
4718               {
4719                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4720                   "Help Viewer - Image Crop",ImageCropHelp);
4721                 break;
4722               }
4723               case CutMode:
4724               {
4725                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4726                   "Help Viewer - Image Cut",ImageCutHelp);
4727                 break;
4728               }
4729             }
4730             break;
4731           }
4732           case CropDismissCommand:
4733           {
4734             /*
4735               Prematurely exit.
4736             */
4737             state|=EscapeState;
4738             state|=ExitState;
4739             break;
4740           }
4741           default:
4742             break;
4743         }
4744         continue;
4745       }
4746     switch (event.type)
4747     {
4748       case ButtonPress:
4749       {
4750         if (event.xbutton.button != Button1)
4751           break;
4752         if (event.xbutton.window != windows->image.id)
4753           break;
4754         /*
4755           Note first corner of cropping rectangle-- exit loop.
4756         */
4757         (void) XCheckDefineCursor(display,windows->image.id,cursor);
4758         crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4759         crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4760         state|=ExitState;
4761         break;
4762       }
4763       case ButtonRelease:
4764         break;
4765       case Expose:
4766         break;
4767       case KeyPress:
4768       {
4769         if (event.xkey.window != windows->image.id)
4770           break;
4771         /*
4772           Respond to a user key press.
4773         */
4774         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
4775           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4776         switch ((int) key_symbol)
4777         {
4778           case XK_Escape:
4779           case XK_F20:
4780           {
4781             /*
4782               Prematurely exit.
4783             */
4784             state|=EscapeState;
4785             state|=ExitState;
4786             break;
4787           }
4788           case XK_F1:
4789           case XK_Help:
4790           {
4791             switch (mode)
4792             {
4793               case CopyMode:
4794               {
4795                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4796                   "Help Viewer - Image Copy",ImageCopyHelp);
4797                 break;
4798               }
4799               case CropMode:
4800               {
4801                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4802                   "Help Viewer - Image Crop",ImageCropHelp);
4803                 break;
4804               }
4805               case CutMode:
4806               {
4807                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4808                   "Help Viewer - Image Cut",ImageCutHelp);
4809                 break;
4810               }
4811             }
4812             break;
4813           }
4814           default:
4815           {
4816             (void) XBell(display,0);
4817             break;
4818           }
4819         }
4820         break;
4821       }
4822       case MotionNotify:
4823       {
4824         if (event.xmotion.window != windows->image.id)
4825           break;
4826         /*
4827           Map and unmap Info widget as text cursor crosses its boundaries.
4828         */
4829         x=event.xmotion.x;
4830         y=event.xmotion.y;
4831         if (IfMagickTrue(windows->info.mapped) )
4832           {
4833             if ((x < (int) (windows->info.x+windows->info.width)) &&
4834                 (y < (int) (windows->info.y+windows->info.height)))
4835               (void) XWithdrawWindow(display,windows->info.id,
4836                 windows->info.screen);
4837           }
4838         else
4839           if ((x > (int) (windows->info.x+windows->info.width)) ||
4840               (y > (int) (windows->info.y+windows->info.height)))
4841             (void) XMapWindow(display,windows->info.id);
4842         crop_info.x=(ssize_t) windows->image.x+x;
4843         crop_info.y=(ssize_t) windows->image.y+y;
4844         break;
4845       }
4846       default:
4847         break;
4848     }
4849   } while ((state & ExitState) == 0);
4850   (void) XSelectInput(display,windows->image.id,
4851     windows->image.attributes.event_mask);
4852   if ((state & EscapeState) != 0)
4853     {
4854       /*
4855         User want to exit without cropping.
4856       */
4857       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4858       (void) XFreeCursor(display,cursor);
4859       return(MagickTrue);
4860     }
4861   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
4862   do
4863   {
4864     /*
4865       Size rectangle as pointer moves until the mouse button is released.
4866     */
4867     x=(int) crop_info.x;
4868     y=(int) crop_info.y;
4869     crop_info.width=0;
4870     crop_info.height=0;
4871     state=DefaultState;
4872     do
4873     {
4874       highlight_info=crop_info;
4875       highlight_info.x=crop_info.x-windows->image.x;
4876       highlight_info.y=crop_info.y-windows->image.y;
4877       if ((highlight_info.width > 3) && (highlight_info.height > 3))
4878         {
4879           /*
4880             Display info and draw cropping rectangle.
4881           */
4882           if (IfMagickFalse(windows->info.mapped) )
4883             (void) XMapWindow(display,windows->info.id);
4884           (void) FormatLocaleString(text,MaxTextExtent,
4885             " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
4886             crop_info.height,(double) crop_info.x,(double) crop_info.y);
4887           XInfoWidget(display,windows,text);
4888           XHighlightRectangle(display,windows->image.id,
4889             windows->image.highlight_context,&highlight_info);
4890         }
4891       else
4892         if (IfMagickTrue(windows->info.mapped) )
4893           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4894       /*
4895         Wait for next event.
4896       */
4897       XScreenEvent(display,windows,&event,exception);
4898       if ((highlight_info.width > 3) && (highlight_info.height > 3))
4899         XHighlightRectangle(display,windows->image.id,
4900           windows->image.highlight_context,&highlight_info);
4901       switch (event.type)
4902       {
4903         case ButtonPress:
4904         {
4905           crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4906           crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4907           break;
4908         }
4909         case ButtonRelease:
4910         {
4911           /*
4912             User has committed to cropping rectangle.
4913           */
4914           crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4915           crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4916           XSetCursorState(display,windows,MagickFalse);
4917           state|=ExitState;
4918           windows->command.data=0;
4919           (void) XCommandWidget(display,windows,RectifyModeMenu,
4920             (XEvent *) NULL);
4921           break;
4922         }
4923         case Expose:
4924           break;
4925         case MotionNotify:
4926         {
4927           crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
4928           crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
4929         }
4930         default:
4931           break;
4932       }
4933       if ((((int) crop_info.x != x) && ((int) crop_info.y != y)) ||
4934           ((state & ExitState) != 0))
4935         {
4936           /*
4937             Check boundary conditions.
4938           */
4939           if (crop_info.x < 0)
4940             crop_info.x=0;
4941           else
4942             if (crop_info.x > (ssize_t) windows->image.ximage->width)
4943               crop_info.x=(ssize_t) windows->image.ximage->width;
4944           if ((int) crop_info.x < x)
4945             crop_info.width=(unsigned int) (x-crop_info.x);
4946           else
4947             {
4948               crop_info.width=(unsigned int) (crop_info.x-x);
4949               crop_info.x=(ssize_t) x;
4950             }
4951           if (crop_info.y < 0)
4952             crop_info.y=0;
4953           else
4954             if (crop_info.y > (ssize_t) windows->image.ximage->height)
4955               crop_info.y=(ssize_t) windows->image.ximage->height;
4956           if ((int) crop_info.y < y)
4957             crop_info.height=(unsigned int) (y-crop_info.y);
4958           else
4959             {
4960               crop_info.height=(unsigned int) (crop_info.y-y);
4961               crop_info.y=(ssize_t) y;
4962             }
4963         }
4964     } while ((state & ExitState) == 0);
4965     /*
4966       Wait for user to grab a corner of the rectangle or press return.
4967     */
4968     state=DefaultState;
4969     (void) XMapWindow(display,windows->info.id);
4970     do
4971     {
4972       if (IfMagickTrue(windows->info.mapped) )
4973         {
4974           /*
4975             Display pointer position.
4976           */
4977           (void) FormatLocaleString(text,MaxTextExtent,
4978             " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
4979             crop_info.height,(double) crop_info.x,(double) crop_info.y);
4980           XInfoWidget(display,windows,text);
4981         }
4982       highlight_info=crop_info;
4983       highlight_info.x=crop_info.x-windows->image.x;
4984       highlight_info.y=crop_info.y-windows->image.y;
4985       if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
4986         {
4987           state|=EscapeState;
4988           state|=ExitState;
4989           break;
4990         }
4991       XHighlightRectangle(display,windows->image.id,
4992         windows->image.highlight_context,&highlight_info);
4993       XScreenEvent(display,windows,&event,exception);
4994       if (event.xany.window == windows->command.id)
4995         {
4996           /*
4997             Select a command from the Command widget.
4998           */
4999           (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
5000           id=XCommandWidget(display,windows,RectifyModeMenu,&event);
5001           (void) XSetFunction(display,windows->image.highlight_context,
5002             GXinvert);
5003           XHighlightRectangle(display,windows->image.id,
5004             windows->image.highlight_context,&highlight_info);
5005           if (id >= 0)
5006             switch (RectifyCommands[id])
5007             {
5008               case RectifyCopyCommand:
5009               {
5010                 state|=ExitState;
5011                 break;
5012               }
5013               case RectifyHelpCommand:
5014               {
5015                 (void) XSetFunction(display,windows->image.highlight_context,
5016                   GXcopy);
5017                 switch (mode)
5018                 {
5019                   case CopyMode:
5020                   {
5021                     XTextViewWidget(display,resource_info,windows,MagickFalse,
5022                       "Help Viewer - Image Copy",ImageCopyHelp);
5023                     break;
5024                   }
5025                   case CropMode:
5026                   {
5027                     XTextViewWidget(display,resource_info,windows,MagickFalse,
5028                       "Help Viewer - Image Crop",ImageCropHelp);
5029                     break;
5030                   }
5031                   case CutMode:
5032                   {
5033                     XTextViewWidget(display,resource_info,windows,MagickFalse,
5034                       "Help Viewer - Image Cut",ImageCutHelp);
5035                     break;
5036                   }
5037                 }
5038                 (void) XSetFunction(display,windows->image.highlight_context,
5039                   GXinvert);
5040                 break;
5041               }
5042               case RectifyDismissCommand:
5043               {
5044                 /*
5045                   Prematurely exit.
5046                 */
5047                 state|=EscapeState;
5048                 state|=ExitState;
5049                 break;
5050               }
5051               default:
5052                 break;
5053             }
5054           continue;
5055         }
5056       XHighlightRectangle(display,windows->image.id,
5057         windows->image.highlight_context,&highlight_info);
5058       switch (event.type)
5059       {
5060         case ButtonPress:
5061         {
5062           if (event.xbutton.button != Button1)
5063             break;
5064           if (event.xbutton.window != windows->image.id)
5065             break;
5066           x=windows->image.x+event.xbutton.x;
5067           y=windows->image.y+event.xbutton.y;
5068           if ((x < (int) (crop_info.x+RoiDelta)) &&
5069               (x > (int) (crop_info.x-RoiDelta)) &&
5070               (y < (int) (crop_info.y+RoiDelta)) &&
5071               (y > (int) (crop_info.y-RoiDelta)))
5072             {
5073               crop_info.x=(ssize_t) (crop_info.x+crop_info.width);
5074               crop_info.y=(ssize_t) (crop_info.y+crop_info.height);
5075               state|=UpdateConfigurationState;
5076               break;
5077             }
5078           if ((x < (int) (crop_info.x+RoiDelta)) &&
5079               (x > (int) (crop_info.x-RoiDelta)) &&
5080               (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5081               (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5082             {
5083               crop_info.x=(ssize_t) (crop_info.x+crop_info.width);
5084               state|=UpdateConfigurationState;
5085               break;
5086             }
5087           if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5088               (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5089               (y < (int) (crop_info.y+RoiDelta)) &&
5090               (y > (int) (crop_info.y-RoiDelta)))
5091             {
5092               crop_info.y=(ssize_t) (crop_info.y+crop_info.height);
5093               state|=UpdateConfigurationState;
5094               break;
5095             }
5096           if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5097               (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5098               (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5099               (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5100             {
5101               state|=UpdateConfigurationState;
5102               break;
5103             }
5104         }
5105         case ButtonRelease:
5106         {
5107           if (event.xbutton.window == windows->pan.id)
5108             if ((highlight_info.x != crop_info.x-windows->image.x) ||
5109                 (highlight_info.y != crop_info.y-windows->image.y))
5110               XHighlightRectangle(display,windows->image.id,
5111                 windows->image.highlight_context,&highlight_info);
5112           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5113             event.xbutton.time);
5114           break;
5115         }
5116         case Expose:
5117         {
5118           if (event.xexpose.window == windows->image.id)
5119             if (event.xexpose.count == 0)
5120               {
5121                 event.xexpose.x=(int) highlight_info.x;
5122                 event.xexpose.y=(int) highlight_info.y;
5123                 event.xexpose.width=(int) highlight_info.width;
5124                 event.xexpose.height=(int) highlight_info.height;
5125                 XRefreshWindow(display,&windows->image,&event);
5126               }
5127           if (event.xexpose.window == windows->info.id)
5128             if (event.xexpose.count == 0)
5129               XInfoWidget(display,windows,text);
5130           break;
5131         }
5132         case KeyPress:
5133         {
5134           if (event.xkey.window != windows->image.id)
5135             break;
5136           /*
5137             Respond to a user key press.
5138           */
5139           (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5140             sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5141           switch ((int) key_symbol)
5142           {
5143             case XK_Escape:
5144             case XK_F20:
5145               state|=EscapeState;
5146             case XK_Return:
5147             {
5148               state|=ExitState;
5149               break;
5150             }
5151             case XK_Home:
5152             case XK_KP_Home:
5153             {
5154               crop_info.x=(ssize_t) (windows->image.width/2L-
5155                 crop_info.width/2L);
5156               crop_info.y=(ssize_t) (windows->image.height/2L-
5157                 crop_info.height/2L);
5158               break;
5159             }
5160             case XK_Left:
5161             case XK_KP_Left:
5162             {
5163               crop_info.x--;
5164               break;
5165             }
5166             case XK_Up:
5167             case XK_KP_Up:
5168             case XK_Next:
5169             {
5170               crop_info.y--;
5171               break;
5172             }
5173             case XK_Right:
5174             case XK_KP_Right:
5175             {
5176               crop_info.x++;
5177               break;
5178             }
5179             case XK_Prior:
5180             case XK_Down:
5181             case XK_KP_Down:
5182             {
5183               crop_info.y++;
5184               break;
5185             }
5186             case XK_F1:
5187             case XK_Help:
5188             {
5189               (void) XSetFunction(display,windows->image.highlight_context,
5190                 GXcopy);
5191               switch (mode)
5192               {
5193                 case CopyMode:
5194                 {
5195                   XTextViewWidget(display,resource_info,windows,MagickFalse,
5196                     "Help Viewer - Image Copy",ImageCopyHelp);
5197                   break;
5198                 }
5199                 case CropMode:
5200                 {
5201                   XTextViewWidget(display,resource_info,windows,MagickFalse,
5202                     "Help Viewer - Image Cropg",ImageCropHelp);
5203                   break;
5204                 }
5205                 case CutMode:
5206                 {
5207                   XTextViewWidget(display,resource_info,windows,MagickFalse,
5208                     "Help Viewer - Image Cutg",ImageCutHelp);
5209                   break;
5210                 }
5211               }
5212               (void) XSetFunction(display,windows->image.highlight_context,
5213                 GXinvert);
5214               break;
5215             }
5216             default:
5217             {
5218               (void) XBell(display,0);
5219               break;
5220             }
5221           }
5222           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5223             event.xkey.time);
5224           break;
5225         }
5226         case KeyRelease:
5227           break;
5228         case MotionNotify:
5229         {
5230           if (event.xmotion.window != windows->image.id)
5231             break;
5232           /*
5233             Map and unmap Info widget as text cursor crosses its boundaries.
5234           */
5235           x=event.xmotion.x;
5236           y=event.xmotion.y;
5237           if (IfMagickTrue(windows->info.mapped) )
5238             {
5239               if ((x < (int) (windows->info.x+windows->info.width)) &&
5240                   (y < (int) (windows->info.y+windows->info.height)))
5241                 (void) XWithdrawWindow(display,windows->info.id,
5242                   windows->info.screen);
5243             }
5244           else
5245             if ((x > (int) (windows->info.x+windows->info.width)) ||
5246                 (y > (int) (windows->info.y+windows->info.height)))
5247               (void) XMapWindow(display,windows->info.id);
5248           crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
5249           crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
5250           break;
5251         }
5252         case SelectionRequest:
5253         {
5254           XSelectionEvent
5255             notify;
5256
5257           XSelectionRequestEvent
5258             *request;
5259
5260           /*
5261             Set primary selection.
5262           */
5263           (void) FormatLocaleString(text,MaxTextExtent,
5264             "%.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
5265             crop_info.height,(double) crop_info.x,(double) crop_info.y);
5266           request=(&(event.xselectionrequest));
5267           (void) XChangeProperty(request->display,request->requestor,
5268             request->property,request->target,8,PropModeReplace,
5269             (unsigned char *) text,(int) strlen(text));
5270           notify.type=SelectionNotify;
5271           notify.display=request->display;
5272           notify.requestor=request->requestor;
5273           notify.selection=request->selection;
5274           notify.target=request->target;
5275           notify.time=request->time;
5276           if (request->property == None)
5277             notify.property=request->target;
5278           else
5279             notify.property=request->property;
5280           (void) XSendEvent(request->display,request->requestor,False,0,
5281             (XEvent *) &notify);
5282         }
5283         default:
5284           break;
5285       }
5286       if ((state & UpdateConfigurationState) != 0)
5287         {
5288           (void) XPutBackEvent(display,&event);
5289           (void) XCheckDefineCursor(display,windows->image.id,cursor);
5290           break;
5291         }
5292     } while ((state & ExitState) == 0);
5293   } while ((state & ExitState) == 0);
5294   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
5295   XSetCursorState(display,windows,MagickFalse);
5296   if ((state & EscapeState) != 0)
5297     return(MagickTrue);
5298   if (mode == CropMode)
5299     if (((int) crop_info.width != windows->image.ximage->width) ||
5300         ((int) crop_info.height != windows->image.ximage->height))
5301       {
5302         /*
5303           Reconfigure Image window as defined by cropping rectangle.
5304         */
5305         XSetCropGeometry(display,windows,&crop_info,image);
5306         windows->image.window_changes.width=(int) crop_info.width;
5307         windows->image.window_changes.height=(int) crop_info.height;
5308         (void) XConfigureImage(display,resource_info,windows,image,exception);
5309         return(MagickTrue);
5310       }
5311   /*
5312     Copy image before applying image transforms.
5313   */
5314   XSetCursorState(display,windows,MagickTrue);
5315   XCheckRefreshWindows(display,windows);
5316   width=(unsigned int) image->columns;
5317   height=(unsigned int) image->rows;
5318   x=0;
5319   y=0;
5320   if (windows->image.crop_geometry != (char *) NULL)
5321     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
5322   scale_factor=(double) width/windows->image.ximage->width;
5323   crop_info.x+=x;
5324   crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
5325   crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
5326   scale_factor=(double) height/windows->image.ximage->height;
5327   crop_info.y+=y;
5328   crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
5329   crop_info.height=(unsigned int) (scale_factor*crop_info.height+0.5);
5330   crop_image=CropImage(image,&crop_info,exception);
5331   XSetCursorState(display,windows,MagickFalse);
5332   if (crop_image == (Image *) NULL)
5333     return(MagickFalse);
5334   if (resource_info->copy_image != (Image *) NULL)
5335     resource_info->copy_image=DestroyImage(resource_info->copy_image);
5336   resource_info->copy_image=crop_image;
5337   if (mode == CopyMode)
5338     {
5339       (void) XConfigureImage(display,resource_info,windows,image,exception);
5340       return(MagickTrue);
5341     }
5342   /*
5343     Cut image.
5344   */
5345   if (IfMagickFalse(SetImageStorageClass(image,DirectClass,exception)) )
5346     return(MagickFalse);
5347   image->alpha_trait=BlendPixelTrait;
5348   image_view=AcquireAuthenticCacheView(image,exception);
5349   for (y=0; y < (int) crop_info.height; y++)
5350   {
5351     q=GetCacheViewAuthenticPixels(image_view,crop_info.x,y+crop_info.y,
5352       crop_info.width,1,exception);
5353     if (q == (Quantum *) NULL)
5354       break;
5355     for (x=0; x < (int) crop_info.width; x++)
5356     {
5357       SetPixelAlpha(image,TransparentAlpha,q);
5358       q+=GetPixelChannels(image);
5359     }
5360     if (IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
5361       break;
5362   }
5363   image_view=DestroyCacheView(image_view);
5364   /*
5365     Update image configuration.
5366   */
5367   XConfigureImageColormap(display,resource_info,windows,image,exception);
5368   (void) XConfigureImage(display,resource_info,windows,image,exception);
5369   return(MagickTrue);
5370 }
5371 \f
5372 /*
5373 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5374 %                                                                             %
5375 %                                                                             %
5376 %                                                                             %
5377 +   X D r a w I m a g e                                                       %
5378 %                                                                             %
5379 %                                                                             %
5380 %                                                                             %
5381 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5382 %
5383 %  XDrawEditImage() draws a graphic element (point, line, rectangle, etc.) on
5384 %  the image.
5385 %
5386 %  The format of the XDrawEditImage method is:
5387 %
5388 %      MagickBooleanType XDrawEditImage(Display *display,
5389 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
5390 %        ExceptionInfo *exception)
5391 %
5392 %  A description of each parameter follows:
5393 %
5394 %    o display: Specifies a connection to an X server; returned from
5395 %      XOpenDisplay.
5396 %
5397 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
5398 %
5399 %    o windows: Specifies a pointer to a XWindows structure.
5400 %
5401 %    o image: the image.
5402 %
5403 %    o exception: return any errors or warnings in this structure.
5404 %
5405 */
5406 static MagickBooleanType XDrawEditImage(Display *display,
5407   XResourceInfo *resource_info,XWindows *windows,Image **image,
5408   ExceptionInfo *exception)
5409 {
5410   static const char
5411     *DrawMenu[] =
5412     {
5413       "Element",
5414       "Color",
5415       "Stipple",
5416       "Width",
5417       "Undo",
5418       "Help",
5419       "Dismiss",
5420       (char *) NULL
5421     };
5422
5423   static ElementType
5424     element = PointElement;
5425
5426   static const ModeType
5427     DrawCommands[] =
5428     {
5429       DrawElementCommand,
5430       DrawColorCommand,
5431       DrawStippleCommand,
5432       DrawWidthCommand,
5433       DrawUndoCommand,
5434       DrawHelpCommand,
5435       DrawDismissCommand
5436     };
5437
5438   static Pixmap
5439     stipple = (Pixmap) NULL;
5440
5441   static unsigned int
5442     pen_id = 0,
5443     line_width = 1;
5444
5445   char
5446     command[MaxTextExtent],
5447     text[MaxTextExtent];
5448
5449   Cursor
5450     cursor;
5451
5452   int
5453     entry,
5454     id,
5455     number_coordinates,
5456     x,
5457     y;
5458
5459   double
5460     degrees;
5461
5462   MagickStatusType
5463     status;
5464
5465   RectangleInfo
5466     rectangle_info;
5467
5468   register int
5469     i;
5470
5471   unsigned int
5472     distance,
5473     height,
5474     max_coordinates,
5475     width;
5476
5477   size_t
5478     state;
5479
5480   Window
5481     root_window;
5482
5483   XDrawInfo
5484     draw_info;
5485
5486   XEvent
5487     event;
5488
5489   XPoint
5490     *coordinate_info;
5491
5492   XSegment
5493     line_info;
5494
5495   /*
5496     Allocate polygon info.
5497   */
5498   max_coordinates=2048;
5499   coordinate_info=(XPoint *) AcquireQuantumMemory((size_t) max_coordinates,
5500     sizeof(*coordinate_info));
5501   if (coordinate_info == (XPoint *) NULL)
5502     {
5503       (void) ThrowMagickException(exception,GetMagickModule(),
5504         ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
5505       return(MagickFalse);
5506     }
5507   /*
5508     Map Command widget.
5509   */
5510   (void) CloneString(&windows->command.name,"Draw");
5511   windows->command.data=4;
5512   (void) XCommandWidget(display,windows,DrawMenu,(XEvent *) NULL);
5513   (void) XMapRaised(display,windows->command.id);
5514   XClientMessage(display,windows->image.id,windows->im_protocols,
5515     windows->im_update_widget,CurrentTime);
5516   /*
5517     Wait for first button press.
5518   */
5519   root_window=XRootWindow(display,XDefaultScreen(display));
5520   draw_info.stencil=OpaqueStencil;
5521   status=MagickTrue;
5522   cursor=XCreateFontCursor(display,XC_tcross);
5523   for ( ; ; )
5524   {
5525     XQueryPosition(display,windows->image.id,&x,&y);
5526     (void) XSelectInput(display,windows->image.id,
5527       windows->image.attributes.event_mask | PointerMotionMask);
5528     (void) XCheckDefineCursor(display,windows->image.id,cursor);
5529     state=DefaultState;
5530     do
5531     {
5532       if (IfMagickTrue(windows->info.mapped) )
5533         {
5534           /*
5535             Display pointer position.
5536           */
5537           (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
5538             x+windows->image.x,y+windows->image.y);
5539           XInfoWidget(display,windows,text);
5540         }
5541       /*
5542         Wait for next event.
5543       */
5544       XScreenEvent(display,windows,&event,exception);
5545       if (event.xany.window == windows->command.id)
5546         {
5547           /*
5548             Select a command from the Command widget.
5549           */
5550           id=XCommandWidget(display,windows,DrawMenu,&event);
5551           if (id < 0)
5552             continue;
5553           switch (DrawCommands[id])
5554           {
5555             case DrawElementCommand:
5556             {
5557               static const char
5558                 *Elements[] =
5559                 {
5560                   "point",
5561                   "line",
5562                   "rectangle",
5563                   "fill rectangle",
5564                   "circle",
5565                   "fill circle",
5566                   "ellipse",
5567                   "fill ellipse",
5568                   "polygon",
5569                   "fill polygon",
5570                   (char *) NULL,
5571                 };
5572
5573               /*
5574                 Select a command from the pop-up menu.
5575               */
5576               element=(ElementType) (XMenuWidget(display,windows,
5577                 DrawMenu[id],Elements,command)+1);
5578               break;
5579             }
5580             case DrawColorCommand:
5581             {
5582               const char
5583                 *ColorMenu[MaxNumberPens+1];
5584
5585               int
5586                 pen_number;
5587
5588               MagickBooleanType
5589                 transparent;
5590
5591               XColor
5592                 color;
5593
5594               /*
5595                 Initialize menu selections.
5596               */
5597               for (i=0; i < (int) (MaxNumberPens-2); i++)
5598                 ColorMenu[i]=resource_info->pen_colors[i];
5599               ColorMenu[MaxNumberPens-2]="transparent";
5600               ColorMenu[MaxNumberPens-1]="Browser...";
5601               ColorMenu[MaxNumberPens]=(char *) NULL;
5602               /*
5603                 Select a pen color from the pop-up menu.
5604               */
5605               pen_number=XMenuWidget(display,windows,DrawMenu[id],
5606                 (const char **) ColorMenu,command);
5607               if (pen_number < 0)
5608                 break;
5609               transparent=pen_number == (MaxNumberPens-2) ? MagickTrue :
5610                 MagickFalse;
5611               if (IfMagickTrue(transparent) )
5612                 {
5613                   draw_info.stencil=TransparentStencil;
5614                   break;
5615                 }
5616               if (pen_number == (MaxNumberPens-1))
5617                 {
5618                   static char
5619                     color_name[MaxTextExtent] = "gray";
5620
5621                   /*
5622                     Select a pen color from a dialog.
5623                   */
5624                   resource_info->pen_colors[pen_number]=color_name;
5625                   XColorBrowserWidget(display,windows,"Select",color_name);
5626                   if (*color_name == '\0')
5627                     break;
5628                 }
5629               /*
5630                 Set pen color.
5631               */
5632               (void) XParseColor(display,windows->map_info->colormap,
5633                 resource_info->pen_colors[pen_number],&color);
5634               XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
5635                 (unsigned int) MaxColors,&color);
5636               windows->pixel_info->pen_colors[pen_number]=color;
5637               pen_id=(unsigned int) pen_number;
5638               draw_info.stencil=OpaqueStencil;
5639               break;
5640             }
5641             case DrawStippleCommand:
5642             {
5643               Image
5644                 *stipple_image;
5645
5646               ImageInfo
5647                 *image_info;
5648
5649               int
5650                 status;
5651
5652               static char
5653                 filename[MaxTextExtent] = "\0";
5654
5655               static const char
5656                 *StipplesMenu[] =
5657                 {
5658                   "Brick",
5659                   "Diagonal",
5660                   "Scales",
5661                   "Vertical",
5662                   "Wavy",
5663                   "Translucent",
5664                   "Opaque",
5665                   (char *) NULL,
5666                   (char *) NULL,
5667                 };
5668
5669               /*
5670                 Select a command from the pop-up menu.
5671               */
5672               StipplesMenu[7]="Open...";
5673               entry=XMenuWidget(display,windows,DrawMenu[id],StipplesMenu,
5674                 command);
5675               if (entry < 0)
5676                 break;
5677               if (stipple != (Pixmap) NULL)
5678                 (void) XFreePixmap(display,stipple);
5679               stipple=(Pixmap) NULL;
5680               if (entry != 7)
5681                 {
5682                   switch (entry)
5683                   {
5684                     case 0:
5685                     {
5686                       stipple=XCreateBitmapFromData(display,root_window,
5687                         (char *) BricksBitmap,BricksWidth,BricksHeight);
5688                       break;
5689                     }
5690                     case 1:
5691                     {
5692                       stipple=XCreateBitmapFromData(display,root_window,
5693                         (char *) DiagonalBitmap,DiagonalWidth,DiagonalHeight);
5694                       break;
5695                     }
5696                     case 2:
5697                     {
5698                       stipple=XCreateBitmapFromData(display,root_window,
5699                         (char *) ScalesBitmap,ScalesWidth,ScalesHeight);
5700                       break;
5701                     }
5702                     case 3:
5703                     {
5704                       stipple=XCreateBitmapFromData(display,root_window,
5705                         (char *) VerticalBitmap,VerticalWidth,VerticalHeight);
5706                       break;
5707                     }
5708                     case 4:
5709                     {
5710                       stipple=XCreateBitmapFromData(display,root_window,
5711                         (char *) WavyBitmap,WavyWidth,WavyHeight);
5712                       break;
5713                     }
5714                     case 5:
5715                     {
5716                       stipple=XCreateBitmapFromData(display,root_window,
5717                         (char *) HighlightBitmap,HighlightWidth,
5718                         HighlightHeight);
5719                       break;
5720                     }
5721                     case 6:
5722                     default:
5723                     {
5724                       stipple=XCreateBitmapFromData(display,root_window,
5725                         (char *) OpaqueBitmap,OpaqueWidth,OpaqueHeight);
5726                       break;
5727                     }
5728                   }
5729                   break;
5730                 }
5731               XFileBrowserWidget(display,windows,"Stipple",filename);
5732               if (*filename == '\0')
5733                 break;
5734               /*
5735                 Read image.
5736               */
5737               XSetCursorState(display,windows,MagickTrue);
5738               XCheckRefreshWindows(display,windows);
5739               image_info=AcquireImageInfo();
5740               (void) CopyMagickString(image_info->filename,filename,
5741                 MaxTextExtent);
5742               stipple_image=ReadImage(image_info,exception);
5743               CatchException(exception);
5744               XSetCursorState(display,windows,MagickFalse);
5745               if (stipple_image == (Image *) NULL)
5746                 break;
5747               (void) AcquireUniqueFileResource(filename);
5748               (void) FormatLocaleString(stipple_image->filename,MaxTextExtent,
5749                 "xbm:%s",filename);
5750               (void) WriteImage(image_info,stipple_image,exception);
5751               stipple_image=DestroyImage(stipple_image);
5752               image_info=DestroyImageInfo(image_info);
5753               status=XReadBitmapFile(display,root_window,filename,&width,
5754                 &height,&stipple,&x,&y);
5755               (void) RelinquishUniqueFileResource(filename);
5756               if ((status != BitmapSuccess) != 0)
5757                 XNoticeWidget(display,windows,"Unable to read X bitmap image:",
5758                   filename);
5759               break;
5760             }
5761             case DrawWidthCommand:
5762             {
5763               static char
5764                 width[MaxTextExtent] = "0";
5765
5766               static const char
5767                 *WidthsMenu[] =
5768                 {
5769                   "1",
5770                   "2",
5771                   "4",
5772                   "8",
5773                   "16",
5774                   "Dialog...",
5775                   (char *) NULL,
5776                 };
5777
5778               /*
5779                 Select a command from the pop-up menu.
5780               */
5781               entry=XMenuWidget(display,windows,DrawMenu[id],WidthsMenu,
5782                 command);
5783               if (entry < 0)
5784                 break;
5785               if (entry != 5)
5786                 {
5787                   line_width=(unsigned int) StringToUnsignedLong(
5788                     WidthsMenu[entry]);
5789                   break;
5790                 }
5791               (void) XDialogWidget(display,windows,"Ok","Enter line width:",
5792                 width);
5793               if (*width == '\0')
5794                 break;
5795               line_width=(unsigned int) StringToUnsignedLong(width);
5796               break;
5797             }
5798             case DrawUndoCommand:
5799             {
5800               (void) XMagickCommand(display,resource_info,windows,UndoCommand,
5801                 image,exception);
5802               break;
5803             }
5804             case DrawHelpCommand:
5805             {
5806               XTextViewWidget(display,resource_info,windows,MagickFalse,
5807                 "Help Viewer - Image Rotation",ImageDrawHelp);
5808               (void) XCheckDefineCursor(display,windows->image.id,cursor);
5809               break;
5810             }
5811             case DrawDismissCommand:
5812             {
5813               /*
5814                 Prematurely exit.
5815               */
5816               state|=EscapeState;
5817               state|=ExitState;
5818               break;
5819             }
5820             default:
5821               break;
5822           }
5823           (void) XCheckDefineCursor(display,windows->image.id,cursor);
5824           continue;
5825         }
5826       switch (event.type)
5827       {
5828         case ButtonPress:
5829         {
5830           if (event.xbutton.button != Button1)
5831             break;
5832           if (event.xbutton.window != windows->image.id)
5833             break;
5834           /*
5835             exit loop.
5836           */
5837           x=event.xbutton.x;
5838           y=event.xbutton.y;
5839           state|=ExitState;
5840           break;
5841         }
5842         case ButtonRelease:
5843           break;
5844         case Expose:
5845           break;
5846         case KeyPress:
5847         {
5848           KeySym
5849             key_symbol;
5850
5851           if (event.xkey.window != windows->image.id)
5852             break;
5853           /*
5854             Respond to a user key press.
5855           */
5856           (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5857             sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5858           switch ((int) key_symbol)
5859           {
5860             case XK_Escape:
5861             case XK_F20:
5862             {
5863               /*
5864                 Prematurely exit.
5865               */
5866               state|=EscapeState;
5867               state|=ExitState;
5868               break;
5869             }
5870             case XK_F1:
5871             case XK_Help:
5872             {
5873               XTextViewWidget(display,resource_info,windows,MagickFalse,
5874                 "Help Viewer - Image Rotation",ImageDrawHelp);
5875               break;
5876             }
5877             default:
5878             {
5879               (void) XBell(display,0);
5880               break;
5881             }
5882           }
5883           break;
5884         }
5885         case MotionNotify:
5886         {
5887           /*
5888             Map and unmap Info widget as text cursor crosses its boundaries.
5889           */
5890           x=event.xmotion.x;
5891           y=event.xmotion.y;
5892           if (IfMagickTrue(windows->info.mapped) )
5893             {
5894               if ((x < (int) (windows->info.x+windows->info.width)) &&
5895                   (y < (int) (windows->info.y+windows->info.height)))
5896                 (void) XWithdrawWindow(display,windows->info.id,
5897                   windows->info.screen);
5898             }
5899           else
5900             if ((x > (int) (windows->info.x+windows->info.width)) ||
5901                 (y > (int) (windows->info.y+windows->info.height)))
5902               (void) XMapWindow(display,windows->info.id);
5903           break;
5904         }
5905       }
5906     } while ((state & ExitState) == 0);
5907     (void) XSelectInput(display,windows->image.id,
5908       windows->image.attributes.event_mask);
5909     (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
5910     if ((state & EscapeState) != 0)
5911       break;
5912     /*
5913       Draw element as pointer moves until the button is released.
5914     */
5915     distance=0;
5916     degrees=0.0;
5917     line_info.x1=x;
5918     line_info.y1=y;
5919     line_info.x2=x;
5920     line_info.y2=y;
5921     rectangle_info.x=(ssize_t) x;
5922     rectangle_info.y=(ssize_t) y;
5923     rectangle_info.width=0;
5924     rectangle_info.height=0;
5925     number_coordinates=1;
5926     coordinate_info->x=x;
5927     coordinate_info->y=y;
5928     (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
5929     state=DefaultState;
5930     do
5931     {
5932       switch (element)
5933       {
5934         case PointElement:
5935         default:
5936         {
5937           if (number_coordinates > 1)
5938             {
5939               (void) XDrawLines(display,windows->image.id,
5940                 windows->image.highlight_context,coordinate_info,
5941                 number_coordinates,CoordModeOrigin);
5942               (void) FormatLocaleString(text,MaxTextExtent," %+d%+d",
5943                 coordinate_info[number_coordinates-1].x,
5944                 coordinate_info[number_coordinates-1].y);
5945               XInfoWidget(display,windows,text);
5946             }
5947           break;
5948         }
5949         case LineElement:
5950         {
5951           if (distance > 9)
5952             {
5953               /*
5954                 Display angle of the line.
5955               */
5956               degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
5957                 line_info.y1),(double) (line_info.x2-line_info.x1)));
5958               (void) FormatLocaleString(text,MaxTextExtent," %g",
5959                 (double) degrees);
5960               XInfoWidget(display,windows,text);
5961               XHighlightLine(display,windows->image.id,
5962                 windows->image.highlight_context,&line_info);
5963             }
5964           else
5965             if (IfMagickTrue(windows->info.mapped) )
5966               (void) XWithdrawWindow(display,windows->info.id,
5967                 windows->info.screen);
5968           break;
5969         }
5970         case RectangleElement:
5971         case FillRectangleElement:
5972         {
5973           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
5974             {
5975               /*
5976                 Display info and draw drawing rectangle.
5977               */
5978               (void) FormatLocaleString(text,MaxTextExtent,
5979                 " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
5980                 (double) rectangle_info.height,(double) rectangle_info.x,
5981                 (double) rectangle_info.y);
5982               XInfoWidget(display,windows,text);
5983               XHighlightRectangle(display,windows->image.id,
5984                 windows->image.highlight_context,&rectangle_info);
5985             }
5986           else
5987             if (IfMagickTrue(windows->info.mapped) )
5988               (void) XWithdrawWindow(display,windows->info.id,
5989                 windows->info.screen);
5990           break;
5991         }
5992         case CircleElement:
5993         case FillCircleElement:
5994         case EllipseElement:
5995         case FillEllipseElement:
5996         {
5997           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
5998             {
5999               /*
6000                 Display info and draw drawing rectangle.
6001               */
6002               (void) FormatLocaleString(text,MaxTextExtent,
6003                 " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
6004                 (double) rectangle_info.height,(double) rectangle_info.x,
6005                 (double) rectangle_info.y);
6006               XInfoWidget(display,windows,text);
6007               XHighlightEllipse(display,windows->image.id,
6008                 windows->image.highlight_context,&rectangle_info);
6009             }
6010           else
6011             if (IfMagickTrue(windows->info.mapped) )
6012               (void) XWithdrawWindow(display,windows->info.id,
6013                 windows->info.screen);
6014           break;
6015         }
6016         case PolygonElement:
6017         case FillPolygonElement:
6018         {
6019           if (number_coordinates > 1)
6020             (void) XDrawLines(display,windows->image.id,
6021               windows->image.highlight_context,coordinate_info,
6022               number_coordinates,CoordModeOrigin);
6023           if (distance > 9)
6024             {
6025               /*
6026                 Display angle of the line.
6027               */
6028               degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
6029                 line_info.y1),(double) (line_info.x2-line_info.x1)));
6030               (void) FormatLocaleString(text,MaxTextExtent," %g",
6031                 (double) degrees);
6032               XInfoWidget(display,windows,text);
6033               XHighlightLine(display,windows->image.id,
6034                 windows->image.highlight_context,&line_info);
6035             }
6036           else
6037             if (IfMagickTrue(windows->info.mapped) )
6038               (void) XWithdrawWindow(display,windows->info.id,
6039                 windows->info.screen);
6040           break;
6041         }
6042       }
6043       /*
6044         Wait for next event.
6045       */
6046       XScreenEvent(display,windows,&event,exception);
6047       switch (element)
6048       {
6049         case PointElement:
6050         default:
6051         {
6052           if (number_coordinates > 1)
6053             (void) XDrawLines(display,windows->image.id,
6054               windows->image.highlight_context,coordinate_info,
6055               number_coordinates,CoordModeOrigin);
6056           break;
6057         }
6058         case LineElement:
6059         {
6060           if (distance > 9)
6061             XHighlightLine(display,windows->image.id,
6062               windows->image.highlight_context,&line_info);
6063           break;
6064         }
6065         case RectangleElement:
6066         case FillRectangleElement:
6067         {
6068           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6069             XHighlightRectangle(display,windows->image.id,
6070               windows->image.highlight_context,&rectangle_info);
6071           break;
6072         }
6073         case CircleElement:
6074         case FillCircleElement:
6075         case EllipseElement:
6076         case FillEllipseElement:
6077         {
6078           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6079             XHighlightEllipse(display,windows->image.id,
6080               windows->image.highlight_context,&rectangle_info);
6081           break;
6082         }
6083         case PolygonElement:
6084         case FillPolygonElement:
6085         {
6086           if (number_coordinates > 1)
6087             (void) XDrawLines(display,windows->image.id,
6088               windows->image.highlight_context,coordinate_info,
6089               number_coordinates,CoordModeOrigin);
6090           if (distance > 9)
6091             XHighlightLine(display,windows->image.id,
6092               windows->image.highlight_context,&line_info);
6093           break;
6094         }
6095       }
6096       switch (event.type)
6097       {
6098         case ButtonPress:
6099           break;
6100         case ButtonRelease:
6101         {
6102           /*
6103             User has committed to element.
6104           */
6105           line_info.x2=event.xbutton.x;
6106           line_info.y2=event.xbutton.y;
6107           rectangle_info.x=(ssize_t) event.xbutton.x;
6108           rectangle_info.y=(ssize_t) event.xbutton.y;
6109           coordinate_info[number_coordinates].x=event.xbutton.x;
6110           coordinate_info[number_coordinates].y=event.xbutton.y;
6111           if (((element != PolygonElement) &&
6112                (element != FillPolygonElement)) || (distance <= 9))
6113             {
6114               state|=ExitState;
6115               break;
6116             }
6117           number_coordinates++;
6118           if (number_coordinates < (int) max_coordinates)
6119             {
6120               line_info.x1=event.xbutton.x;
6121               line_info.y1=event.xbutton.y;
6122               break;
6123             }
6124           max_coordinates<<=1;
6125           coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6126             max_coordinates,sizeof(*coordinate_info));
6127           if (coordinate_info == (XPoint *) NULL)
6128             (void) ThrowMagickException(exception,GetMagickModule(),
6129               ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6130           break;
6131         }
6132         case Expose:
6133           break;
6134         case MotionNotify:
6135         {
6136           if (event.xmotion.window != windows->image.id)
6137             break;
6138           if (element != PointElement)
6139             {
6140               line_info.x2=event.xmotion.x;
6141               line_info.y2=event.xmotion.y;
6142               rectangle_info.x=(ssize_t) event.xmotion.x;
6143               rectangle_info.y=(ssize_t) event.xmotion.y;
6144               break;
6145             }
6146           coordinate_info[number_coordinates].x=event.xbutton.x;
6147           coordinate_info[number_coordinates].y=event.xbutton.y;
6148           number_coordinates++;
6149           if (number_coordinates < (int) max_coordinates)
6150             break;
6151           max_coordinates<<=1;
6152           coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6153             max_coordinates,sizeof(*coordinate_info));
6154           if (coordinate_info == (XPoint *) NULL)
6155             (void) ThrowMagickException(exception,GetMagickModule(),
6156               ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6157           break;
6158         }
6159         default:
6160           break;
6161       }
6162       /*
6163         Check boundary conditions.
6164       */
6165       if (line_info.x2 < 0)
6166         line_info.x2=0;
6167       else
6168         if (line_info.x2 > (int) windows->image.width)
6169           line_info.x2=(short) windows->image.width;
6170       if (line_info.y2 < 0)
6171         line_info.y2=0;
6172       else
6173         if (line_info.y2 > (int) windows->image.height)
6174           line_info.y2=(short) windows->image.height;
6175       distance=(unsigned int)
6176         (((line_info.x2-line_info.x1+1)*(line_info.x2-line_info.x1+1))+
6177          ((line_info.y2-line_info.y1+1)*(line_info.y2-line_info.y1+1)));
6178       if ((((int) rectangle_info.x != x) && ((int) rectangle_info.y != y)) ||
6179           ((state & ExitState) != 0))
6180         {
6181           if (rectangle_info.x < 0)
6182             rectangle_info.x=0;
6183           else
6184             if (rectangle_info.x > (ssize_t) windows->image.width)
6185               rectangle_info.x=(ssize_t) windows->image.width;
6186           if ((int) rectangle_info.x < x)
6187             rectangle_info.width=(unsigned int) (x-rectangle_info.x);
6188           else
6189             {
6190               rectangle_info.width=(unsigned int) (rectangle_info.x-x);
6191               rectangle_info.x=(ssize_t) x;
6192             }
6193           if (rectangle_info.y < 0)
6194             rectangle_info.y=0;
6195           else
6196             if (rectangle_info.y > (ssize_t) windows->image.height)
6197               rectangle_info.y=(ssize_t) windows->image.height;
6198           if ((int) rectangle_info.y < y)
6199             rectangle_info.height=(unsigned int) (y-rectangle_info.y);
6200           else
6201             {
6202               rectangle_info.height=(unsigned int) (rectangle_info.y-y);
6203               rectangle_info.y=(ssize_t) y;
6204             }
6205         }
6206     } while ((state & ExitState) == 0);
6207     (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
6208     if ((element == PointElement) || (element == PolygonElement) ||
6209         (element == FillPolygonElement))
6210       {
6211         /*
6212           Determine polygon bounding box.
6213         */
6214         rectangle_info.x=(ssize_t) coordinate_info->x;
6215         rectangle_info.y=(ssize_t) coordinate_info->y;
6216         x=coordinate_info->x;
6217         y=coordinate_info->y;
6218         for (i=1; i < number_coordinates; i++)
6219         {
6220           if (coordinate_info[i].x > x)
6221             x=coordinate_info[i].x;
6222           if (coordinate_info[i].y > y)
6223             y=coordinate_info[i].y;
6224           if ((ssize_t) coordinate_info[i].x < rectangle_info.x)
6225             rectangle_info.x=MagickMax((ssize_t) coordinate_info[i].x,0);
6226           if ((ssize_t) coordinate_info[i].y < rectangle_info.y)
6227             rectangle_info.y=MagickMax((ssize_t) coordinate_info[i].y,0);
6228         }
6229         rectangle_info.width=(size_t) (x-rectangle_info.x);
6230         rectangle_info.height=(size_t) (y-rectangle_info.y);
6231         for (i=0; i < number_coordinates; i++)
6232         {
6233           coordinate_info[i].x-=rectangle_info.x;
6234           coordinate_info[i].y-=rectangle_info.y;
6235         }
6236       }
6237     else
6238       if (distance <= 9)
6239         continue;
6240       else
6241         if ((element == RectangleElement) ||
6242             (element == CircleElement) || (element == EllipseElement))
6243           {
6244             rectangle_info.width--;
6245             rectangle_info.height--;
6246           }
6247     /*
6248       Drawing is relative to image configuration.
6249     */
6250     draw_info.x=(int) rectangle_info.x;
6251     draw_info.y=(int) rectangle_info.y;
6252     (void) XMagickCommand(display,resource_info,windows,SaveToUndoBufferCommand,
6253       image,exception);
6254     width=(unsigned int) (*image)->columns;
6255     height=(unsigned int) (*image)->rows;
6256     x=0;
6257     y=0;
6258     if (windows->image.crop_geometry != (char *) NULL)
6259       (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
6260     draw_info.x+=windows->image.x-(line_width/2);
6261     if (draw_info.x < 0)
6262       draw_info.x=0;
6263     draw_info.x=(int) (width*draw_info.x/windows->image.ximage->width);
6264     draw_info.y+=windows->image.y-(line_width/2);
6265     if (draw_info.y < 0)
6266       draw_info.y=0;
6267     draw_info.y=(int) height*draw_info.y/windows->image.ximage->height;
6268     draw_info.width=(unsigned int) rectangle_info.width+(line_width << 1);
6269     if (draw_info.width > (unsigned int) (*image)->columns)
6270       draw_info.width=(unsigned int) (*image)->columns;
6271     draw_info.height=(unsigned int) rectangle_info.height+(line_width << 1);
6272     if (draw_info.height > (unsigned int) (*image)->rows)
6273       draw_info.height=(unsigned int) (*image)->rows;
6274     (void) FormatLocaleString(draw_info.geometry,MaxTextExtent,"%ux%u%+d%+d",
6275       width*draw_info.width/windows->image.ximage->width,
6276       height*draw_info.height/windows->image.ximage->height,
6277       draw_info.x+x,draw_info.y+y);
6278     /*
6279       Initialize drawing attributes.
6280     */
6281     draw_info.degrees=0.0;
6282     draw_info.element=element;
6283     draw_info.stipple=stipple;
6284     draw_info.line_width=line_width;
6285     draw_info.line_info=line_info;
6286     if (line_info.x1 > (int) (line_width/2))
6287       draw_info.line_info.x1=(short) line_width/2;
6288     if (line_info.y1 > (int) (line_width/2))
6289       draw_info.line_info.y1=(short) line_width/2;
6290     draw_info.line_info.x2=(short) (line_info.x2-line_info.x1+(line_width/2));
6291     draw_info.line_info.y2=(short) (line_info.y2-line_info.y1+(line_width/2));
6292     if ((draw_info.line_info.x2 < 0) && (draw_info.line_info.y2 < 0))
6293       {
6294         draw_info.line_info.x2=(-draw_info.line_info.x2);
6295         draw_info.line_info.y2=(-draw_info.line_info.y2);
6296       }
6297     if (draw_info.line_info.x2 < 0)
6298       {
6299         draw_info.line_info.x2=(-draw_info.line_info.x2);
6300         Swap(draw_info.line_info.x1,draw_info.line_info.x2);
6301       }
6302     if (draw_info.line_info.y2 < 0)
6303       {
6304         draw_info.line_info.y2=(-draw_info.line_info.y2);
6305         Swap(draw_info.line_info.y1,draw_info.line_info.y2);
6306       }
6307     draw_info.rectangle_info=rectangle_info;
6308     if (draw_info.rectangle_info.x > (ssize_t) (line_width/2))
6309       draw_info.rectangle_info.x=(ssize_t) line_width/2;
6310     if (draw_info.rectangle_info.y > (ssize_t) (line_width/2))
6311       draw_info.rectangle_info.y=(ssize_t) line_width/2;
6312     draw_info.number_coordinates=(unsigned int) number_coordinates;
6313     draw_info.coordinate_info=coordinate_info;
6314     windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
6315     /*
6316       Draw element on image.
6317     */
6318     XSetCursorState(display,windows,MagickTrue);
6319     XCheckRefreshWindows(display,windows);
6320     status=XDrawImage(display,windows->pixel_info,&draw_info,*image,exception);
6321     XSetCursorState(display,windows,MagickFalse);
6322     /*
6323       Update image colormap and return to image drawing.
6324     */
6325     XConfigureImageColormap(display,resource_info,windows,*image,exception);
6326     (void) XConfigureImage(display,resource_info,windows,*image,exception);
6327   }
6328   XSetCursorState(display,windows,MagickFalse);
6329   coordinate_info=(XPoint *) RelinquishMagickMemory(coordinate_info);
6330   return(IsMagickTrue(status));
6331 }
6332 \f
6333 /*
6334 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6335 %                                                                             %
6336 %                                                                             %
6337 %                                                                             %
6338 +   X D r a w P a n R e c t a n g l e                                         %
6339 %                                                                             %
6340 %                                                                             %
6341 %                                                                             %
6342 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6343 %
6344 %  XDrawPanRectangle() draws a rectangle in the pan window.  The pan window
6345 %  displays a zoom image and the rectangle shows which portion of the image is
6346 %  displayed in the Image window.
6347 %
6348 %  The format of the XDrawPanRectangle method is:
6349 %
6350 %      XDrawPanRectangle(Display *display,XWindows *windows)
6351 %
6352 %  A description of each parameter follows:
6353 %
6354 %    o display: Specifies a connection to an X server;  returned from
6355 %      XOpenDisplay.
6356 %
6357 %    o windows: Specifies a pointer to a XWindows structure.
6358 %
6359 */
6360 static void XDrawPanRectangle(Display *display,XWindows *windows)
6361 {
6362   double
6363     scale_factor;
6364
6365   RectangleInfo
6366     highlight_info;
6367
6368   /*
6369     Determine dimensions of the panning rectangle.
6370   */
6371   scale_factor=(double) windows->pan.width/windows->image.ximage->width;
6372   highlight_info.x=(ssize_t) (scale_factor*windows->image.x+0.5);
6373   highlight_info.width=(unsigned int) (scale_factor*windows->image.width+0.5);
6374   scale_factor=(double)
6375     windows->pan.height/windows->image.ximage->height;
6376   highlight_info.y=(ssize_t) (scale_factor*windows->image.y+0.5);
6377   highlight_info.height=(unsigned int) (scale_factor*windows->image.height+0.5);
6378   /*
6379     Display the panning rectangle.
6380   */
6381   (void) XClearWindow(display,windows->pan.id);
6382   XHighlightRectangle(display,windows->pan.id,windows->pan.annotate_context,
6383     &highlight_info);
6384 }
6385 \f
6386 /*
6387 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6388 %                                                                             %
6389 %                                                                             %
6390 %                                                                             %
6391 +   X I m a g e C a c h e                                                     %
6392 %                                                                             %
6393 %                                                                             %
6394 %                                                                             %
6395 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6396 %
6397 %  XImageCache() handles the creation, manipulation, and destruction of the
6398 %  image cache (undo and redo buffers).
6399 %
6400 %  The format of the XImageCache method is:
6401 %
6402 %      void XImageCache(Display *display,XResourceInfo *resource_info,
6403 %        XWindows *windows,const CommandType command,Image **image,
6404 %        ExceptionInfo *exception)
6405 %
6406 %  A description of each parameter follows:
6407 %
6408 %    o display: Specifies a connection to an X server; returned from
6409 %      XOpenDisplay.
6410 %
6411 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6412 %
6413 %    o windows: Specifies a pointer to a XWindows structure.
6414 %
6415 %    o command: Specifies a command to perform.
6416 %
6417 %    o image: the image;  XImageCache may transform the image and return a new
6418 %      image pointer.
6419 %
6420 %    o exception: return any errors or warnings in this structure.
6421 %
6422 */
6423 static void XImageCache(Display *display,XResourceInfo *resource_info,
6424   XWindows *windows,const CommandType command,Image **image,
6425   ExceptionInfo *exception)
6426 {
6427   Image
6428     *cache_image;
6429
6430   static Image
6431     *redo_image = (Image *) NULL,
6432     *undo_image = (Image *) NULL;
6433
6434   switch (command)
6435   {
6436     case FreeBuffersCommand:
6437     {
6438       /*
6439         Free memory from the undo and redo cache.
6440       */
6441       while (undo_image != (Image *) NULL)
6442       {
6443         cache_image=undo_image;
6444         undo_image=GetPreviousImageInList(undo_image);
6445         cache_image->list=DestroyImage(cache_image->list);
6446         cache_image=DestroyImage(cache_image);
6447       }
6448       undo_image=NewImageList();
6449       if (redo_image != (Image *) NULL)
6450         redo_image=DestroyImage(redo_image);
6451       redo_image=NewImageList();
6452       return;
6453     }
6454     case UndoCommand:
6455     {
6456       char
6457         image_geometry[MaxTextExtent];
6458
6459       /*
6460         Undo the last image transformation.
6461       */
6462       if (undo_image == (Image *) NULL)
6463         {
6464           (void) XBell(display,0);
6465           return;
6466         }
6467       cache_image=undo_image;
6468       undo_image=GetPreviousImageInList(undo_image);
6469       windows->image.window_changes.width=(int) cache_image->columns;
6470       windows->image.window_changes.height=(int) cache_image->rows;
6471       (void) FormatLocaleString(image_geometry,MaxTextExtent,"%dx%d!",
6472         windows->image.ximage->width,windows->image.ximage->height);
6473       (void) TransformImage(image,windows->image.crop_geometry,image_geometry,
6474         exception);
6475       if (windows->image.crop_geometry != (char *) NULL)
6476         windows->image.crop_geometry=(char *) RelinquishMagickMemory(
6477           windows->image.crop_geometry);
6478       windows->image.crop_geometry=cache_image->geometry;
6479       if (redo_image != (Image *) NULL)
6480         redo_image=DestroyImage(redo_image);
6481       redo_image=(*image);
6482       *image=cache_image->list;
6483       cache_image=DestroyImage(cache_image);
6484       if (IfMagickTrue(windows->image.orphan) )
6485         return;
6486       XConfigureImageColormap(display,resource_info,windows,*image,exception);
6487       (void) XConfigureImage(display,resource_info,windows,*image,exception);
6488       return;
6489     }
6490     case CutCommand:
6491     case PasteCommand:
6492     case ApplyCommand:
6493     case HalfSizeCommand:
6494     case OriginalSizeCommand:
6495     case DoubleSizeCommand:
6496     case ResizeCommand:
6497     case TrimCommand:
6498     case CropCommand:
6499     case ChopCommand:
6500     case FlipCommand:
6501     case FlopCommand:
6502     case RotateRightCommand:
6503     case RotateLeftCommand:
6504     case RotateCommand:
6505     case ShearCommand:
6506     case RollCommand:
6507     case NegateCommand:
6508     case ContrastStretchCommand:
6509     case SigmoidalContrastCommand:
6510     case NormalizeCommand:
6511     case EqualizeCommand:
6512     case HueCommand:
6513     case SaturationCommand:
6514     case BrightnessCommand:
6515     case GammaCommand:
6516     case SpiffCommand:
6517     case DullCommand:
6518     case GrayscaleCommand:
6519     case MapCommand:
6520     case QuantizeCommand:
6521     case DespeckleCommand:
6522     case EmbossCommand:
6523     case ReduceNoiseCommand:
6524     case AddNoiseCommand:
6525     case SharpenCommand:
6526     case BlurCommand:
6527     case ThresholdCommand:
6528     case EdgeDetectCommand:
6529     case SpreadCommand:
6530     case ShadeCommand:
6531     case RaiseCommand:
6532     case SegmentCommand:
6533     case SolarizeCommand:
6534     case SepiaToneCommand:
6535     case SwirlCommand:
6536     case ImplodeCommand:
6537     case VignetteCommand:
6538     case WaveCommand:
6539     case OilPaintCommand:
6540     case CharcoalDrawCommand:
6541     case AnnotateCommand:
6542     case AddBorderCommand:
6543     case AddFrameCommand:
6544     case CompositeCommand:
6545     case CommentCommand:
6546     case LaunchCommand:
6547     case RegionofInterestCommand:
6548     case SaveToUndoBufferCommand:
6549     case RedoCommand:
6550     {
6551       Image
6552         *previous_image;
6553
6554       ssize_t
6555         bytes;
6556
6557       bytes=(ssize_t) ((*image)->columns*(*image)->rows*sizeof(PixelInfo));
6558       if (undo_image != (Image *) NULL)
6559         {
6560           /*
6561             Ensure the undo cache has enough memory available.
6562           */
6563           previous_image=undo_image;
6564           while (previous_image != (Image *) NULL)
6565           {
6566             bytes+=previous_image->list->columns*previous_image->list->rows*
6567               sizeof(PixelInfo);
6568             if (bytes <= (ssize_t) (resource_info->undo_cache << 20))
6569               {
6570                 previous_image=GetPreviousImageInList(previous_image);
6571                 continue;
6572               }
6573             bytes-=previous_image->list->columns*previous_image->list->rows*
6574               sizeof(PixelInfo);
6575             if (previous_image == undo_image)
6576               undo_image=NewImageList();
6577             else
6578               previous_image->next->previous=NewImageList();
6579             break;
6580           }
6581           while (previous_image != (Image *) NULL)
6582           {
6583             /*
6584               Delete any excess memory from undo cache.
6585             */
6586             cache_image=previous_image;
6587             previous_image=GetPreviousImageInList(previous_image);
6588             cache_image->list=DestroyImage(cache_image->list);
6589             cache_image=DestroyImage(cache_image);
6590           }
6591         }
6592       if (bytes > (ssize_t) (resource_info->undo_cache << 20))
6593         break;
6594       /*
6595         Save image before transformations are applied.
6596       */
6597       cache_image=AcquireImage((ImageInfo *) NULL,exception);
6598       if (cache_image == (Image *) NULL)
6599         break;
6600       XSetCursorState(display,windows,MagickTrue);
6601       XCheckRefreshWindows(display,windows);
6602       cache_image->list=CloneImage(*image,0,0,MagickTrue,exception);
6603       XSetCursorState(display,windows,MagickFalse);
6604       if (cache_image->list == (Image *) NULL)
6605         {
6606           cache_image=DestroyImage(cache_image);
6607           break;
6608         }
6609       cache_image->columns=(size_t) windows->image.ximage->width;
6610       cache_image->rows=(size_t) windows->image.ximage->height;
6611       cache_image->geometry=windows->image.crop_geometry;
6612       if (windows->image.crop_geometry != (char *) NULL)
6613         {
6614           cache_image->geometry=AcquireString((char *) NULL);
6615           (void) CopyMagickString(cache_image->geometry,
6616             windows->image.crop_geometry,MaxTextExtent);
6617         }
6618       if (undo_image == (Image *) NULL)
6619         {
6620           undo_image=cache_image;
6621           break;
6622         }
6623       undo_image->next=cache_image;
6624       undo_image->next->previous=undo_image;
6625       undo_image=undo_image->next;
6626       break;
6627     }
6628     default:
6629       break;
6630   }
6631   if (command == RedoCommand)
6632     {
6633       /*
6634         Redo the last image transformation.
6635       */
6636       if (redo_image == (Image *) NULL)
6637         {
6638           (void) XBell(display,0);
6639           return;
6640         }
6641       windows->image.window_changes.width=(int) redo_image->columns;
6642       windows->image.window_changes.height=(int) redo_image->rows;
6643       if (windows->image.crop_geometry != (char *) NULL)
6644         windows->image.crop_geometry=(char *)
6645           RelinquishMagickMemory(windows->image.crop_geometry);
6646       windows->image.crop_geometry=redo_image->geometry;
6647       *image=DestroyImage(*image);
6648       *image=redo_image;
6649       redo_image=NewImageList();
6650       if (IfMagickTrue(windows->image.orphan) )
6651         return;
6652       XConfigureImageColormap(display,resource_info,windows,*image,exception);
6653       (void) XConfigureImage(display,resource_info,windows,*image,exception);
6654       return;
6655     }
6656   if (command != InfoCommand)
6657     return;
6658   /*
6659     Display image info.
6660   */
6661   XSetCursorState(display,windows,MagickTrue);
6662   XCheckRefreshWindows(display,windows);
6663   XDisplayImageInfo(display,resource_info,windows,undo_image,*image,exception);
6664   XSetCursorState(display,windows,MagickFalse);
6665 }
6666 \f
6667 /*
6668 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6669 %                                                                             %
6670 %                                                                             %
6671 %                                                                             %
6672 +   X I m a g e W i n d o w C o m m a n d                                     %
6673 %                                                                             %
6674 %                                                                             %
6675 %                                                                             %
6676 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6677 %
6678 %  XImageWindowCommand() makes a transform to the image or Image window as
6679 %  specified by a user menu button or keyboard command.
6680 %
6681 %  The format of the XImageWindowCommand method is:
6682 %
6683 %      CommandType XImageWindowCommand(Display *display,
6684 %        XResourceInfo *resource_info,XWindows *windows,
6685 %        const MagickStatusType state,KeySym key_symbol,Image **image,
6686 %        ExceptionInfo *exception)
6687 %
6688 %  A description of each parameter follows:
6689 %
6690 %    o nexus:  Method XImageWindowCommand returns an image when the
6691 %      user chooses 'Open Image' from the command menu.  Otherwise a null
6692 %      image is returned.
6693 %
6694 %    o display: Specifies a connection to an X server; returned from
6695 %      XOpenDisplay.
6696 %
6697 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6698 %
6699 %    o windows: Specifies a pointer to a XWindows structure.
6700 %
6701 %    o state: key mask.
6702 %
6703 %    o key_symbol: Specifies a command to perform.
6704 %
6705 %    o image: the image;  XImageWIndowCommand may transform the image and
6706 %      return a new image pointer.
6707 %
6708 %    o exception: return any errors or warnings in this structure.
6709 %
6710 */
6711 static CommandType XImageWindowCommand(Display *display,
6712   XResourceInfo *resource_info,XWindows *windows,const MagickStatusType state,
6713   KeySym key_symbol,Image **image,ExceptionInfo *exception)
6714 {
6715   static char
6716     delta[MaxTextExtent] = "";
6717
6718   static const char
6719     Digits[] = "01234567890";
6720
6721   static KeySym
6722     last_symbol = XK_0;
6723
6724   if ((key_symbol >= XK_0) && (key_symbol <= XK_9))
6725     {
6726       if (((last_symbol < XK_0) || (last_symbol > XK_9)))
6727         {
6728           *delta='\0';
6729           resource_info->quantum=1;
6730         }
6731       last_symbol=key_symbol;
6732       delta[strlen(delta)+1]='\0';
6733       delta[strlen(delta)]=Digits[key_symbol-XK_0];
6734       resource_info->quantum=StringToLong(delta);
6735       return(NullCommand);
6736     }
6737   last_symbol=key_symbol;
6738   if (resource_info->immutable)
6739     {
6740       /*
6741         Virtual image window has a restricted command set.
6742       */
6743       switch (key_symbol)
6744       {
6745         case XK_question:
6746           return(InfoCommand);
6747         case XK_p:
6748         case XK_Print:
6749           return(PrintCommand);
6750         case XK_space:
6751           return(NextCommand);
6752         case XK_q:
6753         case XK_Escape:
6754           return(QuitCommand);
6755         default:
6756           break;
6757       }
6758       return(NullCommand);
6759     }
6760   switch ((int) key_symbol)
6761   {
6762     case XK_o:
6763     {
6764       if ((state & ControlMask) == 0)
6765         break;
6766       return(OpenCommand);
6767     }
6768     case XK_space:
6769       return(NextCommand);
6770     case XK_BackSpace:
6771       return(FormerCommand);
6772     case XK_s:
6773     {
6774       if ((state & Mod1Mask) != 0)
6775         return(SwirlCommand);
6776       if ((state & ControlMask) == 0)
6777         return(ShearCommand);
6778       return(SaveCommand);
6779     }
6780     case XK_p:
6781     case XK_Print:
6782     {
6783       if ((state & Mod1Mask) != 0)
6784         return(OilPaintCommand);
6785       if ((state & Mod4Mask) != 0)
6786         return(ColorCommand);
6787       if ((state & ControlMask) == 0)
6788         return(NullCommand);
6789       return(PrintCommand);
6790     }
6791     case XK_d:
6792     {
6793       if ((state & Mod4Mask) != 0)
6794         return(DrawCommand);
6795       if ((state & ControlMask) == 0)
6796         return(NullCommand);
6797       return(DeleteCommand);
6798     }
6799     case XK_Select:
6800     {
6801       if ((state & ControlMask) == 0)
6802         return(NullCommand);
6803       return(SelectCommand);
6804     }
6805     case XK_n:
6806     {
6807       if ((state & ControlMask) == 0)
6808         return(NullCommand);
6809       return(NewCommand);
6810     }
6811     case XK_q:
6812     case XK_Escape:
6813       return(QuitCommand);
6814     case XK_z:
6815     case XK_Undo:
6816     {
6817       if ((state & ControlMask) == 0)
6818         return(NullCommand);
6819       return(UndoCommand);
6820     }
6821     case XK_r:
6822     case XK_Redo:
6823     {
6824       if ((state & ControlMask) == 0)
6825         return(RollCommand);
6826       return(RedoCommand);
6827     }
6828     case XK_x:
6829     {
6830       if ((state & ControlMask) == 0)
6831         return(NullCommand);
6832       return(CutCommand);
6833     }
6834     case XK_c:
6835     {
6836       if ((state & Mod1Mask) != 0)
6837         return(CharcoalDrawCommand);
6838       if ((state & ControlMask) == 0)
6839         return(CropCommand);
6840       return(CopyCommand);
6841     }
6842     case XK_v:
6843     case XK_Insert:
6844     {
6845       if ((state & Mod4Mask) != 0)
6846         return(CompositeCommand);
6847       if ((state & ControlMask) == 0)
6848         return(FlipCommand);
6849       return(PasteCommand);
6850     }
6851     case XK_less:
6852       return(HalfSizeCommand);
6853     case XK_minus:
6854       return(OriginalSizeCommand);
6855     case XK_greater:
6856       return(DoubleSizeCommand);
6857     case XK_percent:
6858       return(ResizeCommand);
6859     case XK_at:
6860       return(RefreshCommand);
6861     case XK_bracketleft:
6862       return(ChopCommand);
6863     case XK_h:
6864       return(FlopCommand);
6865     case XK_slash:
6866       return(RotateRightCommand);
6867     case XK_backslash:
6868       return(RotateLeftCommand);
6869     case XK_asterisk:
6870       return(RotateCommand);
6871     case XK_t:
6872       return(TrimCommand);
6873     case XK_H:
6874       return(HueCommand);
6875     case XK_S:
6876       return(SaturationCommand);
6877     case XK_L:
6878       return(BrightnessCommand);
6879     case XK_G:
6880       return(GammaCommand);
6881     case XK_C:
6882       return(SpiffCommand);
6883     case XK_Z:
6884       return(DullCommand);
6885     case XK_N:
6886       return(NormalizeCommand);
6887     case XK_equal:
6888       return(EqualizeCommand);
6889     case XK_asciitilde:
6890       return(NegateCommand);
6891     case XK_period:
6892       return(GrayscaleCommand);
6893     case XK_numbersign:
6894       return(QuantizeCommand);
6895     case XK_F2:
6896       return(DespeckleCommand);
6897     case XK_F3:
6898       return(EmbossCommand);
6899     case XK_F4:
6900       return(ReduceNoiseCommand);
6901     case XK_F5:
6902       return(AddNoiseCommand);
6903     case XK_F6:
6904       return(SharpenCommand);
6905     case XK_F7:
6906       return(BlurCommand);
6907     case XK_F8:
6908       return(ThresholdCommand);
6909     case XK_F9:
6910       return(EdgeDetectCommand);
6911     case XK_F10:
6912       return(SpreadCommand);
6913     case XK_F11:
6914       return(ShadeCommand);
6915     case XK_F12:
6916       return(RaiseCommand);
6917     case XK_F13:
6918       return(SegmentCommand);
6919     case XK_i:
6920     {
6921       if ((state & Mod1Mask) == 0)
6922         return(NullCommand);
6923       return(ImplodeCommand);
6924     }
6925     case XK_w:
6926     {
6927       if ((state & Mod1Mask) == 0)
6928         return(NullCommand);
6929       return(WaveCommand);
6930     }
6931     case XK_m:
6932     {
6933       if ((state & Mod4Mask) == 0)
6934         return(NullCommand);
6935       return(MatteCommand);
6936     }
6937     case XK_b:
6938     {
6939       if ((state & Mod4Mask) == 0)
6940         return(NullCommand);
6941       return(AddBorderCommand);
6942     }
6943     case XK_f:
6944     {
6945       if ((state & Mod4Mask) == 0)
6946         return(NullCommand);
6947       return(AddFrameCommand);
6948     }
6949     case XK_exclam:
6950     {
6951       if ((state & Mod4Mask) == 0)
6952         return(NullCommand);
6953       return(CommentCommand);
6954     }
6955     case XK_a:
6956     {
6957       if ((state & Mod1Mask) != 0)
6958         return(ApplyCommand);
6959       if ((state & Mod4Mask) != 0)
6960         return(AnnotateCommand);
6961       if ((state & ControlMask) == 0)
6962         return(NullCommand);
6963       return(RegionofInterestCommand);
6964     }
6965     case XK_question:
6966       return(InfoCommand);
6967     case XK_plus:
6968       return(ZoomCommand);
6969     case XK_P:
6970     {
6971       if ((state & ShiftMask) == 0)
6972         return(NullCommand);
6973       return(ShowPreviewCommand);
6974     }
6975     case XK_Execute:
6976       return(LaunchCommand);
6977     case XK_F1:
6978       return(HelpCommand);
6979     case XK_Find:
6980       return(BrowseDocumentationCommand);
6981     case XK_Menu:
6982     {
6983       (void) XMapRaised(display,windows->command.id);
6984       return(NullCommand);
6985     }
6986     case XK_Next:
6987     case XK_Prior:
6988     case XK_Home:
6989     case XK_KP_Home:
6990     {
6991       XTranslateImage(display,windows,*image,key_symbol);
6992       return(NullCommand);
6993     }
6994     case XK_Up:
6995     case XK_KP_Up:
6996     case XK_Down:
6997     case XK_KP_Down:
6998     case XK_Left:
6999     case XK_KP_Left:
7000     case XK_Right:
7001     case XK_KP_Right:
7002     {
7003       if ((state & Mod1Mask) != 0)
7004         {
7005           RectangleInfo
7006             crop_info;
7007
7008           /*
7009             Trim one pixel from edge of image.
7010           */
7011           crop_info.x=0;
7012           crop_info.y=0;
7013           crop_info.width=(size_t) windows->image.ximage->width;
7014           crop_info.height=(size_t) windows->image.ximage->height;
7015           if ((key_symbol == XK_Up) || (key_symbol == XK_KP_Up))
7016             {
7017               if (resource_info->quantum >= (int) crop_info.height)
7018                 resource_info->quantum=(int) crop_info.height-1;
7019               crop_info.height-=resource_info->quantum;
7020             }
7021           if ((key_symbol == XK_Down) || (key_symbol == XK_KP_Down))
7022             {
7023               if (resource_info->quantum >= (int) (crop_info.height-crop_info.y))
7024                 resource_info->quantum=(int) (crop_info.height-crop_info.y-1);
7025               crop_info.y+=resource_info->quantum;
7026               crop_info.height-=resource_info->quantum;
7027             }
7028           if ((key_symbol == XK_Left) || (key_symbol == XK_KP_Left))
7029             {
7030               if (resource_info->quantum >= (int) crop_info.width)
7031                 resource_info->quantum=(int) crop_info.width-1;
7032               crop_info.width-=resource_info->quantum;
7033             }
7034           if ((key_symbol == XK_Right) || (key_symbol == XK_KP_Right))
7035             {
7036               if (resource_info->quantum >= (int) (crop_info.width-crop_info.x))
7037                 resource_info->quantum=(int) (crop_info.width-crop_info.x-1);
7038               crop_info.x+=resource_info->quantum;
7039               crop_info.width-=resource_info->quantum;
7040             }
7041           if ((int) (windows->image.x+windows->image.width) >
7042               (int) crop_info.width)
7043             windows->image.x=(int) (crop_info.width-windows->image.width);
7044           if ((int) (windows->image.y+windows->image.height) >
7045               (int) crop_info.height)
7046             windows->image.y=(int) (crop_info.height-windows->image.height);
7047           XSetCropGeometry(display,windows,&crop_info,*image);
7048           windows->image.window_changes.width=(int) crop_info.width;
7049           windows->image.window_changes.height=(int) crop_info.height;
7050           (void) XSetWindowBackgroundPixmap(display,windows->image.id,None);
7051           (void) XConfigureImage(display,resource_info,windows,*image,
7052             exception);
7053           return(NullCommand);
7054         }
7055       XTranslateImage(display,windows,*image,key_symbol);
7056       return(NullCommand);
7057     }
7058     default:
7059       return(NullCommand);
7060   }
7061   return(NullCommand);
7062 }
7063 \f
7064 /*
7065 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7066 %                                                                             %
7067 %                                                                             %
7068 %                                                                             %
7069 +   X M a g i c k C o m m a n d                                               %
7070 %                                                                             %
7071 %                                                                             %
7072 %                                                                             %
7073 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7074 %
7075 %  XMagickCommand() makes a transform to the image or Image window as
7076 %  specified by a user menu button or keyboard command.
7077 %
7078 %  The format of the XMagickCommand method is:
7079 %
7080 %      Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7081 %        XWindows *windows,const CommandType command,Image **image,
7082 %        ExceptionInfo *exception)
7083 %
7084 %  A description of each parameter follows:
7085 %
7086 %    o display: Specifies a connection to an X server; returned from
7087 %      XOpenDisplay.
7088 %
7089 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
7090 %
7091 %    o windows: Specifies a pointer to a XWindows structure.
7092 %
7093 %    o command: Specifies a command to perform.
7094 %
7095 %    o image: the image;  XMagickCommand may transform the image and return a
7096 %      new image pointer.
7097 %
7098 %    o exception: return any errors or warnings in this structure.
7099 %
7100 */
7101 static Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7102   XWindows *windows,const CommandType command,Image **image,
7103   ExceptionInfo *exception)
7104 {
7105   char
7106     filename[MaxTextExtent],
7107     geometry[MaxTextExtent],
7108     modulate_factors[MaxTextExtent];
7109
7110   GeometryInfo
7111     geometry_info;
7112
7113   Image
7114     *nexus;
7115
7116   ImageInfo
7117     *image_info;
7118
7119   int
7120     x,
7121     y;
7122
7123   MagickStatusType
7124     flags,
7125     status;
7126
7127   QuantizeInfo
7128     quantize_info;
7129
7130   RectangleInfo
7131     page_geometry;
7132
7133   register int
7134     i;
7135
7136   static char
7137     color[MaxTextExtent] = "gray";
7138
7139   unsigned int
7140     height,
7141     width;
7142
7143   /*
7144     Process user command.
7145   */
7146   XCheckRefreshWindows(display,windows);
7147   XImageCache(display,resource_info,windows,command,image,exception);
7148   nexus=NewImageList();
7149   windows->image.window_changes.width=windows->image.ximage->width;
7150   windows->image.window_changes.height=windows->image.ximage->height;
7151   image_info=CloneImageInfo(resource_info->image_info);
7152   SetGeometryInfo(&geometry_info);
7153   GetQuantizeInfo(&quantize_info);
7154   switch (command)
7155   {
7156     case OpenCommand:
7157     {
7158       /*
7159         Load image.
7160       */
7161       nexus=XOpenImage(display,resource_info,windows,MagickFalse);
7162       break;
7163     }
7164     case NextCommand:
7165     {
7166       /*
7167         Display next image.
7168       */
7169       for (i=0; i < resource_info->quantum; i++)
7170         XClientMessage(display,windows->image.id,windows->im_protocols,
7171           windows->im_next_image,CurrentTime);
7172       break;
7173     }
7174     case FormerCommand:
7175     {
7176       /*
7177         Display former image.
7178       */
7179       for (i=0; i < resource_info->quantum; i++)
7180         XClientMessage(display,windows->image.id,windows->im_protocols,
7181           windows->im_former_image,CurrentTime);
7182       break;
7183     }
7184     case SelectCommand:
7185     {
7186       int
7187         status;
7188
7189       /*
7190         Select image.
7191       */
7192       if (*resource_info->home_directory == '\0')
7193         (void) CopyMagickString(resource_info->home_directory,".",
7194           MaxTextExtent);
7195       status=chdir(resource_info->home_directory);
7196       if (status == -1)
7197         (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
7198           "UnableToOpenFile","%s",resource_info->home_directory);
7199       nexus=XOpenImage(display,resource_info,windows,MagickTrue);
7200       break;
7201     }
7202     case SaveCommand:
7203     {
7204       /*
7205         Save image.
7206       */
7207       status=XSaveImage(display,resource_info,windows,*image,exception);
7208       if (IfMagickFalse(status) )
7209         {
7210           char
7211             message[MaxTextExtent];
7212
7213           (void) FormatLocaleString(message,MaxTextExtent,"%s:%s",
7214             exception->reason != (char *) NULL ? exception->reason : "",
7215             exception->description != (char *) NULL ? exception->description :
7216             "");
7217           XNoticeWidget(display,windows,"Unable to save file:",message);
7218           break;
7219         }
7220       break;
7221     }
7222     case PrintCommand:
7223     {
7224       /*
7225         Print image.
7226       */
7227       status=XPrintImage(display,resource_info,windows,*image,exception);
7228       if (IfMagickFalse(status) )
7229         {
7230           char
7231             message[MaxTextExtent];
7232
7233           (void) FormatLocaleString(message,MaxTextExtent,"%s:%s",
7234             exception->reason != (char *) NULL ? exception->reason : "",
7235             exception->description != (char *) NULL ? exception->description :
7236             "");
7237           XNoticeWidget(display,windows,"Unable to print file:",message);
7238           break;
7239         }
7240       break;
7241     }
7242     case DeleteCommand:
7243     {
7244       static char
7245         filename[MaxTextExtent] = "\0";
7246
7247       /*
7248         Delete image file.
7249       */
7250       XFileBrowserWidget(display,windows,"Delete",filename);
7251       if (*filename == '\0')
7252         break;
7253       status=ShredFile(filename);
7254       if (IfMagickTrue(status) )
7255         XNoticeWidget(display,windows,"Unable to delete image file:",filename);
7256       break;
7257     }
7258     case NewCommand:
7259     {
7260       int
7261         status;
7262
7263       static char
7264         color[MaxTextExtent] = "gray",
7265         geometry[MaxTextExtent] = "640x480";
7266
7267       static const char
7268         *format = "gradient";
7269
7270       /*
7271         Query user for canvas geometry.
7272       */
7273       status=XDialogWidget(display,windows,"New","Enter image geometry:",
7274         geometry);
7275       if (*geometry == '\0')
7276         break;
7277       if (status == 0)
7278         format="xc";
7279       XColorBrowserWidget(display,windows,"Select",color);
7280       if (*color == '\0')
7281         break;
7282       /*
7283         Create canvas.
7284       */
7285       (void) FormatLocaleString(image_info->filename,MaxTextExtent,
7286         "%s:%s",format,color);
7287       (void) CloneString(&image_info->size,geometry);
7288       nexus=ReadImage(image_info,exception);
7289       CatchException(exception);
7290       XClientMessage(display,windows->image.id,windows->im_protocols,
7291         windows->im_next_image,CurrentTime);
7292       break;
7293     }
7294     case VisualDirectoryCommand:
7295     {
7296       /*
7297         Visual Image directory.
7298       */
7299       nexus=XVisualDirectoryImage(display,resource_info,windows,exception);
7300       break;
7301     }
7302     case QuitCommand:
7303     {
7304       /*
7305         exit program.
7306       */
7307       if (IfMagickFalse(resource_info->confirm_exit) )
7308         XClientMessage(display,windows->image.id,windows->im_protocols,
7309           windows->im_exit,CurrentTime);
7310       else
7311         {
7312           int
7313             status;
7314
7315           /*
7316             Confirm program exit.
7317           */
7318           status=XConfirmWidget(display,windows,"Do you really want to exit",
7319             resource_info->client_name);
7320           if (status > 0)
7321             XClientMessage(display,windows->image.id,windows->im_protocols,
7322               windows->im_exit,CurrentTime);
7323         }
7324       break;
7325     }
7326     case CutCommand:
7327     {
7328       /*
7329         Cut image.
7330       */
7331       (void) XCropImage(display,resource_info,windows,*image,CutMode,exception);
7332       break;
7333     }
7334     case CopyCommand:
7335     {
7336       /*
7337         Copy image.
7338       */
7339       (void) XCropImage(display,resource_info,windows,*image,CopyMode,
7340         exception);
7341       break;
7342     }
7343     case PasteCommand:
7344     {
7345       /*
7346         Paste image.
7347       */
7348       status=XPasteImage(display,resource_info,windows,*image,exception);
7349       if (IfMagickFalse(status) )
7350         {
7351           XNoticeWidget(display,windows,"Unable to paste X image",
7352             (*image)->filename);
7353           break;
7354         }
7355       break;
7356     }
7357     case HalfSizeCommand:
7358     {
7359       /*
7360         Half image size.
7361       */
7362       windows->image.window_changes.width=windows->image.ximage->width/2;
7363       windows->image.window_changes.height=windows->image.ximage->height/2;
7364       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7365       break;
7366     }
7367     case OriginalSizeCommand:
7368     {
7369       /*
7370         Original image size.
7371       */
7372       windows->image.window_changes.width=(int) (*image)->columns;
7373       windows->image.window_changes.height=(int) (*image)->rows;
7374       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7375       break;
7376     }
7377     case DoubleSizeCommand:
7378     {
7379       /*
7380         Double the image size.
7381       */
7382       windows->image.window_changes.width=windows->image.ximage->width << 1;
7383       windows->image.window_changes.height=windows->image.ximage->height << 1;
7384       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7385       break;
7386     }
7387     case ResizeCommand:
7388     {
7389       int
7390         status;
7391
7392       size_t
7393         height,
7394         width;
7395
7396       ssize_t
7397         x,
7398         y;
7399
7400       /*
7401         Resize image.
7402       */
7403       width=(size_t) windows->image.ximage->width;
7404       height=(size_t) windows->image.ximage->height;
7405       x=0;
7406       y=0;
7407       (void) FormatLocaleString(geometry,MaxTextExtent,"%.20gx%.20g+0+0",
7408         (double) width,(double) height);
7409       status=XDialogWidget(display,windows,"Resize",
7410         "Enter resize geometry (e.g. 640x480, 200%):",geometry);
7411       if (*geometry == '\0')
7412         break;
7413       if (status == 0)
7414         (void) ConcatenateMagickString(geometry,"!",MaxTextExtent);
7415       (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
7416       windows->image.window_changes.width=(int) width;
7417       windows->image.window_changes.height=(int) height;
7418       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7419       break;
7420     }
7421     case ApplyCommand:
7422     {
7423       char
7424         image_geometry[MaxTextExtent];
7425
7426       if ((windows->image.crop_geometry == (char *) NULL) &&
7427           ((int) (*image)->columns == windows->image.ximage->width) &&
7428           ((int) (*image)->rows == windows->image.ximage->height))
7429         break;
7430       /*
7431         Apply size transforms to image.
7432       */
7433       XSetCursorState(display,windows,MagickTrue);
7434       XCheckRefreshWindows(display,windows);
7435       /*
7436         Crop and/or scale displayed image.
7437       */
7438       (void) FormatLocaleString(image_geometry,MaxTextExtent,"%dx%d!",
7439         windows->image.ximage->width,windows->image.ximage->height);
7440       (void) TransformImage(image,windows->image.crop_geometry,image_geometry,
7441         exception);
7442       if (windows->image.crop_geometry != (char *) NULL)
7443         windows->image.crop_geometry=(char *) RelinquishMagickMemory(
7444           windows->image.crop_geometry);
7445       windows->image.x=0;
7446       windows->image.y=0;
7447       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7448       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7449       break;
7450     }
7451     case RefreshCommand:
7452     {
7453       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7454       break;
7455     }
7456     case RestoreCommand:
7457     {
7458       /*
7459         Restore Image window to its original size.
7460       */
7461       if ((windows->image.width == (unsigned int) (*image)->columns) &&
7462           (windows->image.height == (unsigned int) (*image)->rows) &&
7463           (windows->image.crop_geometry == (char *) NULL))
7464         {
7465           (void) XBell(display,0);
7466           break;
7467         }
7468       windows->image.window_changes.width=(int) (*image)->columns;
7469       windows->image.window_changes.height=(int) (*image)->rows;
7470       if (windows->image.crop_geometry != (char *) NULL)
7471         {
7472           windows->image.crop_geometry=(char *)
7473             RelinquishMagickMemory(windows->image.crop_geometry);
7474           windows->image.crop_geometry=(char *) NULL;
7475           windows->image.x=0;
7476           windows->image.y=0;
7477         }
7478       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7479       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7480       break;
7481     }
7482     case CropCommand:
7483     {
7484       /*
7485         Crop image.
7486       */
7487       (void) XCropImage(display,resource_info,windows,*image,CropMode,
7488         exception);
7489       break;
7490     }
7491     case ChopCommand:
7492     {
7493       /*
7494         Chop image.
7495       */
7496       status=XChopImage(display,resource_info,windows,image,exception);
7497       if (IfMagickFalse(status) )
7498         {
7499           XNoticeWidget(display,windows,"Unable to cut X image",
7500             (*image)->filename);
7501           break;
7502         }
7503       break;
7504     }
7505     case FlopCommand:
7506     {
7507       Image
7508         *flop_image;
7509
7510       /*
7511         Flop image scanlines.
7512       */
7513       XSetCursorState(display,windows,MagickTrue);
7514       XCheckRefreshWindows(display,windows);
7515       flop_image=FlopImage(*image,exception);
7516       if (flop_image != (Image *) NULL)
7517         {
7518           *image=DestroyImage(*image);
7519           *image=flop_image;
7520         }
7521       CatchException(exception);
7522       XSetCursorState(display,windows,MagickFalse);
7523       if (windows->image.crop_geometry != (char *) NULL)
7524         {
7525           /*
7526             Flop crop geometry.
7527           */
7528           width=(unsigned int) (*image)->columns;
7529           height=(unsigned int) (*image)->rows;
7530           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7531             &width,&height);
7532           (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
7533             "%ux%u%+d%+d",width,height,(int) (*image)->columns-(int) width-x,y);
7534         }
7535       if (IfMagickTrue(windows->image.orphan) )
7536         break;
7537       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7538       break;
7539     }
7540     case FlipCommand:
7541     {
7542       Image
7543         *flip_image;
7544
7545       /*
7546         Flip image scanlines.
7547       */
7548       XSetCursorState(display,windows,MagickTrue);
7549       XCheckRefreshWindows(display,windows);
7550       flip_image=FlipImage(*image,exception);
7551       if (flip_image != (Image *) NULL)
7552         {
7553           *image=DestroyImage(*image);
7554           *image=flip_image;
7555         }
7556       CatchException(exception);
7557       XSetCursorState(display,windows,MagickFalse);
7558       if (windows->image.crop_geometry != (char *) NULL)
7559         {
7560           /*
7561             Flip crop geometry.
7562           */
7563           width=(unsigned int) (*image)->columns;
7564           height=(unsigned int) (*image)->rows;
7565           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7566             &width,&height);
7567           (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
7568             "%ux%u%+d%+d",width,height,x,(int) (*image)->rows-(int) height-y);
7569         }
7570       if (IfMagickTrue(windows->image.orphan) )
7571         break;
7572       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7573       break;
7574     }
7575     case RotateRightCommand:
7576     {
7577       /*
7578         Rotate image 90 degrees clockwise.
7579       */
7580       status=XRotateImage(display,resource_info,windows,90.0,image,exception);
7581       if (IfMagickFalse(status) )
7582         {
7583           XNoticeWidget(display,windows,"Unable to rotate X image",
7584             (*image)->filename);
7585           break;
7586         }
7587       break;
7588     }
7589     case RotateLeftCommand:
7590     {
7591       /*
7592         Rotate image 90 degrees counter-clockwise.
7593       */
7594       status=XRotateImage(display,resource_info,windows,-90.0,image,exception);
7595       if (IfMagickFalse(status) )
7596         {
7597           XNoticeWidget(display,windows,"Unable to rotate X image",
7598             (*image)->filename);
7599           break;
7600         }
7601       break;
7602     }
7603     case RotateCommand:
7604     {
7605       /*
7606         Rotate image.
7607       */
7608       status=XRotateImage(display,resource_info,windows,0.0,image,exception);
7609       if (IfMagickFalse(status) )
7610         {
7611           XNoticeWidget(display,windows,"Unable to rotate X image",
7612             (*image)->filename);
7613           break;
7614         }
7615       break;
7616     }
7617     case ShearCommand:
7618     {
7619       Image
7620         *shear_image;
7621
7622       static char
7623         geometry[MaxTextExtent] = "45.0x45.0";
7624
7625       /*
7626         Query user for shear color and geometry.
7627       */
7628       XColorBrowserWidget(display,windows,"Select",color);
7629       if (*color == '\0')
7630         break;
7631       (void) XDialogWidget(display,windows,"Shear","Enter shear geometry:",
7632         geometry);
7633       if (*geometry == '\0')
7634         break;
7635       /*
7636         Shear image.
7637       */
7638       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
7639         exception);
7640       XSetCursorState(display,windows,MagickTrue);
7641       XCheckRefreshWindows(display,windows);
7642       (void) QueryColorCompliance(color,AllCompliance,
7643         &(*image)->background_color,exception);
7644       flags=ParseGeometry(geometry,&geometry_info);
7645       if ((flags & SigmaValue) == 0)
7646         geometry_info.sigma=geometry_info.rho;
7647       shear_image=ShearImage(*image,geometry_info.rho,geometry_info.sigma,
7648         exception);
7649       if (shear_image != (Image *) NULL)
7650         {
7651           *image=DestroyImage(*image);
7652           *image=shear_image;
7653         }
7654       CatchException(exception);
7655       XSetCursorState(display,windows,MagickFalse);
7656       if (IfMagickTrue(windows->image.orphan) )
7657         break;
7658       windows->image.window_changes.width=(int) (*image)->columns;
7659       windows->image.window_changes.height=(int) (*image)->rows;
7660       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7661       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7662       break;
7663     }
7664     case RollCommand:
7665     {
7666       Image
7667         *roll_image;
7668
7669       static char
7670         geometry[MaxTextExtent] = "+2+2";
7671
7672       /*
7673         Query user for the roll geometry.
7674       */
7675       (void) XDialogWidget(display,windows,"Roll","Enter roll geometry:",
7676         geometry);
7677       if (*geometry == '\0')
7678         break;
7679       /*
7680         Roll image.
7681       */
7682       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
7683         exception);
7684       XSetCursorState(display,windows,MagickTrue);
7685       XCheckRefreshWindows(display,windows);
7686       (void) ParsePageGeometry(*image,geometry,&page_geometry,
7687         exception);
7688       roll_image=RollImage(*image,page_geometry.x,page_geometry.y,
7689         exception);
7690       if (roll_image != (Image *) NULL)
7691         {
7692           *image=DestroyImage(*image);
7693           *image=roll_image;
7694         }
7695       CatchException(exception);
7696       XSetCursorState(display,windows,MagickFalse);
7697       if (IfMagickTrue(windows->image.orphan) )
7698         break;
7699       windows->image.window_changes.width=(int) (*image)->columns;
7700       windows->image.window_changes.height=(int) (*image)->rows;
7701       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7702       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7703       break;
7704     }
7705     case TrimCommand:
7706     {
7707       static char
7708         fuzz[MaxTextExtent];
7709
7710       /*
7711         Query user for the fuzz factor.
7712       */
7713       (void) FormatLocaleString(fuzz,MaxTextExtent,"%g%%",100.0*
7714         (*image)->fuzz/(QuantumRange+1.0));
7715       (void) XDialogWidget(display,windows,"Trim","Enter fuzz factor:",fuzz);
7716       if (*fuzz == '\0')
7717         break;
7718       (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+1.0);
7719       /*
7720         Trim image.
7721       */
7722       status=XTrimImage(display,resource_info,windows,*image,exception);
7723       if (IfMagickFalse(status) )
7724         {
7725           XNoticeWidget(display,windows,"Unable to trim X image",
7726             (*image)->filename);
7727           break;
7728         }
7729       break;
7730     }
7731     case HueCommand:
7732     {
7733       static char
7734         hue_percent[MaxTextExtent] = "110";
7735
7736       /*
7737         Query user for percent hue change.
7738       */
7739       (void) XDialogWidget(display,windows,"Apply",
7740         "Enter percent change in image hue (0-200):",hue_percent);
7741       if (*hue_percent == '\0')
7742         break;
7743       /*
7744         Vary the image hue.
7745       */
7746       XSetCursorState(display,windows,MagickTrue);
7747       XCheckRefreshWindows(display,windows);
7748       (void) CopyMagickString(modulate_factors,"100.0/100.0/",MaxTextExtent);
7749       (void) ConcatenateMagickString(modulate_factors,hue_percent,
7750         MaxTextExtent);
7751       (void) ModulateImage(*image,modulate_factors,exception);
7752       XSetCursorState(display,windows,MagickFalse);
7753       if (IfMagickTrue(windows->image.orphan) )
7754         break;
7755       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7756       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7757       break;
7758     }
7759     case SaturationCommand:
7760     {
7761       static char
7762         saturation_percent[MaxTextExtent] = "110";
7763
7764       /*
7765         Query user for percent saturation change.
7766       */
7767       (void) XDialogWidget(display,windows,"Apply",
7768         "Enter percent change in color saturation (0-200):",saturation_percent);
7769       if (*saturation_percent == '\0')
7770         break;
7771       /*
7772         Vary color saturation.
7773       */
7774       XSetCursorState(display,windows,MagickTrue);
7775       XCheckRefreshWindows(display,windows);
7776       (void) CopyMagickString(modulate_factors,"100.0/",MaxTextExtent);
7777       (void) ConcatenateMagickString(modulate_factors,saturation_percent,
7778         MaxTextExtent);
7779       (void) ModulateImage(*image,modulate_factors,exception);
7780       XSetCursorState(display,windows,MagickFalse);
7781       if (IfMagickTrue(windows->image.orphan) )
7782         break;
7783       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7784       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7785       break;
7786     }
7787     case BrightnessCommand:
7788     {
7789       static char
7790         brightness_percent[MaxTextExtent] = "110";
7791
7792       /*
7793         Query user for percent brightness change.
7794       */
7795       (void) XDialogWidget(display,windows,"Apply",
7796         "Enter percent change in color brightness (0-200):",brightness_percent);
7797       if (*brightness_percent == '\0')
7798         break;
7799       /*
7800         Vary the color brightness.
7801       */
7802       XSetCursorState(display,windows,MagickTrue);
7803       XCheckRefreshWindows(display,windows);
7804       (void) CopyMagickString(modulate_factors,brightness_percent,
7805         MaxTextExtent);
7806       (void) ModulateImage(*image,modulate_factors,exception);
7807       XSetCursorState(display,windows,MagickFalse);
7808       if (IfMagickTrue(windows->image.orphan) )
7809         break;
7810       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7811       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7812       break;
7813     }
7814     case GammaCommand:
7815     {
7816       static char
7817         factor[MaxTextExtent] = "1.6";
7818
7819       /*
7820         Query user for gamma value.
7821       */
7822       (void) XDialogWidget(display,windows,"Gamma",
7823         "Enter gamma value (e.g. 1.2):",factor);
7824       if (*factor == '\0')
7825         break;
7826       /*
7827         Gamma correct image.
7828       */
7829       XSetCursorState(display,windows,MagickTrue);
7830       XCheckRefreshWindows(display,windows);
7831       (void) GammaImage(*image,atof(factor),exception);
7832       XSetCursorState(display,windows,MagickFalse);
7833       if (IfMagickTrue(windows->image.orphan) )
7834         break;
7835       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7836       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7837       break;
7838     }
7839     case SpiffCommand:
7840     {
7841       /*
7842         Sharpen the image contrast.
7843       */
7844       XSetCursorState(display,windows,MagickTrue);
7845       XCheckRefreshWindows(display,windows);
7846       (void) ContrastImage(*image,MagickTrue,exception);
7847       XSetCursorState(display,windows,MagickFalse);
7848       if (IfMagickTrue(windows->image.orphan) )
7849         break;
7850       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7851       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7852       break;
7853     }
7854     case DullCommand:
7855     {
7856       /*
7857         Dull the image contrast.
7858       */
7859       XSetCursorState(display,windows,MagickTrue);
7860       XCheckRefreshWindows(display,windows);
7861       (void) ContrastImage(*image,MagickFalse,exception);
7862       XSetCursorState(display,windows,MagickFalse);
7863       if (IfMagickTrue(windows->image.orphan) )
7864         break;
7865       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7866       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7867       break;
7868     }
7869     case ContrastStretchCommand:
7870     {
7871       double
7872         black_point,
7873         white_point;
7874
7875       static char
7876         levels[MaxTextExtent] = "1%";
7877
7878       /*
7879         Query user for gamma value.
7880       */
7881       (void) XDialogWidget(display,windows,"Contrast Stretch",
7882         "Enter black and white points:",levels);
7883       if (*levels == '\0')
7884         break;
7885       /*
7886         Contrast stretch image.
7887       */
7888       XSetCursorState(display,windows,MagickTrue);
7889       XCheckRefreshWindows(display,windows);
7890       flags=ParseGeometry(levels,&geometry_info);
7891       black_point=geometry_info.rho;
7892       white_point=(flags & SigmaValue) != 0 ? geometry_info.sigma : black_point;
7893       if ((flags & PercentValue) != 0)
7894         {
7895           black_point*=(double) (*image)->columns*(*image)->rows/100.0;
7896           white_point*=(double) (*image)->columns*(*image)->rows/100.0;
7897         }
7898       white_point=(double) (*image)->columns*(*image)->rows-white_point;
7899       (void) ContrastStretchImage(*image,black_point,white_point,
7900         exception);
7901       XSetCursorState(display,windows,MagickFalse);
7902       if (IfMagickTrue(windows->image.orphan) )
7903         break;
7904       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7905       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7906       break;
7907     }
7908     case SigmoidalContrastCommand:
7909     {
7910       GeometryInfo
7911         geometry_info;
7912
7913       MagickStatusType
7914         flags;
7915
7916       static char
7917         levels[MaxTextExtent] = "3x50%";
7918
7919       /*
7920         Query user for gamma value.
7921       */
7922       (void) XDialogWidget(display,windows,"Sigmoidal Contrast",
7923         "Enter contrast and midpoint:",levels);
7924       if (*levels == '\0')
7925         break;
7926       /*
7927         Contrast stretch image.
7928       */
7929       XSetCursorState(display,windows,MagickTrue);
7930       XCheckRefreshWindows(display,windows);
7931       flags=ParseGeometry(levels,&geometry_info);
7932       if ((flags & SigmaValue) == 0)
7933         geometry_info.sigma=1.0*QuantumRange/2.0;
7934       if ((flags & PercentValue) != 0)
7935         geometry_info.sigma=1.0*QuantumRange*geometry_info.sigma/100.0;
7936       (void) SigmoidalContrastImage(*image,MagickTrue,geometry_info.rho,
7937         geometry_info.sigma,exception);
7938       XSetCursorState(display,windows,MagickFalse);
7939       if (IfMagickTrue(windows->image.orphan) )
7940         break;
7941       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7942       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7943       break;
7944     }
7945     case NormalizeCommand:
7946     {
7947       /*
7948         Perform histogram normalization on the image.
7949       */
7950       XSetCursorState(display,windows,MagickTrue);
7951       XCheckRefreshWindows(display,windows);
7952       (void) NormalizeImage(*image,exception);
7953       XSetCursorState(display,windows,MagickFalse);
7954       if (IfMagickTrue(windows->image.orphan) )
7955         break;
7956       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7957       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7958       break;
7959     }
7960     case EqualizeCommand:
7961     {
7962       /*
7963         Perform histogram equalization on the image.
7964       */
7965       XSetCursorState(display,windows,MagickTrue);
7966       XCheckRefreshWindows(display,windows);
7967       (void) EqualizeImage(*image,exception);
7968       XSetCursorState(display,windows,MagickFalse);
7969       if (IfMagickTrue(windows->image.orphan) )
7970         break;
7971       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7972       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7973       break;
7974     }
7975     case NegateCommand:
7976     {
7977       /*
7978         Negate colors in image.
7979       */
7980       XSetCursorState(display,windows,MagickTrue);
7981       XCheckRefreshWindows(display,windows);
7982       (void) NegateImage(*image,MagickFalse,exception);
7983       XSetCursorState(display,windows,MagickFalse);
7984       if (IfMagickTrue(windows->image.orphan) )
7985         break;
7986       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7987       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7988       break;
7989     }
7990     case GrayscaleCommand:
7991     {
7992       /*
7993         Convert image to grayscale.
7994       */
7995       XSetCursorState(display,windows,MagickTrue);
7996       XCheckRefreshWindows(display,windows);
7997       (void) SetImageType(*image,(*image)->alpha_trait == UndefinedPixelTrait ?
7998         GrayscaleType : GrayscaleMatteType,exception);
7999       XSetCursorState(display,windows,MagickFalse);
8000       if (IfMagickTrue(windows->image.orphan) )
8001         break;
8002       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8003       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8004       break;
8005     }
8006     case MapCommand:
8007     {
8008       Image
8009         *affinity_image;
8010
8011       static char
8012         filename[MaxTextExtent] = "\0";
8013
8014       /*
8015         Request image file name from user.
8016       */
8017       XFileBrowserWidget(display,windows,"Map",filename);
8018       if (*filename == '\0')
8019         break;
8020       /*
8021         Map image.
8022       */
8023       XSetCursorState(display,windows,MagickTrue);
8024       XCheckRefreshWindows(display,windows);
8025       (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
8026       affinity_image=ReadImage(image_info,exception);
8027       if (affinity_image != (Image *) NULL)
8028         {
8029           (void) RemapImage(&quantize_info,*image,affinity_image,exception);
8030           affinity_image=DestroyImage(affinity_image);
8031         }
8032       CatchException(exception);
8033       XSetCursorState(display,windows,MagickFalse);
8034       if (IfMagickTrue(windows->image.orphan) )
8035         break;
8036       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8037       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8038       break;
8039     }
8040     case QuantizeCommand:
8041     {
8042       int
8043         status;
8044
8045       static char
8046         colors[MaxTextExtent] = "256";
8047
8048       /*
8049         Query user for maximum number of colors.
8050       */
8051       status=XDialogWidget(display,windows,"Quantize",
8052         "Maximum number of colors:",colors);
8053       if (*colors == '\0')
8054         break;
8055       /*
8056         Color reduce the image.
8057       */
8058       XSetCursorState(display,windows,MagickTrue);
8059       XCheckRefreshWindows(display,windows);
8060       quantize_info.number_colors=StringToUnsignedLong(colors);
8061       quantize_info.dither_method=status != 0 ? RiemersmaDitherMethod :
8062         NoDitherMethod;
8063       (void) QuantizeImage(&quantize_info,*image,exception);
8064       XSetCursorState(display,windows,MagickFalse);
8065       if (IfMagickTrue(windows->image.orphan) )
8066         break;
8067       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8068       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8069       break;
8070     }
8071     case DespeckleCommand:
8072     {
8073       Image
8074         *despeckle_image;
8075
8076       /*
8077         Despeckle image.
8078       */
8079       XSetCursorState(display,windows,MagickTrue);
8080       XCheckRefreshWindows(display,windows);
8081       despeckle_image=DespeckleImage(*image,exception);
8082       if (despeckle_image != (Image *) NULL)
8083         {
8084           *image=DestroyImage(*image);
8085           *image=despeckle_image;
8086         }
8087       CatchException(exception);
8088       XSetCursorState(display,windows,MagickFalse);
8089       if (IfMagickTrue(windows->image.orphan) )
8090         break;
8091       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8092       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8093       break;
8094     }
8095     case EmbossCommand:
8096     {
8097       Image
8098         *emboss_image;
8099
8100       static char
8101         radius[MaxTextExtent] = "0.0x1.0";
8102
8103       /*
8104         Query user for emboss radius.
8105       */
8106       (void) XDialogWidget(display,windows,"Emboss",
8107         "Enter the emboss radius and standard deviation:",radius);
8108       if (*radius == '\0')
8109         break;
8110       /*
8111         Reduce noise in the image.
8112       */
8113       XSetCursorState(display,windows,MagickTrue);
8114       XCheckRefreshWindows(display,windows);
8115       flags=ParseGeometry(radius,&geometry_info);
8116       if ((flags & SigmaValue) == 0)
8117         geometry_info.sigma=1.0;
8118       emboss_image=EmbossImage(*image,geometry_info.rho,geometry_info.sigma,
8119         exception);
8120       if (emboss_image != (Image *) NULL)
8121         {
8122           *image=DestroyImage(*image);
8123           *image=emboss_image;
8124         }
8125       CatchException(exception);
8126       XSetCursorState(display,windows,MagickFalse);
8127       if (IfMagickTrue(windows->image.orphan) )
8128         break;
8129       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8130       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8131       break;
8132     }
8133     case ReduceNoiseCommand:
8134     {
8135       Image
8136         *noise_image;
8137
8138       static char
8139         radius[MaxTextExtent] = "0";
8140
8141       /*
8142         Query user for noise radius.
8143       */
8144       (void) XDialogWidget(display,windows,"Reduce Noise",
8145         "Enter the noise radius:",radius);
8146       if (*radius == '\0')
8147         break;
8148       /*
8149         Reduce noise in the image.
8150       */
8151       XSetCursorState(display,windows,MagickTrue);
8152       XCheckRefreshWindows(display,windows);
8153       flags=ParseGeometry(radius,&geometry_info);
8154       noise_image=StatisticImage(*image,NonpeakStatistic,(size_t)
8155         geometry_info.rho,(size_t) geometry_info.rho,exception);
8156       if (noise_image != (Image *) NULL)
8157         {
8158           *image=DestroyImage(*image);
8159           *image=noise_image;
8160         }
8161       CatchException(exception);
8162       XSetCursorState(display,windows,MagickFalse);
8163       if (IfMagickTrue(windows->image.orphan) )
8164         break;
8165       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8166       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8167       break;
8168     }
8169     case AddNoiseCommand:
8170     {
8171       char
8172         **noises;
8173
8174       Image
8175         *noise_image;
8176
8177       static char
8178         noise_type[MaxTextExtent] = "Gaussian";
8179
8180       /*
8181         Add noise to the image.
8182       */
8183       noises=GetCommandOptions(MagickNoiseOptions);
8184       if (noises == (char **) NULL)
8185         break;
8186       XListBrowserWidget(display,windows,&windows->widget,
8187         (const char **) noises,"Add Noise",
8188         "Select a type of noise to add to your image:",noise_type);
8189       noises=DestroyStringList(noises);
8190       if (*noise_type == '\0')
8191         break;
8192       XSetCursorState(display,windows,MagickTrue);
8193       XCheckRefreshWindows(display,windows);
8194       noise_image=AddNoiseImage(*image,(NoiseType) ParseCommandOption(
8195         MagickNoiseOptions,MagickFalse,noise_type),1.0,exception);
8196       if (noise_image != (Image *) NULL)
8197         {
8198           *image=DestroyImage(*image);
8199           *image=noise_image;
8200         }
8201       CatchException(exception);
8202       XSetCursorState(display,windows,MagickFalse);
8203       if (IfMagickTrue(windows->image.orphan) )
8204         break;
8205       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8206       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8207       break;
8208     }
8209     case SharpenCommand:
8210     {
8211       Image
8212         *sharp_image;
8213
8214       static char
8215         radius[MaxTextExtent] = "0.0x1.0";
8216
8217       /*
8218         Query user for sharpen radius.
8219       */
8220       (void) XDialogWidget(display,windows,"Sharpen",
8221         "Enter the sharpen radius and standard deviation:",radius);
8222       if (*radius == '\0')
8223         break;
8224       /*
8225         Sharpen image scanlines.
8226       */
8227       XSetCursorState(display,windows,MagickTrue);
8228       XCheckRefreshWindows(display,windows);
8229       flags=ParseGeometry(radius,&geometry_info);
8230       sharp_image=SharpenImage(*image,geometry_info.rho,geometry_info.sigma,
8231         exception);
8232       if (sharp_image != (Image *) NULL)
8233         {
8234           *image=DestroyImage(*image);
8235           *image=sharp_image;
8236         }
8237       CatchException(exception);
8238       XSetCursorState(display,windows,MagickFalse);
8239       if (IfMagickTrue(windows->image.orphan) )
8240         break;
8241       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8242       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8243       break;
8244     }
8245     case BlurCommand:
8246     {
8247       Image
8248         *blur_image;
8249
8250       static char
8251         radius[MaxTextExtent] = "0.0x1.0";
8252
8253       /*
8254         Query user for blur radius.
8255       */
8256       (void) XDialogWidget(display,windows,"Blur",
8257         "Enter the blur radius and standard deviation:",radius);
8258       if (*radius == '\0')
8259         break;
8260       /*
8261         Blur an image.
8262       */
8263       XSetCursorState(display,windows,MagickTrue);
8264       XCheckRefreshWindows(display,windows);
8265       flags=ParseGeometry(radius,&geometry_info);
8266       blur_image=BlurImage(*image,geometry_info.rho,geometry_info.sigma,
8267         exception);
8268       if (blur_image != (Image *) NULL)
8269         {
8270           *image=DestroyImage(*image);
8271           *image=blur_image;
8272         }
8273       CatchException(exception);
8274       XSetCursorState(display,windows,MagickFalse);
8275       if (IfMagickTrue(windows->image.orphan) )
8276         break;
8277       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8278       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8279       break;
8280     }
8281     case ThresholdCommand:
8282     {
8283       double
8284         threshold;
8285
8286       static char
8287         factor[MaxTextExtent] = "128";
8288
8289       /*
8290         Query user for threshold value.
8291       */
8292       (void) XDialogWidget(display,windows,"Threshold",
8293         "Enter threshold value:",factor);
8294       if (*factor == '\0')
8295         break;
8296       /*
8297         Gamma correct image.
8298       */
8299       XSetCursorState(display,windows,MagickTrue);
8300       XCheckRefreshWindows(display,windows);
8301       threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8302       (void) BilevelImage(*image,threshold,exception);
8303       XSetCursorState(display,windows,MagickFalse);
8304       if (IfMagickTrue(windows->image.orphan) )
8305         break;
8306       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8307       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8308       break;
8309     }
8310     case EdgeDetectCommand:
8311     {
8312       Image
8313         *edge_image;
8314
8315       static char
8316         radius[MaxTextExtent] = "0";
8317
8318       /*
8319         Query user for edge factor.
8320       */
8321       (void) XDialogWidget(display,windows,"Detect Edges",
8322         "Enter the edge detect radius:",radius);
8323       if (*radius == '\0')
8324         break;
8325       /*
8326         Detect edge in image.
8327       */
8328       XSetCursorState(display,windows,MagickTrue);
8329       XCheckRefreshWindows(display,windows);
8330       flags=ParseGeometry(radius,&geometry_info);
8331       edge_image=EdgeImage(*image,geometry_info.rho,exception);
8332       if (edge_image != (Image *) NULL)
8333         {
8334           *image=DestroyImage(*image);
8335           *image=edge_image;
8336         }
8337       CatchException(exception);
8338       XSetCursorState(display,windows,MagickFalse);
8339       if (IfMagickTrue(windows->image.orphan) )
8340         break;
8341       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8342       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8343       break;
8344     }
8345     case SpreadCommand:
8346     {
8347       Image
8348         *spread_image;
8349
8350       static char
8351         amount[MaxTextExtent] = "2";
8352
8353       /*
8354         Query user for spread amount.
8355       */
8356       (void) XDialogWidget(display,windows,"Spread",
8357         "Enter the displacement amount:",amount);
8358       if (*amount == '\0')
8359         break;
8360       /*
8361         Displace image pixels by a random amount.
8362       */
8363       XSetCursorState(display,windows,MagickTrue);
8364       XCheckRefreshWindows(display,windows);
8365       flags=ParseGeometry(amount,&geometry_info);
8366       spread_image=EdgeImage(*image,geometry_info.rho,exception);
8367       if (spread_image != (Image *) NULL)
8368         {
8369           *image=DestroyImage(*image);
8370           *image=spread_image;
8371         }
8372       CatchException(exception);
8373       XSetCursorState(display,windows,MagickFalse);
8374       if (IfMagickTrue(windows->image.orphan) )
8375         break;
8376       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8377       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8378       break;
8379     }
8380     case ShadeCommand:
8381     {
8382       Image
8383         *shade_image;
8384
8385       int
8386         status;
8387
8388       static char
8389         geometry[MaxTextExtent] = "30x30";
8390
8391       /*
8392         Query user for the shade geometry.
8393       */
8394       status=XDialogWidget(display,windows,"Shade",
8395         "Enter the azimuth and elevation of the light source:",geometry);
8396       if (*geometry == '\0')
8397         break;
8398       /*
8399         Shade image pixels.
8400       */
8401       XSetCursorState(display,windows,MagickTrue);
8402       XCheckRefreshWindows(display,windows);
8403       flags=ParseGeometry(geometry,&geometry_info);
8404       if ((flags & SigmaValue) == 0)
8405         geometry_info.sigma=1.0;
8406       shade_image=ShadeImage(*image,IsMagickTrue(status),
8407         geometry_info.rho,geometry_info.sigma,exception);
8408       if (shade_image != (Image *) NULL)
8409         {
8410           *image=DestroyImage(*image);
8411           *image=shade_image;
8412         }
8413       CatchException(exception);
8414       XSetCursorState(display,windows,MagickFalse);
8415       if (IfMagickTrue(windows->image.orphan) )
8416         break;
8417       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8418       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8419       break;
8420     }
8421     case RaiseCommand:
8422     {
8423       static char
8424         bevel_width[MaxTextExtent] = "10";
8425
8426       /*
8427         Query user for bevel width.
8428       */
8429       (void) XDialogWidget(display,windows,"Raise","Bevel width:",bevel_width);
8430       if (*bevel_width == '\0')
8431         break;
8432       /*
8433         Raise an image.
8434       */
8435       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8436         exception);
8437       XSetCursorState(display,windows,MagickTrue);
8438       XCheckRefreshWindows(display,windows);
8439       (void) ParsePageGeometry(*image,bevel_width,&page_geometry,
8440         exception);
8441       (void) RaiseImage(*image,&page_geometry,MagickTrue,exception);
8442       XSetCursorState(display,windows,MagickFalse);
8443       if (IfMagickTrue(windows->image.orphan) )
8444         break;
8445       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8446       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8447       break;
8448     }
8449     case SegmentCommand:
8450     {
8451       static char
8452         threshold[MaxTextExtent] = "1.0x1.5";
8453
8454       /*
8455         Query user for smoothing threshold.
8456       */
8457       (void) XDialogWidget(display,windows,"Segment","Smooth threshold:",
8458         threshold);
8459       if (*threshold == '\0')
8460         break;
8461       /*
8462         Segment an image.
8463       */
8464       XSetCursorState(display,windows,MagickTrue);
8465       XCheckRefreshWindows(display,windows);
8466       flags=ParseGeometry(threshold,&geometry_info);
8467       if ((flags & SigmaValue) == 0)
8468         geometry_info.sigma=1.0;
8469       (void) SegmentImage(*image,sRGBColorspace,MagickFalse,geometry_info.rho,
8470         geometry_info.sigma,exception);
8471       XSetCursorState(display,windows,MagickFalse);
8472       if (IfMagickTrue(windows->image.orphan) )
8473         break;
8474       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8475       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8476       break;
8477     }
8478     case SepiaToneCommand:
8479     {
8480       double
8481         threshold;
8482
8483       Image
8484         *sepia_image;
8485
8486       static char
8487         factor[MaxTextExtent] = "80%";
8488
8489       /*
8490         Query user for sepia-tone factor.
8491       */
8492       (void) XDialogWidget(display,windows,"Sepia Tone",
8493         "Enter the sepia tone factor (0 - 99.9%):",factor);
8494       if (*factor == '\0')
8495         break;
8496       /*
8497         Sepia tone image pixels.
8498       */
8499       XSetCursorState(display,windows,MagickTrue);
8500       XCheckRefreshWindows(display,windows);
8501       threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8502       sepia_image=SepiaToneImage(*image,threshold,exception);
8503       if (sepia_image != (Image *) NULL)
8504         {
8505           *image=DestroyImage(*image);
8506           *image=sepia_image;
8507         }
8508       CatchException(exception);
8509       XSetCursorState(display,windows,MagickFalse);
8510       if (IfMagickTrue(windows->image.orphan) )
8511         break;
8512       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8513       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8514       break;
8515     }
8516     case SolarizeCommand:
8517     {
8518       double
8519         threshold;
8520
8521       static char
8522         factor[MaxTextExtent] = "60%";
8523
8524       /*
8525         Query user for solarize factor.
8526       */
8527       (void) XDialogWidget(display,windows,"Solarize",
8528         "Enter the solarize factor (0 - 99.9%):",factor);
8529       if (*factor == '\0')
8530         break;
8531       /*
8532         Solarize image pixels.
8533       */
8534       XSetCursorState(display,windows,MagickTrue);
8535       XCheckRefreshWindows(display,windows);
8536       threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8537       (void) SolarizeImage(*image,threshold,exception);
8538       XSetCursorState(display,windows,MagickFalse);
8539       if (IfMagickTrue(windows->image.orphan) )
8540         break;
8541       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8542       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8543       break;
8544     }
8545     case SwirlCommand:
8546     {
8547       Image
8548         *swirl_image;
8549
8550       static char
8551         degrees[MaxTextExtent] = "60";
8552
8553       /*
8554         Query user for swirl angle.
8555       */
8556       (void) XDialogWidget(display,windows,"Swirl","Enter the swirl angle:",
8557         degrees);
8558       if (*degrees == '\0')
8559         break;
8560       /*
8561         Swirl image pixels about the center.
8562       */
8563       XSetCursorState(display,windows,MagickTrue);
8564       XCheckRefreshWindows(display,windows);
8565       flags=ParseGeometry(degrees,&geometry_info);
8566       swirl_image=SwirlImage(*image,geometry_info.rho,(*image)->interpolate,
8567         exception);
8568       if (swirl_image != (Image *) NULL)
8569         {
8570           *image=DestroyImage(*image);
8571           *image=swirl_image;
8572         }
8573       CatchException(exception);
8574       XSetCursorState(display,windows,MagickFalse);
8575       if (IfMagickTrue(windows->image.orphan) )
8576         break;
8577       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8578       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8579       break;
8580     }
8581     case ImplodeCommand:
8582     {
8583       Image
8584         *implode_image;
8585
8586       static char
8587         factor[MaxTextExtent] = "0.3";
8588
8589       /*
8590         Query user for implode factor.
8591       */
8592       (void) XDialogWidget(display,windows,"Implode",
8593         "Enter the implosion/explosion factor (-1.0 - 1.0):",factor);
8594       if (*factor == '\0')
8595         break;
8596       /*
8597         Implode image pixels about the center.
8598       */
8599       XSetCursorState(display,windows,MagickTrue);
8600       XCheckRefreshWindows(display,windows);
8601       flags=ParseGeometry(factor,&geometry_info);
8602       implode_image=ImplodeImage(*image,geometry_info.rho,(*image)->interpolate,
8603         exception);
8604       if (implode_image != (Image *) NULL)
8605         {
8606           *image=DestroyImage(*image);
8607           *image=implode_image;
8608         }
8609       CatchException(exception);
8610       XSetCursorState(display,windows,MagickFalse);
8611       if (IfMagickTrue(windows->image.orphan) )
8612         break;
8613       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8614       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8615       break;
8616     }
8617     case VignetteCommand:
8618     {
8619       Image
8620         *vignette_image;
8621
8622       static char
8623         geometry[MaxTextExtent] = "0x20";
8624
8625       /*
8626         Query user for the vignette geometry.
8627       */
8628       (void) XDialogWidget(display,windows,"Vignette",
8629         "Enter the radius, sigma, and x and y offsets:",geometry);
8630       if (*geometry == '\0')
8631         break;
8632       /*
8633         Soften the edges of the image in vignette style
8634       */
8635       XSetCursorState(display,windows,MagickTrue);
8636       XCheckRefreshWindows(display,windows);
8637       flags=ParseGeometry(geometry,&geometry_info);
8638       if ((flags & SigmaValue) == 0)
8639         geometry_info.sigma=1.0;
8640       if ((flags & XiValue) == 0)
8641         geometry_info.xi=0.1*(*image)->columns;
8642       if ((flags & PsiValue) == 0)
8643         geometry_info.psi=0.1*(*image)->rows;
8644       vignette_image=VignetteImage(*image,geometry_info.rho,0.0,(ssize_t)
8645         ceil(geometry_info.xi-0.5),(ssize_t) ceil(geometry_info.psi-0.5),
8646         exception);
8647       if (vignette_image != (Image *) NULL)
8648         {
8649           *image=DestroyImage(*image);
8650           *image=vignette_image;
8651         }
8652       CatchException(exception);
8653       XSetCursorState(display,windows,MagickFalse);
8654       if (IfMagickTrue(windows->image.orphan) )
8655         break;
8656       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8657       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8658       break;
8659     }
8660     case WaveCommand:
8661     {
8662       Image
8663         *wave_image;
8664
8665       static char
8666         geometry[MaxTextExtent] = "25x150";
8667
8668       /*
8669         Query user for the wave geometry.
8670       */
8671       (void) XDialogWidget(display,windows,"Wave",
8672         "Enter the amplitude and length of the wave:",geometry);
8673       if (*geometry == '\0')
8674         break;
8675       /*
8676         Alter an image along a sine wave.
8677       */
8678       XSetCursorState(display,windows,MagickTrue);
8679       XCheckRefreshWindows(display,windows);
8680       flags=ParseGeometry(geometry,&geometry_info);
8681       if ((flags & SigmaValue) == 0)
8682         geometry_info.sigma=1.0;
8683       wave_image=WaveImage(*image,geometry_info.rho,geometry_info.sigma,
8684         (*image)->interpolate,exception);
8685       if (wave_image != (Image *) NULL)
8686         {
8687           *image=DestroyImage(*image);
8688           *image=wave_image;
8689         }
8690       CatchException(exception);
8691       XSetCursorState(display,windows,MagickFalse);
8692       if (IfMagickTrue(windows->image.orphan) )
8693         break;
8694       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8695       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8696       break;
8697     }
8698     case OilPaintCommand:
8699     {
8700       Image
8701         *paint_image;
8702
8703       static char
8704         radius[MaxTextExtent] = "0";
8705
8706       /*
8707         Query user for circular neighborhood radius.
8708       */
8709       (void) XDialogWidget(display,windows,"Oil Paint",
8710         "Enter the mask radius:",radius);
8711       if (*radius == '\0')
8712         break;
8713       /*
8714         OilPaint image scanlines.
8715       */
8716       XSetCursorState(display,windows,MagickTrue);
8717       XCheckRefreshWindows(display,windows);
8718       flags=ParseGeometry(radius,&geometry_info);
8719       paint_image=OilPaintImage(*image,geometry_info.rho,geometry_info.sigma,
8720         exception);
8721       if (paint_image != (Image *) NULL)
8722         {
8723           *image=DestroyImage(*image);
8724           *image=paint_image;
8725         }
8726       CatchException(exception);
8727       XSetCursorState(display,windows,MagickFalse);
8728       if (IfMagickTrue(windows->image.orphan) )
8729         break;
8730       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8731       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8732       break;
8733     }
8734     case CharcoalDrawCommand:
8735     {
8736       Image
8737         *charcoal_image;
8738
8739       static char
8740         radius[MaxTextExtent] = "0x1";
8741
8742       /*
8743         Query user for charcoal radius.
8744       */
8745       (void) XDialogWidget(display,windows,"Charcoal Draw",
8746         "Enter the charcoal radius and sigma:",radius);
8747       if (*radius == '\0')
8748         break;
8749       /*
8750         Charcoal the image.
8751       */
8752       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8753         exception);
8754       XSetCursorState(display,windows,MagickTrue);
8755       XCheckRefreshWindows(display,windows);
8756       flags=ParseGeometry(radius,&geometry_info);
8757       if ((flags & SigmaValue) == 0)
8758         geometry_info.sigma=geometry_info.rho;
8759       charcoal_image=CharcoalImage(*image,geometry_info.rho,geometry_info.sigma,
8760         exception);
8761       if (charcoal_image != (Image *) NULL)
8762         {
8763           *image=DestroyImage(*image);
8764           *image=charcoal_image;
8765         }
8766       CatchException(exception);
8767       XSetCursorState(display,windows,MagickFalse);
8768       if (IfMagickTrue(windows->image.orphan) )
8769         break;
8770       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8771       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8772       break;
8773     }
8774     case AnnotateCommand:
8775     {
8776       /*
8777         Annotate the image with text.
8778       */
8779       status=XAnnotateEditImage(display,resource_info,windows,*image,exception);
8780       if (IfMagickFalse(status) )
8781         {
8782           XNoticeWidget(display,windows,"Unable to annotate X image",
8783             (*image)->filename);
8784           break;
8785         }
8786       break;
8787     }
8788     case DrawCommand:
8789     {
8790       /*
8791         Draw image.
8792       */
8793       status=XDrawEditImage(display,resource_info,windows,image,exception);
8794       if (IfMagickFalse(status) )
8795         {
8796           XNoticeWidget(display,windows,"Unable to draw on the X image",
8797             (*image)->filename);
8798           break;
8799         }
8800       break;
8801     }
8802     case ColorCommand:
8803     {
8804       /*
8805         Color edit.
8806       */
8807       status=XColorEditImage(display,resource_info,windows,image,exception);
8808       if (IfMagickFalse(status) )
8809         {
8810           XNoticeWidget(display,windows,"Unable to pixel edit X image",
8811             (*image)->filename);
8812           break;
8813         }
8814       break;
8815     }
8816     case MatteCommand:
8817     {
8818       /*
8819         Matte edit.
8820       */
8821       status=XMatteEditImage(display,resource_info,windows,image,exception);
8822       if (IfMagickFalse(status) )
8823         {
8824           XNoticeWidget(display,windows,"Unable to matte edit X image",
8825             (*image)->filename);
8826           break;
8827         }
8828       break;
8829     }
8830     case CompositeCommand:
8831     {
8832       /*
8833         Composite image.
8834       */
8835       status=XCompositeImage(display,resource_info,windows,*image,
8836         exception);
8837       if (IfMagickFalse(status) )
8838         {
8839           XNoticeWidget(display,windows,"Unable to composite X image",
8840             (*image)->filename);
8841           break;
8842         }
8843       break;
8844     }
8845     case AddBorderCommand:
8846     {
8847       Image
8848         *border_image;
8849
8850       static char
8851         geometry[MaxTextExtent] = "6x6";
8852
8853       /*
8854         Query user for border color and geometry.
8855       */
8856       XColorBrowserWidget(display,windows,"Select",color);
8857       if (*color == '\0')
8858         break;
8859       (void) XDialogWidget(display,windows,"Add Border",
8860         "Enter border geometry:",geometry);
8861       if (*geometry == '\0')
8862         break;
8863       /*
8864         Add a border to the image.
8865       */
8866       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8867         exception);
8868       XSetCursorState(display,windows,MagickTrue);
8869       XCheckRefreshWindows(display,windows);
8870       (void) QueryColorCompliance(color,AllCompliance,&(*image)->border_color,
8871         exception);
8872       (void) ParsePageGeometry(*image,geometry,&page_geometry,
8873         exception);
8874       border_image=BorderImage(*image,&page_geometry,(*image)->compose,
8875         exception);
8876       if (border_image != (Image *) NULL)
8877         {
8878           *image=DestroyImage(*image);
8879           *image=border_image;
8880         }
8881       CatchException(exception);
8882       XSetCursorState(display,windows,MagickFalse);
8883       if (IfMagickTrue(windows->image.orphan) )
8884         break;
8885       windows->image.window_changes.width=(int) (*image)->columns;
8886       windows->image.window_changes.height=(int) (*image)->rows;
8887       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8888       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8889       break;
8890     }
8891     case AddFrameCommand:
8892     {
8893       FrameInfo
8894         frame_info;
8895
8896       Image
8897         *frame_image;
8898
8899       static char
8900         geometry[MaxTextExtent] = "6x6";
8901
8902       /*
8903         Query user for frame color and geometry.
8904       */
8905       XColorBrowserWidget(display,windows,"Select",color);
8906       if (*color == '\0')
8907         break;
8908       (void) XDialogWidget(display,windows,"Add Frame","Enter frame geometry:",
8909         geometry);
8910       if (*geometry == '\0')
8911         break;
8912       /*
8913         Surround image with an ornamental border.
8914       */
8915       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8916         exception);
8917       XSetCursorState(display,windows,MagickTrue);
8918       XCheckRefreshWindows(display,windows);
8919       (void) QueryColorCompliance(color,AllCompliance,&(*image)->matte_color,
8920         exception);
8921       (void) ParsePageGeometry(*image,geometry,&page_geometry,
8922         exception);
8923       frame_info.width=page_geometry.width;
8924       frame_info.height=page_geometry.height;
8925       frame_info.outer_bevel=page_geometry.x;
8926       frame_info.inner_bevel=page_geometry.y;
8927       frame_info.x=(ssize_t) frame_info.width;
8928       frame_info.y=(ssize_t) frame_info.height;
8929       frame_info.width=(*image)->columns+2*frame_info.width;
8930       frame_info.height=(*image)->rows+2*frame_info.height;
8931       frame_image=FrameImage(*image,&frame_info,(*image)->compose,exception);
8932       if (frame_image != (Image *) NULL)
8933         {
8934           *image=DestroyImage(*image);
8935           *image=frame_image;
8936         }
8937       CatchException(exception);
8938       XSetCursorState(display,windows,MagickFalse);
8939       if (IfMagickTrue(windows->image.orphan) )
8940         break;
8941       windows->image.window_changes.width=(int) (*image)->columns;
8942       windows->image.window_changes.height=(int) (*image)->rows;
8943       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8944       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8945       break;
8946     }
8947     case CommentCommand:
8948     {
8949       const char
8950         *value;
8951
8952       FILE
8953         *file;
8954
8955       int
8956         unique_file;
8957
8958       /*
8959         Edit image comment.
8960       */
8961       unique_file=AcquireUniqueFileResource(image_info->filename);
8962       if (unique_file == -1)
8963         XNoticeWidget(display,windows,"Unable to edit image comment",
8964           image_info->filename);
8965       value=GetImageProperty(*image,"comment",exception);
8966       if (value == (char *) NULL)
8967         unique_file=close(unique_file)-1;
8968       else
8969         {
8970           register const char
8971             *p;
8972
8973           file=fdopen(unique_file,"w");
8974           if (file == (FILE *) NULL)
8975             {
8976               XNoticeWidget(display,windows,"Unable to edit image comment",
8977                 image_info->filename);
8978               break;
8979             }
8980           for (p=value; *p != '\0'; p++)
8981             (void) fputc((int) *p,file);
8982           (void) fputc('\n',file);
8983           (void) fclose(file);
8984         }
8985       XSetCursorState(display,windows,MagickTrue);
8986       XCheckRefreshWindows(display,windows);
8987       status=InvokeDelegate(image_info,*image,"edit",(char *) NULL,
8988         exception);
8989       if (IfMagickFalse(status) )
8990         XNoticeWidget(display,windows,"Unable to edit image comment",
8991           (char *) NULL);
8992       else
8993         {
8994           char
8995             *comment;
8996
8997           comment=FileToString(image_info->filename,~0UL,exception);
8998           if (comment != (char *) NULL)
8999             {
9000               (void) SetImageProperty(*image,"comment",comment,exception);
9001               (*image)->taint=MagickTrue;
9002             }
9003         }
9004       (void) RelinquishUniqueFileResource(image_info->filename);
9005       XSetCursorState(display,windows,MagickFalse);
9006       break;
9007     }
9008     case LaunchCommand:
9009     {
9010       /*
9011         Launch program.
9012       */
9013       XSetCursorState(display,windows,MagickTrue);
9014       XCheckRefreshWindows(display,windows);
9015       (void) AcquireUniqueFilename(filename);
9016       (void) FormatLocaleString((*image)->filename,MaxTextExtent,"launch:%s",
9017         filename);
9018       status=WriteImage(image_info,*image,exception);
9019       if (IfMagickFalse(status) )
9020         XNoticeWidget(display,windows,"Unable to launch image editor",
9021           (char *) NULL);
9022       else
9023         {
9024           nexus=ReadImage(resource_info->image_info,exception);
9025           CatchException(exception);
9026           XClientMessage(display,windows->image.id,windows->im_protocols,
9027             windows->im_next_image,CurrentTime);
9028         }
9029       (void) RelinquishUniqueFileResource(filename);
9030       XSetCursorState(display,windows,MagickFalse);
9031       break;
9032     }
9033     case RegionofInterestCommand:
9034     {
9035       /*
9036         Apply an image processing technique to a region of interest.
9037       */
9038       (void) XROIImage(display,resource_info,windows,image,exception);
9039       break;
9040     }
9041     case InfoCommand:
9042       break;
9043     case ZoomCommand:
9044     {
9045       /*
9046         Zoom image.
9047       */
9048       if (IfMagickTrue(windows->magnify.mapped) )
9049         (void) XRaiseWindow(display,windows->magnify.id);
9050       else
9051         {
9052           /*
9053             Make magnify image.
9054           */
9055           XSetCursorState(display,windows,MagickTrue);
9056           (void) XMapRaised(display,windows->magnify.id);
9057           XSetCursorState(display,windows,MagickFalse);
9058         }
9059       break;
9060     }
9061     case ShowPreviewCommand:
9062     {
9063       char
9064         **previews;
9065
9066       Image
9067         *preview_image;
9068
9069       static char
9070         preview_type[MaxTextExtent] = "Gamma";
9071
9072       /*
9073         Select preview type from menu.
9074       */
9075       previews=GetCommandOptions(MagickPreviewOptions);
9076       if (previews == (char **) NULL)
9077         break;
9078       XListBrowserWidget(display,windows,&windows->widget,
9079         (const char **) previews,"Preview",
9080         "Select an enhancement, effect, or F/X:",preview_type);
9081       previews=DestroyStringList(previews);
9082       if (*preview_type == '\0')
9083         break;
9084       /*
9085         Show image preview.
9086       */
9087       XSetCursorState(display,windows,MagickTrue);
9088       XCheckRefreshWindows(display,windows);
9089       image_info->preview_type=(PreviewType)
9090         ParseCommandOption(MagickPreviewOptions,MagickFalse,preview_type);
9091       image_info->group=(ssize_t) windows->image.id;
9092       (void) DeleteImageProperty(*image,"label");
9093       (void) SetImageProperty(*image,"label","Preview",exception);
9094       (void) AcquireUniqueFilename(filename);
9095       (void) FormatLocaleString((*image)->filename,MaxTextExtent,"preview:%s",
9096         filename);
9097       status=WriteImage(image_info,*image,exception);
9098       (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9099       preview_image=ReadImage(image_info,exception);
9100       (void) RelinquishUniqueFileResource(filename);
9101       if (preview_image == (Image *) NULL)
9102         break;
9103       (void) FormatLocaleString(preview_image->filename,MaxTextExtent,"show:%s",
9104         filename);
9105       status=WriteImage(image_info,preview_image,exception);
9106       preview_image=DestroyImage(preview_image);
9107       if (IfMagickFalse(status) )
9108         XNoticeWidget(display,windows,"Unable to show image preview",
9109           (*image)->filename);
9110       XDelay(display,1500);
9111       XSetCursorState(display,windows,MagickFalse);
9112       break;
9113     }
9114     case ShowHistogramCommand:
9115     {
9116       Image
9117         *histogram_image;
9118
9119       /*
9120         Show image histogram.
9121       */
9122       XSetCursorState(display,windows,MagickTrue);
9123       XCheckRefreshWindows(display,windows);
9124       image_info->group=(ssize_t) windows->image.id;
9125       (void) DeleteImageProperty(*image,"label");
9126       (void) SetImageProperty(*image,"label","Histogram",exception);
9127       (void) AcquireUniqueFilename(filename);
9128       (void) FormatLocaleString((*image)->filename,MaxTextExtent,"histogram:%s",
9129         filename);
9130       status=WriteImage(image_info,*image,exception);
9131       (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9132       histogram_image=ReadImage(image_info,exception);
9133       (void) RelinquishUniqueFileResource(filename);
9134       if (histogram_image == (Image *) NULL)
9135         break;
9136       (void) FormatLocaleString(histogram_image->filename,MaxTextExtent,
9137         "show:%s",filename);
9138       status=WriteImage(image_info,histogram_image,exception);
9139       histogram_image=DestroyImage(histogram_image);
9140       if (IfMagickFalse(status) )
9141         XNoticeWidget(display,windows,"Unable to show histogram",
9142           (*image)->filename);
9143       XDelay(display,1500);
9144       XSetCursorState(display,windows,MagickFalse);
9145       break;
9146     }
9147     case ShowMatteCommand:
9148     {
9149       Image
9150         *matte_image;
9151
9152       if ((*image)->alpha_trait == UndefinedPixelTrait)
9153         {
9154           XNoticeWidget(display,windows,
9155             "Image does not have any matte information",(*image)->filename);
9156           break;
9157         }
9158       /*
9159         Show image matte.
9160       */
9161       XSetCursorState(display,windows,MagickTrue);
9162       XCheckRefreshWindows(display,windows);
9163       image_info->group=(ssize_t) windows->image.id;
9164       (void) DeleteImageProperty(*image,"label");
9165       (void) SetImageProperty(*image,"label","Matte",exception);
9166       (void) AcquireUniqueFilename(filename);
9167       (void) FormatLocaleString((*image)->filename,MaxTextExtent,"matte:%s",
9168         filename);
9169       status=WriteImage(image_info,*image,exception);
9170       (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9171       matte_image=ReadImage(image_info,exception);
9172       (void) RelinquishUniqueFileResource(filename);
9173       if (matte_image == (Image *) NULL)
9174         break;
9175       (void) FormatLocaleString(matte_image->filename,MaxTextExtent,"show:%s",
9176         filename);
9177       status=WriteImage(image_info,matte_image,exception);
9178       matte_image=DestroyImage(matte_image);
9179       if (IfMagickFalse(status) )
9180         XNoticeWidget(display,windows,"Unable to show matte",
9181           (*image)->filename);
9182       XDelay(display,1500);
9183       XSetCursorState(display,windows,MagickFalse);
9184       break;
9185     }
9186     case BackgroundCommand:
9187     {
9188       /*
9189         Background image.
9190       */
9191       status=XBackgroundImage(display,resource_info,windows,image,exception);
9192       if (IfMagickFalse(status) )
9193         break;
9194       nexus=CloneImage(*image,0,0,MagickTrue,exception);
9195       if (nexus != (Image *) NULL)
9196         XClientMessage(display,windows->image.id,windows->im_protocols,
9197           windows->im_next_image,CurrentTime);
9198       break;
9199     }
9200     case SlideShowCommand:
9201     {
9202       static char
9203         delay[MaxTextExtent] = "5";
9204
9205       /*
9206         Display next image after pausing.
9207       */
9208       (void) XDialogWidget(display,windows,"Slide Show",
9209         "Pause how many 1/100ths of a second between images:",delay);
9210       if (*delay == '\0')
9211         break;
9212       resource_info->delay=StringToUnsignedLong(delay);
9213       XClientMessage(display,windows->image.id,windows->im_protocols,
9214         windows->im_next_image,CurrentTime);
9215       break;
9216     }
9217     case PreferencesCommand:
9218     {
9219       /*
9220         Set user preferences.
9221       */
9222       status=XPreferencesWidget(display,resource_info,windows);
9223       if (IfMagickFalse(status) )
9224         break;
9225       nexus=CloneImage(*image,0,0,MagickTrue,exception);
9226       if (nexus != (Image *) NULL)
9227         XClientMessage(display,windows->image.id,windows->im_protocols,
9228           windows->im_next_image,CurrentTime);
9229       break;
9230     }
9231     case HelpCommand:
9232     {
9233       /*
9234         User requested help.
9235       */
9236       XTextViewWidget(display,resource_info,windows,MagickFalse,
9237         "Help Viewer - Display",DisplayHelp);
9238       break;
9239     }
9240     case BrowseDocumentationCommand:
9241     {
9242       Atom
9243         mozilla_atom;
9244
9245       Window
9246         mozilla_window,
9247         root_window;
9248
9249       /*
9250         Browse the ImageMagick documentation.
9251       */
9252       root_window=XRootWindow(display,XDefaultScreen(display));
9253       mozilla_atom=XInternAtom(display,"_MOZILLA_VERSION",MagickFalse);
9254       mozilla_window=XWindowByProperty(display,root_window,mozilla_atom);
9255       if (mozilla_window != (Window) NULL)
9256         {
9257           char
9258             command[MaxTextExtent],
9259             *url;
9260
9261           /*
9262             Display documentation using Netscape remote control.
9263           */
9264           url=GetMagickHomeURL();
9265           (void) FormatLocaleString(command,MaxTextExtent,
9266             "openurl(%s,new-tab)",url);
9267           url=DestroyString(url);
9268           mozilla_atom=XInternAtom(display,"_MOZILLA_COMMAND",MagickFalse);
9269           (void) XChangeProperty(display,mozilla_window,mozilla_atom,XA_STRING,
9270             8,PropModeReplace,(unsigned char *) command,(int) strlen(command));
9271           XSetCursorState(display,windows,MagickFalse);
9272           break;
9273         }
9274       XSetCursorState(display,windows,MagickTrue);
9275       XCheckRefreshWindows(display,windows);
9276       status=InvokeDelegate(image_info,*image,"browse",(char *) NULL,
9277         exception);
9278       if (IfMagickFalse(status) )
9279         XNoticeWidget(display,windows,"Unable to browse documentation",
9280           (char *) NULL);
9281       XDelay(display,1500);
9282       XSetCursorState(display,windows,MagickFalse);
9283       break;
9284     }
9285     case VersionCommand:
9286     {
9287       XNoticeWidget(display,windows,GetMagickVersion((size_t *) NULL),
9288         GetMagickCopyright());
9289       break;
9290     }
9291     case SaveToUndoBufferCommand:
9292       break;
9293     default:
9294     {
9295       (void) XBell(display,0);
9296       break;
9297     }
9298   }
9299   image_info=DestroyImageInfo(image_info);
9300   return(nexus);
9301 }
9302 \f
9303 /*
9304 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9305 %                                                                             %
9306 %                                                                             %
9307 %                                                                             %
9308 +   X M a g n i f y I m a g e                                                 %
9309 %                                                                             %
9310 %                                                                             %
9311 %                                                                             %
9312 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9313 %
9314 %  XMagnifyImage() magnifies portions of the image as indicated by the pointer.
9315 %  The magnified portion is displayed in a separate window.
9316 %
9317 %  The format of the XMagnifyImage method is:
9318 %
9319 %      void XMagnifyImage(Display *display,XWindows *windows,XEvent *event,
9320 %        ExceptionInfo *exception)
9321 %
9322 %  A description of each parameter follows:
9323 %
9324 %    o display: Specifies a connection to an X server;  returned from
9325 %      XOpenDisplay.
9326 %
9327 %    o windows: Specifies a pointer to a XWindows structure.
9328 %
9329 %    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
9330 %      the entire image is refreshed.
9331 %
9332 %    o exception: return any errors or warnings in this structure.
9333 %
9334 */
9335 static void XMagnifyImage(Display *display,XWindows *windows,XEvent *event,
9336   ExceptionInfo *exception)
9337 {
9338   char
9339     text[MaxTextExtent];
9340
9341   register int
9342     x,
9343     y;
9344
9345   size_t
9346     state;
9347
9348   /*
9349     Update magnified image until the mouse button is released.
9350   */
9351   (void) XCheckDefineCursor(display,windows->image.id,windows->magnify.cursor);
9352   state=DefaultState;
9353   x=event->xbutton.x;
9354   y=event->xbutton.y;
9355   windows->magnify.x=(int) windows->image.x+x;
9356   windows->magnify.y=(int) windows->image.y+y;
9357   do
9358   {
9359     /*
9360       Map and unmap Info widget as text cursor crosses its boundaries.
9361     */
9362     if (IfMagickTrue(windows->info.mapped) )
9363       {
9364         if ((x < (int) (windows->info.x+windows->info.width)) &&
9365             (y < (int) (windows->info.y+windows->info.height)))
9366           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
9367       }
9368     else
9369       if ((x > (int) (windows->info.x+windows->info.width)) ||
9370           (y > (int) (windows->info.y+windows->info.height)))
9371         (void) XMapWindow(display,windows->info.id);
9372     if (IfMagickTrue(windows->info.mapped) )
9373       {
9374         /*
9375           Display pointer position.
9376         */
9377         (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
9378           windows->magnify.x,windows->magnify.y);
9379         XInfoWidget(display,windows,text);
9380       }
9381     /*
9382       Wait for next event.
9383     */
9384     XScreenEvent(display,windows,event,exception);
9385     switch (event->type)
9386     {
9387       case ButtonPress:
9388         break;
9389       case ButtonRelease:
9390       {
9391         /*
9392           User has finished magnifying image.
9393         */
9394         x=event->xbutton.x;
9395         y=event->xbutton.y;
9396         state|=ExitState;
9397         break;
9398       }
9399       case Expose:
9400         break;
9401       case MotionNotify:
9402       {
9403         x=event->xmotion.x;
9404         y=event->xmotion.y;
9405         break;
9406       }
9407       default:
9408         break;
9409     }
9410     /*
9411       Check boundary conditions.
9412     */
9413     if (x < 0)
9414       x=0;
9415     else
9416       if (x >= (int) windows->image.width)
9417         x=(int) windows->image.width-1;
9418     if (y < 0)
9419       y=0;
9420     else
9421      if (y >= (int) windows->image.height)
9422        y=(int) windows->image.height-1;
9423   } while ((state & ExitState) == 0);
9424   /*
9425     Display magnified image.
9426   */
9427   XSetCursorState(display,windows,MagickFalse);
9428 }
9429 \f
9430 /*
9431 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9432 %                                                                             %
9433 %                                                                             %
9434 %                                                                             %
9435 +   X M a g n i f y W i n d o w C o m m a n d                                 %
9436 %                                                                             %
9437 %                                                                             %
9438 %                                                                             %
9439 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9440 %
9441 %  XMagnifyWindowCommand() moves the image within an Magnify window by one
9442 %  pixel as specified by the key symbol.
9443 %
9444 %  The format of the XMagnifyWindowCommand method is:
9445 %
9446 %      void XMagnifyWindowCommand(Display *display,XWindows *windows,
9447 %        const MagickStatusType state,const KeySym key_symbol,
9448 %        ExceptionInfo *exception)
9449 %
9450 %  A description of each parameter follows:
9451 %
9452 %    o display: Specifies a connection to an X server; returned from
9453 %      XOpenDisplay.
9454 %
9455 %    o windows: Specifies a pointer to a XWindows structure.
9456 %
9457 %    o state: key mask.
9458 %
9459 %    o key_symbol: Specifies a KeySym which indicates which side of the image
9460 %      to trim.
9461 %
9462 %    o exception: return any errors or warnings in this structure.
9463 %
9464 */
9465 static void XMagnifyWindowCommand(Display *display,XWindows *windows,
9466   const MagickStatusType state,const KeySym key_symbol,ExceptionInfo *exception)
9467 {
9468   unsigned int
9469     quantum;
9470
9471   /*
9472     User specified a magnify factor or position.
9473   */
9474   quantum=1;
9475   if ((state & Mod1Mask) != 0)
9476     quantum=10;
9477   switch ((int) key_symbol)
9478   {
9479     case QuitCommand:
9480     {
9481       (void) XWithdrawWindow(display,windows->magnify.id,
9482         windows->magnify.screen);
9483       break;
9484     }
9485     case XK_Home:
9486     case XK_KP_Home:
9487     {
9488       windows->magnify.x=(int) windows->image.width/2;
9489       windows->magnify.y=(int) windows->image.height/2;
9490       break;
9491     }
9492     case XK_Left:
9493     case XK_KP_Left:
9494     {
9495       if (windows->magnify.x > 0)
9496         windows->magnify.x-=quantum;
9497       break;
9498     }
9499     case XK_Up:
9500     case XK_KP_Up:
9501     {
9502       if (windows->magnify.y > 0)
9503         windows->magnify.y-=quantum;
9504       break;
9505     }
9506     case XK_Right:
9507     case XK_KP_Right:
9508     {
9509       if (windows->magnify.x < (int) (windows->image.ximage->width-1))
9510         windows->magnify.x+=quantum;
9511       break;
9512     }
9513     case XK_Down:
9514     case XK_KP_Down:
9515     {
9516       if (windows->magnify.y < (int) (windows->image.ximage->height-1))
9517         windows->magnify.y+=quantum;
9518       break;
9519     }
9520     case XK_0:
9521     case XK_1:
9522     case XK_2:
9523     case XK_3:
9524     case XK_4:
9525     case XK_5:
9526     case XK_6:
9527     case XK_7:
9528     case XK_8:
9529     case XK_9:
9530     {
9531       windows->magnify.data=(key_symbol-XK_0);
9532       break;
9533     }
9534     case XK_KP_0:
9535     case XK_KP_1:
9536     case XK_KP_2:
9537     case XK_KP_3:
9538     case XK_KP_4:
9539     case XK_KP_5:
9540     case XK_KP_6:
9541     case XK_KP_7:
9542     case XK_KP_8:
9543     case XK_KP_9:
9544     {
9545       windows->magnify.data=(key_symbol-XK_KP_0);
9546       break;
9547     }
9548     default:
9549       break;
9550   }
9551   XMakeMagnifyImage(display,windows,exception);
9552 }
9553 \f
9554 /*
9555 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9556 %                                                                             %
9557 %                                                                             %
9558 %                                                                             %
9559 +   X M a k e P a n I m a g e                                                 %
9560 %                                                                             %
9561 %                                                                             %
9562 %                                                                             %
9563 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9564 %
9565 %  XMakePanImage() creates a thumbnail of the image and displays it in the Pan
9566 %  icon window.
9567 %
9568 %  The format of the XMakePanImage method is:
9569 %
9570 %        void XMakePanImage(Display *display,XResourceInfo *resource_info,
9571 %          XWindows *windows,Image *image,ExceptionInfo *exception)
9572 %
9573 %  A description of each parameter follows:
9574 %
9575 %    o display: Specifies a connection to an X server;  returned from
9576 %      XOpenDisplay.
9577 %
9578 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9579 %
9580 %    o windows: Specifies a pointer to a XWindows structure.
9581 %
9582 %    o image: the image.
9583 %
9584 %    o exception: return any errors or warnings in this structure.
9585 %
9586 */
9587 static void XMakePanImage(Display *display,XResourceInfo *resource_info,
9588   XWindows *windows,Image *image,ExceptionInfo *exception)
9589 {
9590   MagickStatusType
9591     status;
9592
9593   /*
9594     Create and display image for panning icon.
9595   */
9596   XSetCursorState(display,windows,MagickTrue);
9597   XCheckRefreshWindows(display,windows);
9598   windows->pan.x=(int) windows->image.x;
9599   windows->pan.y=(int) windows->image.y;
9600   status=XMakeImage(display,resource_info,&windows->pan,image,
9601     windows->pan.width,windows->pan.height,exception);
9602   if (IfMagickFalse(status) )
9603     ThrowXWindowException(ResourceLimitError,
9604      "MemoryAllocationFailed",image->filename);
9605   (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
9606     windows->pan.pixmap);
9607   (void) XClearWindow(display,windows->pan.id);
9608   XDrawPanRectangle(display,windows);
9609   XSetCursorState(display,windows,MagickFalse);
9610 }
9611 \f
9612 /*
9613 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9614 %                                                                             %
9615 %                                                                             %
9616 %                                                                             %
9617 +   X M a t t a E d i t I m a g e                                             %
9618 %                                                                             %
9619 %                                                                             %
9620 %                                                                             %
9621 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9622 %
9623 %  XMatteEditImage() allows the user to interactively change the Matte channel
9624 %  of an image.  If the image is PseudoClass it is promoted to DirectClass
9625 %  before the matte information is stored.
9626 %
9627 %  The format of the XMatteEditImage method is:
9628 %
9629 %      MagickBooleanType XMatteEditImage(Display *display,
9630 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
9631 %        ExceptionInfo *exception)
9632 %
9633 %  A description of each parameter follows:
9634 %
9635 %    o display: Specifies a connection to an X server;  returned from
9636 %      XOpenDisplay.
9637 %
9638 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9639 %
9640 %    o windows: Specifies a pointer to a XWindows structure.
9641 %
9642 %    o image: the image; returned from ReadImage.
9643 %
9644 %    o exception: return any errors or warnings in this structure.
9645 %
9646 */
9647 static MagickBooleanType XMatteEditImage(Display *display,
9648   XResourceInfo *resource_info,XWindows *windows,Image **image,
9649   ExceptionInfo *exception)
9650 {
9651   static char
9652     matte[MaxTextExtent] = "0";
9653
9654   static const char
9655     *MatteEditMenu[] =
9656     {
9657       "Method",
9658       "Border Color",
9659       "Fuzz",
9660       "Matte Value",
9661       "Undo",
9662       "Help",
9663       "Dismiss",
9664       (char *) NULL
9665     };
9666
9667   static const ModeType
9668     MatteEditCommands[] =
9669     {
9670       MatteEditMethod,
9671       MatteEditBorderCommand,
9672       MatteEditFuzzCommand,
9673       MatteEditValueCommand,
9674       MatteEditUndoCommand,
9675       MatteEditHelpCommand,
9676       MatteEditDismissCommand
9677     };
9678
9679   static PaintMethod
9680     method = PointMethod;
9681
9682   static XColor
9683     border_color = { 0, 0, 0, 0, 0, 0 };
9684
9685   char
9686     command[MaxTextExtent],
9687     text[MaxTextExtent];
9688
9689   Cursor
9690     cursor;
9691
9692   int
9693     entry,
9694     id,
9695     x,
9696     x_offset,
9697     y,
9698     y_offset;
9699
9700   register int
9701     i;
9702
9703   register Quantum
9704     *q;
9705
9706   unsigned int
9707     height,
9708     width;
9709
9710   size_t
9711     state;
9712
9713   XEvent
9714     event;
9715
9716   /*
9717     Map Command widget.
9718   */
9719   (void) CloneString(&windows->command.name,"Matte Edit");
9720   windows->command.data=4;
9721   (void) XCommandWidget(display,windows,MatteEditMenu,(XEvent *) NULL);
9722   (void) XMapRaised(display,windows->command.id);
9723   XClientMessage(display,windows->image.id,windows->im_protocols,
9724     windows->im_update_widget,CurrentTime);
9725   /*
9726     Make cursor.
9727   */
9728   cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
9729     resource_info->background_color,resource_info->foreground_color);
9730   (void) XCheckDefineCursor(display,windows->image.id,cursor);
9731   /*
9732     Track pointer until button 1 is pressed.
9733   */
9734   XQueryPosition(display,windows->image.id,&x,&y);
9735   (void) XSelectInput(display,windows->image.id,
9736     windows->image.attributes.event_mask | PointerMotionMask);
9737   state=DefaultState;
9738   do
9739   {
9740     if (IfMagickTrue(windows->info.mapped) )
9741       {
9742         /*
9743           Display pointer position.
9744         */
9745         (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
9746           x+windows->image.x,y+windows->image.y);
9747         XInfoWidget(display,windows,text);
9748       }
9749     /*
9750       Wait for next event.
9751     */
9752     XScreenEvent(display,windows,&event,exception);
9753     if (event.xany.window == windows->command.id)
9754       {
9755         /*
9756           Select a command from the Command widget.
9757         */
9758         id=XCommandWidget(display,windows,MatteEditMenu,&event);
9759         if (id < 0)
9760           {
9761             (void) XCheckDefineCursor(display,windows->image.id,cursor);
9762             continue;
9763           }
9764         switch (MatteEditCommands[id])
9765         {
9766           case MatteEditMethod:
9767           {
9768             char
9769               **methods;
9770
9771             /*
9772               Select a method from the pop-up menu.
9773             */
9774             methods=GetCommandOptions(MagickMethodOptions);
9775             if (methods == (char **) NULL)
9776               break;
9777             entry=XMenuWidget(display,windows,MatteEditMenu[id],
9778               (const char **) methods,command);
9779             if (entry >= 0)
9780               method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
9781                 MagickFalse,methods[entry]);
9782             methods=DestroyStringList(methods);
9783             break;
9784           }
9785           case MatteEditBorderCommand:
9786           {
9787             const char
9788               *ColorMenu[MaxNumberPens];
9789
9790             int
9791               pen_number;
9792
9793             /*
9794               Initialize menu selections.
9795             */
9796             for (i=0; i < (int) (MaxNumberPens-2); i++)
9797               ColorMenu[i]=resource_info->pen_colors[i];
9798             ColorMenu[MaxNumberPens-2]="Browser...";
9799             ColorMenu[MaxNumberPens-1]=(const char *) NULL;
9800             /*
9801               Select a pen color from the pop-up menu.
9802             */
9803             pen_number=XMenuWidget(display,windows,MatteEditMenu[id],
9804               (const char **) ColorMenu,command);
9805             if (pen_number < 0)
9806               break;
9807             if (pen_number == (MaxNumberPens-2))
9808               {
9809                 static char
9810                   color_name[MaxTextExtent] = "gray";
9811
9812                 /*
9813                   Select a pen color from a dialog.
9814                 */
9815                 resource_info->pen_colors[pen_number]=color_name;
9816                 XColorBrowserWidget(display,windows,"Select",color_name);
9817                 if (*color_name == '\0')
9818                   break;
9819               }
9820             /*
9821               Set border color.
9822             */
9823             (void) XParseColor(display,windows->map_info->colormap,
9824               resource_info->pen_colors[pen_number],&border_color);
9825             break;
9826           }
9827           case MatteEditFuzzCommand:
9828           {
9829             static char
9830               fuzz[MaxTextExtent];
9831
9832             static const char
9833               *FuzzMenu[] =
9834               {
9835                 "0%",
9836                 "2%",
9837                 "5%",
9838                 "10%",
9839                 "15%",
9840                 "Dialog...",
9841                 (char *) NULL,
9842               };
9843
9844             /*
9845               Select a command from the pop-up menu.
9846             */
9847             entry=XMenuWidget(display,windows,MatteEditMenu[id],FuzzMenu,
9848               command);
9849             if (entry < 0)
9850               break;
9851             if (entry != 5)
9852               {
9853                 (*image)->fuzz=StringToDoubleInterval(FuzzMenu[entry],(double)
9854                   QuantumRange+1.0);
9855                 break;
9856               }
9857             (void) CopyMagickString(fuzz,"20%",MaxTextExtent);
9858             (void) XDialogWidget(display,windows,"Ok",
9859               "Enter fuzz factor (0.0 - 99.9%):",fuzz);
9860             if (*fuzz == '\0')
9861               break;
9862             (void) ConcatenateMagickString(fuzz,"%",MaxTextExtent);
9863             (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+
9864               1.0);
9865             break;
9866           }
9867           case MatteEditValueCommand:
9868           {
9869             static char
9870               message[MaxTextExtent];
9871
9872             static const char
9873               *MatteMenu[] =
9874               {
9875                 "Opaque",
9876                 "Transparent",
9877                 "Dialog...",
9878                 (char *) NULL,
9879               };
9880
9881             /*
9882               Select a command from the pop-up menu.
9883             */
9884             entry=XMenuWidget(display,windows,MatteEditMenu[id],MatteMenu,
9885               command);
9886             if (entry < 0)
9887               break;
9888             if (entry != 2)
9889               {
9890                 (void) FormatLocaleString(matte,MaxTextExtent,QuantumFormat,
9891                   OpaqueAlpha);
9892                 if (LocaleCompare(MatteMenu[entry],"Transparent") == 0)
9893                   (void) FormatLocaleString(matte,MaxTextExtent,QuantumFormat,
9894                     (Quantum) TransparentAlpha);
9895                 break;
9896               }
9897             (void) FormatLocaleString(message,MaxTextExtent,
9898               "Enter matte value (0 - " QuantumFormat "):",(Quantum)
9899               QuantumRange);
9900             (void) XDialogWidget(display,windows,"Matte",message,matte);
9901             if (*matte == '\0')
9902               break;
9903             break;
9904           }
9905           case MatteEditUndoCommand:
9906           {
9907             (void) XMagickCommand(display,resource_info,windows,UndoCommand,
9908               image,exception);
9909             break;
9910           }
9911           case MatteEditHelpCommand:
9912           {
9913             XTextViewWidget(display,resource_info,windows,MagickFalse,
9914               "Help Viewer - Matte Edit",ImageMatteEditHelp);
9915             break;
9916           }
9917           case MatteEditDismissCommand:
9918           {
9919             /*
9920               Prematurely exit.
9921             */
9922             state|=EscapeState;
9923             state|=ExitState;
9924             break;
9925           }
9926           default:
9927             break;
9928         }
9929         (void) XCheckDefineCursor(display,windows->image.id,cursor);
9930         continue;
9931       }
9932     switch (event.type)
9933     {
9934       case ButtonPress:
9935       {
9936         if (event.xbutton.button != Button1)
9937           break;
9938         if ((event.xbutton.window != windows->image.id) &&
9939             (event.xbutton.window != windows->magnify.id))
9940           break;
9941         /*
9942           Update matte data.
9943         */
9944         x=event.xbutton.x;
9945         y=event.xbutton.y;
9946         (void) XMagickCommand(display,resource_info,windows,
9947           SaveToUndoBufferCommand,image,exception);
9948         state|=UpdateConfigurationState;
9949         break;
9950       }
9951       case ButtonRelease:
9952       {
9953         if (event.xbutton.button != Button1)
9954           break;
9955         if ((event.xbutton.window != windows->image.id) &&
9956             (event.xbutton.window != windows->magnify.id))
9957           break;
9958         /*
9959           Update colormap information.
9960         */
9961         x=event.xbutton.x;
9962         y=event.xbutton.y;
9963         XConfigureImageColormap(display,resource_info,windows,*image,exception);
9964         (void) XConfigureImage(display,resource_info,windows,*image,exception);
9965         XInfoWidget(display,windows,text);
9966         (void) XCheckDefineCursor(display,windows->image.id,cursor);
9967         state&=(~UpdateConfigurationState);
9968         break;
9969       }
9970       case Expose:
9971         break;
9972       case KeyPress:
9973       {
9974         char
9975           command[MaxTextExtent];
9976
9977         KeySym
9978           key_symbol;
9979
9980         if (event.xkey.window == windows->magnify.id)
9981           {
9982             Window
9983               window;
9984
9985             window=windows->magnify.id;
9986             while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
9987           }
9988         if (event.xkey.window != windows->image.id)
9989           break;
9990         /*
9991           Respond to a user key press.
9992         */
9993         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
9994           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
9995         switch ((int) key_symbol)
9996         {
9997           case XK_Escape:
9998           case XK_F20:
9999           {
10000             /*
10001               Prematurely exit.
10002             */
10003             state|=ExitState;
10004             break;
10005           }
10006           case XK_F1:
10007           case XK_Help:
10008           {
10009             XTextViewWidget(display,resource_info,windows,MagickFalse,
10010               "Help Viewer - Matte Edit",ImageMatteEditHelp);
10011             break;
10012           }
10013           default:
10014           {
10015             (void) XBell(display,0);
10016             break;
10017           }
10018         }
10019         break;
10020       }
10021       case MotionNotify:
10022       {
10023         /*
10024           Map and unmap Info widget as cursor crosses its boundaries.
10025         */
10026         x=event.xmotion.x;
10027         y=event.xmotion.y;
10028         if (IfMagickTrue(windows->info.mapped) )
10029           {
10030             if ((x < (int) (windows->info.x+windows->info.width)) &&
10031                 (y < (int) (windows->info.y+windows->info.height)))
10032               (void) XWithdrawWindow(display,windows->info.id,
10033                 windows->info.screen);
10034           }
10035         else
10036           if ((x > (int) (windows->info.x+windows->info.width)) ||
10037               (y > (int) (windows->info.y+windows->info.height)))
10038             (void) XMapWindow(display,windows->info.id);
10039         break;
10040       }
10041       default:
10042         break;
10043     }
10044     if (event.xany.window == windows->magnify.id)
10045       {
10046         x=windows->magnify.x-windows->image.x;
10047         y=windows->magnify.y-windows->image.y;
10048       }
10049     x_offset=x;
10050     y_offset=y;
10051     if ((state & UpdateConfigurationState) != 0)
10052       {
10053         CacheView
10054           *image_view;
10055
10056         int
10057           x,
10058           y;
10059
10060         /*
10061           Matte edit is relative to image configuration.
10062         */
10063         (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
10064           MagickTrue);
10065         XPutPixel(windows->image.ximage,x_offset,y_offset,
10066           windows->pixel_info->background_color.pixel);
10067         width=(unsigned int) (*image)->columns;
10068         height=(unsigned int) (*image)->rows;
10069         x=0;
10070         y=0;
10071         if (windows->image.crop_geometry != (char *) NULL)
10072           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,
10073             &height);
10074         x_offset=(int) (width*(windows->image.x+x_offset)/
10075           windows->image.ximage->width+x);
10076         y_offset=(int) (height*(windows->image.y+y_offset)/
10077           windows->image.ximage->height+y);
10078         if ((x_offset < 0) || (y_offset < 0))
10079           continue;
10080         if ((x_offset >= (int) (*image)->columns) ||
10081             (y_offset >= (int) (*image)->rows))
10082           continue;
10083         if (IfMagickFalse(SetImageStorageClass(*image,DirectClass,exception)) )
10084           return(MagickFalse);
10085         if ((*image)->alpha_trait == UndefinedPixelTrait)
10086           (void) SetImageAlphaChannel(*image,OpaqueAlphaChannel,exception);
10087         image_view=AcquireAuthenticCacheView(*image,exception);
10088         switch (method)
10089         {
10090           case PointMethod:
10091           default:
10092           {
10093             /*
10094               Update matte information using point algorithm.
10095             */
10096             q=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,
10097               (ssize_t) y_offset,1,1,exception);
10098             if (q == (Quantum *) NULL)
10099               break;
10100             SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10101             (void) SyncCacheViewAuthenticPixels(image_view,exception);
10102             break;
10103           }
10104           case ReplaceMethod:
10105           {
10106             PixelInfo
10107               pixel,
10108               target;
10109
10110             /*
10111               Update matte information using replace algorithm.
10112             */
10113             (void) GetOneCacheViewVirtualPixelInfo(image_view,(ssize_t)
10114               x_offset,(ssize_t) y_offset,&target,exception);
10115             for (y=0; y < (int) (*image)->rows; y++)
10116             {
10117               q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10118                 (*image)->columns,1,exception);
10119               if (q == (Quantum *) NULL)
10120                 break;
10121               for (x=0; x < (int) (*image)->columns; x++)
10122               {
10123                 GetPixelInfoPixel(*image,q,&pixel);
10124                 if (IsFuzzyEquivalencePixelInfo(&pixel,&target))
10125                   SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10126                 q+=GetPixelChannels(*image);
10127               }
10128               if (IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
10129                 break;
10130             }
10131             break;
10132           }
10133           case FloodfillMethod:
10134           case FillToBorderMethod:
10135           {
10136             ChannelType
10137               channel_mask;
10138
10139             DrawInfo
10140               *draw_info;
10141
10142             PixelInfo
10143               target;
10144
10145             /*
10146               Update matte information using floodfill algorithm.
10147             */
10148             (void) GetOneVirtualPixelInfo(*image,
10149               GetPixelCacheVirtualMethod(*image),(ssize_t) x_offset,(ssize_t)
10150               y_offset,&target,exception);
10151             if (method == FillToBorderMethod)
10152               {
10153                 target.red=(double) ScaleShortToQuantum(
10154                   border_color.red);
10155                 target.green=(double) ScaleShortToQuantum(
10156                   border_color.green);
10157                 target.blue=(double) ScaleShortToQuantum(
10158                   border_color.blue);
10159               }
10160             draw_info=CloneDrawInfo(resource_info->image_info,
10161               (DrawInfo *) NULL);
10162             draw_info->fill.alpha=(double) ClampToQuantum(
10163               StringToDouble(matte,(char **) NULL));
10164             channel_mask=SetImageChannelMask(*image,AlphaChannel);
10165             (void) FloodfillPaintImage(*image,draw_info,&target,(ssize_t)
10166               x_offset,(ssize_t) y_offset,
10167               IsMagickFalse(method == FloodfillMethod),exception);
10168             (void) SetPixelChannelMask(*image,channel_mask);
10169             draw_info=DestroyDrawInfo(draw_info);
10170             break;
10171           }
10172           case ResetMethod:
10173           {
10174             /*
10175               Update matte information using reset algorithm.
10176             */
10177             if (IfMagickFalse(SetImageStorageClass(*image,DirectClass,exception)) )
10178               return(MagickFalse);
10179             for (y=0; y < (int) (*image)->rows; y++)
10180             {
10181               q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10182                 (*image)->columns,1,exception);
10183               if (q == (Quantum *) NULL)
10184                 break;
10185               for (x=0; x < (int) (*image)->columns; x++)
10186               {
10187                 SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10188                 q+=GetPixelChannels(*image);
10189               }
10190               if (IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
10191                 break;
10192             }
10193             if (StringToLong(matte) == (long) OpaqueAlpha)
10194               (*image)->alpha_trait=UndefinedPixelTrait;
10195             break;
10196           }
10197         }
10198         image_view=DestroyCacheView(image_view);
10199         state&=(~UpdateConfigurationState);
10200       }
10201   } while ((state & ExitState) == 0);
10202   (void) XSelectInput(display,windows->image.id,
10203     windows->image.attributes.event_mask);
10204   XSetCursorState(display,windows,MagickFalse);
10205   (void) XFreeCursor(display,cursor);
10206   return(MagickTrue);
10207 }
10208 \f
10209 /*
10210 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10211 %                                                                             %
10212 %                                                                             %
10213 %                                                                             %
10214 +   X O p e n I m a g e                                                       %
10215 %                                                                             %
10216 %                                                                             %
10217 %                                                                             %
10218 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10219 %
10220 %  XOpenImage() loads an image from a file.
10221 %
10222 %  The format of the XOpenImage method is:
10223 %
10224 %     Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10225 %       XWindows *windows,const unsigned int command)
10226 %
10227 %  A description of each parameter follows:
10228 %
10229 %    o display: Specifies a connection to an X server; returned from
10230 %      XOpenDisplay.
10231 %
10232 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10233 %
10234 %    o windows: Specifies a pointer to a XWindows structure.
10235 %
10236 %    o command: A value other than zero indicates that the file is selected
10237 %      from the command line argument list.
10238 %
10239 */
10240 static Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10241   XWindows *windows,const MagickBooleanType command)
10242 {
10243   const MagickInfo
10244     *magick_info;
10245
10246   ExceptionInfo
10247     *exception;
10248
10249   Image
10250     *nexus;
10251
10252   ImageInfo
10253     *image_info;
10254
10255   static char
10256     filename[MaxTextExtent] = "\0";
10257
10258   /*
10259     Request file name from user.
10260   */
10261   if (IfMagickFalse(command) )
10262     XFileBrowserWidget(display,windows,"Open",filename);
10263   else
10264     {
10265       char
10266         **filelist,
10267         **files;
10268
10269       int
10270         count,
10271         status;
10272
10273       register int
10274         i,
10275         j;
10276
10277       /*
10278         Select next image from the command line.
10279       */
10280       status=XGetCommand(display,windows->image.id,&files,&count);
10281       if (status == 0)
10282         {
10283           ThrowXWindowException(XServerError,"UnableToGetProperty","...");
10284           return((Image *) NULL);
10285         }
10286       filelist=(char **) AcquireQuantumMemory((size_t) count,sizeof(*filelist));
10287       if (filelist == (char **) NULL)
10288         {
10289           ThrowXWindowException(ResourceLimitError,
10290             "MemoryAllocationFailed","...");
10291           (void) XFreeStringList(files);
10292           return((Image *) NULL);
10293         }
10294       j=0;
10295       for (i=1; i < count; i++)
10296         if (*files[i] != '-')
10297           filelist[j++]=files[i];
10298       filelist[j]=(char *) NULL;
10299       XListBrowserWidget(display,windows,&windows->widget,
10300         (const char **) filelist,"Load","Select Image to Load:",filename);
10301       filelist=(char **) RelinquishMagickMemory(filelist);
10302       (void) XFreeStringList(files);
10303     }
10304   if (*filename == '\0')
10305     return((Image *) NULL);
10306   image_info=CloneImageInfo(resource_info->image_info);
10307   (void) SetImageInfoProgressMonitor(image_info,(MagickProgressMonitor) NULL,
10308     (void *) NULL);
10309   (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
10310   exception=AcquireExceptionInfo();
10311   (void) SetImageInfo(image_info,0,exception);
10312   if (LocaleCompare(image_info->magick,"X") == 0)
10313     {
10314       char
10315         seconds[MaxTextExtent];
10316
10317       /*
10318         User may want to delay the X server screen grab.
10319       */
10320       (void) CopyMagickString(seconds,"0",MaxTextExtent);
10321       (void) XDialogWidget(display,windows,"Grab","Enter any delay in seconds:",
10322         seconds);
10323       if (*seconds == '\0')
10324         return((Image *) NULL);
10325       XDelay(display,(size_t) (1000*StringToLong(seconds)));
10326     }
10327   magick_info=GetMagickInfo(image_info->magick,exception);
10328   if ((magick_info != (const MagickInfo *) NULL) &&
10329       IfMagickTrue(magick_info->raw))
10330     {
10331       char
10332         geometry[MaxTextExtent];
10333
10334       /*
10335         Request image size from the user.
10336       */
10337       (void) CopyMagickString(geometry,"512x512",MaxTextExtent);
10338       if (image_info->size != (char *) NULL)
10339         (void) CopyMagickString(geometry,image_info->size,MaxTextExtent);
10340       (void) XDialogWidget(display,windows,"Load","Enter the image geometry:",
10341         geometry);
10342       (void) CloneString(&image_info->size,geometry);
10343     }
10344   /*
10345     Load the image.
10346   */
10347   XSetCursorState(display,windows,MagickTrue);
10348   XCheckRefreshWindows(display,windows);
10349   (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
10350   nexus=ReadImage(image_info,exception);
10351   CatchException(exception);
10352   XSetCursorState(display,windows,MagickFalse);
10353   if (nexus != (Image *) NULL)
10354     XClientMessage(display,windows->image.id,windows->im_protocols,
10355       windows->im_next_image,CurrentTime);
10356   else
10357     {
10358       char
10359         *text,
10360         **textlist;
10361
10362       /*
10363         Unknown image format.
10364       */
10365       text=FileToString(filename,~0UL,exception);
10366       if (text == (char *) NULL)
10367         return((Image *) NULL);
10368       textlist=StringToList(text);
10369       if (textlist != (char **) NULL)
10370         {
10371           char
10372             title[MaxTextExtent];
10373
10374           register int
10375             i;
10376
10377           (void) FormatLocaleString(title,MaxTextExtent,
10378             "Unknown format: %s",filename);
10379           XTextViewWidget(display,resource_info,windows,MagickTrue,title,
10380             (const char **) textlist);
10381           for (i=0; textlist[i] != (char *) NULL; i++)
10382             textlist[i]=DestroyString(textlist[i]);
10383           textlist=(char **) RelinquishMagickMemory(textlist);
10384         }
10385       text=DestroyString(text);
10386     }
10387   exception=DestroyExceptionInfo(exception);
10388   image_info=DestroyImageInfo(image_info);
10389   return(nexus);
10390 }
10391 \f
10392 /*
10393 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10394 %                                                                             %
10395 %                                                                             %
10396 %                                                                             %
10397 +   X P a n I m a g e                                                         %
10398 %                                                                             %
10399 %                                                                             %
10400 %                                                                             %
10401 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10402 %
10403 %  XPanImage() pans the image until the mouse button is released.
10404 %
10405 %  The format of the XPanImage method is:
10406 %
10407 %      void XPanImage(Display *display,XWindows *windows,XEvent *event,
10408 %        ExceptionInfo *exception)
10409 %
10410 %  A description of each parameter follows:
10411 %
10412 %    o display: Specifies a connection to an X server;  returned from
10413 %      XOpenDisplay.
10414 %
10415 %    o windows: Specifies a pointer to a XWindows structure.
10416 %
10417 %    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
10418 %      the entire image is refreshed.
10419 %
10420 %    o exception: return any errors or warnings in this structure.
10421 %
10422 */
10423 static void XPanImage(Display *display,XWindows *windows,XEvent *event,
10424   ExceptionInfo *exception)
10425 {
10426   char
10427     text[MaxTextExtent];
10428
10429   Cursor
10430     cursor;
10431
10432   double
10433     x_factor,
10434     y_factor;
10435
10436   RectangleInfo
10437     pan_info;
10438
10439   size_t
10440     state;
10441
10442   /*
10443     Define cursor.
10444   */
10445   if ((windows->image.ximage->width > (int) windows->image.width) &&
10446       (windows->image.ximage->height > (int) windows->image.height))
10447     cursor=XCreateFontCursor(display,XC_fleur);
10448   else
10449     if (windows->image.ximage->width > (int) windows->image.width)
10450       cursor=XCreateFontCursor(display,XC_sb_h_double_arrow);
10451     else
10452       if (windows->image.ximage->height > (int) windows->image.height)
10453         cursor=XCreateFontCursor(display,XC_sb_v_double_arrow);
10454       else
10455         cursor=XCreateFontCursor(display,XC_arrow);
10456   (void) XCheckDefineCursor(display,windows->pan.id,cursor);
10457   /*
10458     Pan image as pointer moves until the mouse button is released.
10459   */
10460   x_factor=(double) windows->image.ximage->width/windows->pan.width;
10461   y_factor=(double) windows->image.ximage->height/windows->pan.height;
10462   pan_info.width=windows->pan.width*windows->image.width/
10463     windows->image.ximage->width;
10464   pan_info.height=windows->pan.height*windows->image.height/
10465     windows->image.ximage->height;
10466   pan_info.x=0;
10467   pan_info.y=0;
10468   state=UpdateConfigurationState;
10469   do
10470   {
10471     switch (event->type)
10472     {
10473       case ButtonPress:
10474       {
10475         /*
10476           User choose an initial pan location.
10477         */
10478         pan_info.x=(ssize_t) event->xbutton.x;
10479         pan_info.y=(ssize_t) event->xbutton.y;
10480         state|=UpdateConfigurationState;
10481         break;
10482       }
10483       case ButtonRelease:
10484       {
10485         /*
10486           User has finished panning the image.
10487         */
10488         pan_info.x=(ssize_t) event->xbutton.x;
10489         pan_info.y=(ssize_t) event->xbutton.y;
10490         state|=UpdateConfigurationState | ExitState;
10491         break;
10492       }
10493       case MotionNotify:
10494       {
10495         pan_info.x=(ssize_t) event->xmotion.x;
10496         pan_info.y=(ssize_t) event->xmotion.y;
10497         state|=UpdateConfigurationState;
10498       }
10499       default:
10500         break;
10501     }
10502     if ((state & UpdateConfigurationState) != 0)
10503       {
10504         /*
10505           Check boundary conditions.
10506         */
10507         if (pan_info.x < (ssize_t) (pan_info.width/2))
10508           pan_info.x=0;
10509         else
10510           pan_info.x=(ssize_t) (x_factor*(pan_info.x-(pan_info.width/2)));
10511         if (pan_info.x < 0)
10512           pan_info.x=0;
10513         else
10514           if ((int) (pan_info.x+windows->image.width) >
10515               windows->image.ximage->width)
10516             pan_info.x=(ssize_t)
10517               (windows->image.ximage->width-windows->image.width);
10518         if (pan_info.y < (ssize_t) (pan_info.height/2))
10519           pan_info.y=0;
10520         else
10521           pan_info.y=(ssize_t) (y_factor*(pan_info.y-(pan_info.height/2)));
10522         if (pan_info.y < 0)
10523           pan_info.y=0;
10524         else
10525           if ((int) (pan_info.y+windows->image.height) >
10526               windows->image.ximage->height)
10527             pan_info.y=(ssize_t)
10528               (windows->image.ximage->height-windows->image.height);
10529         if ((windows->image.x != (int) pan_info.x) ||
10530             (windows->image.y != (int) pan_info.y))
10531           {
10532             /*
10533               Display image pan offset.
10534             */
10535             windows->image.x=(int) pan_info.x;
10536             windows->image.y=(int) pan_info.y;
10537             (void) FormatLocaleString(text,MaxTextExtent," %ux%u%+d%+d ",
10538               windows->image.width,windows->image.height,windows->image.x,
10539               windows->image.y);
10540             XInfoWidget(display,windows,text);
10541             /*
10542               Refresh Image window.
10543             */
10544             XDrawPanRectangle(display,windows);
10545             XRefreshWindow(display,&windows->image,(XEvent *) NULL);
10546           }
10547         state&=(~UpdateConfigurationState);
10548       }
10549     /*
10550       Wait for next event.
10551     */
10552     if ((state & ExitState) == 0)
10553       XScreenEvent(display,windows,event,exception);
10554   } while ((state & ExitState) == 0);
10555   /*
10556     Restore cursor.
10557   */
10558   (void) XCheckDefineCursor(display,windows->pan.id,windows->pan.cursor);
10559   (void) XFreeCursor(display,cursor);
10560   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
10561 }
10562 \f
10563 /*
10564 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10565 %                                                                             %
10566 %                                                                             %
10567 %                                                                             %
10568 +   X P a s t e I m a g e                                                     %
10569 %                                                                             %
10570 %                                                                             %
10571 %                                                                             %
10572 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10573 %
10574 %  XPasteImage() pastes an image previously saved with XCropImage in the X
10575 %  window image at a location the user chooses with the pointer.
10576 %
10577 %  The format of the XPasteImage method is:
10578 %
10579 %      MagickBooleanType XPasteImage(Display *display,
10580 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
10581 %        ExceptionInfo *exception)
10582 %
10583 %  A description of each parameter follows:
10584 %
10585 %    o display: Specifies a connection to an X server;  returned from
10586 %      XOpenDisplay.
10587 %
10588 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10589 %
10590 %    o windows: Specifies a pointer to a XWindows structure.
10591 %
10592 %    o image: the image; returned from ReadImage.
10593 %
10594 %    o exception: return any errors or warnings in this structure.
10595 %
10596 */
10597 static MagickBooleanType XPasteImage(Display *display,
10598   XResourceInfo *resource_info,XWindows *windows,Image *image,
10599   ExceptionInfo *exception)
10600 {
10601   static const char
10602     *PasteMenu[] =
10603     {
10604       "Operator",
10605       "Help",
10606       "Dismiss",
10607       (char *) NULL
10608     };
10609
10610   static const ModeType
10611     PasteCommands[] =
10612     {
10613       PasteOperatorsCommand,
10614       PasteHelpCommand,
10615       PasteDismissCommand
10616     };
10617
10618   static CompositeOperator
10619     compose = CopyCompositeOp;
10620
10621   char
10622     text[MaxTextExtent];
10623
10624   Cursor
10625     cursor;
10626
10627   Image
10628     *paste_image;
10629
10630   int
10631     entry,
10632     id,
10633     x,
10634     y;
10635
10636   double
10637     scale_factor;
10638
10639   RectangleInfo
10640     highlight_info,
10641     paste_info;
10642
10643   unsigned int
10644     height,
10645     width;
10646
10647   size_t
10648     state;
10649
10650   XEvent
10651     event;
10652
10653   /*
10654     Copy image.
10655   */
10656   if (resource_info->copy_image == (Image *) NULL)
10657     return(MagickFalse);
10658   paste_image=CloneImage(resource_info->copy_image,0,0,MagickTrue,exception);
10659   /*
10660     Map Command widget.
10661   */
10662   (void) CloneString(&windows->command.name,"Paste");
10663   windows->command.data=1;
10664   (void) XCommandWidget(display,windows,PasteMenu,(XEvent *) NULL);
10665   (void) XMapRaised(display,windows->command.id);
10666   XClientMessage(display,windows->image.id,windows->im_protocols,
10667     windows->im_update_widget,CurrentTime);
10668   /*
10669     Track pointer until button 1 is pressed.
10670   */
10671   XSetCursorState(display,windows,MagickFalse);
10672   XQueryPosition(display,windows->image.id,&x,&y);
10673   (void) XSelectInput(display,windows->image.id,
10674     windows->image.attributes.event_mask | PointerMotionMask);
10675   paste_info.x=(ssize_t) windows->image.x+x;
10676   paste_info.y=(ssize_t) windows->image.y+y;
10677   paste_info.width=0;
10678   paste_info.height=0;
10679   cursor=XCreateFontCursor(display,XC_ul_angle);
10680   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
10681   state=DefaultState;
10682   do
10683   {
10684     if (IfMagickTrue(windows->info.mapped) )
10685       {
10686         /*
10687           Display pointer position.
10688         */
10689         (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
10690           (long) paste_info.x,(long) paste_info.y);
10691         XInfoWidget(display,windows,text);
10692       }
10693     highlight_info=paste_info;
10694     highlight_info.x=paste_info.x-windows->image.x;
10695     highlight_info.y=paste_info.y-windows->image.y;
10696     XHighlightRectangle(display,windows->image.id,
10697       windows->image.highlight_context,&highlight_info);
10698     /*
10699       Wait for next event.
10700     */
10701     XScreenEvent(display,windows,&event,exception);
10702     XHighlightRectangle(display,windows->image.id,
10703       windows->image.highlight_context,&highlight_info);
10704     if (event.xany.window == windows->command.id)
10705       {
10706         /*
10707           Select a command from the Command widget.
10708         */
10709         id=XCommandWidget(display,windows,PasteMenu,&event);
10710         if (id < 0)
10711           continue;
10712         switch (PasteCommands[id])
10713         {
10714           case PasteOperatorsCommand:
10715           {
10716             char
10717               command[MaxTextExtent],
10718               **operators;
10719
10720             /*
10721               Select a command from the pop-up menu.
10722             */
10723             operators=GetCommandOptions(MagickComposeOptions);
10724             if (operators == (char **) NULL)
10725               break;
10726             entry=XMenuWidget(display,windows,PasteMenu[id],
10727               (const char **) operators,command);
10728             if (entry >= 0)
10729               compose=(CompositeOperator) ParseCommandOption(
10730                 MagickComposeOptions,MagickFalse,operators[entry]);
10731             operators=DestroyStringList(operators);
10732             break;
10733           }
10734           case PasteHelpCommand:
10735           {
10736             XTextViewWidget(display,resource_info,windows,MagickFalse,
10737               "Help Viewer - Image Composite",ImagePasteHelp);
10738             break;
10739           }
10740           case PasteDismissCommand:
10741           {
10742             /*
10743               Prematurely exit.
10744             */
10745             state|=EscapeState;
10746             state|=ExitState;
10747             break;
10748           }
10749           default:
10750             break;
10751         }
10752         continue;
10753       }
10754     switch (event.type)
10755     {
10756       case ButtonPress:
10757       {
10758         if (IfMagickTrue(image->debug) )
10759           (void) LogMagickEvent(X11Event,GetMagickModule(),
10760             "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
10761             event.xbutton.button,event.xbutton.x,event.xbutton.y);
10762         if (event.xbutton.button != Button1)
10763           break;
10764         if (event.xbutton.window != windows->image.id)
10765           break;
10766         /*
10767           Paste rectangle is relative to image configuration.
10768         */
10769         width=(unsigned int) image->columns;
10770         height=(unsigned int) image->rows;
10771         x=0;
10772         y=0;
10773         if (windows->image.crop_geometry != (char *) NULL)
10774           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
10775             &width,&height);
10776         scale_factor=(double) windows->image.ximage->width/width;
10777         paste_info.width=(unsigned int) (scale_factor*paste_image->columns+0.5);
10778         scale_factor=(double) windows->image.ximage->height/height;
10779         paste_info.height=(unsigned int) (scale_factor*paste_image->rows+0.5);
10780         (void) XCheckDefineCursor(display,windows->image.id,cursor);
10781         paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10782         paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10783         break;
10784       }
10785       case ButtonRelease:
10786       {
10787         if (IfMagickTrue(image->debug) )
10788           (void) LogMagickEvent(X11Event,GetMagickModule(),
10789             "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
10790             event.xbutton.button,event.xbutton.x,event.xbutton.y);
10791         if (event.xbutton.button != Button1)
10792           break;
10793         if (event.xbutton.window != windows->image.id)
10794           break;
10795         if ((paste_info.width != 0) && (paste_info.height != 0))
10796           {
10797             /*
10798               User has selected the location of the paste image.
10799             */
10800             paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10801             paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10802             state|=ExitState;
10803           }
10804         break;
10805       }
10806       case Expose:
10807         break;
10808       case KeyPress:
10809       {
10810         char
10811           command[MaxTextExtent];
10812
10813         KeySym
10814           key_symbol;
10815
10816         int
10817           length;
10818
10819         if (event.xkey.window != windows->image.id)
10820           break;
10821         /*
10822           Respond to a user key press.
10823         */
10824         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
10825           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
10826         *(command+length)='\0';
10827         if (IfMagickTrue(image->debug) )
10828           (void) LogMagickEvent(X11Event,GetMagickModule(),
10829             "Key press: 0x%lx (%s)",(long) key_symbol,command);
10830         switch ((int) key_symbol)
10831         {
10832           case XK_Escape:
10833           case XK_F20:
10834           {
10835             /*
10836               Prematurely exit.
10837             */
10838             paste_image=DestroyImage(paste_image);
10839             state|=EscapeState;
10840             state|=ExitState;
10841             break;
10842           }
10843           case XK_F1:
10844           case XK_Help:
10845           {
10846             (void) XSetFunction(display,windows->image.highlight_context,
10847               GXcopy);
10848             XTextViewWidget(display,resource_info,windows,MagickFalse,
10849               "Help Viewer - Image Composite",ImagePasteHelp);
10850             (void) XSetFunction(display,windows->image.highlight_context,
10851               GXinvert);
10852             break;
10853           }
10854           default:
10855           {
10856             (void) XBell(display,0);
10857             break;
10858           }
10859         }
10860         break;
10861       }
10862       case MotionNotify:
10863       {
10864         /*
10865           Map and unmap Info widget as text cursor crosses its boundaries.
10866         */
10867         x=event.xmotion.x;
10868         y=event.xmotion.y;
10869         if (IfMagickTrue(windows->info.mapped) )
10870           {
10871             if ((x < (int) (windows->info.x+windows->info.width)) &&
10872                 (y < (int) (windows->info.y+windows->info.height)))
10873               (void) XWithdrawWindow(display,windows->info.id,
10874                 windows->info.screen);
10875           }
10876         else
10877           if ((x > (int) (windows->info.x+windows->info.width)) ||
10878               (y > (int) (windows->info.y+windows->info.height)))
10879             (void) XMapWindow(display,windows->info.id);
10880         paste_info.x=(ssize_t) windows->image.x+x;
10881         paste_info.y=(ssize_t) windows->image.y+y;
10882         break;
10883       }
10884       default:
10885       {
10886         if (IfMagickTrue(image->debug) )
10887           (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
10888             event.type);
10889         break;
10890       }
10891     }
10892   } while ((state & ExitState) == 0);
10893   (void) XSelectInput(display,windows->image.id,
10894     windows->image.attributes.event_mask);
10895   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
10896   XSetCursorState(display,windows,MagickFalse);
10897   (void) XFreeCursor(display,cursor);
10898   if ((state & EscapeState) != 0)
10899     return(MagickTrue);
10900   /*
10901     Image pasting is relative to image configuration.
10902   */
10903   XSetCursorState(display,windows,MagickTrue);
10904   XCheckRefreshWindows(display,windows);
10905   width=(unsigned int) image->columns;
10906   height=(unsigned int) image->rows;
10907   x=0;
10908   y=0;
10909   if (windows->image.crop_geometry != (char *) NULL)
10910     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
10911   scale_factor=(double) width/windows->image.ximage->width;
10912   paste_info.x+=x;
10913   paste_info.x=(ssize_t) (scale_factor*paste_info.x+0.5);
10914   paste_info.width=(unsigned int) (scale_factor*paste_info.width+0.5);
10915   scale_factor=(double) height/windows->image.ximage->height;
10916   paste_info.y+=y;
10917   paste_info.y=(ssize_t) (scale_factor*paste_info.y*scale_factor+0.5);
10918   paste_info.height=(unsigned int) (scale_factor*paste_info.height+0.5);
10919   /*
10920     Paste image with X Image window.
10921   */
10922   (void) CompositeImage(image,paste_image,compose,MagickTrue,paste_info.x,
10923     paste_info.y,exception);
10924   paste_image=DestroyImage(paste_image);
10925   XSetCursorState(display,windows,MagickFalse);
10926   /*
10927     Update image colormap.
10928   */
10929   XConfigureImageColormap(display,resource_info,windows,image,exception);
10930   (void) XConfigureImage(display,resource_info,windows,image,exception);
10931   return(MagickTrue);
10932 }
10933 \f
10934 /*
10935 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10936 %                                                                             %
10937 %                                                                             %
10938 %                                                                             %
10939 +   X P r i n t I m a g e                                                     %
10940 %                                                                             %
10941 %                                                                             %
10942 %                                                                             %
10943 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10944 %
10945 %  XPrintImage() prints an image to a Postscript printer.
10946 %
10947 %  The format of the XPrintImage method is:
10948 %
10949 %      MagickBooleanType XPrintImage(Display *display,
10950 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
10951 %        ExceptionInfo *exception)
10952 %
10953 %  A description of each parameter follows:
10954 %
10955 %    o display: Specifies a connection to an X server; returned from
10956 %      XOpenDisplay.
10957 %
10958 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10959 %
10960 %    o windows: Specifies a pointer to a XWindows structure.
10961 %
10962 %    o image: the image.
10963 %
10964 %    o exception: return any errors or warnings in this structure.
10965 %
10966 */
10967 static MagickBooleanType XPrintImage(Display *display,
10968   XResourceInfo *resource_info,XWindows *windows,Image *image,
10969   ExceptionInfo *exception)
10970 {
10971   char
10972     filename[MaxTextExtent],
10973     geometry[MaxTextExtent];
10974
10975   Image
10976     *print_image;
10977
10978   ImageInfo
10979     *image_info;
10980
10981   MagickStatusType
10982     status;
10983
10984   /*
10985     Request Postscript page geometry from user.
10986   */
10987   image_info=CloneImageInfo(resource_info->image_info);
10988   (void) FormatLocaleString(geometry,MaxTextExtent,"Letter");
10989   if (image_info->page != (char *) NULL)
10990     (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
10991   XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
10992     "Select Postscript Page Geometry:",geometry);
10993   if (*geometry == '\0')
10994     return(MagickTrue);
10995   image_info->page=GetPageGeometry(geometry);
10996   /*
10997     Apply image transforms.
10998   */
10999   XSetCursorState(display,windows,MagickTrue);
11000   XCheckRefreshWindows(display,windows);
11001   print_image=CloneImage(image,0,0,MagickTrue,exception);
11002   if (print_image == (Image *) NULL)
11003     return(MagickFalse);
11004   (void) FormatLocaleString(geometry,MaxTextExtent,"%dx%d!",
11005     windows->image.ximage->width,windows->image.ximage->height);
11006   (void) TransformImage(&print_image,windows->image.crop_geometry,geometry,
11007     exception);
11008   /*
11009     Print image.
11010   */
11011   (void) AcquireUniqueFilename(filename);
11012   (void) FormatLocaleString(print_image->filename,MaxTextExtent,"print:%s",
11013     filename);
11014   status=WriteImage(image_info,print_image,exception);
11015   (void) RelinquishUniqueFileResource(filename);
11016   print_image=DestroyImage(print_image);
11017   image_info=DestroyImageInfo(image_info);
11018   XSetCursorState(display,windows,MagickFalse);
11019   return(IsMagickTrue(status));
11020 }
11021 \f
11022 /*
11023 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11024 %                                                                             %
11025 %                                                                             %
11026 %                                                                             %
11027 +   X R O I I m a g e                                                         %
11028 %                                                                             %
11029 %                                                                             %
11030 %                                                                             %
11031 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11032 %
11033 %  XROIImage() applies an image processing technique to a region of interest.
11034 %
11035 %  The format of the XROIImage method is:
11036 %
11037 %      MagickBooleanType XROIImage(Display *display,
11038 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
11039 %        ExceptionInfo *exception)
11040 %
11041 %  A description of each parameter follows:
11042 %
11043 %    o display: Specifies a connection to an X server; returned from
11044 %      XOpenDisplay.
11045 %
11046 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
11047 %
11048 %    o windows: Specifies a pointer to a XWindows structure.
11049 %
11050 %    o image: the image; returned from ReadImage.
11051 %
11052 %    o exception: return any errors or warnings in this structure.
11053 %
11054 */
11055 static MagickBooleanType XROIImage(Display *display,
11056   XResourceInfo *resource_info,XWindows *windows,Image **image,
11057   ExceptionInfo *exception)
11058 {
11059 #define ApplyMenus  7
11060
11061   static const char
11062     *ROIMenu[] =
11063     {
11064       "Help",
11065       "Dismiss",
11066       (char *) NULL
11067     },
11068     *ApplyMenu[] =
11069     {
11070       "File",
11071       "Edit",
11072       "Transform",
11073       "Enhance",
11074       "Effects",
11075       "F/X",
11076       "Miscellany",
11077       "Help",
11078       "Dismiss",
11079       (char *) NULL
11080     },
11081     *FileMenu[] =
11082     {
11083       "Save...",
11084       "Print...",
11085       (char *) NULL
11086     },
11087     *EditMenu[] =
11088     {
11089       "Undo",
11090       "Redo",
11091       (char *) NULL
11092     },
11093     *TransformMenu[] =
11094     {
11095       "Flop",
11096       "Flip",
11097       "Rotate Right",
11098       "Rotate Left",
11099       (char *) NULL
11100     },
11101     *EnhanceMenu[] =
11102     {
11103       "Hue...",
11104       "Saturation...",
11105       "Brightness...",
11106       "Gamma...",
11107       "Spiff",
11108       "Dull",
11109       "Contrast Stretch...",
11110       "Sigmoidal Contrast...",
11111       "Normalize",
11112       "Equalize",
11113       "Negate",
11114       "Grayscale",
11115       "Map...",
11116       "Quantize...",
11117       (char *) NULL
11118     },
11119     *EffectsMenu[] =
11120     {
11121       "Despeckle",
11122       "Emboss",
11123       "Reduce Noise",
11124       "Add Noise",
11125       "Sharpen...",
11126       "Blur...",
11127       "Threshold...",
11128       "Edge Detect...",
11129       "Spread...",
11130       "Shade...",
11131       "Raise...",
11132       "Segment...",
11133       (char *) NULL
11134     },
11135     *FXMenu[] =
11136     {
11137       "Solarize...",
11138       "Sepia Tone...",
11139       "Swirl...",
11140       "Implode...",
11141       "Vignette...",
11142       "Wave...",
11143       "Oil Paint...",
11144       "Charcoal Draw...",
11145       (char *) NULL
11146     },
11147     *MiscellanyMenu[] =
11148     {
11149       "Image Info",
11150       "Zoom Image",
11151       "Show Preview...",
11152       "Show Histogram",
11153       "Show Matte",
11154       (char *) NULL
11155     };
11156
11157   static const char
11158     **Menus[ApplyMenus] =
11159     {
11160       FileMenu,
11161       EditMenu,
11162       TransformMenu,
11163       EnhanceMenu,
11164       EffectsMenu,
11165       FXMenu,
11166       MiscellanyMenu
11167     };
11168
11169   static const CommandType
11170     ApplyCommands[] =
11171     {
11172       NullCommand,
11173       NullCommand,
11174       NullCommand,
11175       NullCommand,
11176       NullCommand,
11177       NullCommand,
11178       NullCommand,
11179       HelpCommand,
11180       QuitCommand
11181     },
11182     FileCommands[] =
11183     {
11184       SaveCommand,
11185       PrintCommand
11186     },
11187     EditCommands[] =
11188     {
11189       UndoCommand,
11190       RedoCommand
11191     },
11192     TransformCommands[] =
11193     {
11194       FlopCommand,
11195       FlipCommand,
11196       RotateRightCommand,
11197       RotateLeftCommand
11198     },
11199     EnhanceCommands[] =
11200     {
11201       HueCommand,
11202       SaturationCommand,
11203       BrightnessCommand,
11204       GammaCommand,
11205       SpiffCommand,
11206       DullCommand,
11207       ContrastStretchCommand,
11208       SigmoidalContrastCommand,
11209       NormalizeCommand,
11210       EqualizeCommand,
11211       NegateCommand,
11212       GrayscaleCommand,
11213       MapCommand,
11214       QuantizeCommand
11215     },
11216     EffectsCommands[] =
11217     {
11218       DespeckleCommand,
11219       EmbossCommand,
11220       ReduceNoiseCommand,
11221       AddNoiseCommand,
11222       SharpenCommand,
11223       BlurCommand,
11224       EdgeDetectCommand,
11225       SpreadCommand,
11226       ShadeCommand,
11227       RaiseCommand,
11228       SegmentCommand
11229     },
11230     FXCommands[] =
11231     {
11232       SolarizeCommand,
11233       SepiaToneCommand,
11234       SwirlCommand,
11235       ImplodeCommand,
11236       VignetteCommand,
11237       WaveCommand,
11238       OilPaintCommand,
11239       CharcoalDrawCommand
11240     },
11241     MiscellanyCommands[] =
11242     {
11243       InfoCommand,
11244       ZoomCommand,
11245       ShowPreviewCommand,
11246       ShowHistogramCommand,
11247       ShowMatteCommand
11248     },
11249     ROICommands[] =
11250     {
11251       ROIHelpCommand,
11252       ROIDismissCommand
11253     };
11254
11255   static const CommandType
11256     *Commands[ApplyMenus] =
11257     {
11258       FileCommands,
11259       EditCommands,
11260       TransformCommands,
11261       EnhanceCommands,
11262       EffectsCommands,
11263       FXCommands,
11264       MiscellanyCommands
11265     };
11266
11267   char
11268     command[MaxTextExtent],
11269     text[MaxTextExtent];
11270
11271   CommandType
11272     command_type;
11273
11274   Cursor
11275     cursor;
11276
11277   Image
11278     *roi_image;
11279
11280   int
11281     entry,
11282     id,
11283     x,
11284     y;
11285
11286   double
11287     scale_factor;
11288
11289   MagickProgressMonitor
11290     progress_monitor;
11291
11292   RectangleInfo
11293     crop_info,
11294     highlight_info,
11295     roi_info;
11296
11297   unsigned int
11298     height,
11299     width;
11300
11301   size_t
11302     state;
11303
11304   XEvent
11305     event;
11306
11307   /*
11308     Map Command widget.
11309   */
11310   (void) CloneString(&windows->command.name,"ROI");
11311   windows->command.data=0;
11312   (void) XCommandWidget(display,windows,ROIMenu,(XEvent *) NULL);
11313   (void) XMapRaised(display,windows->command.id);
11314   XClientMessage(display,windows->image.id,windows->im_protocols,
11315     windows->im_update_widget,CurrentTime);
11316   /*
11317     Track pointer until button 1 is pressed.
11318   */
11319   XQueryPosition(display,windows->image.id,&x,&y);
11320   (void) XSelectInput(display,windows->image.id,
11321     windows->image.attributes.event_mask | PointerMotionMask);
11322   roi_info.x=(ssize_t) windows->image.x+x;
11323   roi_info.y=(ssize_t) windows->image.y+y;
11324   roi_info.width=0;
11325   roi_info.height=0;
11326   cursor=XCreateFontCursor(display,XC_fleur);
11327   state=DefaultState;
11328   do
11329   {
11330     if (IfMagickTrue(windows->info.mapped) )
11331       {
11332         /*
11333           Display pointer position.
11334         */
11335         (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
11336           (long) roi_info.x,(long) roi_info.y);
11337         XInfoWidget(display,windows,text);
11338       }
11339     /*
11340       Wait for next event.
11341     */
11342     XScreenEvent(display,windows,&event,exception);
11343     if (event.xany.window == windows->command.id)
11344       {
11345         /*
11346           Select a command from the Command widget.
11347         */
11348         id=XCommandWidget(display,windows,ROIMenu,&event);
11349         if (id < 0)
11350           continue;
11351         switch (ROICommands[id])
11352         {
11353           case ROIHelpCommand:
11354           {
11355             XTextViewWidget(display,resource_info,windows,MagickFalse,
11356               "Help Viewer - Region of Interest",ImageROIHelp);
11357             break;
11358           }
11359           case ROIDismissCommand:
11360           {
11361             /*
11362               Prematurely exit.
11363             */
11364             state|=EscapeState;
11365             state|=ExitState;
11366             break;
11367           }
11368           default:
11369             break;
11370         }
11371         continue;
11372       }
11373     switch (event.type)
11374     {
11375       case ButtonPress:
11376       {
11377         if (event.xbutton.button != Button1)
11378           break;
11379         if (event.xbutton.window != windows->image.id)
11380           break;
11381         /*
11382           Note first corner of region of interest rectangle-- exit loop.
11383         */
11384         (void) XCheckDefineCursor(display,windows->image.id,cursor);
11385         roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11386         roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11387         state|=ExitState;
11388         break;
11389       }
11390       case ButtonRelease:
11391         break;
11392       case Expose:
11393         break;
11394       case KeyPress:
11395       {
11396         KeySym
11397           key_symbol;
11398
11399         if (event.xkey.window != windows->image.id)
11400           break;
11401         /*
11402           Respond to a user key press.
11403         */
11404         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11405           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11406         switch ((int) key_symbol)
11407         {
11408           case XK_Escape:
11409           case XK_F20:
11410           {
11411             /*
11412               Prematurely exit.
11413             */
11414             state|=EscapeState;
11415             state|=ExitState;
11416             break;
11417           }
11418           case XK_F1:
11419           case XK_Help:
11420           {
11421             XTextViewWidget(display,resource_info,windows,MagickFalse,
11422               "Help Viewer - Region of Interest",ImageROIHelp);
11423             break;
11424           }
11425           default:
11426           {
11427             (void) XBell(display,0);
11428             break;
11429           }
11430         }
11431         break;
11432       }
11433       case MotionNotify:
11434       {
11435         /*
11436           Map and unmap Info widget as text cursor crosses its boundaries.
11437         */
11438         x=event.xmotion.x;
11439         y=event.xmotion.y;
11440         if (IfMagickTrue(windows->info.mapped) )
11441           {
11442             if ((x < (int) (windows->info.x+windows->info.width)) &&
11443                 (y < (int) (windows->info.y+windows->info.height)))
11444               (void) XWithdrawWindow(display,windows->info.id,
11445                 windows->info.screen);
11446           }
11447         else
11448           if ((x > (int) (windows->info.x+windows->info.width)) ||
11449               (y > (int) (windows->info.y+windows->info.height)))
11450             (void) XMapWindow(display,windows->info.id);
11451         roi_info.x=(ssize_t) windows->image.x+x;
11452         roi_info.y=(ssize_t) windows->image.y+y;
11453         break;
11454       }
11455       default:
11456         break;
11457     }
11458   } while ((state & ExitState) == 0);
11459   (void) XSelectInput(display,windows->image.id,
11460     windows->image.attributes.event_mask);
11461   if ((state & EscapeState) != 0)
11462     {
11463       /*
11464         User want to exit without region of interest.
11465       */
11466       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11467       (void) XFreeCursor(display,cursor);
11468       return(MagickTrue);
11469     }
11470   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
11471   do
11472   {
11473     /*
11474       Size rectangle as pointer moves until the mouse button is released.
11475     */
11476     x=(int) roi_info.x;
11477     y=(int) roi_info.y;
11478     roi_info.width=0;
11479     roi_info.height=0;
11480     state=DefaultState;
11481     do
11482     {
11483       highlight_info=roi_info;
11484       highlight_info.x=roi_info.x-windows->image.x;
11485       highlight_info.y=roi_info.y-windows->image.y;
11486       if ((highlight_info.width > 3) && (highlight_info.height > 3))
11487         {
11488           /*
11489             Display info and draw region of interest rectangle.
11490           */
11491           if (IfMagickFalse(windows->info.mapped) )
11492             (void) XMapWindow(display,windows->info.id);
11493           (void) FormatLocaleString(text,MaxTextExtent,
11494             " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11495             roi_info.height,(double) roi_info.x,(double) roi_info.y);
11496           XInfoWidget(display,windows,text);
11497           XHighlightRectangle(display,windows->image.id,
11498             windows->image.highlight_context,&highlight_info);
11499         }
11500       else
11501         if (IfMagickTrue(windows->info.mapped) )
11502           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11503       /*
11504         Wait for next event.
11505       */
11506       XScreenEvent(display,windows,&event,exception);
11507       if ((highlight_info.width > 3) && (highlight_info.height > 3))
11508         XHighlightRectangle(display,windows->image.id,
11509           windows->image.highlight_context,&highlight_info);
11510       switch (event.type)
11511       {
11512         case ButtonPress:
11513         {
11514           roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11515           roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11516           break;
11517         }
11518         case ButtonRelease:
11519         {
11520           /*
11521             User has committed to region of interest rectangle.
11522           */
11523           roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11524           roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11525           XSetCursorState(display,windows,MagickFalse);
11526           state|=ExitState;
11527           if (LocaleCompare(windows->command.name,"Apply") == 0)
11528             break;
11529           (void) CloneString(&windows->command.name,"Apply");
11530           windows->command.data=ApplyMenus;
11531           (void) XCommandWidget(display,windows,ApplyMenu,(XEvent *) NULL);
11532           break;
11533         }
11534         case Expose:
11535           break;
11536         case MotionNotify:
11537         {
11538           roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11539           roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11540         }
11541         default:
11542           break;
11543       }
11544       if ((((int) roi_info.x != x) && ((int) roi_info.y != y)) ||
11545           ((state & ExitState) != 0))
11546         {
11547           /*
11548             Check boundary conditions.
11549           */
11550           if (roi_info.x < 0)
11551             roi_info.x=0;
11552           else
11553             if (roi_info.x > (ssize_t) windows->image.ximage->width)
11554               roi_info.x=(ssize_t) windows->image.ximage->width;
11555           if ((int) roi_info.x < x)
11556             roi_info.width=(unsigned int) (x-roi_info.x);
11557           else
11558             {
11559               roi_info.width=(unsigned int) (roi_info.x-x);
11560               roi_info.x=(ssize_t) x;
11561             }
11562           if (roi_info.y < 0)
11563             roi_info.y=0;
11564           else
11565             if (roi_info.y > (ssize_t) windows->image.ximage->height)
11566               roi_info.y=(ssize_t) windows->image.ximage->height;
11567           if ((int) roi_info.y < y)
11568             roi_info.height=(unsigned int) (y-roi_info.y);
11569           else
11570             {
11571               roi_info.height=(unsigned int) (roi_info.y-y);
11572               roi_info.y=(ssize_t) y;
11573             }
11574         }
11575     } while ((state & ExitState) == 0);
11576     /*
11577       Wait for user to grab a corner of the rectangle or press return.
11578     */
11579     state=DefaultState;
11580     command_type=NullCommand;
11581     crop_info.x=0;
11582     crop_info.y=0;
11583     (void) XMapWindow(display,windows->info.id);
11584     do
11585     {
11586       if (IfMagickTrue(windows->info.mapped) )
11587         {
11588           /*
11589             Display pointer position.
11590           */
11591           (void) FormatLocaleString(text,MaxTextExtent,
11592             " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11593             roi_info.height,(double) roi_info.x,(double) roi_info.y);
11594           XInfoWidget(display,windows,text);
11595         }
11596       highlight_info=roi_info;
11597       highlight_info.x=roi_info.x-windows->image.x;
11598       highlight_info.y=roi_info.y-windows->image.y;
11599       if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
11600         {
11601           state|=EscapeState;
11602           state|=ExitState;
11603           break;
11604         }
11605       if ((state & UpdateRegionState) != 0)
11606         {
11607           (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11608           switch (command_type)
11609           {
11610             case UndoCommand:
11611             case RedoCommand:
11612             {
11613               (void) XMagickCommand(display,resource_info,windows,command_type,
11614                 image,exception);
11615               break;
11616             }
11617             default:
11618             {
11619               /*
11620                 Region of interest is relative to image configuration.
11621               */
11622               progress_monitor=SetImageProgressMonitor(*image,
11623                 (MagickProgressMonitor) NULL,(*image)->client_data);
11624               crop_info=roi_info;
11625               width=(unsigned int) (*image)->columns;
11626               height=(unsigned int) (*image)->rows;
11627               x=0;
11628               y=0;
11629               if (windows->image.crop_geometry != (char *) NULL)
11630                 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
11631                   &width,&height);
11632               scale_factor=(double) width/windows->image.ximage->width;
11633               crop_info.x+=x;
11634               crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
11635               crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
11636               scale_factor=(double)
11637                 height/windows->image.ximage->height;
11638               crop_info.y+=y;
11639               crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
11640               crop_info.height=(unsigned int)
11641                 (scale_factor*crop_info.height+0.5);
11642               roi_image=CropImage(*image,&crop_info,exception);
11643               (void) SetImageProgressMonitor(*image,progress_monitor,
11644                 (*image)->client_data);
11645               if (roi_image == (Image *) NULL)
11646                 continue;
11647               /*
11648                 Apply image processing technique to the region of interest.
11649               */
11650               windows->image.orphan=MagickTrue;
11651               (void) XMagickCommand(display,resource_info,windows,command_type,
11652                 &roi_image,exception);
11653               progress_monitor=SetImageProgressMonitor(*image,
11654                 (MagickProgressMonitor) NULL,(*image)->client_data);
11655               (void) XMagickCommand(display,resource_info,windows,
11656                 SaveToUndoBufferCommand,image,exception);
11657               windows->image.orphan=MagickFalse;
11658               (void) CompositeImage(*image,roi_image,CopyCompositeOp,
11659                 MagickTrue,crop_info.x,crop_info.y,exception);
11660               roi_image=DestroyImage(roi_image);
11661               (void) SetImageProgressMonitor(*image,progress_monitor,
11662                 (*image)->client_data);
11663               break;
11664             }
11665           }
11666           if (command_type != InfoCommand)
11667             {
11668               XConfigureImageColormap(display,resource_info,windows,*image,
11669                 exception);
11670               (void) XConfigureImage(display,resource_info,windows,*image,
11671                 exception);
11672             }
11673           XCheckRefreshWindows(display,windows);
11674           XInfoWidget(display,windows,text);
11675           (void) XSetFunction(display,windows->image.highlight_context,
11676             GXinvert);
11677           state&=(~UpdateRegionState);
11678         }
11679       XHighlightRectangle(display,windows->image.id,
11680         windows->image.highlight_context,&highlight_info);
11681       XScreenEvent(display,windows,&event,exception);
11682       if (event.xany.window == windows->command.id)
11683         {
11684           /*
11685             Select a command from the Command widget.
11686           */
11687           (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11688           command_type=NullCommand;
11689           id=XCommandWidget(display,windows,ApplyMenu,&event);
11690           if (id >= 0)
11691             {
11692               (void) CopyMagickString(command,ApplyMenu[id],MaxTextExtent);
11693               command_type=ApplyCommands[id];
11694               if (id < ApplyMenus)
11695                 {
11696                   /*
11697                     Select a command from a pop-up menu.
11698                   */
11699                   entry=XMenuWidget(display,windows,ApplyMenu[id],
11700                     (const char **) Menus[id],command);
11701                   if (entry >= 0)
11702                     {
11703                       (void) CopyMagickString(command,Menus[id][entry],
11704                         MaxTextExtent);
11705                       command_type=Commands[id][entry];
11706                     }
11707                 }
11708             }
11709           (void) XSetFunction(display,windows->image.highlight_context,
11710             GXinvert);
11711           XHighlightRectangle(display,windows->image.id,
11712             windows->image.highlight_context,&highlight_info);
11713           if (command_type == HelpCommand)
11714             {
11715               (void) XSetFunction(display,windows->image.highlight_context,
11716                 GXcopy);
11717               XTextViewWidget(display,resource_info,windows,MagickFalse,
11718                 "Help Viewer - Region of Interest",ImageROIHelp);
11719               (void) XSetFunction(display,windows->image.highlight_context,
11720                 GXinvert);
11721               continue;
11722             }
11723           if (command_type == QuitCommand)
11724             {
11725               /*
11726                 exit.
11727               */
11728               state|=EscapeState;
11729               state|=ExitState;
11730               continue;
11731             }
11732           if (command_type != NullCommand)
11733             state|=UpdateRegionState;
11734           continue;
11735         }
11736       XHighlightRectangle(display,windows->image.id,
11737         windows->image.highlight_context,&highlight_info);
11738       switch (event.type)
11739       {
11740         case ButtonPress:
11741         {
11742           x=windows->image.x;
11743           y=windows->image.y;
11744           if (event.xbutton.button != Button1)
11745             break;
11746           if (event.xbutton.window != windows->image.id)
11747             break;
11748           x=windows->image.x+event.xbutton.x;
11749           y=windows->image.y+event.xbutton.y;
11750           if ((x < (int) (roi_info.x+RoiDelta)) &&
11751               (x > (int) (roi_info.x-RoiDelta)) &&
11752               (y < (int) (roi_info.y+RoiDelta)) &&
11753               (y > (int) (roi_info.y-RoiDelta)))
11754             {
11755               roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11756               roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11757               state|=UpdateConfigurationState;
11758               break;
11759             }
11760           if ((x < (int) (roi_info.x+RoiDelta)) &&
11761               (x > (int) (roi_info.x-RoiDelta)) &&
11762               (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11763               (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11764             {
11765               roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11766               state|=UpdateConfigurationState;
11767               break;
11768             }
11769           if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11770               (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11771               (y < (int) (roi_info.y+RoiDelta)) &&
11772               (y > (int) (roi_info.y-RoiDelta)))
11773             {
11774               roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11775               state|=UpdateConfigurationState;
11776               break;
11777             }
11778           if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11779               (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11780               (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11781               (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11782             {
11783               state|=UpdateConfigurationState;
11784               break;
11785             }
11786         }
11787         case ButtonRelease:
11788         {
11789           if (event.xbutton.window == windows->pan.id)
11790             if ((highlight_info.x != crop_info.x-windows->image.x) ||
11791                 (highlight_info.y != crop_info.y-windows->image.y))
11792               XHighlightRectangle(display,windows->image.id,
11793                 windows->image.highlight_context,&highlight_info);
11794           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11795             event.xbutton.time);
11796           break;
11797         }
11798         case Expose:
11799         {
11800           if (event.xexpose.window == windows->image.id)
11801             if (event.xexpose.count == 0)
11802               {
11803                 event.xexpose.x=(int) highlight_info.x;
11804                 event.xexpose.y=(int) highlight_info.y;
11805                 event.xexpose.width=(int) highlight_info.width;
11806                 event.xexpose.height=(int) highlight_info.height;
11807                 XRefreshWindow(display,&windows->image,&event);
11808               }
11809           if (event.xexpose.window == windows->info.id)
11810             if (event.xexpose.count == 0)
11811               XInfoWidget(display,windows,text);
11812           break;
11813         }
11814         case KeyPress:
11815         {
11816           KeySym
11817             key_symbol;
11818
11819           if (event.xkey.window != windows->image.id)
11820             break;
11821           /*
11822             Respond to a user key press.
11823           */
11824           (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11825             sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11826           switch ((int) key_symbol)
11827           {
11828             case XK_Shift_L:
11829             case XK_Shift_R:
11830               break;
11831             case XK_Escape:
11832             case XK_F20:
11833               state|=EscapeState;
11834             case XK_Return:
11835             {
11836               state|=ExitState;
11837               break;
11838             }
11839             case XK_Home:
11840             case XK_KP_Home:
11841             {
11842               roi_info.x=(ssize_t) (windows->image.width/2L-roi_info.width/2L);
11843               roi_info.y=(ssize_t) (windows->image.height/2L-
11844                 roi_info.height/2L);
11845               break;
11846             }
11847             case XK_Left:
11848             case XK_KP_Left:
11849             {
11850               roi_info.x--;
11851               break;
11852             }
11853             case XK_Up:
11854             case XK_KP_Up:
11855             case XK_Next:
11856             {
11857               roi_info.y--;
11858               break;
11859             }
11860             case XK_Right:
11861             case XK_KP_Right:
11862             {
11863               roi_info.x++;
11864               break;
11865             }
11866             case XK_Prior:
11867             case XK_Down:
11868             case XK_KP_Down:
11869             {
11870               roi_info.y++;
11871               break;
11872             }
11873             case XK_F1:
11874             case XK_Help:
11875             {
11876               (void) XSetFunction(display,windows->image.highlight_context,
11877                 GXcopy);
11878               XTextViewWidget(display,resource_info,windows,MagickFalse,
11879                 "Help Viewer - Region of Interest",ImageROIHelp);
11880               (void) XSetFunction(display,windows->image.highlight_context,
11881                 GXinvert);
11882               break;
11883             }
11884             default:
11885             {
11886               command_type=XImageWindowCommand(display,resource_info,windows,
11887                 event.xkey.state,key_symbol,image,exception);
11888               if (command_type != NullCommand)
11889                 state|=UpdateRegionState;
11890               break;
11891             }
11892           }
11893           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11894             event.xkey.time);
11895           break;
11896         }
11897         case KeyRelease:
11898           break;
11899         case MotionNotify:
11900         {
11901           if (event.xbutton.window != windows->image.id)
11902             break;
11903           /*
11904             Map and unmap Info widget as text cursor crosses its boundaries.
11905           */
11906           x=event.xmotion.x;
11907           y=event.xmotion.y;
11908           if (IfMagickTrue(windows->info.mapped) )
11909             {
11910               if ((x < (int) (windows->info.x+windows->info.width)) &&
11911                   (y < (int) (windows->info.y+windows->info.height)))
11912                 (void) XWithdrawWindow(display,windows->info.id,
11913                   windows->info.screen);
11914             }
11915           else
11916             if ((x > (int) (windows->info.x+windows->info.width)) ||
11917                 (y > (int) (windows->info.y+windows->info.height)))
11918               (void) XMapWindow(display,windows->info.id);
11919           roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11920           roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11921           break;
11922         }
11923         case SelectionRequest:
11924         {
11925           XSelectionEvent
11926             notify;
11927
11928           XSelectionRequestEvent
11929             *request;
11930
11931           /*
11932             Set primary selection.
11933           */
11934           (void) FormatLocaleString(text,MaxTextExtent,
11935             "%.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11936             roi_info.height,(double) roi_info.x,(double) roi_info.y);
11937           request=(&(event.xselectionrequest));
11938           (void) XChangeProperty(request->display,request->requestor,
11939             request->property,request->target,8,PropModeReplace,
11940             (unsigned char *) text,(int) strlen(text));
11941           notify.type=SelectionNotify;
11942           notify.display=request->display;
11943           notify.requestor=request->requestor;
11944           notify.selection=request->selection;
11945           notify.target=request->target;
11946           notify.time=request->time;
11947           if (request->property == None)
11948             notify.property=request->target;
11949           else
11950             notify.property=request->property;
11951           (void) XSendEvent(request->display,request->requestor,False,0,
11952             (XEvent *) &notify);
11953         }
11954         default:
11955           break;
11956       }
11957       if ((state & UpdateConfigurationState) != 0)
11958         {
11959           (void) XPutBackEvent(display,&event);
11960           (void) XCheckDefineCursor(display,windows->image.id,cursor);
11961           break;
11962         }
11963     } while ((state & ExitState) == 0);
11964   } while ((state & ExitState) == 0);
11965   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11966   XSetCursorState(display,windows,MagickFalse);
11967   if ((state & EscapeState) != 0)
11968     return(MagickTrue);
11969   return(MagickTrue);
11970 }
11971 \f
11972 /*
11973 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11974 %                                                                             %
11975 %                                                                             %
11976 %                                                                             %
11977 +   X R o t a t e I m a g e                                                   %
11978 %                                                                             %
11979 %                                                                             %
11980 %                                                                             %
11981 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11982 %
11983 %  XRotateImage() rotates the X image.  If the degrees parameter if zero, the
11984 %  rotation angle is computed from the slope of a line drawn by the user.
11985 %
11986 %  The format of the XRotateImage method is:
11987 %
11988 %      MagickBooleanType XRotateImage(Display *display,
11989 %        XResourceInfo *resource_info,XWindows *windows,double degrees,
11990 %        Image **image,ExceptionInfo *exception)
11991 %
11992 %  A description of each parameter follows:
11993 %
11994 %    o display: Specifies a connection to an X server; returned from
11995 %      XOpenDisplay.
11996 %
11997 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
11998 %
11999 %    o windows: Specifies a pointer to a XWindows structure.
12000 %
12001 %    o degrees: Specifies the number of degrees to rotate the image.
12002 %
12003 %    o image: the image.
12004 %
12005 %    o exception: return any errors or warnings in this structure.
12006 %
12007 */
12008 static MagickBooleanType XRotateImage(Display *display,
12009   XResourceInfo *resource_info,XWindows *windows,double degrees,Image **image,
12010   ExceptionInfo *exception)
12011 {
12012   static const char
12013     *RotateMenu[] =
12014     {
12015       "Pixel Color",
12016       "Direction",
12017       "Help",
12018       "Dismiss",
12019       (char *) NULL
12020     };
12021
12022   static ModeType
12023     direction = HorizontalRotateCommand;
12024
12025   static const ModeType
12026     DirectionCommands[] =
12027     {
12028       HorizontalRotateCommand,
12029       VerticalRotateCommand
12030     },
12031     RotateCommands[] =
12032     {
12033       RotateColorCommand,
12034       RotateDirectionCommand,
12035       RotateHelpCommand,
12036       RotateDismissCommand
12037     };
12038
12039   static unsigned int
12040     pen_id = 0;
12041
12042   char
12043     command[MaxTextExtent],
12044     text[MaxTextExtent];
12045
12046   Image
12047     *rotate_image;
12048
12049   int
12050     id,
12051     x,
12052     y;
12053
12054   double
12055     normalized_degrees;
12056
12057   register int
12058     i;
12059
12060   unsigned int
12061     height,
12062     rotations,
12063     width;
12064
12065   if (degrees == 0.0)
12066     {
12067       unsigned int
12068         distance;
12069
12070       size_t
12071         state;
12072
12073       XEvent
12074         event;
12075
12076       XSegment
12077         rotate_info;
12078
12079       /*
12080         Map Command widget.
12081       */
12082       (void) CloneString(&windows->command.name,"Rotate");
12083       windows->command.data=2;
12084       (void) XCommandWidget(display,windows,RotateMenu,(XEvent *) NULL);
12085       (void) XMapRaised(display,windows->command.id);
12086       XClientMessage(display,windows->image.id,windows->im_protocols,
12087         windows->im_update_widget,CurrentTime);
12088       /*
12089         Wait for first button press.
12090       */
12091       (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12092       XQueryPosition(display,windows->image.id,&x,&y);
12093       rotate_info.x1=x;
12094       rotate_info.y1=y;
12095       rotate_info.x2=x;
12096       rotate_info.y2=y;
12097       state=DefaultState;
12098       do
12099       {
12100         XHighlightLine(display,windows->image.id,
12101           windows->image.highlight_context,&rotate_info);
12102         /*
12103           Wait for next event.
12104         */
12105         XScreenEvent(display,windows,&event,exception);
12106         XHighlightLine(display,windows->image.id,
12107           windows->image.highlight_context,&rotate_info);
12108         if (event.xany.window == windows->command.id)
12109           {
12110             /*
12111               Select a command from the Command widget.
12112             */
12113             id=XCommandWidget(display,windows,RotateMenu,&event);
12114             if (id < 0)
12115               continue;
12116             (void) XSetFunction(display,windows->image.highlight_context,
12117               GXcopy);
12118             switch (RotateCommands[id])
12119             {
12120               case RotateColorCommand:
12121               {
12122                 const char
12123                   *ColorMenu[MaxNumberPens];
12124
12125                 int
12126                   pen_number;
12127
12128                 XColor
12129                   color;
12130
12131                 /*
12132                   Initialize menu selections.
12133                 */
12134                 for (i=0; i < (int) (MaxNumberPens-2); i++)
12135                   ColorMenu[i]=resource_info->pen_colors[i];
12136                 ColorMenu[MaxNumberPens-2]="Browser...";
12137                 ColorMenu[MaxNumberPens-1]=(const char *) NULL;
12138                 /*
12139                   Select a pen color from the pop-up menu.
12140                 */
12141                 pen_number=XMenuWidget(display,windows,RotateMenu[id],
12142                   (const char **) ColorMenu,command);
12143                 if (pen_number < 0)
12144                   break;
12145                 if (pen_number == (MaxNumberPens-2))
12146                   {
12147                     static char
12148                       color_name[MaxTextExtent] = "gray";
12149
12150                     /*
12151                       Select a pen color from a dialog.
12152                     */
12153                     resource_info->pen_colors[pen_number]=color_name;
12154                     XColorBrowserWidget(display,windows,"Select",color_name);
12155                     if (*color_name == '\0')
12156                       break;
12157                   }
12158                 /*
12159                   Set pen color.
12160                 */
12161                 (void) XParseColor(display,windows->map_info->colormap,
12162                   resource_info->pen_colors[pen_number],&color);
12163                 XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
12164                   (unsigned int) MaxColors,&color);
12165                 windows->pixel_info->pen_colors[pen_number]=color;
12166                 pen_id=(unsigned int) pen_number;
12167                 break;
12168               }
12169               case RotateDirectionCommand:
12170               {
12171                 static const char
12172                   *Directions[] =
12173                   {
12174                     "horizontal",
12175                     "vertical",
12176                     (char *) NULL,
12177                   };
12178
12179                 /*
12180                   Select a command from the pop-up menu.
12181                 */
12182                 id=XMenuWidget(display,windows,RotateMenu[id],
12183                   Directions,command);
12184                 if (id >= 0)
12185                   direction=DirectionCommands[id];
12186                 break;
12187               }
12188               case RotateHelpCommand:
12189               {
12190                 XTextViewWidget(display,resource_info,windows,MagickFalse,
12191                   "Help Viewer - Image Rotation",ImageRotateHelp);
12192                 break;
12193               }
12194               case RotateDismissCommand:
12195               {
12196                 /*
12197                   Prematurely exit.
12198                 */
12199                 state|=EscapeState;
12200                 state|=ExitState;
12201                 break;
12202               }
12203               default:
12204                 break;
12205             }
12206             (void) XSetFunction(display,windows->image.highlight_context,
12207               GXinvert);
12208             continue;
12209           }
12210         switch (event.type)
12211         {
12212           case ButtonPress:
12213           {
12214             if (event.xbutton.button != Button1)
12215               break;
12216             if (event.xbutton.window != windows->image.id)
12217               break;
12218             /*
12219               exit loop.
12220             */
12221             (void) XSetFunction(display,windows->image.highlight_context,
12222               GXcopy);
12223             rotate_info.x1=event.xbutton.x;
12224             rotate_info.y1=event.xbutton.y;
12225             state|=ExitState;
12226             break;
12227           }
12228           case ButtonRelease:
12229             break;
12230           case Expose:
12231             break;
12232           case KeyPress:
12233           {
12234             char
12235               command[MaxTextExtent];
12236
12237             KeySym
12238               key_symbol;
12239
12240             if (event.xkey.window != windows->image.id)
12241               break;
12242             /*
12243               Respond to a user key press.
12244             */
12245             (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
12246               sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12247             switch ((int) key_symbol)
12248             {
12249               case XK_Escape:
12250               case XK_F20:
12251               {
12252                 /*
12253                   Prematurely exit.
12254                 */
12255                 state|=EscapeState;
12256                 state|=ExitState;
12257                 break;
12258               }
12259               case XK_F1:
12260               case XK_Help:
12261               {
12262                 (void) XSetFunction(display,windows->image.highlight_context,
12263                   GXcopy);
12264                 XTextViewWidget(display,resource_info,windows,MagickFalse,
12265                   "Help Viewer - Image Rotation",ImageRotateHelp);
12266                 (void) XSetFunction(display,windows->image.highlight_context,
12267                   GXinvert);
12268                 break;
12269               }
12270               default:
12271               {
12272                 (void) XBell(display,0);
12273                 break;
12274               }
12275             }
12276             break;
12277           }
12278           case MotionNotify:
12279           {
12280             rotate_info.x1=event.xmotion.x;
12281             rotate_info.y1=event.xmotion.y;
12282           }
12283         }
12284         rotate_info.x2=rotate_info.x1;
12285         rotate_info.y2=rotate_info.y1;
12286         if (direction == HorizontalRotateCommand)
12287           rotate_info.x2+=32;
12288         else
12289           rotate_info.y2-=32;
12290       } while ((state & ExitState) == 0);
12291       (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12292       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12293       if ((state & EscapeState) != 0)
12294         return(MagickTrue);
12295       /*
12296         Draw line as pointer moves until the mouse button is released.
12297       */
12298       distance=0;
12299       (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12300       state=DefaultState;
12301       do
12302       {
12303         if (distance > 9)
12304           {
12305             /*
12306               Display info and draw rotation line.
12307             */
12308             if (IfMagickFalse(windows->info.mapped) )
12309               (void) XMapWindow(display,windows->info.id);
12310             (void) FormatLocaleString(text,MaxTextExtent," %g",
12311               direction == VerticalRotateCommand ? degrees-90.0 : degrees);
12312             XInfoWidget(display,windows,text);
12313             XHighlightLine(display,windows->image.id,
12314               windows->image.highlight_context,&rotate_info);
12315           }
12316         else
12317           if (IfMagickTrue(windows->info.mapped) )
12318             (void) XWithdrawWindow(display,windows->info.id,
12319               windows->info.screen);
12320         /*
12321           Wait for next event.
12322         */
12323         XScreenEvent(display,windows,&event,exception);
12324         if (distance > 9)
12325           XHighlightLine(display,windows->image.id,
12326             windows->image.highlight_context,&rotate_info);
12327         switch (event.type)
12328         {
12329           case ButtonPress:
12330             break;
12331           case ButtonRelease:
12332           {
12333             /*
12334               User has committed to rotation line.
12335             */
12336             rotate_info.x2=event.xbutton.x;
12337             rotate_info.y2=event.xbutton.y;
12338             state|=ExitState;
12339             break;
12340           }
12341           case Expose:
12342             break;
12343           case MotionNotify:
12344           {
12345             rotate_info.x2=event.xmotion.x;
12346             rotate_info.y2=event.xmotion.y;
12347           }
12348           default:
12349             break;
12350         }
12351         /*
12352           Check boundary conditions.
12353         */
12354         if (rotate_info.x2 < 0)
12355           rotate_info.x2=0;
12356         else
12357           if (rotate_info.x2 > (int) windows->image.width)
12358             rotate_info.x2=(short) windows->image.width;
12359         if (rotate_info.y2 < 0)
12360           rotate_info.y2=0;
12361         else
12362           if (rotate_info.y2 > (int) windows->image.height)
12363             rotate_info.y2=(short) windows->image.height;
12364         /*
12365           Compute rotation angle from the slope of the line.
12366         */
12367         degrees=0.0;
12368         distance=(unsigned int)
12369           ((rotate_info.x2-rotate_info.x1+1)*(rotate_info.x2-rotate_info.x1+1))+
12370           ((rotate_info.y2-rotate_info.y1+1)*(rotate_info.y2-rotate_info.y1+1));
12371         if (distance > 9)
12372           degrees=RadiansToDegrees(-atan2((double) (rotate_info.y2-
12373             rotate_info.y1),(double) (rotate_info.x2-rotate_info.x1)));
12374       } while ((state & ExitState) == 0);
12375       (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12376       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12377       if (distance <= 9)
12378         return(MagickTrue);
12379     }
12380   if (direction == VerticalRotateCommand)
12381     degrees-=90.0;
12382   if (degrees == 0.0)
12383     return(MagickTrue);
12384   /*
12385     Rotate image.
12386   */
12387   normalized_degrees=degrees;
12388   while (normalized_degrees < -45.0)
12389     normalized_degrees+=360.0;
12390   for (rotations=0; normalized_degrees > 45.0; rotations++)
12391     normalized_degrees-=90.0;
12392   if (normalized_degrees != 0.0)
12393     (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
12394       exception);
12395   XSetCursorState(display,windows,MagickTrue);
12396   XCheckRefreshWindows(display,windows);
12397   (*image)->background_color.red=(double) ScaleShortToQuantum(
12398     windows->pixel_info->pen_colors[pen_id].red);
12399   (*image)->background_color.green=(double) ScaleShortToQuantum(
12400     windows->pixel_info->pen_colors[pen_id].green);
12401   (*image)->background_color.blue=(double) ScaleShortToQuantum(
12402     windows->pixel_info->pen_colors[pen_id].blue);
12403   rotate_image=RotateImage(*image,degrees,exception);
12404   XSetCursorState(display,windows,MagickFalse);
12405   if (rotate_image == (Image *) NULL)
12406     return(MagickFalse);
12407   *image=DestroyImage(*image);
12408   *image=rotate_image;
12409   if (windows->image.crop_geometry != (char *) NULL)
12410     {
12411       /*
12412         Rotate crop geometry.
12413       */
12414       width=(unsigned int) (*image)->columns;
12415       height=(unsigned int) (*image)->rows;
12416       (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12417       switch (rotations % 4)
12418       {
12419         default:
12420         case 0:
12421           break;
12422         case 1:
12423         {
12424           /*
12425             Rotate 90 degrees.
12426           */
12427           (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12428             "%ux%u%+d%+d",height,width,(int) (*image)->columns-
12429             (int) height-y,x);
12430           break;
12431         }
12432         case 2:
12433         {
12434           /*
12435             Rotate 180 degrees.
12436           */
12437           (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12438             "%ux%u%+d%+d",width,height,(int) width-x,(int) height-y);
12439           break;
12440         }
12441         case 3:
12442         {
12443           /*
12444             Rotate 270 degrees.
12445           */
12446           (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12447             "%ux%u%+d%+d",height,width,y,(int) (*image)->rows-(int) width-x);
12448           break;
12449         }
12450       }
12451     }
12452   if (IfMagickTrue(windows->image.orphan) )
12453     return(MagickTrue);
12454   if (normalized_degrees != 0.0)
12455     {
12456       /*
12457         Update image colormap.
12458       */
12459       windows->image.window_changes.width=(int) (*image)->columns;
12460       windows->image.window_changes.height=(int) (*image)->rows;
12461       if (windows->image.crop_geometry != (char *) NULL)
12462         {
12463           /*
12464             Obtain dimensions of image from crop geometry.
12465           */
12466           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
12467             &width,&height);
12468           windows->image.window_changes.width=(int) width;
12469           windows->image.window_changes.height=(int) height;
12470         }
12471       XConfigureImageColormap(display,resource_info,windows,*image,exception);
12472     }
12473   else
12474     if (((rotations % 4) == 1) || ((rotations % 4) == 3))
12475       {
12476         windows->image.window_changes.width=windows->image.ximage->height;
12477         windows->image.window_changes.height=windows->image.ximage->width;
12478       }
12479   /*
12480     Update image configuration.
12481   */
12482   (void) XConfigureImage(display,resource_info,windows,*image,exception);
12483   return(MagickTrue);
12484 }
12485 \f
12486 /*
12487 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12488 %                                                                             %
12489 %                                                                             %
12490 %                                                                             %
12491 +   X S a v e I m a g e                                                       %
12492 %                                                                             %
12493 %                                                                             %
12494 %                                                                             %
12495 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12496 %
12497 %  XSaveImage() saves an image to a file.
12498 %
12499 %  The format of the XSaveImage method is:
12500 %
12501 %      MagickBooleanType XSaveImage(Display *display,
12502 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
12503 %        ExceptionInfo *exception)
12504 %
12505 %  A description of each parameter follows:
12506 %
12507 %    o display: Specifies a connection to an X server; returned from
12508 %      XOpenDisplay.
12509 %
12510 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12511 %
12512 %    o windows: Specifies a pointer to a XWindows structure.
12513 %
12514 %    o image: the image.
12515 %
12516 %    o exception: return any errors or warnings in this structure.
12517 %
12518 */
12519 static MagickBooleanType XSaveImage(Display *display,
12520   XResourceInfo *resource_info,XWindows *windows,Image *image,
12521   ExceptionInfo *exception)
12522 {
12523   char
12524     filename[MaxTextExtent],
12525     geometry[MaxTextExtent];
12526
12527   Image
12528     *save_image;
12529
12530   ImageInfo
12531     *image_info;
12532
12533   MagickStatusType
12534     status;
12535
12536   /*
12537     Request file name from user.
12538   */
12539   if (resource_info->write_filename != (char *) NULL)
12540     (void) CopyMagickString(filename,resource_info->write_filename,
12541       MaxTextExtent);
12542   else
12543     {
12544       char
12545         path[MaxTextExtent];
12546
12547       int
12548         status;
12549
12550       GetPathComponent(image->filename,HeadPath,path);
12551       GetPathComponent(image->filename,TailPath,filename);
12552       if (*path != '\0')
12553         {
12554           status=chdir(path);
12555           if (status == -1)
12556             (void) ThrowMagickException(exception,GetMagickModule(),
12557               FileOpenError,"UnableToOpenFile","%s",path);
12558         }
12559     }
12560   XFileBrowserWidget(display,windows,"Save",filename);
12561   if (*filename == '\0')
12562     return(MagickTrue);
12563   if (IfMagickTrue(IsPathAccessible(filename)) )
12564     {
12565       int
12566         status;
12567
12568       /*
12569         File exists-- seek user's permission before overwriting.
12570       */
12571       status=XConfirmWidget(display,windows,"Overwrite",filename);
12572       if (status <= 0)
12573         return(MagickTrue);
12574     }
12575   image_info=CloneImageInfo(resource_info->image_info);
12576   (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
12577   (void) SetImageInfo(image_info,1,exception);
12578   if ((LocaleCompare(image_info->magick,"JPEG") == 0) ||
12579       (LocaleCompare(image_info->magick,"JPG") == 0))
12580     {
12581       char
12582         quality[MaxTextExtent];
12583
12584       int
12585         status;
12586
12587       /*
12588         Request JPEG quality from user.
12589       */
12590       (void) FormatLocaleString(quality,MaxTextExtent,"%.20g",(double)
12591         image->quality);
12592       status=XDialogWidget(display,windows,"Save","Enter JPEG quality:",
12593         quality);
12594       if (*quality == '\0')
12595         return(MagickTrue);
12596       image->quality=StringToUnsignedLong(quality);
12597       image_info->interlace=status != 0 ? NoInterlace : PlaneInterlace;
12598     }
12599   if ((LocaleCompare(image_info->magick,"EPS") == 0) ||
12600       (LocaleCompare(image_info->magick,"PDF") == 0) ||
12601       (LocaleCompare(image_info->magick,"PS") == 0) ||
12602       (LocaleCompare(image_info->magick,"PS2") == 0))
12603     {
12604       char
12605         geometry[MaxTextExtent];
12606
12607       /*
12608         Request page geometry from user.
12609       */
12610       (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
12611       if (LocaleCompare(image_info->magick,"PDF") == 0)
12612         (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
12613       if (image_info->page != (char *) NULL)
12614         (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
12615       XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
12616         "Select page geometry:",geometry);
12617       if (*geometry != '\0')
12618         image_info->page=GetPageGeometry(geometry);
12619     }
12620   /*
12621     Apply image transforms.
12622   */
12623   XSetCursorState(display,windows,MagickTrue);
12624   XCheckRefreshWindows(display,windows);
12625   save_image=CloneImage(image,0,0,MagickTrue,exception);
12626   if (save_image == (Image *) NULL)
12627     return(MagickFalse);
12628   (void) FormatLocaleString(geometry,MaxTextExtent,"%dx%d!",
12629     windows->image.ximage->width,windows->image.ximage->height);
12630   (void) TransformImage(&save_image,windows->image.crop_geometry,geometry,
12631     exception);
12632   /*
12633     Write image.
12634   */
12635   (void) CopyMagickString(save_image->filename,filename,MaxTextExtent);
12636   status=WriteImage(image_info,save_image,exception);
12637   if (IfMagickTrue(status) )
12638     image->taint=MagickFalse;
12639   save_image=DestroyImage(save_image);
12640   image_info=DestroyImageInfo(image_info);
12641   XSetCursorState(display,windows,MagickFalse);
12642   return(IsMagickTrue(status));
12643 }
12644 \f
12645 /*
12646 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12647 %                                                                             %
12648 %                                                                             %
12649 %                                                                             %
12650 +   X S c r e e n E v e n t                                                   %
12651 %                                                                             %
12652 %                                                                             %
12653 %                                                                             %
12654 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12655 %
12656 %  XScreenEvent() handles global events associated with the Pan and Magnify
12657 %  windows.
12658 %
12659 %  The format of the XScreenEvent function is:
12660 %
12661 %      void XScreenEvent(Display *display,XWindows *windows,XEvent *event,
12662 %        ExceptionInfo *exception)
12663 %
12664 %  A description of each parameter follows:
12665 %
12666 %    o display: Specifies a pointer to the Display structure;  returned from
12667 %      XOpenDisplay.
12668 %
12669 %    o windows: Specifies a pointer to a XWindows structure.
12670 %
12671 %    o event: Specifies a pointer to a X11 XEvent structure.
12672 %
12673 %    o exception: return any errors or warnings in this structure.
12674 %
12675 */
12676
12677 #if defined(__cplusplus) || defined(c_plusplus)
12678 extern "C" {
12679 #endif
12680
12681 static int XPredicate(Display *magick_unused(display),XEvent *event,char *data)
12682 {
12683   register XWindows
12684     *windows;
12685
12686   windows=(XWindows *) data;
12687   if ((event->type == ClientMessage) &&
12688       (event->xclient.window == windows->image.id))
12689     return(MagickFalse);
12690   return(MagickTrue);
12691 }
12692
12693 #if defined(__cplusplus) || defined(c_plusplus)
12694 }
12695 #endif
12696
12697 static void XScreenEvent(Display *display,XWindows *windows,XEvent *event,
12698   ExceptionInfo *exception)
12699 {
12700   register int
12701     x,
12702     y;
12703
12704   (void) XIfEvent(display,event,XPredicate,(char *) windows);
12705   if (event->xany.window == windows->command.id)
12706     return;
12707   switch (event->type)
12708   {
12709     case ButtonPress:
12710     case ButtonRelease:
12711     {
12712       if ((event->xbutton.button == Button3) &&
12713           (event->xbutton.state & Mod1Mask))
12714         {
12715           /*
12716             Convert Alt-Button3 to Button2.
12717           */
12718           event->xbutton.button=Button2;
12719           event->xbutton.state&=(~Mod1Mask);
12720         }
12721       if (event->xbutton.window == windows->backdrop.id)
12722         {
12723           (void) XSetInputFocus(display,event->xbutton.window,RevertToParent,
12724             event->xbutton.time);
12725           break;
12726         }
12727       if (event->xbutton.window == windows->pan.id)
12728         {
12729           XPanImage(display,windows,event,exception);
12730           break;
12731         }
12732       if (event->xbutton.window == windows->image.id)
12733         if (event->xbutton.button == Button2)
12734           {
12735             /*
12736               Update magnified image.
12737             */
12738             x=event->xbutton.x;
12739             y=event->xbutton.y;
12740             if (x < 0)
12741               x=0;
12742             else
12743               if (x >= (int) windows->image.width)
12744                 x=(int) (windows->image.width-1);
12745             windows->magnify.x=(int) windows->image.x+x;
12746             if (y < 0)
12747               y=0;
12748             else
12749              if (y >= (int) windows->image.height)
12750                y=(int) (windows->image.height-1);
12751             windows->magnify.y=windows->image.y+y;
12752             if (IfMagickFalse(windows->magnify.mapped) )
12753               (void) XMapRaised(display,windows->magnify.id);
12754             XMakeMagnifyImage(display,windows,exception);
12755             if (event->type == ButtonRelease)
12756               (void) XWithdrawWindow(display,windows->info.id,
12757                 windows->info.screen);
12758             break;
12759           }
12760       break;
12761     }
12762     case ClientMessage:
12763     {
12764       /*
12765         If client window delete message, exit.
12766       */
12767       if (event->xclient.message_type != windows->wm_protocols)
12768         break;
12769       if (*event->xclient.data.l != (long) windows->wm_delete_window)
12770         break;
12771       if (event->xclient.window == windows->magnify.id)
12772         {
12773           (void) XWithdrawWindow(display,windows->magnify.id,
12774             windows->magnify.screen);
12775           break;
12776         }
12777       break;
12778     }
12779     case ConfigureNotify:
12780     {
12781       if (event->xconfigure.window == windows->magnify.id)
12782         {
12783           unsigned int
12784             magnify;
12785
12786           /*
12787             Magnify window has a new configuration.
12788           */
12789           windows->magnify.width=(unsigned int) event->xconfigure.width;
12790           windows->magnify.height=(unsigned int) event->xconfigure.height;
12791           if (IfMagickFalse(windows->magnify.mapped) )
12792             break;
12793           magnify=1;
12794           while ((int) magnify <= event->xconfigure.width)
12795             magnify<<=1;
12796           while ((int) magnify <= event->xconfigure.height)
12797             magnify<<=1;
12798           magnify>>=1;
12799           if (((int) magnify != event->xconfigure.width) ||
12800               ((int) magnify != event->xconfigure.height))
12801             {
12802               XWindowChanges
12803                 window_changes;
12804
12805               window_changes.width=(int) magnify;
12806               window_changes.height=(int) magnify;
12807               (void) XReconfigureWMWindow(display,windows->magnify.id,
12808                 windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
12809                 &window_changes);
12810               break;
12811             }
12812           XMakeMagnifyImage(display,windows,exception);
12813           break;
12814         }
12815       break;
12816     }
12817     case Expose:
12818     {
12819       if (event->xexpose.window == windows->image.id)
12820         {
12821           XRefreshWindow(display,&windows->image,event);
12822           break;
12823         }
12824       if (event->xexpose.window == windows->pan.id)
12825         if (event->xexpose.count == 0)
12826           {
12827             XDrawPanRectangle(display,windows);
12828             break;
12829           }
12830       if (event->xexpose.window == windows->magnify.id)
12831         if (event->xexpose.count == 0)
12832           {
12833             XMakeMagnifyImage(display,windows,exception);
12834             break;
12835           }
12836       break;
12837     }
12838     case KeyPress:
12839     {
12840       char
12841         command[MaxTextExtent];
12842
12843       KeySym
12844         key_symbol;
12845
12846       if (event->xkey.window != windows->magnify.id)
12847         break;
12848       /*
12849         Respond to a user key press.
12850       */
12851       (void) XLookupString((XKeyEvent *) &event->xkey,command,(int)
12852         sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12853       XMagnifyWindowCommand(display,windows,event->xkey.state,key_symbol,
12854         exception);
12855       break;
12856     }
12857     case MapNotify:
12858     {
12859       if (event->xmap.window == windows->magnify.id)
12860         {
12861           windows->magnify.mapped=MagickTrue;
12862           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12863           break;
12864         }
12865       if (event->xmap.window == windows->info.id)
12866         {
12867           windows->info.mapped=MagickTrue;
12868           break;
12869         }
12870       break;
12871     }
12872     case MotionNotify:
12873     {
12874       while (XCheckMaskEvent(display,ButtonMotionMask,event)) ;
12875       if (event->xmotion.window == windows->image.id)
12876         if (IfMagickTrue(windows->magnify.mapped) )
12877           {
12878             /*
12879               Update magnified image.
12880             */
12881             x=event->xmotion.x;
12882             y=event->xmotion.y;
12883             if (x < 0)
12884               x=0;
12885             else
12886               if (x >= (int) windows->image.width)
12887                 x=(int) (windows->image.width-1);
12888             windows->magnify.x=(int) windows->image.x+x;
12889             if (y < 0)
12890               y=0;
12891             else
12892              if (y >= (int) windows->image.height)
12893                y=(int) (windows->image.height-1);
12894             windows->magnify.y=windows->image.y+y;
12895             XMakeMagnifyImage(display,windows,exception);
12896           }
12897       break;
12898     }
12899     case UnmapNotify:
12900     {
12901       if (event->xunmap.window == windows->magnify.id)
12902         {
12903           windows->magnify.mapped=MagickFalse;
12904           break;
12905         }
12906       if (event->xunmap.window == windows->info.id)
12907         {
12908           windows->info.mapped=MagickFalse;
12909           break;
12910         }
12911       break;
12912     }
12913     default:
12914       break;
12915   }
12916 }
12917 \f
12918 /*
12919 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12920 %                                                                             %
12921 %                                                                             %
12922 %                                                                             %
12923 +   X S e t C r o p G e o m e t r y                                           %
12924 %                                                                             %
12925 %                                                                             %
12926 %                                                                             %
12927 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12928 %
12929 %  XSetCropGeometry() accepts a cropping geometry relative to the Image window
12930 %  and translates it to a cropping geometry relative to the image.
12931 %
12932 %  The format of the XSetCropGeometry method is:
12933 %
12934 %      void XSetCropGeometry(Display *display,XWindows *windows,
12935 %        RectangleInfo *crop_info,Image *image)
12936 %
12937 %  A description of each parameter follows:
12938 %
12939 %    o display: Specifies a connection to an X server; returned from
12940 %      XOpenDisplay.
12941 %
12942 %    o windows: Specifies a pointer to a XWindows structure.
12943 %
12944 %    o crop_info:  A pointer to a RectangleInfo that defines a region of the
12945 %      Image window to crop.
12946 %
12947 %    o image: the image.
12948 %
12949 */
12950 static void XSetCropGeometry(Display *display,XWindows *windows,
12951   RectangleInfo *crop_info,Image *image)
12952 {
12953   char
12954     text[MaxTextExtent];
12955
12956   int
12957     x,
12958     y;
12959
12960   double
12961     scale_factor;
12962
12963   unsigned int
12964     height,
12965     width;
12966
12967   if (IfMagickTrue(windows->info.mapped) )
12968     {
12969       /*
12970         Display info on cropping rectangle.
12971       */
12972       (void) FormatLocaleString(text,MaxTextExtent," %.20gx%.20g%+.20g%+.20g",
12973         (double) crop_info->width,(double) crop_info->height,(double)
12974         crop_info->x,(double) crop_info->y);
12975       XInfoWidget(display,windows,text);
12976     }
12977   /*
12978     Cropping geometry is relative to any previous crop geometry.
12979   */
12980   x=0;
12981   y=0;
12982   width=(unsigned int) image->columns;
12983   height=(unsigned int) image->rows;
12984   if (windows->image.crop_geometry != (char *) NULL)
12985     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12986   else
12987     windows->image.crop_geometry=AcquireString((char *) NULL);
12988   /*
12989     Define the crop geometry string from the cropping rectangle.
12990   */
12991   scale_factor=(double) width/windows->image.ximage->width;
12992   if (crop_info->x > 0)
12993     x+=(int) (scale_factor*crop_info->x+0.5);
12994   width=(unsigned int) (scale_factor*crop_info->width+0.5);
12995   if (width == 0)
12996     width=1;
12997   scale_factor=(double) height/windows->image.ximage->height;
12998   if (crop_info->y > 0)
12999     y+=(int) (scale_factor*crop_info->y+0.5);
13000   height=(unsigned int) (scale_factor*crop_info->height+0.5);
13001   if (height == 0)
13002     height=1;
13003   (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
13004     "%ux%u%+d%+d",width,height,x,y);
13005 }
13006 \f
13007 /*
13008 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13009 %                                                                             %
13010 %                                                                             %
13011 %                                                                             %
13012 +   X T i l e I m a g e                                                       %
13013 %                                                                             %
13014 %                                                                             %
13015 %                                                                             %
13016 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13017 %
13018 %  XTileImage() loads or deletes a selected tile from a visual image directory.
13019 %  The load or delete command is chosen from a menu.
13020 %
13021 %  The format of the XTileImage method is:
13022 %
13023 %      Image *XTileImage(Display *display,XResourceInfo *resource_info,
13024 %        XWindows *windows,Image *image,XEvent *event,ExceptionInfo *exception)
13025 %
13026 %  A description of each parameter follows:
13027 %
13028 %    o tile_image:  XTileImage reads or deletes the tile image
13029 %      and returns it.  A null image is returned if an error occurs.
13030 %
13031 %    o display: Specifies a connection to an X server;  returned from
13032 %      XOpenDisplay.
13033 %
13034 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13035 %
13036 %    o windows: Specifies a pointer to a XWindows structure.
13037 %
13038 %    o image: the image; returned from ReadImage.
13039 %
13040 %    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
13041 %      the entire image is refreshed.
13042 %
13043 %    o exception: return any errors or warnings in this structure.
13044 %
13045 */
13046 static Image *XTileImage(Display *display,XResourceInfo *resource_info,
13047   XWindows *windows,Image *image,XEvent *event,ExceptionInfo *exception)
13048 {
13049   static const char
13050     *VerbMenu[] =
13051     {
13052       "Load",
13053       "Next",
13054       "Former",
13055       "Delete",
13056       "Update",
13057       (char *) NULL,
13058     };
13059
13060   static const ModeType
13061     TileCommands[] =
13062     {
13063       TileLoadCommand,
13064       TileNextCommand,
13065       TileFormerCommand,
13066       TileDeleteCommand,
13067       TileUpdateCommand
13068     };
13069
13070   char
13071     command[MaxTextExtent],
13072     filename[MaxTextExtent];
13073
13074   Image
13075     *tile_image;
13076
13077   int
13078     id,
13079     status,
13080     tile,
13081     x,
13082     y;
13083
13084   double
13085     scale_factor;
13086
13087   register char
13088     *p,
13089     *q;
13090
13091   register int
13092     i;
13093
13094   unsigned int
13095     height,
13096     width;
13097
13098   /*
13099     Tile image is relative to montage image configuration.
13100   */
13101   x=0;
13102   y=0;
13103   width=(unsigned int) image->columns;
13104   height=(unsigned int) image->rows;
13105   if (windows->image.crop_geometry != (char *) NULL)
13106     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
13107   scale_factor=(double) width/windows->image.ximage->width;
13108   event->xbutton.x+=windows->image.x;
13109   event->xbutton.x=(int) (scale_factor*event->xbutton.x+x+0.5);
13110   scale_factor=(double) height/windows->image.ximage->height;
13111   event->xbutton.y+=windows->image.y;
13112   event->xbutton.y=(int) (scale_factor*event->xbutton.y+y+0.5);
13113   /*
13114     Determine size and location of each tile in the visual image directory.
13115   */
13116   width=(unsigned int) image->columns;
13117   height=(unsigned int) image->rows;
13118   x=0;
13119   y=0;
13120   (void) XParseGeometry(image->montage,&x,&y,&width,&height);
13121   tile=((event->xbutton.y-y)/height)*(((int) image->columns-x)/width)+
13122     (event->xbutton.x-x)/width;
13123   if (tile < 0)
13124     {
13125       /*
13126         Button press is outside any tile.
13127       */
13128       (void) XBell(display,0);
13129       return((Image *) NULL);
13130     }
13131   /*
13132     Determine file name from the tile directory.
13133   */
13134   p=image->directory;
13135   for (i=tile; (i != 0) && (*p != '\0'); )
13136   {
13137     if (*p == '\n')
13138       i--;
13139     p++;
13140   }
13141   if (*p == '\0')
13142     {
13143       /*
13144         Button press is outside any tile.
13145       */
13146       (void) XBell(display,0);
13147       return((Image *) NULL);
13148     }
13149   /*
13150     Select a command from the pop-up menu.
13151   */
13152   id=XMenuWidget(display,windows,"Tile Verb",VerbMenu,command);
13153   if (id < 0)
13154     return((Image *) NULL);
13155   q=p;
13156   while ((*q != '\n') && (*q != '\0'))
13157     q++;
13158   (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13159   /*
13160     Perform command for the selected tile.
13161   */
13162   XSetCursorState(display,windows,MagickTrue);
13163   XCheckRefreshWindows(display,windows);
13164   tile_image=NewImageList();
13165   switch (TileCommands[id])
13166   {
13167     case TileLoadCommand:
13168     {
13169       /*
13170         Load tile image.
13171       */
13172       XCheckRefreshWindows(display,windows);
13173       (void) CopyMagickString(resource_info->image_info->magick,"MIFF",
13174         MaxTextExtent);
13175       (void) CopyMagickString(resource_info->image_info->filename,filename,
13176         MaxTextExtent);
13177       tile_image=ReadImage(resource_info->image_info,exception);
13178       CatchException(exception);
13179       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13180       break;
13181     }
13182     case TileNextCommand:
13183     {
13184       /*
13185         Display next image.
13186       */
13187       XClientMessage(display,windows->image.id,windows->im_protocols,
13188         windows->im_next_image,CurrentTime);
13189       break;
13190     }
13191     case TileFormerCommand:
13192     {
13193       /*
13194         Display former image.
13195       */
13196       XClientMessage(display,windows->image.id,windows->im_protocols,
13197         windows->im_former_image,CurrentTime);
13198       break;
13199     }
13200     case TileDeleteCommand:
13201     {
13202       /*
13203         Delete tile image.
13204       */
13205       if (IfMagickFalse(IsPathAccessible(filename)) )
13206         {
13207           XNoticeWidget(display,windows,"Image file does not exist:",filename);
13208           break;
13209         }
13210       status=XConfirmWidget(display,windows,"Really delete tile",filename);
13211       if (status <= 0)
13212         break;
13213       status=ShredFile(filename);
13214       if (IfMagickTrue(status) )
13215         {
13216           XNoticeWidget(display,windows,"Unable to delete image file:",
13217             filename);
13218           break;
13219         }
13220     }
13221     case TileUpdateCommand:
13222     {
13223       int
13224         x_offset,
13225         y_offset;
13226
13227       PixelInfo
13228         pixel;
13229
13230       register int
13231         j;
13232
13233       register Quantum
13234         *s;
13235
13236       /*
13237         Ensure all the images exist.
13238       */
13239       tile=0;
13240       GetPixelInfo(image,&pixel);
13241       for (p=image->directory; *p != '\0'; p++)
13242       {
13243         CacheView
13244           *image_view;
13245
13246         q=p;
13247         while ((*q != '\n') && (*q != '\0'))
13248           q++;
13249         (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13250         p=q;
13251         if (IfMagickTrue(IsPathAccessible(filename)) )
13252           {
13253             tile++;
13254             continue;
13255           }
13256         /*
13257           Overwrite tile with background color.
13258         */
13259         x_offset=(int) (width*(tile % (((int) image->columns-x)/width))+x);
13260         y_offset=(int) (height*(tile/(((int) image->columns-x)/width))+y);
13261         image_view=AcquireAuthenticCacheView(image,exception);
13262         (void) GetOneCacheViewVirtualPixelInfo(image_view,0,0,&pixel,exception);
13263         for (i=0; i < (int) height; i++)
13264         {
13265           s=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,(ssize_t)
13266             y_offset+i,width,1,exception);
13267           if (s == (Quantum *) NULL)
13268             break;
13269           for (j=0; j < (int) width; j++)
13270           {
13271             SetPixelInfoPixel(image,&pixel,s);
13272             s+=GetPixelChannels(image);
13273           }
13274           if (IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
13275             break;
13276         }
13277         image_view=DestroyCacheView(image_view);
13278         tile++;
13279       }
13280       windows->image.window_changes.width=(int) image->columns;
13281       windows->image.window_changes.height=(int) image->rows;
13282       XConfigureImageColormap(display,resource_info,windows,image,exception);
13283       (void) XConfigureImage(display,resource_info,windows,image,exception);
13284       break;
13285     }
13286     default:
13287       break;
13288   }
13289   XSetCursorState(display,windows,MagickFalse);
13290   return(tile_image);
13291 }
13292 \f
13293 /*
13294 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13295 %                                                                             %
13296 %                                                                             %
13297 %                                                                             %
13298 +   X T r a n s l a t e I m a g e                                             %
13299 %                                                                             %
13300 %                                                                             %
13301 %                                                                             %
13302 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13303 %
13304 %  XTranslateImage() translates the image within an Image window by one pixel
13305 %  as specified by the key symbol.  If the image has a montage string the
13306 %  translation is respect to the width and height contained within the string.
13307 %
13308 %  The format of the XTranslateImage method is:
13309 %
13310 %      void XTranslateImage(Display *display,XWindows *windows,
13311 %        Image *image,const KeySym key_symbol)
13312 %
13313 %  A description of each parameter follows:
13314 %
13315 %    o display: Specifies a connection to an X server; returned from
13316 %      XOpenDisplay.
13317 %
13318 %    o windows: Specifies a pointer to a XWindows structure.
13319 %
13320 %    o image: the image.
13321 %
13322 %    o key_symbol: Specifies a KeySym which indicates which side of the image
13323 %      to trim.
13324 %
13325 */
13326 static void XTranslateImage(Display *display,XWindows *windows,
13327   Image *image,const KeySym key_symbol)
13328 {
13329   char
13330     text[MaxTextExtent];
13331
13332   int
13333     x,
13334     y;
13335
13336   unsigned int
13337     x_offset,
13338     y_offset;
13339
13340   /*
13341     User specified a pan position offset.
13342   */
13343   x_offset=windows->image.width;
13344   y_offset=windows->image.height;
13345   if (image->montage != (char *) NULL)
13346     (void) XParseGeometry(image->montage,&x,&y,&x_offset,&y_offset);
13347   switch ((int) key_symbol)
13348   {
13349     case XK_Home:
13350     case XK_KP_Home:
13351     {
13352       windows->image.x=(int) windows->image.width/2;
13353       windows->image.y=(int) windows->image.height/2;
13354       break;
13355     }
13356     case XK_Left:
13357     case XK_KP_Left:
13358     {
13359       windows->image.x-=x_offset;
13360       break;
13361     }
13362     case XK_Next:
13363     case XK_Up:
13364     case XK_KP_Up:
13365     {
13366       windows->image.y-=y_offset;
13367       break;
13368     }
13369     case XK_Right:
13370     case XK_KP_Right:
13371     {
13372       windows->image.x+=x_offset;
13373       break;
13374     }
13375     case XK_Prior:
13376     case XK_Down:
13377     case XK_KP_Down:
13378     {
13379       windows->image.y+=y_offset;
13380       break;
13381     }
13382     default:
13383       return;
13384   }
13385   /*
13386     Check boundary conditions.
13387   */
13388   if (windows->image.x < 0)
13389     windows->image.x=0;
13390   else
13391     if ((int) (windows->image.x+windows->image.width) >
13392         windows->image.ximage->width)
13393       windows->image.x=(int) windows->image.ximage->width-windows->image.width;
13394   if (windows->image.y < 0)
13395     windows->image.y=0;
13396   else
13397     if ((int) (windows->image.y+windows->image.height) >
13398         windows->image.ximage->height)
13399       windows->image.y=(int) windows->image.ximage->height-windows->image.height;
13400   /*
13401     Refresh Image window.
13402   */
13403   (void) FormatLocaleString(text,MaxTextExtent," %ux%u%+d%+d ",
13404     windows->image.width,windows->image.height,windows->image.x,
13405     windows->image.y);
13406   XInfoWidget(display,windows,text);
13407   XCheckRefreshWindows(display,windows);
13408   XDrawPanRectangle(display,windows);
13409   XRefreshWindow(display,&windows->image,(XEvent *) NULL);
13410   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13411 }
13412 \f
13413 /*
13414 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13415 %                                                                             %
13416 %                                                                             %
13417 %                                                                             %
13418 +   X T r i m I m a g e                                                       %
13419 %                                                                             %
13420 %                                                                             %
13421 %                                                                             %
13422 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13423 %
13424 %  XTrimImage() trims the edges from the Image window.
13425 %
13426 %  The format of the XTrimImage method is:
13427 %
13428 %      MagickBooleanType XTrimImage(Display *display,
13429 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
13430 %        ExceptionInfo *exception)
13431 %
13432 %  A description of each parameter follows:
13433 %
13434 %    o display: Specifies a connection to an X server; returned from
13435 %      XOpenDisplay.
13436 %
13437 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13438 %
13439 %    o windows: Specifies a pointer to a XWindows structure.
13440 %
13441 %    o image: the image.
13442 %
13443 %    o exception: return any errors or warnings in this structure.
13444 %
13445 */
13446 static MagickBooleanType XTrimImage(Display *display,
13447   XResourceInfo *resource_info,XWindows *windows,Image *image,
13448   ExceptionInfo *exception)
13449 {
13450   RectangleInfo
13451     trim_info;
13452
13453   register int
13454     x,
13455     y;
13456
13457   size_t
13458     background,
13459     pixel;
13460
13461   /*
13462     Trim edges from image.
13463   */
13464   XSetCursorState(display,windows,MagickTrue);
13465   XCheckRefreshWindows(display,windows);
13466   /*
13467     Crop the left edge.
13468   */
13469   background=XGetPixel(windows->image.ximage,0,0);
13470   trim_info.width=(size_t) windows->image.ximage->width;
13471   for (x=0; x < windows->image.ximage->width; x++)
13472   {
13473     for (y=0; y < windows->image.ximage->height; y++)
13474     {
13475       pixel=XGetPixel(windows->image.ximage,x,y);
13476       if (pixel != background)
13477         break;
13478     }
13479     if (y < windows->image.ximage->height)
13480       break;
13481   }
13482   trim_info.x=(ssize_t) x;
13483   if (trim_info.x == (ssize_t) windows->image.ximage->width)
13484     {
13485       XSetCursorState(display,windows,MagickFalse);
13486       return(MagickFalse);
13487     }
13488   /*
13489     Crop the right edge.
13490   */
13491   background=XGetPixel(windows->image.ximage,windows->image.ximage->width-1,0);
13492   for (x=windows->image.ximage->width-1; x != 0; x--)
13493   {
13494     for (y=0; y < windows->image.ximage->height; y++)
13495     {
13496       pixel=XGetPixel(windows->image.ximage,x,y);
13497       if (pixel != background)
13498         break;
13499     }
13500     if (y < windows->image.ximage->height)
13501       break;
13502   }
13503   trim_info.width=(size_t) (x-trim_info.x+1);
13504   /*
13505     Crop the top edge.
13506   */
13507   background=XGetPixel(windows->image.ximage,0,0);
13508   trim_info.height=(size_t) windows->image.ximage->height;
13509   for (y=0; y < windows->image.ximage->height; y++)
13510   {
13511     for (x=0; x < windows->image.ximage->width; x++)
13512     {
13513       pixel=XGetPixel(windows->image.ximage,x,y);
13514       if (pixel != background)
13515         break;
13516     }
13517     if (x < windows->image.ximage->width)
13518       break;
13519   }
13520   trim_info.y=(ssize_t) y;
13521   /*
13522     Crop the bottom edge.
13523   */
13524   background=XGetPixel(windows->image.ximage,0,windows->image.ximage->height-1);
13525   for (y=windows->image.ximage->height-1; y != 0; y--)
13526   {
13527     for (x=0; x < windows->image.ximage->width; x++)
13528     {
13529       pixel=XGetPixel(windows->image.ximage,x,y);
13530       if (pixel != background)
13531         break;
13532     }
13533     if (x < windows->image.ximage->width)
13534       break;
13535   }
13536   trim_info.height=(size_t) y-trim_info.y+1;
13537   if (((unsigned int) trim_info.width != windows->image.width) ||
13538       ((unsigned int) trim_info.height != windows->image.height))
13539     {
13540       /*
13541         Reconfigure Image window as defined by the trimming rectangle.
13542       */
13543       XSetCropGeometry(display,windows,&trim_info,image);
13544       windows->image.window_changes.width=(int) trim_info.width;
13545       windows->image.window_changes.height=(int) trim_info.height;
13546       (void) XConfigureImage(display,resource_info,windows,image,exception);
13547     }
13548   XSetCursorState(display,windows,MagickFalse);
13549   return(MagickTrue);
13550 }
13551 \f
13552 /*
13553 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13554 %                                                                             %
13555 %                                                                             %
13556 %                                                                             %
13557 +   X V i s u a l D i r e c t o r y I m a g e                                 %
13558 %                                                                             %
13559 %                                                                             %
13560 %                                                                             %
13561 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13562 %
13563 %  XVisualDirectoryImage() creates a Visual Image Directory.
13564 %
13565 %  The format of the XVisualDirectoryImage method is:
13566 %
13567 %      Image *XVisualDirectoryImage(Display *display,
13568 %        XResourceInfo *resource_info,XWindows *windows,
13569 %        ExceptionInfo *exception)
13570 %
13571 %  A description of each parameter follows:
13572 %
13573 %    o display: Specifies a connection to an X server; returned from
13574 %      XOpenDisplay.
13575 %
13576 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13577 %
13578 %    o windows: Specifies a pointer to a XWindows structure.
13579 %
13580 %    o exception: return any errors or warnings in this structure.
13581 %
13582 */
13583 static Image *XVisualDirectoryImage(Display *display,
13584   XResourceInfo *resource_info,XWindows *windows,ExceptionInfo *exception)
13585 {
13586 #define TileImageTag  "Scale/Image"
13587 #define XClientName  "montage"
13588
13589   char
13590     **filelist;
13591
13592   Image
13593     *images,
13594     *montage_image,
13595     *next_image,
13596     *thumbnail_image;
13597
13598   ImageInfo
13599     *read_info;
13600
13601   int
13602     number_files;
13603
13604   MagickBooleanType
13605     backdrop;
13606
13607   MagickStatusType
13608     status;
13609
13610   MontageInfo
13611     *montage_info;
13612
13613   RectangleInfo
13614     geometry;
13615
13616   register int
13617     i;
13618
13619   static char
13620     filename[MaxTextExtent] = "\0",
13621     filenames[MaxTextExtent] = "*";
13622
13623   XResourceInfo
13624     background_resources;
13625
13626   /*
13627     Request file name from user.
13628   */
13629   XFileBrowserWidget(display,windows,"Directory",filenames);
13630   if (*filenames == '\0')
13631     return((Image *) NULL);
13632   /*
13633     Expand the filenames.
13634   */
13635   filelist=(char **) AcquireMagickMemory(sizeof(*filelist));
13636   if (filelist == (char **) NULL)
13637     {
13638       ThrowXWindowException(ResourceLimitError,"MemoryAllocationFailed",
13639         filenames);
13640       return((Image *) NULL);
13641     }
13642   number_files=1;
13643   filelist[0]=filenames;
13644   status=ExpandFilenames(&number_files,&filelist);
13645   if (IfMagickFalse(status) || (number_files == 0))
13646     {
13647       if (number_files == 0)
13648         ThrowXWindowException(ImageError,"NoImagesWereFound",filenames)
13649       else
13650         ThrowXWindowException(ResourceLimitError,"MemoryAllocationFailed",
13651           filenames);
13652       return((Image *) NULL);
13653     }
13654   /*
13655     Set image background resources.
13656   */
13657   background_resources=(*resource_info);
13658   background_resources.window_id=AcquireString("");
13659   (void) FormatLocaleString(background_resources.window_id,MaxTextExtent,
13660     "0x%lx",windows->image.id);
13661   background_resources.backdrop=MagickTrue;
13662   /*
13663     Read each image and convert them to a tile.
13664   */
13665   backdrop=IsMagickTrue( (windows->visual_info->klass == TrueColor) ||
13666     (windows->visual_info->klass == DirectColor) );
13667   read_info=CloneImageInfo(resource_info->image_info);
13668   (void) SetImageOption(read_info,"jpeg:size","120x120");
13669   (void) CloneString(&read_info->size,DefaultTileGeometry);
13670   (void) SetImageInfoProgressMonitor(read_info,(MagickProgressMonitor) NULL,
13671     (void *) NULL);
13672   images=NewImageList();
13673   XSetCursorState(display,windows,MagickTrue);
13674   XCheckRefreshWindows(display,windows);
13675   for (i=0; i < (int) number_files; i++)
13676   {
13677     (void) CopyMagickString(read_info->filename,filelist[i],MaxTextExtent);
13678     filelist[i]=DestroyString(filelist[i]);
13679     *read_info->magick='\0';
13680     next_image=ReadImage(read_info,exception);
13681     CatchException(exception);
13682     if (next_image != (Image *) NULL)
13683       {
13684         (void) DeleteImageProperty(next_image,"label");
13685         (void) SetImageProperty(next_image,"label",InterpretImageProperties(
13686           read_info,next_image,DefaultTileLabel,exception),exception);
13687         (void) ParseRegionGeometry(next_image,read_info->size,&geometry,
13688           exception);
13689         thumbnail_image=ThumbnailImage(next_image,geometry.width,
13690           geometry.height,exception);
13691         if (thumbnail_image != (Image *) NULL)
13692           {
13693             next_image=DestroyImage(next_image);
13694             next_image=thumbnail_image;
13695           }
13696         if (backdrop)
13697           {
13698             (void) XDisplayBackgroundImage(display,&background_resources,
13699               next_image,exception);
13700             XSetCursorState(display,windows,MagickTrue);
13701           }
13702         AppendImageToList(&images,next_image);
13703         if (images->progress_monitor != (MagickProgressMonitor) NULL)
13704           {
13705             MagickBooleanType
13706               proceed;
13707
13708             proceed=SetImageProgress(images,LoadImageTag,(MagickOffsetType) i,
13709               (MagickSizeType) number_files);
13710             if (IfMagickFalse(proceed) )
13711               break;
13712           }
13713       }
13714   }
13715   filelist=(char **) RelinquishMagickMemory(filelist);
13716   if (images == (Image *) NULL)
13717     {
13718       read_info=DestroyImageInfo(read_info);
13719       XSetCursorState(display,windows,MagickFalse);
13720       ThrowXWindowException(ImageError,"NoImagesWereLoaded",filenames);
13721       return((Image *) NULL);
13722     }
13723   /*
13724     Create the Visual Image Directory.
13725   */
13726   montage_info=CloneMontageInfo(read_info,(MontageInfo *) NULL);
13727   montage_info->pointsize=10;
13728   if (resource_info->font != (char *) NULL)
13729     (void) CloneString(&montage_info->font,resource_info->font);
13730   (void) CopyMagickString(montage_info->filename,filename,MaxTextExtent);
13731   montage_image=MontageImageList(read_info,montage_info,GetFirstImageInList(
13732     images),exception);
13733   images=DestroyImageList(images);
13734   montage_info=DestroyMontageInfo(montage_info);
13735   read_info=DestroyImageInfo(read_info);
13736   XSetCursorState(display,windows,MagickFalse);
13737   if (montage_image == (Image *) NULL)
13738     return(montage_image);
13739   XClientMessage(display,windows->image.id,windows->im_protocols,
13740     windows->im_next_image,CurrentTime);
13741   return(montage_image);
13742 }
13743 \f
13744 /*
13745 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13746 %                                                                             %
13747 %                                                                             %
13748 %                                                                             %
13749 %   X D i s p l a y B a c k g r o u n d I m a g e                             %
13750 %                                                                             %
13751 %                                                                             %
13752 %                                                                             %
13753 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13754 %
13755 %  XDisplayBackgroundImage() displays an image in the background of a window.
13756 %
13757 %  The format of the XDisplayBackgroundImage method is:
13758 %
13759 %      MagickBooleanType XDisplayBackgroundImage(Display *display,
13760 %        XResourceInfo *resource_info,Image *image,ExceptionInfo *exception)
13761 %
13762 %  A description of each parameter follows:
13763 %
13764 %    o display: Specifies a connection to an X server;  returned from
13765 %      XOpenDisplay.
13766 %
13767 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13768 %
13769 %    o image: the image.
13770 %
13771 %    o exception: return any errors or warnings in this structure.
13772 %
13773 */
13774 MagickExport MagickBooleanType XDisplayBackgroundImage(Display *display,
13775   XResourceInfo *resource_info,Image *image,ExceptionInfo *exception)
13776 {
13777   char
13778     geometry[MaxTextExtent],
13779     visual_type[MaxTextExtent];
13780
13781   int
13782     height,
13783     status,
13784     width;
13785
13786   RectangleInfo
13787     geometry_info;
13788
13789   static XPixelInfo
13790     pixel;
13791
13792   static XStandardColormap
13793     *map_info;
13794
13795   static XVisualInfo
13796     *visual_info = (XVisualInfo *) NULL;
13797
13798   static XWindowInfo
13799     window_info;
13800
13801   size_t
13802     delay;
13803
13804   Window
13805     root_window;
13806
13807   XGCValues
13808     context_values;
13809
13810   XResourceInfo
13811     resources;
13812
13813   XWindowAttributes
13814     window_attributes;
13815
13816   /*
13817     Determine target window.
13818   */
13819   assert(image != (Image *) NULL);
13820   assert(image->signature == MagickSignature);
13821   if (IfMagickTrue(image->debug) )
13822     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
13823   resources=(*resource_info);
13824   window_info.id=(Window) NULL;
13825   root_window=XRootWindow(display,XDefaultScreen(display));
13826   if (LocaleCompare(resources.window_id,"root") == 0)
13827     window_info.id=root_window;
13828   else
13829     {
13830       if (isdigit((int) ((unsigned char) *resources.window_id)) != 0)
13831         window_info.id=XWindowByID(display,root_window,
13832           (Window) strtol((char *) resources.window_id,(char **) NULL,0));
13833       if (window_info.id == (Window) NULL)
13834         window_info.id=XWindowByName(display,root_window,resources.window_id);
13835     }
13836   if (window_info.id == (Window) NULL)
13837     {
13838       ThrowXWindowException(XServerError,"NoWindowWithSpecifiedIDExists",
13839         resources.window_id);
13840       return(MagickFalse);
13841     }
13842   /*
13843     Determine window visual id.
13844   */
13845   window_attributes.width=XDisplayWidth(display,XDefaultScreen(display));
13846   window_attributes.height=XDisplayHeight(display,XDefaultScreen(display));
13847   (void) CopyMagickString(visual_type,"default",MaxTextExtent);
13848   status=XGetWindowAttributes(display,window_info.id,&window_attributes);
13849   if (status != 0)
13850     (void) FormatLocaleString(visual_type,MaxTextExtent,"0x%lx",
13851       XVisualIDFromVisual(window_attributes.visual));
13852   if (visual_info == (XVisualInfo *) NULL)
13853     {
13854       /*
13855         Allocate standard colormap.
13856       */
13857       map_info=XAllocStandardColormap();
13858       if (map_info == (XStandardColormap *) NULL)
13859         ThrowXWindowFatalException(XServerFatalError,"MemoryAllocationFailed",
13860           image->filename);
13861       map_info->colormap=(Colormap) NULL;
13862       pixel.pixels=(unsigned long *) NULL;
13863       /*
13864         Initialize visual info.
13865       */
13866       resources.map_type=(char *) NULL;
13867       resources.visual_type=visual_type;
13868       visual_info=XBestVisualInfo(display,map_info,&resources);
13869       if (visual_info == (XVisualInfo *) NULL)
13870         ThrowXWindowFatalException(XServerFatalError,"UnableToGetVisual",
13871           resources.visual_type);
13872       /*
13873         Initialize window info.
13874       */
13875       window_info.ximage=(XImage *) NULL;
13876       window_info.matte_image=(XImage *) NULL;
13877       window_info.pixmap=(Pixmap) NULL;
13878       window_info.matte_pixmap=(Pixmap) NULL;
13879     }
13880   /*
13881     Free previous root colors.
13882   */
13883   if (window_info.id == root_window)
13884     (void) XDestroyWindowColors(display,root_window);
13885   /*
13886     Initialize Standard Colormap.
13887   */
13888   resources.colormap=SharedColormap;
13889   XMakeStandardColormap(display,visual_info,&resources,image,map_info,&pixel,
13890     exception);
13891   /*
13892     Graphic context superclass.
13893   */
13894   context_values.background=pixel.background_color.pixel;
13895   context_values.foreground=pixel.foreground_color.pixel;
13896   pixel.annotate_context=XCreateGC(display,window_info.id,
13897     (size_t) (GCBackground | GCForeground),&context_values);
13898   if (pixel.annotate_context == (GC) NULL)
13899     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
13900       image->filename);
13901   /*
13902     Initialize Image window attributes.
13903   */
13904   window_info.name=AcquireString("\0");
13905   window_info.icon_name=AcquireString("\0");
13906   XGetWindowInfo(display,visual_info,map_info,&pixel,(XFontStruct *) NULL,
13907     &resources,&window_info);
13908   /*
13909     Create the X image.
13910   */
13911   window_info.width=(unsigned int) image->columns;
13912   window_info.height=(unsigned int) image->rows;
13913   if ((image->columns != window_info.width) ||
13914       (image->rows != window_info.height))
13915     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13916       image->filename);
13917   (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>",
13918     window_attributes.width,window_attributes.height);
13919   geometry_info.width=window_info.width;
13920   geometry_info.height=window_info.height;
13921   geometry_info.x=(ssize_t) window_info.x;
13922   geometry_info.y=(ssize_t) window_info.y;
13923   (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
13924     &geometry_info.width,&geometry_info.height);
13925   window_info.width=(unsigned int) geometry_info.width;
13926   window_info.height=(unsigned int) geometry_info.height;
13927   window_info.x=(int) geometry_info.x;
13928   window_info.y=(int) geometry_info.y;
13929   status=XMakeImage(display,&resources,&window_info,image,window_info.width,
13930     window_info.height,exception);
13931   if (IfMagickFalse(status) )
13932     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13933       image->filename);
13934   window_info.x=0;
13935   window_info.y=0;
13936   if (IfMagickTrue(image->debug) )
13937     {
13938       (void) LogMagickEvent(X11Event,GetMagickModule(),
13939         "Image: %s[%.20g] %.20gx%.20g ",image->filename,(double) image->scene,
13940         (double) image->columns,(double) image->rows);
13941       if (image->colors != 0)
13942         (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
13943           image->colors);
13944       (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",image->magick);
13945     }
13946   /*
13947     Adjust image dimensions as specified by backdrop or geometry options.
13948   */
13949   width=(int) window_info.width;
13950   height=(int) window_info.height;
13951   if (IfMagickTrue(resources.backdrop) )
13952     {
13953       /*
13954         Center image on window.
13955       */
13956       window_info.x=(window_attributes.width/2)-
13957         (window_info.ximage->width/2);
13958       window_info.y=(window_attributes.height/2)-
13959         (window_info.ximage->height/2);
13960       width=window_attributes.width;
13961       height=window_attributes.height;
13962     }
13963   if ((resources.image_geometry != (char *) NULL) &&
13964       (*resources.image_geometry != '\0'))
13965     {
13966       char
13967         default_geometry[MaxTextExtent];
13968
13969       int
13970         flags,
13971         gravity;
13972
13973       XSizeHints
13974         *size_hints;
13975
13976       /*
13977         User specified geometry.
13978       */
13979       size_hints=XAllocSizeHints();
13980       if (size_hints == (XSizeHints *) NULL)
13981         ThrowXWindowFatalException(ResourceLimitFatalError,
13982           "MemoryAllocationFailed",image->filename);
13983       size_hints->flags=0L;
13984       (void) FormatLocaleString(default_geometry,MaxTextExtent,"%dx%d",
13985         width,height);
13986       flags=XWMGeometry(display,visual_info->screen,resources.image_geometry,
13987         default_geometry,window_info.border_width,size_hints,&window_info.x,
13988         &window_info.y,&width,&height,&gravity);
13989       if (flags & (XValue | YValue))
13990         {
13991           width=window_attributes.width;
13992           height=window_attributes.height;
13993         }
13994       (void) XFree((void *) size_hints);
13995     }
13996   /*
13997     Create the X pixmap.
13998   */
13999   window_info.pixmap=XCreatePixmap(display,window_info.id,(unsigned int) width,
14000     (unsigned int) height,window_info.depth);
14001   if (window_info.pixmap == (Pixmap) NULL)
14002     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXPixmap",
14003       image->filename);
14004   /*
14005     Display pixmap on the window.
14006   */
14007   if (((unsigned int) width > window_info.width) ||
14008       ((unsigned int) height > window_info.height))
14009     (void) XFillRectangle(display,window_info.pixmap,
14010       window_info.annotate_context,0,0,(unsigned int) width,
14011       (unsigned int) height);
14012   (void) XPutImage(display,window_info.pixmap,window_info.annotate_context,
14013     window_info.ximage,0,0,window_info.x,window_info.y,(unsigned int)
14014     window_info.width,(unsigned int) window_info.height);
14015   (void) XSetWindowBackgroundPixmap(display,window_info.id,window_info.pixmap);
14016   (void) XClearWindow(display,window_info.id);
14017   delay=1000*image->delay/MagickMax(image->ticks_per_second,1L);
14018   XDelay(display,delay == 0UL ? 10UL : delay);
14019   (void) XSync(display,MagickFalse);
14020   return(IsMagickTrue(window_info.id == root_window));
14021 }
14022 \f
14023 /*
14024 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
14025 %                                                                             %
14026 %                                                                             %
14027 %                                                                             %
14028 +   X D i s p l a y I m a g e                                                 %
14029 %                                                                             %
14030 %                                                                             %
14031 %                                                                             %
14032 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
14033 %
14034 %  XDisplayImage() displays an image via X11.  A new image is created and
14035 %  returned if the user interactively transforms the displayed image.
14036 %
14037 %  The format of the XDisplayImage method is:
14038 %
14039 %      Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
14040 %        char **argv,int argc,Image **image,size_t *state,
14041 %        ExceptionInfo *exception)
14042 %
14043 %  A description of each parameter follows:
14044 %
14045 %    o nexus:  Method XDisplayImage returns an image when the
14046 %      user chooses 'Open Image' from the command menu or picks a tile
14047 %      from the image directory.  Otherwise a null image is returned.
14048 %
14049 %    o display: Specifies a connection to an X server;  returned from
14050 %      XOpenDisplay.
14051 %
14052 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
14053 %
14054 %    o argv: Specifies the application's argument list.
14055 %
14056 %    o argc: Specifies the number of arguments.
14057 %
14058 %    o image: Specifies an address to an address of an Image structure;
14059 %
14060 %    o exception: return any errors or warnings in this structure.
14061 %
14062 */
14063 MagickExport Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
14064   char **argv,int argc,Image **image,size_t *state,ExceptionInfo *exception)
14065 {
14066 #define MagnifySize  256  /* must be a power of 2 */
14067 #define MagickMenus  10
14068 #define MagickTitle  "Commands"
14069
14070   static const char
14071     *CommandMenu[] =
14072     {
14073       "File",
14074       "Edit",
14075       "View",
14076       "Transform",
14077       "Enhance",
14078       "Effects",
14079       "F/X",
14080       "Image Edit",
14081       "Miscellany",
14082       "Help",
14083       (char *) NULL
14084     },
14085     *FileMenu[] =
14086     {
14087       "Open...",
14088       "Next",
14089       "Former",
14090       "Select...",
14091       "Save...",
14092       "Print...",
14093       "Delete...",
14094       "New...",
14095       "Visual Directory...",
14096       "Quit",
14097       (char *) NULL
14098     },
14099     *EditMenu[] =
14100     {
14101       "Undo",
14102       "Redo",
14103       "Cut",
14104       "Copy",
14105       "Paste",
14106       (char *) NULL
14107     },
14108     *ViewMenu[] =
14109     {
14110       "Half Size",
14111       "Original Size",
14112       "Double Size",
14113       "Resize...",
14114       "Apply",
14115       "Refresh",
14116       "Restore",
14117       (char *) NULL
14118     },
14119     *TransformMenu[] =
14120     {
14121       "Crop",
14122       "Chop",
14123       "Flop",
14124       "Flip",
14125       "Rotate Right",
14126       "Rotate Left",
14127       "Rotate...",
14128       "Shear...",
14129       "Roll...",
14130       "Trim Edges",
14131       (char *) NULL
14132     },
14133     *EnhanceMenu[] =
14134     {
14135       "Hue...",
14136       "Saturation...",
14137       "Brightness...",
14138       "Gamma...",
14139       "Spiff",
14140       "Dull",
14141       "Contrast Stretch...",
14142       "Sigmoidal Contrast...",
14143       "Normalize",
14144       "Equalize",
14145       "Negate",
14146       "Grayscale",
14147       "Map...",
14148       "Quantize...",
14149       (char *) NULL
14150     },
14151     *EffectsMenu[] =
14152     {
14153       "Despeckle",
14154       "Emboss",
14155       "Reduce Noise",
14156       "Add Noise...",
14157       "Sharpen...",
14158       "Blur...",
14159       "Threshold...",
14160       "Edge Detect...",
14161       "Spread...",
14162       "Shade...",
14163       "Raise...",
14164       "Segment...",
14165       (char *) NULL
14166     },
14167     *FXMenu[] =
14168     {
14169       "Solarize...",
14170       "Sepia Tone...",
14171       "Swirl...",
14172       "Implode...",
14173       "Vignette...",
14174       "Wave...",
14175       "Oil Paint...",
14176       "Charcoal Draw...",
14177       (char *) NULL
14178     },
14179     *ImageEditMenu[] =
14180     {
14181       "Annotate...",
14182       "Draw...",
14183       "Color...",
14184       "Matte...",
14185       "Composite...",
14186       "Add Border...",
14187       "Add Frame...",
14188       "Comment...",
14189       "Launch...",
14190       "Region of Interest...",
14191       (char *) NULL
14192     },
14193     *MiscellanyMenu[] =
14194     {
14195       "Image Info",
14196       "Zoom Image",
14197       "Show Preview...",
14198       "Show Histogram",
14199       "Show Matte",
14200       "Background...",
14201       "Slide Show...",
14202       "Preferences...",
14203       (char *) NULL
14204     },
14205     *HelpMenu[] =
14206     {
14207       "Overview",
14208       "Browse Documentation",
14209       "About Display",
14210       (char *) NULL
14211     },
14212     *ShortCutsMenu[] =
14213     {
14214       "Next",
14215       "Former",
14216       "Open...",
14217       "Save...",
14218       "Print...",
14219       "Undo",
14220       "Restore",
14221       "Image Info",
14222       "Quit",
14223       (char *) NULL
14224     },
14225     *VirtualMenu[] =
14226     {
14227       "Image Info",
14228       "Print",
14229       "Next",
14230       "Quit",
14231       (char *) NULL
14232     };
14233
14234   static const char
14235     **Menus[MagickMenus] =
14236     {
14237       FileMenu,
14238       EditMenu,
14239       ViewMenu,
14240       TransformMenu,
14241       EnhanceMenu,
14242       EffectsMenu,
14243       FXMenu,
14244       ImageEditMenu,
14245       MiscellanyMenu,
14246       HelpMenu
14247     };
14248
14249   static CommandType
14250     CommandMenus[] =
14251     {
14252       NullCommand,
14253       NullCommand,
14254       NullCommand,
14255       NullCommand,
14256       NullCommand,
14257       NullCommand,
14258       NullCommand,
14259       NullCommand,
14260       NullCommand,
14261       NullCommand,
14262     },
14263     FileCommands[] =
14264     {
14265       OpenCommand,
14266       NextCommand,
14267       FormerCommand,
14268       SelectCommand,
14269       SaveCommand,
14270       PrintCommand,
14271       DeleteCommand,
14272       NewCommand,
14273       VisualDirectoryCommand,
14274       QuitCommand
14275     },
14276     EditCommands[] =
14277     {
14278       UndoCommand,
14279       RedoCommand,
14280       CutCommand,
14281       CopyCommand,
14282       PasteCommand
14283     },
14284     ViewCommands[] =
14285     {
14286       HalfSizeCommand,
14287       OriginalSizeCommand,
14288       DoubleSizeCommand,
14289       ResizeCommand,
14290       ApplyCommand,
14291       RefreshCommand,
14292       RestoreCommand
14293     },
14294     TransformCommands[] =
14295     {
14296       CropCommand,
14297       ChopCommand,
14298       FlopCommand,
14299       FlipCommand,
14300       RotateRightCommand,
14301       RotateLeftCommand,
14302       RotateCommand,
14303       ShearCommand,
14304       RollCommand,
14305       TrimCommand
14306     },
14307     EnhanceCommands[] =
14308     {
14309       HueCommand,
14310       SaturationCommand,
14311       BrightnessCommand,
14312       GammaCommand,
14313       SpiffCommand,
14314       DullCommand,
14315       ContrastStretchCommand,
14316       SigmoidalContrastCommand,
14317       NormalizeCommand,
14318       EqualizeCommand,
14319       NegateCommand,
14320       GrayscaleCommand,
14321       MapCommand,
14322       QuantizeCommand
14323     },
14324     EffectsCommands[] =
14325     {
14326       DespeckleCommand,
14327       EmbossCommand,
14328       ReduceNoiseCommand,
14329       AddNoiseCommand,
14330       SharpenCommand,
14331       BlurCommand,
14332       ThresholdCommand,
14333       EdgeDetectCommand,
14334       SpreadCommand,
14335       ShadeCommand,
14336       RaiseCommand,
14337       SegmentCommand
14338     },
14339     FXCommands[] =
14340     {
14341       SolarizeCommand,
14342       SepiaToneCommand,
14343       SwirlCommand,
14344       ImplodeCommand,
14345       VignetteCommand,
14346       WaveCommand,
14347       OilPaintCommand,
14348       CharcoalDrawCommand
14349     },
14350     ImageEditCommands[] =
14351     {
14352       AnnotateCommand,
14353       DrawCommand,
14354       ColorCommand,
14355       MatteCommand,
14356       CompositeCommand,
14357       AddBorderCommand,
14358       AddFrameCommand,
14359       CommentCommand,
14360       LaunchCommand,
14361       RegionofInterestCommand
14362     },
14363     MiscellanyCommands[] =
14364     {
14365       InfoCommand,
14366       ZoomCommand,
14367       ShowPreviewCommand,
14368       ShowHistogramCommand,
14369       ShowMatteCommand,
14370       BackgroundCommand,
14371       SlideShowCommand,
14372       PreferencesCommand
14373     },
14374     HelpCommands[] =
14375     {
14376       HelpCommand,
14377       BrowseDocumentationCommand,
14378       VersionCommand
14379     },
14380     ShortCutsCommands[] =
14381     {
14382       NextCommand,
14383       FormerCommand,
14384       OpenCommand,
14385       SaveCommand,
14386       PrintCommand,
14387       UndoCommand,
14388       RestoreCommand,
14389       InfoCommand,
14390       QuitCommand
14391     },
14392     VirtualCommands[] =
14393     {
14394       InfoCommand,
14395       PrintCommand,
14396       NextCommand,
14397       QuitCommand
14398     };
14399
14400   static CommandType
14401     *Commands[MagickMenus] =
14402     {
14403       FileCommands,
14404       EditCommands,
14405       ViewCommands,
14406       TransformCommands,
14407       EnhanceCommands,
14408       EffectsCommands,
14409       FXCommands,
14410       ImageEditCommands,
14411       MiscellanyCommands,
14412       HelpCommands
14413     };
14414
14415   char
14416     command[MaxTextExtent],
14417     *directory,
14418     geometry[MaxTextExtent],
14419     resource_name[MaxTextExtent];
14420
14421   CommandType
14422     command_type;
14423
14424   Image
14425     *display_image,
14426     *nexus;
14427
14428   int
14429     entry,
14430     id;
14431
14432   KeySym
14433     key_symbol;
14434
14435   MagickStatusType
14436     context_mask,
14437     status;
14438
14439   RectangleInfo
14440     geometry_info;
14441
14442   register int
14443     i;
14444
14445   static char
14446     working_directory[MaxTextExtent];
14447
14448   static XPoint
14449     vid_info;
14450
14451   static XWindowInfo
14452     *magick_windows[MaxXWindows];
14453
14454   static unsigned int
14455     number_windows;
14456
14457   struct stat
14458     attributes;
14459
14460   time_t
14461     timer,
14462     timestamp,
14463     update_time;
14464
14465   unsigned int
14466     height,
14467     width;
14468
14469   size_t
14470     delay;
14471
14472   WarningHandler
14473     warning_handler;
14474
14475   Window
14476     root_window;
14477
14478   XClassHint
14479     *class_hints;
14480
14481   XEvent
14482     event;
14483
14484   XFontStruct
14485     *font_info;
14486
14487   XGCValues
14488     context_values;
14489
14490   XPixelInfo
14491     *icon_pixel,
14492     *pixel;
14493
14494   XResourceInfo
14495     *icon_resources;
14496
14497   XStandardColormap
14498     *icon_map,
14499     *map_info;
14500
14501   XVisualInfo
14502     *icon_visual,
14503     *visual_info;
14504
14505   XWindowChanges
14506     window_changes;
14507
14508   XWindows
14509     *windows;
14510
14511   XWMHints
14512     *manager_hints;
14513
14514   assert(image != (Image **) NULL);
14515   assert((*image)->signature == MagickSignature);
14516   if (IfMagickTrue((*image)->debug) )
14517     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*image)->filename);
14518   display_image=(*image);
14519   warning_handler=(WarningHandler) NULL;
14520   windows=XSetWindows((XWindows *) ~0);
14521   if (windows != (XWindows *) NULL)
14522     {
14523       int
14524         status;
14525
14526       if (*working_directory == '\0')
14527         (void) CopyMagickString(working_directory,".",MaxTextExtent);
14528       status=chdir(working_directory);
14529       if (status == -1)
14530         (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
14531           "UnableToOpenFile","%s",working_directory);
14532       warning_handler=resource_info->display_warnings ?
14533         SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
14534       warning_handler=resource_info->display_warnings ?
14535         SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
14536     }
14537   else
14538     {
14539       /*
14540         Allocate windows structure.
14541       */
14542       resource_info->colors=display_image->colors;
14543       windows=XSetWindows(XInitializeWindows(display,resource_info));
14544       if (windows == (XWindows *) NULL)
14545         ThrowXWindowFatalException(XServerFatalError,"UnableToCreateWindow",
14546           (*image)->filename);
14547       /*
14548         Initialize window id's.
14549       */
14550       number_windows=0;
14551       magick_windows[number_windows++]=(&windows->icon);
14552       magick_windows[number_windows++]=(&windows->backdrop);
14553       magick_windows[number_windows++]=(&windows->image);
14554       magick_windows[number_windows++]=(&windows->info);
14555       magick_windows[number_windows++]=(&windows->command);
14556       magick_windows[number_windows++]=(&windows->widget);
14557       magick_windows[number_windows++]=(&windows->popup);
14558       magick_windows[number_windows++]=(&windows->magnify);
14559       magick_windows[number_windows++]=(&windows->pan);
14560       for (i=0; i < (int) number_windows; i++)
14561         magick_windows[i]->id=(Window) NULL;
14562       vid_info.x=0;
14563       vid_info.y=0;
14564     }
14565   /*
14566     Initialize font info.
14567   */
14568   if (windows->font_info != (XFontStruct *) NULL)
14569     (void) XFreeFont(display,windows->font_info);
14570   windows->font_info=XBestFont(display,resource_info,MagickFalse);
14571   if (windows->font_info == (XFontStruct *) NULL)
14572     ThrowXWindowFatalException(XServerFatalError,"UnableToLoadFont",
14573       resource_info->font);
14574   /*
14575     Initialize Standard Colormap.
14576   */
14577   map_info=windows->map_info;
14578   icon_map=windows->icon_map;
14579   visual_info=windows->visual_info;
14580   icon_visual=windows->icon_visual;
14581   pixel=windows->pixel_info;
14582   icon_pixel=windows->icon_pixel;
14583   font_info=windows->font_info;
14584   icon_resources=windows->icon_resources;
14585   class_hints=windows->class_hints;
14586   manager_hints=windows->manager_hints;
14587   root_window=XRootWindow(display,visual_info->screen);
14588   nexus=NewImageList();
14589   if (IfMagickTrue(display_image->debug) )
14590     {
14591       (void) LogMagickEvent(X11Event,GetMagickModule(),
14592         "Image: %s[%.20g] %.20gx%.20g ",display_image->filename,
14593         (double) display_image->scene,(double) display_image->columns,
14594         (double) display_image->rows);
14595       if (display_image->colors != 0)
14596         (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
14597           display_image->colors);
14598       (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",
14599         display_image->magick);
14600     }
14601   XMakeStandardColormap(display,visual_info,resource_info,display_image,
14602     map_info,pixel,exception);
14603   display_image->taint=MagickFalse;
14604   /*
14605     Initialize graphic context.
14606   */
14607   windows->context.id=(Window) NULL;
14608   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14609     resource_info,&windows->context);
14610   (void) CloneString(&class_hints->res_name,resource_info->client_name);
14611   (void) CloneString(&class_hints->res_class,resource_info->client_name);
14612   class_hints->res_class[0]=(char) toupper((int) class_hints->res_class[0]);
14613   manager_hints->flags=InputHint | StateHint;
14614   manager_hints->input=MagickFalse;
14615   manager_hints->initial_state=WithdrawnState;
14616   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14617     &windows->context);
14618   if (IfMagickTrue(display_image->debug) )
14619     (void) LogMagickEvent(X11Event,GetMagickModule(),
14620       "Window id: 0x%lx (context)",windows->context.id);
14621   context_values.background=pixel->background_color.pixel;
14622   context_values.font=font_info->fid;
14623   context_values.foreground=pixel->foreground_color.pixel;
14624   context_values.graphics_exposures=MagickFalse;
14625   context_mask=(MagickStatusType)
14626     (GCBackground | GCFont | GCForeground | GCGraphicsExposures);
14627   if (pixel->annotate_context != (GC) NULL)
14628     (void) XFreeGC(display,pixel->annotate_context);
14629   pixel->annotate_context=XCreateGC(display,windows->context.id,
14630     context_mask,&context_values);
14631   if (pixel->annotate_context == (GC) NULL)
14632     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14633       display_image->filename);
14634   context_values.background=pixel->depth_color.pixel;
14635   if (pixel->widget_context != (GC) NULL)
14636     (void) XFreeGC(display,pixel->widget_context);
14637   pixel->widget_context=XCreateGC(display,windows->context.id,context_mask,
14638     &context_values);
14639   if (pixel->widget_context == (GC) NULL)
14640     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14641       display_image->filename);
14642   context_values.background=pixel->foreground_color.pixel;
14643   context_values.foreground=pixel->background_color.pixel;
14644   context_values.plane_mask=context_values.background ^
14645     context_values.foreground;
14646   if (pixel->highlight_context != (GC) NULL)
14647     (void) XFreeGC(display,pixel->highlight_context);
14648   pixel->highlight_context=XCreateGC(display,windows->context.id,
14649     (size_t) (context_mask | GCPlaneMask),&context_values);
14650   if (pixel->highlight_context == (GC) NULL)
14651     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14652       display_image->filename);
14653   (void) XDestroyWindow(display,windows->context.id);
14654   /*
14655     Initialize icon window.
14656   */
14657   XGetWindowInfo(display,icon_visual,icon_map,icon_pixel,(XFontStruct *) NULL,
14658     icon_resources,&windows->icon);
14659   windows->icon.geometry=resource_info->icon_geometry;
14660   XBestIconSize(display,&windows->icon,display_image);
14661   windows->icon.attributes.colormap=XDefaultColormap(display,
14662     icon_visual->screen);
14663   windows->icon.attributes.event_mask=ExposureMask | StructureNotifyMask;
14664   manager_hints->flags=InputHint | StateHint;
14665   manager_hints->input=MagickFalse;
14666   manager_hints->initial_state=IconicState;
14667   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14668     &windows->icon);
14669   if (IfMagickTrue(display_image->debug) )
14670     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (icon)",
14671       windows->icon.id);
14672   /*
14673     Initialize graphic context for icon window.
14674   */
14675   if (icon_pixel->annotate_context != (GC) NULL)
14676     (void) XFreeGC(display,icon_pixel->annotate_context);
14677   context_values.background=icon_pixel->background_color.pixel;
14678   context_values.foreground=icon_pixel->foreground_color.pixel;
14679   icon_pixel->annotate_context=XCreateGC(display,windows->icon.id,
14680     (size_t) (GCBackground | GCForeground),&context_values);
14681   if (icon_pixel->annotate_context == (GC) NULL)
14682     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14683       display_image->filename);
14684   windows->icon.annotate_context=icon_pixel->annotate_context;
14685   /*
14686     Initialize Image window.
14687   */
14688   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14689     &windows->image);
14690   windows->image.shape=MagickTrue;  /* non-rectangular shape hint */
14691   if (IfMagickFalse(resource_info->use_shared_memory) )
14692     windows->image.shared_memory=MagickFalse;
14693   if ((resource_info->title != (char *) NULL) && !(*state & MontageImageState))
14694     {
14695       char
14696         *title;
14697
14698       title=InterpretImageProperties(resource_info->image_info,display_image,
14699         resource_info->title,exception);
14700       (void) CopyMagickString(windows->image.name,title,MaxTextExtent);
14701       (void) CopyMagickString(windows->image.icon_name,title,MaxTextExtent);
14702       title=DestroyString(title);
14703     }
14704   else
14705     {
14706       char
14707         filename[MaxTextExtent];
14708
14709       /*
14710         Window name is the base of the filename.
14711       */
14712       GetPathComponent(display_image->magick_filename,TailPath,filename);
14713       if (display_image->scene == 0)
14714         (void) FormatLocaleString(windows->image.name,MaxTextExtent,
14715           "%s: %s",MagickPackageName,filename);
14716       else
14717         (void) FormatLocaleString(windows->image.name,MaxTextExtent,
14718           "%s: %s[scene: %.20g frames: %.20g]",MagickPackageName,filename,
14719           (double) display_image->scene,(double) GetImageListLength(
14720           display_image));
14721       (void) CopyMagickString(windows->image.icon_name,filename,MaxTextExtent);
14722     }
14723   if (resource_info->immutable)
14724     windows->image.immutable=MagickTrue;
14725   windows->image.use_pixmap=resource_info->use_pixmap;
14726   windows->image.geometry=resource_info->image_geometry;
14727   (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>!",
14728     XDisplayWidth(display,visual_info->screen),
14729     XDisplayHeight(display,visual_info->screen));
14730   geometry_info.width=display_image->columns;
14731   geometry_info.height=display_image->rows;
14732   geometry_info.x=0;
14733   geometry_info.y=0;
14734   (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
14735     &geometry_info.width,&geometry_info.height);
14736   windows->image.width=(unsigned int) geometry_info.width;
14737   windows->image.height=(unsigned int) geometry_info.height;
14738   windows->image.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14739     ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14740     KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14741     PropertyChangeMask | StructureNotifyMask | SubstructureNotifyMask;
14742   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14743     resource_info,&windows->backdrop);
14744   if ((resource_info->backdrop) || (windows->backdrop.id != (Window) NULL))
14745     {
14746       /*
14747         Initialize backdrop window.
14748       */
14749       windows->backdrop.x=0;
14750       windows->backdrop.y=0;
14751       (void) CloneString(&windows->backdrop.name,"Backdrop");
14752       windows->backdrop.flags=(size_t) (USSize | USPosition);
14753       windows->backdrop.width=(unsigned int)
14754         XDisplayWidth(display,visual_info->screen);
14755       windows->backdrop.height=(unsigned int)
14756         XDisplayHeight(display,visual_info->screen);
14757       windows->backdrop.border_width=0;
14758       windows->backdrop.immutable=MagickTrue;
14759       windows->backdrop.attributes.do_not_propagate_mask=ButtonPressMask |
14760         ButtonReleaseMask;
14761       windows->backdrop.attributes.event_mask=ButtonPressMask | KeyPressMask |
14762         StructureNotifyMask;
14763       manager_hints->flags=IconWindowHint | InputHint | StateHint;
14764       manager_hints->icon_window=windows->icon.id;
14765       manager_hints->input=MagickTrue;
14766       manager_hints->initial_state=resource_info->iconic ? IconicState :
14767         NormalState;
14768       XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14769         &windows->backdrop);
14770       if (IfMagickTrue(display_image->debug) )
14771         (void) LogMagickEvent(X11Event,GetMagickModule(),
14772           "Window id: 0x%lx (backdrop)",windows->backdrop.id);
14773       (void) XMapWindow(display,windows->backdrop.id);
14774       (void) XClearWindow(display,windows->backdrop.id);
14775       if (windows->image.id != (Window) NULL)
14776         {
14777           (void) XDestroyWindow(display,windows->image.id);
14778           windows->image.id=(Window) NULL;
14779         }
14780       /*
14781         Position image in the center the backdrop.
14782       */
14783       windows->image.flags|=USPosition;
14784       windows->image.x=(XDisplayWidth(display,visual_info->screen)/2)-
14785         (windows->image.width/2);
14786       windows->image.y=(XDisplayHeight(display,visual_info->screen)/2)-
14787         (windows->image.height/2);
14788     }
14789   manager_hints->flags=IconWindowHint | InputHint | StateHint;
14790   manager_hints->icon_window=windows->icon.id;
14791   manager_hints->input=MagickTrue;
14792   manager_hints->initial_state=resource_info->iconic ? IconicState :
14793     NormalState;
14794   if (windows->group_leader.id != (Window) NULL)
14795     {
14796       /*
14797         Follow the leader.
14798       */
14799       manager_hints->flags|=WindowGroupHint;
14800       manager_hints->window_group=windows->group_leader.id;
14801       (void) XSelectInput(display,windows->group_leader.id,StructureNotifyMask);
14802       if (IfMagickTrue(display_image->debug) )
14803         (void) LogMagickEvent(X11Event,GetMagickModule(),
14804           "Window id: 0x%lx (group leader)",windows->group_leader.id);
14805     }
14806   XMakeWindow(display,
14807     (Window) (resource_info->backdrop ? windows->backdrop.id : root_window),
14808     argv,argc,class_hints,manager_hints,&windows->image);
14809   (void) XChangeProperty(display,windows->image.id,windows->im_protocols,
14810     XA_STRING,8,PropModeReplace,(unsigned char *) NULL,0);
14811   if (windows->group_leader.id != (Window) NULL)
14812     (void) XSetTransientForHint(display,windows->image.id,
14813       windows->group_leader.id);
14814   if (IfMagickTrue(display_image->debug) )
14815     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (image)",
14816       windows->image.id);
14817   /*
14818     Initialize Info widget.
14819   */
14820   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14821     &windows->info);
14822   (void) CloneString(&windows->info.name,"Info");
14823   (void) CloneString(&windows->info.icon_name,"Info");
14824   windows->info.border_width=1;
14825   windows->info.x=2;
14826   windows->info.y=2;
14827   windows->info.flags|=PPosition;
14828   windows->info.attributes.win_gravity=UnmapGravity;
14829   windows->info.attributes.event_mask=ButtonPressMask | ExposureMask |
14830     StructureNotifyMask;
14831   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14832   manager_hints->input=MagickFalse;
14833   manager_hints->initial_state=NormalState;
14834   manager_hints->window_group=windows->image.id;
14835   XMakeWindow(display,windows->image.id,argv,argc,class_hints,manager_hints,
14836     &windows->info);
14837   windows->info.highlight_stipple=XCreateBitmapFromData(display,
14838     windows->info.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14839   windows->info.shadow_stipple=XCreateBitmapFromData(display,
14840     windows->info.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14841   (void) XSetTransientForHint(display,windows->info.id,windows->image.id);
14842   if (IfMagickTrue(windows->image.mapped) )
14843     (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14844   if (IfMagickTrue(display_image->debug) )
14845     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (info)",
14846       windows->info.id);
14847   /*
14848     Initialize Command widget.
14849   */
14850   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14851     resource_info,&windows->command);
14852   windows->command.data=MagickMenus;
14853   (void) XCommandWidget(display,windows,CommandMenu,(XEvent *) NULL);
14854   (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.command",
14855     resource_info->client_name);
14856   windows->command.geometry=XGetResourceClass(resource_info->resource_database,
14857     resource_name,"geometry",(char *) NULL);
14858   (void) CloneString(&windows->command.name,MagickTitle);
14859   windows->command.border_width=0;
14860   windows->command.flags|=PPosition;
14861   windows->command.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14862     ButtonReleaseMask | EnterWindowMask | ExposureMask | LeaveWindowMask |
14863     OwnerGrabButtonMask | StructureNotifyMask;
14864   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14865   manager_hints->input=MagickTrue;
14866   manager_hints->initial_state=NormalState;
14867   manager_hints->window_group=windows->image.id;
14868   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14869     &windows->command);
14870   windows->command.highlight_stipple=XCreateBitmapFromData(display,
14871     windows->command.id,(char *) HighlightBitmap,HighlightWidth,
14872     HighlightHeight);
14873   windows->command.shadow_stipple=XCreateBitmapFromData(display,
14874     windows->command.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14875   (void) XSetTransientForHint(display,windows->command.id,windows->image.id);
14876   if (IfMagickTrue(windows->command.mapped) )
14877     (void) XMapRaised(display,windows->command.id);
14878   if (IfMagickTrue(display_image->debug) )
14879     (void) LogMagickEvent(X11Event,GetMagickModule(),
14880       "Window id: 0x%lx (command)",windows->command.id);
14881   /*
14882     Initialize Widget window.
14883   */
14884   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14885     resource_info,&windows->widget);
14886   (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.widget",
14887     resource_info->client_name);
14888   windows->widget.geometry=XGetResourceClass(resource_info->resource_database,
14889     resource_name,"geometry",(char *) NULL);
14890   windows->widget.border_width=0;
14891   windows->widget.flags|=PPosition;
14892   windows->widget.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14893     ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14894     KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14895     StructureNotifyMask;
14896   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14897   manager_hints->input=MagickTrue;
14898   manager_hints->initial_state=NormalState;
14899   manager_hints->window_group=windows->image.id;
14900   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14901     &windows->widget);
14902   windows->widget.highlight_stipple=XCreateBitmapFromData(display,
14903     windows->widget.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14904   windows->widget.shadow_stipple=XCreateBitmapFromData(display,
14905     windows->widget.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14906   (void) XSetTransientForHint(display,windows->widget.id,windows->image.id);
14907   if (IfMagickTrue(display_image->debug) )
14908     (void) LogMagickEvent(X11Event,GetMagickModule(),
14909       "Window id: 0x%lx (widget)",windows->widget.id);
14910   /*
14911     Initialize popup window.
14912   */
14913   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14914     resource_info,&windows->popup);
14915   windows->popup.border_width=0;
14916   windows->popup.flags|=PPosition;
14917   windows->popup.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14918     ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14919     KeyReleaseMask | LeaveWindowMask | StructureNotifyMask;
14920   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14921   manager_hints->input=MagickTrue;
14922   manager_hints->initial_state=NormalState;
14923   manager_hints->window_group=windows->image.id;
14924   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14925     &windows->popup);
14926   windows->popup.highlight_stipple=XCreateBitmapFromData(display,
14927     windows->popup.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14928   windows->popup.shadow_stipple=XCreateBitmapFromData(display,
14929     windows->popup.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14930   (void) XSetTransientForHint(display,windows->popup.id,windows->image.id);
14931   if (IfMagickTrue(display_image->debug) )
14932     (void) LogMagickEvent(X11Event,GetMagickModule(),
14933       "Window id: 0x%lx (pop up)",windows->popup.id);
14934   /*
14935     Initialize Magnify window and cursor.
14936   */
14937   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14938     resource_info,&windows->magnify);
14939   if (IfMagickFalse(resource_info->use_shared_memory) )
14940     windows->magnify.shared_memory=MagickFalse;
14941   (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.magnify",
14942     resource_info->client_name);
14943   windows->magnify.geometry=XGetResourceClass(resource_info->resource_database,
14944     resource_name,"geometry",(char *) NULL);
14945   (void) FormatLocaleString(windows->magnify.name,MaxTextExtent,"Magnify %uX",
14946     resource_info->magnify);
14947   if (windows->magnify.cursor != (Cursor) NULL)
14948     (void) XFreeCursor(display,windows->magnify.cursor);
14949   windows->magnify.cursor=XMakeCursor(display,windows->image.id,
14950     map_info->colormap,resource_info->background_color,
14951     resource_info->foreground_color);
14952   if (windows->magnify.cursor == (Cursor) NULL)
14953     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateCursor",
14954       display_image->filename);
14955   windows->magnify.width=MagnifySize;
14956   windows->magnify.height=MagnifySize;
14957   windows->magnify.flags|=PPosition;
14958   windows->magnify.min_width=MagnifySize;
14959   windows->magnify.min_height=MagnifySize;
14960   windows->magnify.width_inc=MagnifySize;
14961   windows->magnify.height_inc=MagnifySize;
14962   windows->magnify.data=resource_info->magnify;
14963   windows->magnify.attributes.cursor=windows->magnify.cursor;
14964   windows->magnify.attributes.event_mask=ButtonPressMask | ButtonReleaseMask |
14965     ExposureMask | KeyPressMask | KeyReleaseMask | OwnerGrabButtonMask |
14966     StructureNotifyMask;
14967   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14968   manager_hints->input=MagickTrue;
14969   manager_hints->initial_state=NormalState;
14970   manager_hints->window_group=windows->image.id;
14971   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14972     &windows->magnify);
14973   if (IfMagickTrue(display_image->debug) )
14974     (void) LogMagickEvent(X11Event,GetMagickModule(),
14975       "Window id: 0x%lx (magnify)",windows->magnify.id);
14976   (void) XSetTransientForHint(display,windows->magnify.id,windows->image.id);
14977   /*
14978     Initialize panning window.
14979   */
14980   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14981     resource_info,&windows->pan);
14982   (void) CloneString(&windows->pan.name,"Pan Icon");
14983   windows->pan.width=windows->icon.width;
14984   windows->pan.height=windows->icon.height;
14985   (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.pan",
14986     resource_info->client_name);
14987   windows->pan.geometry=XGetResourceClass(resource_info->resource_database,
14988     resource_name,"geometry",(char *) NULL);
14989   (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
14990     &windows->pan.width,&windows->pan.height);
14991   windows->pan.flags|=PPosition;
14992   windows->pan.immutable=MagickTrue;
14993   windows->pan.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14994     ButtonReleaseMask | ExposureMask | KeyPressMask | KeyReleaseMask |
14995     StructureNotifyMask;
14996   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14997   manager_hints->input=MagickFalse;
14998   manager_hints->initial_state=NormalState;
14999   manager_hints->window_group=windows->image.id;
15000   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
15001     &windows->pan);
15002   if (IfMagickTrue(display_image->debug) )
15003     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (pan)",
15004       windows->pan.id);
15005   (void) XSetTransientForHint(display,windows->pan.id,windows->image.id);
15006   if (IfMagickTrue(windows->info.mapped) )
15007     (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15008   if (IfMagickFalse(windows->image.mapped) ||
15009       (windows->backdrop.id != (Window) NULL))
15010     (void) XMapWindow(display,windows->image.id);
15011   /*
15012     Set our progress monitor and warning handlers.
15013   */
15014   if (warning_handler == (WarningHandler) NULL)
15015     {
15016       warning_handler=resource_info->display_warnings ?
15017         SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
15018       warning_handler=resource_info->display_warnings ?
15019         SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
15020     }
15021   /*
15022     Initialize Image and Magnify X images.
15023   */
15024   windows->image.x=0;
15025   windows->image.y=0;
15026   windows->magnify.shape=MagickFalse;
15027   width=(unsigned int) display_image->columns;
15028   height=(unsigned int) display_image->rows;
15029   if ((display_image->columns != width) || (display_image->rows != height))
15030     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15031       display_image->filename);
15032   status=XMakeImage(display,resource_info,&windows->image,display_image,
15033     width,height,exception);
15034   if (IfMagickFalse(status) )
15035     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15036       display_image->filename);
15037   status=XMakeImage(display,resource_info,&windows->magnify,(Image *) NULL,
15038     windows->magnify.width,windows->magnify.height,exception);
15039   if (IfMagickFalse(status))
15040     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15041       display_image->filename);
15042   if (IfMagickTrue(windows->magnify.mapped) )
15043     (void) XMapRaised(display,windows->magnify.id);
15044   if (IfMagickTrue(windows->pan.mapped) )
15045     (void) XMapRaised(display,windows->pan.id);
15046   windows->image.window_changes.width=(int) display_image->columns;
15047   windows->image.window_changes.height=(int) display_image->rows;
15048   (void) XConfigureImage(display,resource_info,windows,display_image,exception);
15049   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15050   (void) XSync(display,MagickFalse);
15051   /*
15052     Respond to events.
15053   */
15054   delay=display_image->delay/MagickMax(display_image->ticks_per_second,1L);
15055   timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15056   update_time=0;
15057   if (IfMagickTrue(resource_info->update) )
15058     {
15059       MagickBooleanType
15060         status;
15061
15062       /*
15063         Determine when file data was last modified.
15064       */
15065       status=GetPathAttributes(display_image->filename,&attributes);
15066       if (IfMagickTrue(status) )
15067         update_time=attributes.st_mtime;
15068     }
15069   *state&=(~FormerImageState);
15070   *state&=(~MontageImageState);
15071   *state&=(~NextImageState);
15072   do
15073   {
15074     /*
15075       Handle a window event.
15076     */
15077     if (IfMagickTrue(windows->image.mapped) )
15078       if ((display_image->delay != 0) || (resource_info->update != 0))
15079         {
15080           if (timer < time((time_t *) NULL))
15081             {
15082               if (IfMagickFalse(resource_info->update) )
15083                 *state|=NextImageState | ExitState;
15084               else
15085                 {
15086                   MagickBooleanType
15087                     status;
15088
15089                   /*
15090                     Determine if image file was modified.
15091                   */
15092                   status=GetPathAttributes(display_image->filename,&attributes);
15093                   if (IfMagickTrue(status) )
15094                     if (update_time != attributes.st_mtime)
15095                       {
15096                         /*
15097                           Redisplay image.
15098                         */
15099                         (void) FormatLocaleString(
15100                           resource_info->image_info->filename,MaxTextExtent,
15101                           "%s:%s",display_image->magick,
15102                           display_image->filename);
15103                         nexus=ReadImage(resource_info->image_info,exception);
15104                         if (nexus != (Image *) NULL)
15105                           {
15106                             nexus=DestroyImage(nexus);
15107                             *state|=NextImageState | ExitState;
15108                           }
15109                       }
15110                   delay=display_image->delay/MagickMax(
15111                     display_image->ticks_per_second,1L);
15112                   timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15113                 }
15114             }
15115           if (XEventsQueued(display,QueuedAfterFlush) == 0)
15116             {
15117               /*
15118                 Do not block if delay > 0.
15119               */
15120               XDelay(display,SuspendTime << 2);
15121               continue;
15122             }
15123         }
15124     timestamp=time((time_t *) NULL);
15125     (void) XNextEvent(display,&event);
15126     if (IfMagickFalse(windows->image.stasis) )
15127       windows->image.stasis=IsMagickTrue((time((time_t *) NULL)-timestamp) > 0);
15128     if (IfMagickFalse(windows->magnify.stasis) )
15129       windows->magnify.stasis=IsMagickTrue((time((time_t *) NULL)-timestamp) > 0);
15130     if (event.xany.window == windows->command.id)
15131       {
15132         /*
15133           Select a command from the Command widget.
15134         */
15135         id=XCommandWidget(display,windows,CommandMenu,&event);
15136         if (id < 0)
15137           continue;
15138         (void) CopyMagickString(command,CommandMenu[id],MaxTextExtent);
15139         command_type=CommandMenus[id];
15140         if (id < MagickMenus)
15141           {
15142             /*
15143               Select a command from a pop-up menu.
15144             */
15145             entry=XMenuWidget(display,windows,CommandMenu[id],Menus[id],
15146               command);
15147             if (entry < 0)
15148               continue;
15149             (void) CopyMagickString(command,Menus[id][entry],MaxTextExtent);
15150             command_type=Commands[id][entry];
15151           }
15152         if (command_type != NullCommand)
15153           nexus=XMagickCommand(display,resource_info,windows,command_type,
15154             &display_image,exception);
15155         continue;
15156       }
15157     switch (event.type)
15158     {
15159       case ButtonPress:
15160       {
15161         if (IfMagickTrue(display_image->debug) )
15162           (void) LogMagickEvent(X11Event,GetMagickModule(),
15163             "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
15164             event.xbutton.button,event.xbutton.x,event.xbutton.y);
15165         if ((event.xbutton.button == Button3) &&
15166             (event.xbutton.state & Mod1Mask))
15167           {
15168             /*
15169               Convert Alt-Button3 to Button2.
15170             */
15171             event.xbutton.button=Button2;
15172             event.xbutton.state&=(~Mod1Mask);
15173           }
15174         if (event.xbutton.window == windows->backdrop.id)
15175           {
15176             (void) XSetInputFocus(display,event.xbutton.window,RevertToParent,
15177               event.xbutton.time);
15178             break;
15179           }
15180         if (event.xbutton.window == windows->image.id)
15181           {
15182             switch (event.xbutton.button)
15183             {
15184               case Button1:
15185               {
15186                 if (resource_info->immutable)
15187                   {
15188                     /*
15189                       Select a command from the Virtual menu.
15190                     */
15191                     entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15192                       command);
15193                     if (entry >= 0)
15194                       nexus=XMagickCommand(display,resource_info,windows,
15195                         VirtualCommands[entry],&display_image,exception);
15196                     break;
15197                   }
15198                 /*
15199                   Map/unmap Command widget.
15200                 */
15201                 if (IfMagickTrue(windows->command.mapped) )
15202                   (void) XWithdrawWindow(display,windows->command.id,
15203                     windows->command.screen);
15204                 else
15205                   {
15206                     (void) XCommandWidget(display,windows,CommandMenu,
15207                       (XEvent *) NULL);
15208                     (void) XMapRaised(display,windows->command.id);
15209                   }
15210                 break;
15211               }
15212               case Button2:
15213               {
15214                 /*
15215                   User pressed the image magnify button.
15216                 */
15217                 (void) XMagickCommand(display,resource_info,windows,ZoomCommand,
15218                   &display_image,exception);
15219                 XMagnifyImage(display,windows,&event,exception);
15220                 break;
15221               }
15222               case Button3:
15223               {
15224                 if (resource_info->immutable)
15225                   {
15226                     /*
15227                       Select a command from the Virtual menu.
15228                     */
15229                     entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15230                       command);
15231                     if (entry >= 0)
15232                       nexus=XMagickCommand(display,resource_info,windows,
15233                         VirtualCommands[entry],&display_image,exception);
15234                     break;
15235                   }
15236                 if (display_image->montage != (char *) NULL)
15237                   {
15238                     /*
15239                       Open or delete a tile from a visual image directory.
15240                     */
15241                     nexus=XTileImage(display,resource_info,windows,
15242                       display_image,&event,exception);
15243                     if (nexus != (Image *) NULL)
15244                       *state|=MontageImageState | NextImageState | ExitState;
15245                     vid_info.x=(short int) windows->image.x;
15246                     vid_info.y=(short int) windows->image.y;
15247                     break;
15248                   }
15249                 /*
15250                   Select a command from the Short Cuts menu.
15251                 */
15252                 entry=XMenuWidget(display,windows,"Short Cuts",ShortCutsMenu,
15253                   command);
15254                 if (entry >= 0)
15255                   nexus=XMagickCommand(display,resource_info,windows,
15256                     ShortCutsCommands[entry],&display_image,exception);
15257                 break;
15258               }
15259               case Button4:
15260               {
15261                 /*
15262                   Wheel up.
15263                 */
15264                 XTranslateImage(display,windows,*image,XK_Up);
15265                 break;
15266               }
15267               case Button5:
15268               {
15269                 /*
15270                   Wheel down.
15271                 */
15272                 XTranslateImage(display,windows,*image,XK_Down);
15273                 break;
15274               }
15275               default:
15276                 break;
15277             }
15278             break;
15279           }
15280         if (event.xbutton.window == windows->magnify.id)
15281           {
15282             int
15283               factor;
15284
15285             static const char
15286               *MagnifyMenu[] =
15287               {
15288                 "2",
15289                 "4",
15290                 "5",
15291                 "6",
15292                 "7",
15293                 "8",
15294                 "9",
15295                 "3",
15296                 (char *) NULL,
15297               };
15298
15299             static KeySym
15300               MagnifyCommands[] =
15301               {
15302                 XK_2,
15303                 XK_4,
15304                 XK_5,
15305                 XK_6,
15306                 XK_7,
15307                 XK_8,
15308                 XK_9,
15309                 XK_3
15310               };
15311
15312             /*
15313               Select a magnify factor from the pop-up menu.
15314             */
15315             factor=XMenuWidget(display,windows,"Magnify",MagnifyMenu,command);
15316             if (factor >= 0)
15317               XMagnifyWindowCommand(display,windows,0,MagnifyCommands[factor],
15318                 exception);
15319             break;
15320           }
15321         if (event.xbutton.window == windows->pan.id)
15322           {
15323             switch (event.xbutton.button)
15324             {
15325               case Button4:
15326               {
15327                 /*
15328                   Wheel up.
15329                 */
15330                 XTranslateImage(display,windows,*image,XK_Up);
15331                 break;
15332               }
15333               case Button5:
15334               {
15335                 /*
15336                   Wheel down.
15337                 */
15338                 XTranslateImage(display,windows,*image,XK_Down);
15339                 break;
15340               }
15341               default:
15342               {
15343                 XPanImage(display,windows,&event,exception);
15344                 break;
15345               }
15346             }
15347             break;
15348           }
15349         delay=display_image->delay/MagickMax(display_image->ticks_per_second,
15350           1L);
15351         timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15352         break;
15353       }
15354       case ButtonRelease:
15355       {
15356         if (IfMagickTrue(display_image->debug) )
15357           (void) LogMagickEvent(X11Event,GetMagickModule(),
15358             "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
15359             event.xbutton.button,event.xbutton.x,event.xbutton.y);
15360         break;
15361       }
15362       case ClientMessage:
15363       {
15364         if (IfMagickTrue(display_image->debug) )
15365           (void) LogMagickEvent(X11Event,GetMagickModule(),
15366             "Client Message: 0x%lx 0x%lx %d 0x%lx",event.xclient.window,
15367             event.xclient.message_type,event.xclient.format,(unsigned long)
15368             event.xclient.data.l[0]);
15369         if (event.xclient.message_type == windows->im_protocols)
15370           {
15371             if (*event.xclient.data.l == (long) windows->im_update_widget)
15372               {
15373                 (void) CloneString(&windows->command.name,MagickTitle);
15374                 windows->command.data=MagickMenus;
15375                 (void) XCommandWidget(display,windows,CommandMenu,
15376                   (XEvent *) NULL);
15377                 break;
15378               }
15379             if (*event.xclient.data.l == (long) windows->im_update_colormap)
15380               {
15381                 /*
15382                   Update graphic context and window colormap.
15383                 */
15384                 for (i=0; i < (int) number_windows; i++)
15385                 {
15386                   if (magick_windows[i]->id == windows->icon.id)
15387                     continue;
15388                   context_values.background=pixel->background_color.pixel;
15389                   context_values.foreground=pixel->foreground_color.pixel;
15390                   (void) XChangeGC(display,magick_windows[i]->annotate_context,
15391                     context_mask,&context_values);
15392                   (void) XChangeGC(display,magick_windows[i]->widget_context,
15393                     context_mask,&context_values);
15394                   context_values.background=pixel->foreground_color.pixel;
15395                   context_values.foreground=pixel->background_color.pixel;
15396                   context_values.plane_mask=context_values.background ^
15397                     context_values.foreground;
15398                   (void) XChangeGC(display,magick_windows[i]->highlight_context,
15399                     (size_t) (context_mask | GCPlaneMask),
15400                     &context_values);
15401                   magick_windows[i]->attributes.background_pixel=
15402                     pixel->background_color.pixel;
15403                   magick_windows[i]->attributes.border_pixel=
15404                     pixel->border_color.pixel;
15405                   magick_windows[i]->attributes.colormap=map_info->colormap;
15406                   (void) XChangeWindowAttributes(display,magick_windows[i]->id,
15407                     (unsigned long) magick_windows[i]->mask,
15408                     &magick_windows[i]->attributes);
15409                 }
15410                 if (IfMagickTrue(windows->pan.mapped) )
15411                   {
15412                     (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
15413                       windows->pan.pixmap);
15414                     (void) XClearWindow(display,windows->pan.id);
15415                     XDrawPanRectangle(display,windows);
15416                   }
15417                 if (windows->backdrop.id != (Window) NULL)
15418                   (void) XInstallColormap(display,map_info->colormap);
15419                 break;
15420               }
15421             if (*event.xclient.data.l == (long) windows->im_former_image)
15422               {
15423                 *state|=FormerImageState | ExitState;
15424                 break;
15425               }
15426             if (*event.xclient.data.l == (long) windows->im_next_image)
15427               {
15428                 *state|=NextImageState | ExitState;
15429                 break;
15430               }
15431             if (*event.xclient.data.l == (long) windows->im_retain_colors)
15432               {
15433                 *state|=RetainColorsState;
15434                 break;
15435               }
15436             if (*event.xclient.data.l == (long) windows->im_exit)
15437               {
15438                 *state|=ExitState;
15439                 break;
15440               }
15441             break;
15442           }
15443         if (event.xclient.message_type == windows->dnd_protocols)
15444           {
15445             Atom
15446               selection,
15447               type;
15448
15449             int
15450               format,
15451               status;
15452
15453             unsigned char
15454               *data;
15455
15456             unsigned long
15457               after,
15458               length;
15459
15460             /*
15461               Display image named by the Drag-and-Drop selection.
15462             */
15463             if ((*event.xclient.data.l != 2) && (*event.xclient.data.l != 128))
15464               break;
15465             selection=XInternAtom(display,"DndSelection",MagickFalse);
15466             status=XGetWindowProperty(display,root_window,selection,0L,(long)
15467               MaxTextExtent,MagickFalse,(Atom) AnyPropertyType,&type,&format,
15468               &length,&after,&data);
15469             if ((status != Success) || (length == 0))
15470               break;
15471             if (*event.xclient.data.l == 2)
15472               {
15473                 /*
15474                   Offix DND.
15475                 */
15476                 (void) CopyMagickString(resource_info->image_info->filename,
15477                   (char *) data,MaxTextExtent);
15478               }
15479             else
15480               {
15481                 /*
15482                   XDND.
15483                 */
15484                 if (strncmp((char *) data, "file:", 5) != 0)
15485                   {
15486                     (void) XFree((void *) data);
15487                     break;
15488                   }
15489                 (void) CopyMagickString(resource_info->image_info->filename,
15490                   ((char *) data)+5,MaxTextExtent);
15491               }
15492             nexus=ReadImage(resource_info->image_info,exception);
15493             CatchException(exception);
15494             if (nexus != (Image *) NULL)
15495               *state|=NextImageState | ExitState;
15496             (void) XFree((void *) data);
15497             break;
15498           }
15499         /*
15500           If client window delete message, exit.
15501         */
15502         if (event.xclient.message_type != windows->wm_protocols)
15503           break;
15504         if (*event.xclient.data.l != (long) windows->wm_delete_window)
15505           break;
15506         (void) XWithdrawWindow(display,event.xclient.window,
15507           visual_info->screen);
15508         if (event.xclient.window == windows->image.id)
15509           {
15510             *state|=ExitState;
15511             break;
15512           }
15513         if (event.xclient.window == windows->pan.id)
15514           {
15515             /*
15516               Restore original image size when pan window is deleted.
15517             */
15518             windows->image.window_changes.width=windows->image.ximage->width;
15519             windows->image.window_changes.height=windows->image.ximage->height;
15520             (void) XConfigureImage(display,resource_info,windows,
15521               display_image,exception);
15522           }
15523         break;
15524       }
15525       case ConfigureNotify:
15526       {
15527         if (IfMagickTrue(display_image->debug) )
15528           (void) LogMagickEvent(X11Event,GetMagickModule(),
15529             "Configure Notify: 0x%lx %dx%d+%d+%d %d",event.xconfigure.window,
15530             event.xconfigure.width,event.xconfigure.height,event.xconfigure.x,
15531             event.xconfigure.y,event.xconfigure.send_event);
15532         if (event.xconfigure.window == windows->image.id)
15533           {
15534             /*
15535               Image window has a new configuration.
15536             */
15537             if (event.xconfigure.send_event != 0)
15538               {
15539                 XWindowChanges
15540                   window_changes;
15541
15542                 /*
15543                   Position the transient windows relative of the Image window.
15544                 */
15545                 if (windows->command.geometry == (char *) NULL)
15546                   if (IfMagickFalse(windows->command.mapped) )
15547                     {
15548                       windows->command.x=event.xconfigure.x-
15549                         windows->command.width-25;
15550                       windows->command.y=event.xconfigure.y;
15551                       XConstrainWindowPosition(display,&windows->command);
15552                       window_changes.x=windows->command.x;
15553                       window_changes.y=windows->command.y;
15554                       (void) XReconfigureWMWindow(display,windows->command.id,
15555                         windows->command.screen,(unsigned int) (CWX | CWY),
15556                         &window_changes);
15557                     }
15558                 if (windows->widget.geometry == (char *) NULL)
15559                   if (IfMagickFalse(windows->widget.mapped) )
15560                     {
15561                       windows->widget.x=event.xconfigure.x+
15562                         event.xconfigure.width/10;
15563                       windows->widget.y=event.xconfigure.y+
15564                         event.xconfigure.height/10;
15565                       XConstrainWindowPosition(display,&windows->widget);
15566                       window_changes.x=windows->widget.x;
15567                       window_changes.y=windows->widget.y;
15568                       (void) XReconfigureWMWindow(display,windows->widget.id,
15569                         windows->widget.screen,(unsigned int) (CWX | CWY),
15570                         &window_changes);
15571                     }
15572                 if (windows->magnify.geometry == (char *) NULL)
15573                   if (IfMagickFalse(windows->magnify.mapped) )
15574                     {
15575                       windows->magnify.x=event.xconfigure.x+
15576                         event.xconfigure.width+25;
15577                       windows->magnify.y=event.xconfigure.y;
15578                       XConstrainWindowPosition(display,&windows->magnify);
15579                       window_changes.x=windows->magnify.x;
15580                       window_changes.y=windows->magnify.y;
15581                       (void) XReconfigureWMWindow(display,windows->magnify.id,
15582                         windows->magnify.screen,(unsigned int) (CWX | CWY),
15583                         &window_changes);
15584                     }
15585                 if (windows->pan.geometry == (char *) NULL)
15586                   if (IfMagickFalse(windows->pan.mapped) )
15587                     {
15588                       windows->pan.x=event.xconfigure.x+
15589                         event.xconfigure.width+25;
15590                       windows->pan.y=event.xconfigure.y+
15591                         windows->magnify.height+50;
15592                       XConstrainWindowPosition(display,&windows->pan);
15593                       window_changes.x=windows->pan.x;
15594                       window_changes.y=windows->pan.y;
15595                       (void) XReconfigureWMWindow(display,windows->pan.id,
15596                         windows->pan.screen,(unsigned int) (CWX | CWY),
15597                         &window_changes);
15598                     }
15599               }
15600             if ((event.xconfigure.width == (int) windows->image.width) &&
15601                 (event.xconfigure.height == (int) windows->image.height))
15602               break;
15603             windows->image.width=(unsigned int) event.xconfigure.width;
15604             windows->image.height=(unsigned int) event.xconfigure.height;
15605             windows->image.x=0;
15606             windows->image.y=0;
15607             if (display_image->montage != (char *) NULL)
15608               {
15609                 windows->image.x=vid_info.x;
15610                 windows->image.y=vid_info.y;
15611               }
15612             if (IfMagickTrue(windows->image.mapped) &&
15613                 IfMagickTrue(windows->image.stasis) )
15614               {
15615                 /*
15616                   Update image window configuration.
15617                 */
15618                 windows->image.window_changes.width=event.xconfigure.width;
15619                 windows->image.window_changes.height=event.xconfigure.height;
15620                 (void) XConfigureImage(display,resource_info,windows,
15621                   display_image,exception);
15622               }
15623             /*
15624               Update pan window configuration.
15625             */
15626             if ((event.xconfigure.width < windows->image.ximage->width) ||
15627                 (event.xconfigure.height < windows->image.ximage->height))
15628               {
15629                 (void) XMapRaised(display,windows->pan.id);
15630                 XDrawPanRectangle(display,windows);
15631               }
15632             else
15633               if (IfMagickTrue(windows->pan.mapped) )
15634                 (void) XWithdrawWindow(display,windows->pan.id,
15635                   windows->pan.screen);
15636             break;
15637           }
15638         if (event.xconfigure.window == windows->magnify.id)
15639           {
15640             unsigned int
15641               magnify;
15642
15643             /*
15644               Magnify window has a new configuration.
15645             */
15646             windows->magnify.width=(unsigned int) event.xconfigure.width;
15647             windows->magnify.height=(unsigned int) event.xconfigure.height;
15648             if (IfMagickFalse(windows->magnify.mapped) )
15649               break;
15650             magnify=1;
15651             while ((int) magnify <= event.xconfigure.width)
15652               magnify<<=1;
15653             while ((int) magnify <= event.xconfigure.height)
15654               magnify<<=1;
15655             magnify>>=1;
15656             if (((int) magnify != event.xconfigure.width) ||
15657                 ((int) magnify != event.xconfigure.height))
15658               {
15659                 window_changes.width=(int) magnify;
15660                 window_changes.height=(int) magnify;
15661                 (void) XReconfigureWMWindow(display,windows->magnify.id,
15662                   windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
15663                   &window_changes);
15664                 break;
15665               }
15666             if (IfMagickTrue(windows->magnify.mapped) &&
15667                 IfMagickTrue(windows->magnify.stasis) )
15668               {
15669                 status=XMakeImage(display,resource_info,&windows->magnify,
15670                   display_image,windows->magnify.width,windows->magnify.height,
15671                   exception);
15672                 XMakeMagnifyImage(display,windows,exception);
15673               }
15674             break;
15675           }
15676         if (IfMagickTrue(windows->magnify.mapped) &&
15677             (event.xconfigure.window == windows->pan.id))
15678           {
15679             /*
15680               Pan icon window has a new configuration.
15681             */
15682             if (event.xconfigure.send_event != 0)
15683               {
15684                 windows->pan.x=event.xconfigure.x;
15685                 windows->pan.y=event.xconfigure.y;
15686               }
15687             windows->pan.width=(unsigned int) event.xconfigure.width;
15688             windows->pan.height=(unsigned int) event.xconfigure.height;
15689             break;
15690           }
15691         if (event.xconfigure.window == windows->icon.id)
15692           {
15693             /*
15694               Icon window has a new configuration.
15695             */
15696             windows->icon.width=(unsigned int) event.xconfigure.width;
15697             windows->icon.height=(unsigned int) event.xconfigure.height;
15698             break;
15699           }
15700         break;
15701       }
15702       case DestroyNotify:
15703       {
15704         /*
15705           Group leader has exited.
15706         */
15707         if (IfMagickTrue(display_image->debug) )
15708           (void) LogMagickEvent(X11Event,GetMagickModule(),
15709             "Destroy Notify: 0x%lx",event.xdestroywindow.window);
15710         if (event.xdestroywindow.window == windows->group_leader.id)
15711           {
15712             *state|=ExitState;
15713             break;
15714           }
15715         break;
15716       }
15717       case EnterNotify:
15718       {
15719         /*
15720           Selectively install colormap.
15721         */
15722         if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15723           if (event.xcrossing.mode != NotifyUngrab)
15724             XInstallColormap(display,map_info->colormap);
15725         break;
15726       }
15727       case Expose:
15728       {
15729         if (IfMagickTrue(display_image->debug) )
15730           (void) LogMagickEvent(X11Event,GetMagickModule(),
15731             "Expose: 0x%lx %dx%d+%d+%d",event.xexpose.window,
15732             event.xexpose.width,event.xexpose.height,event.xexpose.x,
15733             event.xexpose.y);
15734         /*
15735           Refresh windows that are now exposed.
15736         */
15737         if ((event.xexpose.window == windows->image.id) &&
15738             IfMagickTrue(windows->image.mapped) )
15739           {
15740             XRefreshWindow(display,&windows->image,&event);
15741             delay=display_image->delay/MagickMax(
15742               display_image->ticks_per_second,1L);
15743             timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15744             break;
15745           }
15746         if ((event.xexpose.window == windows->magnify.id) &&
15747             IfMagickTrue(windows->magnify.mapped))
15748           {
15749             XMakeMagnifyImage(display,windows,exception);
15750             break;
15751           }
15752         if (event.xexpose.window == windows->pan.id)
15753           {
15754             XDrawPanRectangle(display,windows);
15755             break;
15756           }
15757         if (event.xexpose.window == windows->icon.id)
15758           {
15759             XRefreshWindow(display,&windows->icon,&event);
15760             break;
15761           }
15762         break;
15763       }
15764       case KeyPress:
15765       {
15766         int
15767           length;
15768
15769         /*
15770           Respond to a user key press.
15771         */
15772         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
15773           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15774         *(command+length)='\0';
15775         if (IfMagickTrue(display_image->debug) )
15776           (void) LogMagickEvent(X11Event,GetMagickModule(),
15777             "Key press: %d 0x%lx (%s)",event.xkey.state,(unsigned long)
15778             key_symbol,command);
15779         if (event.xkey.window == windows->image.id)
15780           {
15781             command_type=XImageWindowCommand(display,resource_info,windows,
15782               event.xkey.state,key_symbol,&display_image,exception);
15783             if (command_type != NullCommand)
15784               nexus=XMagickCommand(display,resource_info,windows,command_type,
15785                 &display_image,exception);
15786           }
15787         if (event.xkey.window == windows->magnify.id)
15788           XMagnifyWindowCommand(display,windows,event.xkey.state,key_symbol,
15789             exception);
15790         if (event.xkey.window == windows->pan.id)
15791           {
15792             if ((key_symbol == XK_q) || (key_symbol == XK_Escape))
15793               (void) XWithdrawWindow(display,windows->pan.id,
15794                 windows->pan.screen);
15795             else
15796               if ((key_symbol == XK_F1) || (key_symbol == XK_Help))
15797                 XTextViewWidget(display,resource_info,windows,MagickFalse,
15798                   "Help Viewer - Image Pan",ImagePanHelp);
15799               else
15800                 XTranslateImage(display,windows,*image,key_symbol);
15801           }
15802         delay=display_image->delay/MagickMax(
15803           display_image->ticks_per_second,1L);
15804         timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15805         break;
15806       }
15807       case KeyRelease:
15808       {
15809         /*
15810           Respond to a user key release.
15811         */
15812         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
15813           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15814         if (IfMagickTrue(display_image->debug) )
15815           (void) LogMagickEvent(X11Event,GetMagickModule(),
15816             "Key release: 0x%lx (%c)",(unsigned long) key_symbol,*command);
15817         break;
15818       }
15819       case LeaveNotify:
15820       {
15821         /*
15822           Selectively uninstall colormap.
15823         */
15824         if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15825           if (event.xcrossing.mode != NotifyUngrab)
15826             XUninstallColormap(display,map_info->colormap);
15827         break;
15828       }
15829       case MapNotify:
15830       {
15831         if (IfMagickTrue(display_image->debug) )
15832           (void) LogMagickEvent(X11Event,GetMagickModule(),"Map Notify: 0x%lx",
15833             event.xmap.window);
15834         if (event.xmap.window == windows->backdrop.id)
15835           {
15836             (void) XSetInputFocus(display,event.xmap.window,RevertToParent,
15837               CurrentTime);
15838             windows->backdrop.mapped=MagickTrue;
15839             break;
15840           }
15841         if (event.xmap.window == windows->image.id)
15842           {
15843             if (windows->backdrop.id != (Window) NULL)
15844               (void) XInstallColormap(display,map_info->colormap);
15845             if (LocaleCompare(display_image->magick,"LOGO") == 0)
15846               {
15847                 if (LocaleCompare(display_image->filename,"LOGO") == 0)
15848                   nexus=XOpenImage(display,resource_info,windows,MagickFalse);
15849               }
15850             if (((int) windows->image.width < windows->image.ximage->width) ||
15851                 ((int) windows->image.height < windows->image.ximage->height))
15852               (void) XMapRaised(display,windows->pan.id);
15853             windows->image.mapped=MagickTrue;
15854             break;
15855           }
15856         if (event.xmap.window == windows->magnify.id)
15857           {
15858             XMakeMagnifyImage(display,windows,exception);
15859             windows->magnify.mapped=MagickTrue;
15860             (void) XWithdrawWindow(display,windows->info.id,
15861               windows->info.screen);
15862             break;
15863           }
15864         if (event.xmap.window == windows->pan.id)
15865           {
15866             XMakePanImage(display,resource_info,windows,display_image,
15867               exception);
15868             windows->pan.mapped=MagickTrue;
15869             break;
15870           }
15871         if (event.xmap.window == windows->info.id)
15872           {
15873             windows->info.mapped=MagickTrue;
15874             break;
15875           }
15876         if (event.xmap.window == windows->icon.id)
15877           {
15878             MagickBooleanType
15879               taint;
15880
15881             /*
15882               Create an icon image.
15883             */
15884             taint=display_image->taint;
15885             XMakeStandardColormap(display,icon_visual,icon_resources,
15886               display_image,icon_map,icon_pixel,exception);
15887             (void) XMakeImage(display,icon_resources,&windows->icon,
15888               display_image,windows->icon.width,windows->icon.height,
15889               exception);
15890             display_image->taint=taint;
15891             (void) XSetWindowBackgroundPixmap(display,windows->icon.id,
15892               windows->icon.pixmap);
15893             (void) XClearWindow(display,windows->icon.id);
15894             (void) XWithdrawWindow(display,windows->info.id,
15895               windows->info.screen);
15896             windows->icon.mapped=MagickTrue;
15897             break;
15898           }
15899         if (event.xmap.window == windows->command.id)
15900           {
15901             windows->command.mapped=MagickTrue;
15902             break;
15903           }
15904         if (event.xmap.window == windows->popup.id)
15905           {
15906             windows->popup.mapped=MagickTrue;
15907             break;
15908           }
15909         if (event.xmap.window == windows->widget.id)
15910           {
15911             windows->widget.mapped=MagickTrue;
15912             break;
15913           }
15914         break;
15915       }
15916       case MappingNotify:
15917       {
15918         (void) XRefreshKeyboardMapping(&event.xmapping);
15919         break;
15920       }
15921       case NoExpose:
15922         break;
15923       case PropertyNotify:
15924       {
15925         Atom
15926           type;
15927
15928         int
15929           format,
15930           status;
15931
15932         unsigned char
15933           *data;
15934
15935         unsigned long
15936           after,
15937           length;
15938
15939         if (IfMagickTrue(display_image->debug) )
15940           (void) LogMagickEvent(X11Event,GetMagickModule(),
15941             "Property Notify: 0x%lx 0x%lx %d",event.xproperty.window,
15942             event.xproperty.atom,event.xproperty.state);
15943         if (event.xproperty.atom != windows->im_remote_command)
15944           break;
15945         /*
15946           Display image named by the remote command protocol.
15947         */
15948         status=XGetWindowProperty(display,event.xproperty.window,
15949           event.xproperty.atom,0L,(long) MaxTextExtent,MagickFalse,(Atom)
15950           AnyPropertyType,&type,&format,&length,&after,&data);
15951         if ((status != Success) || (length == 0))
15952           break;
15953         if (LocaleCompare((char *) data,"-quit") == 0)
15954           {
15955             XClientMessage(display,windows->image.id,windows->im_protocols,
15956               windows->im_exit,CurrentTime);
15957             (void) XFree((void *) data);
15958             break;
15959           }
15960         (void) CopyMagickString(resource_info->image_info->filename,
15961           (char *) data,MaxTextExtent);
15962         (void) XFree((void *) data);
15963         nexus=ReadImage(resource_info->image_info,exception);
15964         CatchException(exception);
15965         if (nexus != (Image *) NULL)
15966           *state|=NextImageState | ExitState;
15967         break;
15968       }
15969       case ReparentNotify:
15970       {
15971         if (IfMagickTrue(display_image->debug) )
15972           (void) LogMagickEvent(X11Event,GetMagickModule(),
15973             "Reparent Notify: 0x%lx=>0x%lx",event.xreparent.parent,
15974             event.xreparent.window);
15975         break;
15976       }
15977       case UnmapNotify:
15978       {
15979         if (IfMagickTrue(display_image->debug) )
15980           (void) LogMagickEvent(X11Event,GetMagickModule(),
15981             "Unmap Notify: 0x%lx",event.xunmap.window);
15982         if (event.xunmap.window == windows->backdrop.id)
15983           {
15984             windows->backdrop.mapped=MagickFalse;
15985             break;
15986           }
15987         if (event.xunmap.window == windows->image.id)
15988           {
15989             windows->image.mapped=MagickFalse;
15990             break;
15991           }
15992         if (event.xunmap.window == windows->magnify.id)
15993           {
15994             windows->magnify.mapped=MagickFalse;
15995             break;
15996           }
15997         if (event.xunmap.window == windows->pan.id)
15998           {
15999             windows->pan.mapped=MagickFalse;
16000             break;
16001           }
16002         if (event.xunmap.window == windows->info.id)
16003           {
16004             windows->info.mapped=MagickFalse;
16005             break;
16006           }
16007         if (event.xunmap.window == windows->icon.id)
16008           {
16009             if (map_info->colormap == icon_map->colormap)
16010               XConfigureImageColormap(display,resource_info,windows,
16011                 display_image,exception);
16012             (void) XFreeStandardColormap(display,icon_visual,icon_map,
16013               icon_pixel);
16014             windows->icon.mapped=MagickFalse;
16015             break;
16016           }
16017         if (event.xunmap.window == windows->command.id)
16018           {
16019             windows->command.mapped=MagickFalse;
16020             break;
16021           }
16022         if (event.xunmap.window == windows->popup.id)
16023           {
16024             if (windows->backdrop.id != (Window) NULL)
16025               (void) XSetInputFocus(display,windows->image.id,RevertToParent,
16026                 CurrentTime);
16027             windows->popup.mapped=MagickFalse;
16028             break;
16029           }
16030         if (event.xunmap.window == windows->widget.id)
16031           {
16032             if (windows->backdrop.id != (Window) NULL)
16033               (void) XSetInputFocus(display,windows->image.id,RevertToParent,
16034                 CurrentTime);
16035             windows->widget.mapped=MagickFalse;
16036             break;
16037           }
16038         break;
16039       }
16040       default:
16041       {
16042         if (IfMagickTrue(display_image->debug) )
16043           (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
16044             event.type);
16045         break;
16046       }
16047     }
16048   } while (!(*state & ExitState));
16049   if ((*state & ExitState) == 0)
16050     (void) XMagickCommand(display,resource_info,windows,FreeBuffersCommand,
16051       &display_image,exception);
16052   else
16053     if (IfMagickTrue(resource_info->confirm_edit) )
16054       {
16055         /*
16056           Query user if image has changed.
16057         */
16058         if (IfMagickFalse(resource_info->immutable) &&
16059             IfMagickTrue(display_image->taint))
16060           {
16061             int
16062               status;
16063
16064             status=XConfirmWidget(display,windows,"Your image changed.",
16065               "Do you want to save it");
16066             if (status == 0)
16067               *state&=(~ExitState);
16068             else
16069               if (status > 0)
16070                 (void) XMagickCommand(display,resource_info,windows,SaveCommand,
16071                   &display_image,exception);
16072           }
16073       }
16074   if ((windows->visual_info->klass == GrayScale) ||
16075       (windows->visual_info->klass == PseudoColor) ||
16076       (windows->visual_info->klass == DirectColor))
16077     {
16078       /*
16079         Withdraw pan and Magnify window.
16080       */
16081       if (IfMagickTrue(windows->info.mapped) )
16082         (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
16083       if (IfMagickTrue(windows->magnify.mapped) )
16084         (void) XWithdrawWindow(display,windows->magnify.id,
16085           windows->magnify.screen);
16086       if (IfMagickTrue(windows->command.mapped) )
16087         (void) XWithdrawWindow(display,windows->command.id,
16088           windows->command.screen);
16089     }
16090   if (IfMagickTrue(windows->pan.mapped) )
16091     (void) XWithdrawWindow(display,windows->pan.id,windows->pan.screen);
16092   if (IfMagickFalse(resource_info->backdrop) )
16093     if (windows->backdrop.mapped)
16094       {
16095         (void) XWithdrawWindow(display,windows->backdrop.id,
16096           windows->backdrop.screen);
16097         (void) XDestroyWindow(display,windows->backdrop.id);
16098         windows->backdrop.id=(Window) NULL;
16099         (void) XWithdrawWindow(display,windows->image.id,
16100           windows->image.screen);
16101         (void) XDestroyWindow(display,windows->image.id);
16102         windows->image.id=(Window) NULL;
16103       }
16104   XSetCursorState(display,windows,MagickTrue);
16105   XCheckRefreshWindows(display,windows);
16106   if (((*state & FormerImageState) != 0) || ((*state & NextImageState) != 0))
16107     *state&=(~ExitState);
16108   if (*state & ExitState)
16109     {
16110       /*
16111         Free Standard Colormap.
16112       */
16113       (void) XFreeStandardColormap(display,icon_visual,icon_map,icon_pixel);
16114       if (resource_info->map_type == (char *) NULL)
16115         (void) XFreeStandardColormap(display,visual_info,map_info,pixel);
16116       /*
16117         Free X resources.
16118       */
16119       if (resource_info->copy_image != (Image *) NULL)
16120         {
16121           resource_info->copy_image=DestroyImage(resource_info->copy_image);
16122           resource_info->copy_image=NewImageList();
16123         }
16124       DestroyXResources();
16125     }
16126   (void) XSync(display,MagickFalse);
16127   /*
16128     Restore our progress monitor and warning handlers.
16129   */
16130   (void) SetErrorHandler(warning_handler);
16131   (void) SetWarningHandler(warning_handler);
16132   /*
16133     Change to home directory.
16134   */
16135   directory=getcwd(working_directory,MaxTextExtent);
16136   (void) directory;
16137   {
16138     int
16139       status;
16140
16141     if (*resource_info->home_directory == '\0')
16142       (void) CopyMagickString(resource_info->home_directory,".",MaxTextExtent);
16143     status=chdir(resource_info->home_directory);
16144     if (status == -1)
16145       (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
16146         "UnableToOpenFile","%s",resource_info->home_directory);
16147   }
16148   *image=display_image;
16149   return(nexus);
16150 }
16151 #else
16152 \f
16153 /*
16154 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16155 %                                                                             %
16156 %                                                                             %
16157 %                                                                             %
16158 +   D i s p l a y I m a g e s                                                 %
16159 %                                                                             %
16160 %                                                                             %
16161 %                                                                             %
16162 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16163 %
16164 %  DisplayImages() displays an image sequence to any X window screen.  It
16165 %  returns a value other than 0 if successful.  Check the exception member
16166 %  of image to determine the reason for any failure.
16167 %
16168 %  The format of the DisplayImages method is:
16169 %
16170 %      MagickBooleanType DisplayImages(const ImageInfo *image_info,
16171 %        Image *images,ExceptionInfo *exception)
16172 %
16173 %  A description of each parameter follows:
16174 %
16175 %    o image_info: the image info.
16176 %
16177 %    o image: the image.
16178 %
16179 %    o exception: return any errors or warnings in this structure.
16180 %
16181 */
16182 MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
16183   Image *image,ExceptionInfo *exception)
16184 {
16185   assert(image_info != (const ImageInfo *) NULL);
16186   assert(image_info->signature == MagickSignature);
16187   assert(image != (Image *) NULL);
16188   assert(image->signature == MagickSignature);
16189   (void) image_info;
16190   if (IfMagickTrue(image->debug) )
16191     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
16192   (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16193     "DelegateLibrarySupportNotBuiltIn","'%s' (X11)",image->filename);
16194   return(MagickFalse);
16195 }
16196 \f
16197 /*
16198 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16199 %                                                                             %
16200 %                                                                             %
16201 %                                                                             %
16202 +   R e m o t e D i s p l a y C o m m a n d                                   %
16203 %                                                                             %
16204 %                                                                             %
16205 %                                                                             %
16206 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16207 %
16208 %  RemoteDisplayCommand() encourages a remote display program to display the
16209 %  specified image filename.
16210 %
16211 %  The format of the RemoteDisplayCommand method is:
16212 %
16213 %      MagickBooleanType RemoteDisplayCommand(const ImageInfo *image,
16214 %        const char *window,const char *filename,ExceptionInfo *exception)
16215 %
16216 %  A description of each parameter follows:
16217 %
16218 %    o image_info: the image info.
16219 %
16220 %    o window: Specifies the name or id of an X window.
16221 %
16222 %    o filename: the name of the image filename to display.
16223 %
16224 %    o exception: return any errors or warnings in this structure.
16225 %
16226 */
16227 MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
16228   const char *window,const char *filename,ExceptionInfo *exception)
16229 {
16230   assert(image_info != (const ImageInfo *) NULL);
16231   assert(image_info->signature == MagickSignature);
16232   assert(filename != (char *) NULL);
16233   (void) window;
16234   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
16235   (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16236     "DelegateLibrarySupportNotBuiltIn","'%s' (X11)",image_info->filename);
16237   return(MagickFalse);
16238 }
16239 #endif