]> granicus.if.org Git - imagemagick/blob - MagickCore/display.c
ffcf7eae44a12ce36873042048b7a8f2e2c6dfe7
[imagemagick] / MagickCore / display.c
1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 %                                                                             %
4 %                                                                             %
5 %                                                                             %
6 %               DDDD   IIIII  SSSSS  PPPP   L       AAA   Y   Y               %
7 %               D   D    I    SS     P   P  L      A   A   Y Y                %
8 %               D   D    I     SSS   PPPP   L      AAAAA    Y                 %
9 %               D   D    I       SS  P      L      A   A    Y                 %
10 %               DDDD   IIIII  SSSSS  P      LLLLL  A   A    Y                 %
11 %                                                                             %
12 %                                                                             %
13 %        MagickCore Methods to Interactively Display and Edit an Image        %
14 %                                                                             %
15 %                             Software Design                                 %
16 %                               John Cristy                                   %
17 %                                July 1992                                    %
18 %                                                                             %
19 %                                                                             %
20 %  Copyright 1999-2012 ImageMagick Studio LLC, a non-profit organization      %
21 %  dedicated to making software imaging solutions freely available.           %
22 %                                                                             %
23 %  You may not use this file except in compliance with the License.  You may  %
24 %  obtain a copy of the License at                                            %
25 %                                                                             %
26 %    http://www.imagemagick.org/script/license.php                            %
27 %                                                                             %
28 %  Unless required by applicable law or agreed to in writing, software        %
29 %  distributed under the License is distributed on an "AS IS" BASIS,          %
30 %  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
31 %  See the License for the specific language governing permissions and        %
32 %  limitations under the License.                                             %
33 %                                                                             %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35 %
36 %
37 */
38 \f
39 /*
40   Include declarations.
41 */
42 #include "MagickCore/studio.h"
43 #include "MagickCore/artifact.h"
44 #include "MagickCore/attribute.h"
45 #include "MagickCore/blob.h"
46 #include "MagickCore/cache.h"
47 #include "MagickCore/cache-private.h"
48 #include "MagickCore/client.h"
49 #include "MagickCore/color.h"
50 #include "MagickCore/colorspace.h"
51 #include "MagickCore/composite.h"
52 #include "MagickCore/constitute.h"
53 #include "MagickCore/decorate.h"
54 #include "MagickCore/delegate.h"
55 #include "MagickCore/display.h"
56 #include "MagickCore/display-private.h"
57 #include "MagickCore/distort.h"
58 #include "MagickCore/draw.h"
59 #include "MagickCore/effect.h"
60 #include "MagickCore/enhance.h"
61 #include "MagickCore/exception.h"
62 #include "MagickCore/exception-private.h"
63 #include "MagickCore/fx.h"
64 #include "MagickCore/geometry.h"
65 #include "MagickCore/image.h"
66 #include "MagickCore/image-private.h"
67 #include "MagickCore/list.h"
68 #include "MagickCore/log.h"
69 #include "MagickCore/magick.h"
70 #include "MagickCore/memory_.h"
71 #include "MagickCore/monitor.h"
72 #include "MagickCore/monitor-private.h"
73 #include "MagickCore/montage.h"
74 #include "MagickCore/option.h"
75 #include "MagickCore/paint.h"
76 #include "MagickCore/pixel.h"
77 #include "MagickCore/pixel-accessor.h"
78 #include "MagickCore/PreRvIcccm.h"
79 #include "MagickCore/property.h"
80 #include "MagickCore/quantum.h"
81 #include "MagickCore/quantum-private.h"
82 #include "MagickCore/resize.h"
83 #include "MagickCore/resource_.h"
84 #include "MagickCore/shear.h"
85 #include "MagickCore/segment.h"
86 #include "MagickCore/statistic.h"
87 #include "MagickCore/string_.h"
88 #include "MagickCore/string-private.h"
89 #include "MagickCore/transform.h"
90 #include "MagickCore/threshold.h"
91 #include "MagickCore/utility.h"
92 #include "MagickCore/utility-private.h"
93 #include "MagickCore/version.h"
94 #include "MagickCore/widget.h"
95 #include "MagickCore/widget-private.h"
96 #include "MagickCore/xwindow.h"
97 #include "MagickCore/xwindow-private.h"
98 \f
99 #if defined(MAGICKCORE_X11_DELEGATE)
100 /*
101   Define declarations.
102 */
103 #define MaxColors  MagickMin((ssize_t) windows->visual_info->colormap_size,256L)
104 \f
105 /*
106   Constant declarations.
107 */
108 static const unsigned char
109   HighlightBitmap[8] =
110   {
111     0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55
112   },
113   OpaqueBitmap[8] =
114   {
115     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
116   },
117   ShadowBitmap[8] =
118   {
119     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
120   };
121
122 static const char
123   *PageSizes[] =
124   {
125     "Letter",
126     "Tabloid",
127     "Ledger",
128     "Legal",
129     "Statement",
130     "Executive",
131     "A3",
132     "A4",
133     "A5",
134     "B4",
135     "B5",
136     "Folio",
137     "Quarto",
138     "10x14",
139     (char *) NULL
140   };
141 \f
142 /*
143   Help widget declarations.
144 */
145 static const char
146   *ImageAnnotateHelp[] =
147   {
148     "In annotate mode, the Command widget has these options:",
149     "",
150     "    Font Name",
151     "      fixed",
152     "      variable",
153     "      5x8",
154     "      6x10",
155     "      7x13bold",
156     "      8x13bold",
157     "      9x15bold",
158     "      10x20",
159     "      12x24",
160     "      Browser...",
161     "    Font Color",
162     "      black",
163     "      blue",
164     "      cyan",
165     "      green",
166     "      gray",
167     "      red",
168     "      magenta",
169     "      yellow",
170     "      white",
171     "      transparent",
172     "      Browser...",
173     "    Font Color",
174     "      black",
175     "      blue",
176     "      cyan",
177     "      green",
178     "      gray",
179     "      red",
180     "      magenta",
181     "      yellow",
182     "      white",
183     "      transparent",
184     "      Browser...",
185     "    Rotate Text",
186     "      -90",
187     "      -45",
188     "      -30",
189     "      0",
190     "      30",
191     "      45",
192     "      90",
193     "      180",
194     "      Dialog...",
195     "    Help",
196     "    Dismiss",
197     "",
198     "Choose a font name from the Font Name sub-menu.  Additional",
199     "font names can be specified with the font browser.  You can",
200     "change the menu names by setting the X resources font1",
201     "through font9.",
202     "",
203     "Choose a font color from the Font Color sub-menu.",
204     "Additional font colors can be specified with the color",
205     "browser.  You can change the menu colors by setting the X",
206     "resources pen1 through pen9.",
207     "",
208     "If you select the color browser and press Grab, you can",
209     "choose the font color by moving the pointer to the desired",
210     "color on the screen and press any button.",
211     "",
212     "If you choose to rotate the text, choose Rotate Text from the",
213     "menu and select an angle.  Typically you will only want to",
214     "rotate one line of text at a time.  Depending on the angle you",
215     "choose, subsequent lines may end up overwriting each other.",
216     "",
217     "Choosing a font and its color is optional.  The default font",
218     "is fixed and the default color is black.  However, you must",
219     "choose a location to begin entering text and press button 1.",
220     "An underscore character will appear at the location of the",
221     "pointer.  The cursor changes to a pencil to indicate you are",
222     "in text mode.  To exit immediately, press Dismiss.",
223     "",
224     "In text mode, any key presses will display the character at",
225     "the location of the underscore and advance the underscore",
226     "cursor.  Enter your text and once completed press Apply to",
227     "finish your image annotation.  To correct errors press BACK",
228     "SPACE.  To delete an entire line of text, press DELETE.  Any",
229     "text that exceeds the boundaries of the image window is",
230     "automagically continued onto the next line.",
231     "",
232     "The actual color you request for the font is saved in the",
233     "image.  However, the color that appears in your image window",
234     "may be different.  For example, on a monochrome screen the",
235     "text will appear black or white even if you choose the color",
236     "red as the font color.  However, the image saved to a file",
237     "with -write is written with red lettering.  To assure the",
238     "correct color text in the final image, any PseudoClass image",
239     "is promoted to DirectClass (see miff(5)).  To force a",
240     "PseudoClass image to remain PseudoClass, use -colors.",
241     (char *) NULL,
242   },
243   *ImageChopHelp[] =
244   {
245     "In chop mode, the Command widget has these options:",
246     "",
247     "    Direction",
248     "      horizontal",
249     "      vertical",
250     "    Help",
251     "    Dismiss",
252     "",
253     "If the you choose the horizontal direction (this the",
254     "default), the area of the image between the two horizontal",
255     "endpoints of the chop line is removed.  Otherwise, the area",
256     "of the image between the two vertical endpoints of the chop",
257     "line is removed.",
258     "",
259     "Select a location within the image window to begin your chop,",
260     "press and hold any button.  Next, move the pointer to",
261     "another location in the image.  As you move a line will",
262     "connect the initial location and the pointer.  When you",
263     "release the button, the area within the image to chop is",
264     "determined by which direction you choose from the Command",
265     "widget.",
266     "",
267     "To cancel the image chopping, move the pointer back to the",
268     "starting point of the line and release the button.",
269     (char *) NULL,
270   },
271   *ImageColorEditHelp[] =
272   {
273     "In color edit mode, the Command widget has these options:",
274     "",
275     "    Method",
276     "      point",
277     "      replace",
278     "      floodfill",
279     "      filltoborder",
280     "      reset",
281     "    Pixel Color",
282     "      black",
283     "      blue",
284     "      cyan",
285     "      green",
286     "      gray",
287     "      red",
288     "      magenta",
289     "      yellow",
290     "      white",
291     "      Browser...",
292     "    Border Color",
293     "      black",
294     "      blue",
295     "      cyan",
296     "      green",
297     "      gray",
298     "      red",
299     "      magenta",
300     "      yellow",
301     "      white",
302     "      Browser...",
303     "    Fuzz",
304     "      0%",
305     "      2%",
306     "      5%",
307     "      10%",
308     "      15%",
309     "      Dialog...",
310     "    Undo",
311     "    Help",
312     "    Dismiss",
313     "",
314     "Choose a color editing method from the Method sub-menu",
315     "of the Command widget.  The point method recolors any pixel",
316     "selected with the pointer until the button is released.  The",
317     "replace method recolors any pixel that matches the color of",
318     "the pixel you select with a button press.  Floodfill recolors",
319     "any pixel that matches the color of the pixel you select with",
320     "a button press and is a neighbor.  Whereas filltoborder recolors",
321     "any neighbor pixel that is not the border color.  Finally reset",
322     "changes the entire image to the designated color.",
323     "",
324     "Next, choose a pixel color from the Pixel Color sub-menu.",
325     "Additional pixel colors can be specified with the color",
326     "browser.  You can change the menu colors by setting the X",
327     "resources pen1 through pen9.",
328     "",
329     "Now press button 1 to select a pixel within the image window",
330     "to change its color.  Additional pixels may be recolored as",
331     "prescribed by the method you choose.",
332     "",
333     "If the Magnify widget is mapped, it can be helpful in positioning",
334     "your pointer within the image (refer to button 2).",
335     "",
336     "The actual color you request for the pixels is saved in the",
337     "image.  However, the color that appears in your image window",
338     "may be different.  For example, on a monochrome screen the",
339     "pixel will appear black or white even if you choose the",
340     "color red as the pixel color.  However, the image saved to a",
341     "file with -write is written with red pixels.  To assure the",
342     "correct color text in the final image, any PseudoClass image",
343     "is promoted to DirectClass (see miff(5)).  To force a",
344     "PseudoClass image to remain PseudoClass, use -colors.",
345     (char *) NULL,
346   },
347   *ImageCompositeHelp[] =
348   {
349     "First a widget window is displayed requesting you to enter an",
350     "image name. Press Composite, Grab or type a file name.",
351     "Press Cancel if you choose not to create a composite image.",
352     "When you choose Grab, move the pointer to the desired window",
353     "and press any button.",
354     "",
355     "If the Composite image does not have any matte information,",
356     "you are informed and the file browser is displayed again.",
357     "Enter the name of a mask image.  The image is typically",
358     "grayscale and the same size as the composite image.  If the",
359     "image is not grayscale, it is converted to grayscale and the",
360     "resulting intensities are used as matte information.",
361     "",
362     "A small window appears showing the location of the cursor in",
363     "the image window. You are now in composite mode.  To exit",
364     "immediately, press Dismiss.  In composite mode, the Command",
365     "widget has these options:",
366     "",
367     "    Operators",
368     "      Over",
369     "      In",
370     "      Out",
371     "      Atop",
372     "      Xor",
373     "      Plus",
374     "      Minus",
375     "      Add",
376     "      Subtract",
377     "      Difference",
378     "      Multiply",
379     "      Bumpmap",
380     "      Copy",
381     "      CopyRed",
382     "      CopyGreen",
383     "      CopyBlue",
384     "      CopyOpacity",
385     "      Clear",
386     "    Dissolve",
387     "    Displace",
388     "    Help",
389     "    Dismiss",
390     "",
391     "Choose a composite operation from the Operators sub-menu of",
392     "the Command widget.  How each operator behaves is described",
393     "below.  Image window is the image currently displayed on",
394     "your X server and image is the image obtained with the File",
395     "Browser widget.",
396     "",
397     "Over     The result is the union of the two image shapes,",
398     "         with image obscuring image window in the region of",
399     "         overlap.",
400     "",
401     "In       The result is simply image cut by the shape of",
402     "         image window.  None of the image data of image",
403     "         window is in the result.",
404     "",
405     "Out      The resulting image is image with the shape of",
406     "         image window cut out.",
407     "",
408     "Atop     The result is the same shape as image image window,",
409     "         with image obscuring image window where the image",
410     "         shapes overlap.  Note this differs from over",
411     "         because the portion of image outside image window's",
412     "         shape does not appear in the result.",
413     "",
414     "Xor      The result is the image data from both image and",
415     "         image window that is outside the overlap region.",
416     "         The overlap region is blank.",
417     "",
418     "Plus     The result is just the sum of the image data.",
419     "         Output values are cropped to QuantumRange (no overflow).",
420     "",
421     "Minus    The result of image - image window, with underflow",
422     "         cropped to zero.",
423     "",
424     "Add      The result of image + image window, with overflow",
425     "         wrapping around (mod 256).",
426     "",
427     "Subtract The result of image - image window, with underflow",
428     "         wrapping around (mod 256).  The add and subtract",
429     "         operators can be used to perform reversible",
430     "         transformations.",
431     "",
432     "Difference",
433     "         The result of abs(image - image window).  This",
434     "         useful for comparing two very similar images.",
435     "",
436     "Multiply",
437     "         The result of image * image window.  This",
438     "         useful for the creation of drop-shadows.",
439     "",
440     "Bumpmap  The result of surface normals from image * image",
441     "         window.",
442     "",
443     "Copy     The resulting image is image window replaced with",
444     "         image.  Here the matte information is ignored.",
445     "",
446     "CopyRed  The red layer of the image window is replace with",
447     "         the red layer of the image.  The other layers are",
448     "         untouched.",
449     "",
450     "CopyGreen",
451     "         The green layer of the image window is replace with",
452     "         the green layer of the image.  The other layers are",
453     "         untouched.",
454     "",
455     "CopyBlue The blue layer of the image window is replace with",
456     "         the blue layer of the image.  The other layers are",
457     "         untouched.",
458     "",
459     "CopyOpacity",
460     "         The matte layer of the image window is replace with",
461     "         the matte layer of the image.  The other layers are",
462     "         untouched.",
463     "",
464     "The image compositor requires a matte, or alpha channel in",
465     "the image for some operations.  This extra channel usually",
466     "defines a mask which represents a sort of a cookie-cutter",
467     "for the image.  This the case when matte is opaque (full",
468     "coverage) for pixels inside the shape, zero outside, and",
469     "between 0 and QuantumRange on the boundary.  If image does not",
470     "have a matte channel, it is initialized with 0 for any pixel",
471     "matching in color to pixel location (0,0), otherwise QuantumRange.",
472     "",
473     "If you choose Dissolve, the composite operator becomes Over.  The",
474     "image matte channel percent transparency is initialized to factor.",
475     "The image window is initialized to (100-factor). Where factor is the",
476     "value you specify in the Dialog widget.",
477     "",
478     "Displace shifts the image pixels as defined by a displacement",
479     "map.  With this option, image is used as a displacement map.",
480     "Black, within the displacement map, is a maximum positive",
481     "displacement.  White is a maximum negative displacement and",
482     "middle gray is neutral.  The displacement is scaled to determine",
483     "the pixel shift.  By default, the displacement applies in both the",
484     "horizontal and vertical directions.  However, if you specify a mask,",
485     "image is the horizontal X displacement and mask the vertical Y",
486     "displacement.",
487     "",
488     "Note that matte information for image window is not retained",
489     "for colormapped X server visuals (e.g. StaticColor,",
490     "StaticColor, GrayScale, PseudoColor).  Correct compositing",
491     "behavior may require a TrueColor or DirectColor visual or a",
492     "Standard Colormap.",
493     "",
494     "Choosing a composite operator is optional.  The default",
495     "operator is replace.  However, you must choose a location to",
496     "composite your image and press button 1.  Press and hold the",
497     "button before releasing and an outline of the image will",
498     "appear to help you identify your location.",
499     "",
500     "The actual colors of the composite image is saved.  However,",
501     "the color that appears in image window may be different.",
502     "For example, on a monochrome screen image window will appear",
503     "black or white even though your composited image may have",
504     "many colors.  If the image is saved to a file it is written",
505     "with the correct colors.  To assure the correct colors are",
506     "saved in the final image, any PseudoClass image is promoted",
507     "to DirectClass (see miff(5)).  To force a PseudoClass image",
508     "to remain PseudoClass, use -colors.",
509     (char *) NULL,
510   },
511   *ImageCutHelp[] =
512   {
513     "In cut mode, the Command widget has these options:",
514     "",
515     "    Help",
516     "    Dismiss",
517     "",
518     "To define a cut region, press button 1 and drag.  The",
519     "cut region is defined by a highlighted rectangle that",
520     "expands or contracts as it follows the pointer.  Once you",
521     "are satisfied with the cut region, release the button.",
522     "You are now in rectify mode.  In rectify mode, the Command",
523     "widget has these options:",
524     "",
525     "    Cut",
526     "    Help",
527     "    Dismiss",
528     "",
529     "You can make adjustments by moving the pointer to one of the",
530     "cut rectangle corners, pressing a button, and dragging.",
531     "Finally, press Cut to commit your copy region.  To",
532     "exit without cutting the image, press Dismiss.",
533     (char *) NULL,
534   },
535   *ImageCopyHelp[] =
536   {
537     "In copy mode, the Command widget has these options:",
538     "",
539     "    Help",
540     "    Dismiss",
541     "",
542     "To define a copy region, press button 1 and drag.  The",
543     "copy region is defined by a highlighted rectangle that",
544     "expands or contracts as it follows the pointer.  Once you",
545     "are satisfied with the copy region, release the button.",
546     "You are now in rectify mode.  In rectify mode, the Command",
547     "widget has these options:",
548     "",
549     "    Copy",
550     "    Help",
551     "    Dismiss",
552     "",
553     "You can make adjustments by moving the pointer to one of the",
554     "copy rectangle corners, pressing a button, and dragging.",
555     "Finally, press Copy to commit your copy region.  To",
556     "exit without copying the image, press Dismiss.",
557     (char *) NULL,
558   },
559   *ImageCropHelp[] =
560   {
561     "In crop mode, the Command widget has these options:",
562     "",
563     "    Help",
564     "    Dismiss",
565     "",
566     "To define a cropping region, press button 1 and drag.  The",
567     "cropping region is defined by a highlighted rectangle that",
568     "expands or contracts as it follows the pointer.  Once you",
569     "are satisfied with the cropping region, release the button.",
570     "You are now in rectify mode.  In rectify mode, the Command",
571     "widget has these options:",
572     "",
573     "    Crop",
574     "    Help",
575     "    Dismiss",
576     "",
577     "You can make adjustments by moving the pointer to one of the",
578     "cropping rectangle corners, pressing a button, and dragging.",
579     "Finally, press Crop to commit your cropping region.  To",
580     "exit without cropping the image, press Dismiss.",
581     (char *) NULL,
582   },
583   *ImageDrawHelp[] =
584   {
585     "The cursor changes to a crosshair to indicate you are in",
586     "draw mode.  To exit immediately, press Dismiss.  In draw mode,",
587     "the Command widget has these options:",
588     "",
589     "    Element",
590     "      point",
591     "      line",
592     "      rectangle",
593     "      fill rectangle",
594     "      circle",
595     "      fill circle",
596     "      ellipse",
597     "      fill ellipse",
598     "      polygon",
599     "      fill polygon",
600     "    Color",
601     "      black",
602     "      blue",
603     "      cyan",
604     "      green",
605     "      gray",
606     "      red",
607     "      magenta",
608     "      yellow",
609     "      white",
610     "      transparent",
611     "      Browser...",
612     "    Stipple",
613     "      Brick",
614     "      Diagonal",
615     "      Scales",
616     "      Vertical",
617     "      Wavy",
618     "      Translucent",
619     "      Opaque",
620     "      Open...",
621     "    Width",
622     "      1",
623     "      2",
624     "      4",
625     "      8",
626     "      16",
627     "      Dialog...",
628     "    Undo",
629     "    Help",
630     "    Dismiss",
631     "",
632     "Choose a drawing primitive from the Element sub-menu.",
633     "",
634     "Choose a color from the Color sub-menu.  Additional",
635     "colors can be specified with the color browser.",
636     "",
637     "If you choose the color browser and press Grab, you can",
638     "select the color by moving the pointer to the desired",
639     "color on the screen and press any button.  The transparent",
640     "color updates the image matte channel and is useful for",
641     "image compositing.",
642     "",
643     "Choose a stipple, if appropriate, from the Stipple sub-menu.",
644     "Additional stipples can be specified with the file browser.",
645     "Stipples obtained from the file browser must be on disk in the",
646     "X11 bitmap format.",
647     "",
648     "Choose a width, if appropriate, from the Width sub-menu.  To",
649     "choose a specific width select the Dialog widget.",
650     "",
651     "Choose a point in the Image window and press button 1 and",
652     "hold.  Next, move the pointer to another location in the",
653     "image.  As you move, a line connects the initial location and",
654     "the pointer.  When you release the button, the image is",
655     "updated with the primitive you just drew.  For polygons, the",
656     "image is updated when you press and release the button without",
657     "moving the pointer.",
658     "",
659     "To cancel image drawing, move the pointer back to the",
660     "starting point of the line and release the button.",
661     (char *) NULL,
662   },
663   *DisplayHelp[] =
664   {
665     "BUTTONS",
666     "  The effects of each button press is described below.  Three",
667     "  buttons are required.  If you have a two button mouse,",
668     "  button 1 and 3 are returned.  Press ALT and button 3 to",
669     "  simulate button 2.",
670     "",
671     "  1    Press this button to map or unmap the Command widget.",
672     "",
673     "  2    Press and drag to define a region of the image to",
674     "       magnify.",
675     "",
676     "  3    Press and drag to choose from a select set of commands.",
677     "       This button behaves differently if the image being",
678     "       displayed is a visual image directory.  Here, choose a",
679     "       particular tile of the directory and press this button and",
680     "       drag to select a command from a pop-up menu.  Choose from",
681     "       these menu items:",
682     "",
683     "           Open",
684     "           Next",
685     "           Former",
686     "           Delete",
687     "           Update",
688     "",
689     "       If you choose Open, the image represented by the tile is",
690     "       displayed.  To return to the visual image directory, choose",
691     "       Next from the Command widget.  Next and Former moves to the",
692     "       next or former image respectively.  Choose Delete to delete",
693     "       a particular image tile.  Finally, choose Update to",
694     "       synchronize all the image tiles with their respective",
695     "       images.",
696     "",
697     "COMMAND WIDGET",
698     "  The Command widget lists a number of sub-menus and commands.",
699     "  They are",
700     "",
701     "      File",
702     "        Open...",
703     "        Next",
704     "        Former",
705     "        Select...",
706     "        Save...",
707     "        Print...",
708     "        Delete...",
709     "        New...",
710     "        Visual Directory...",
711     "        Quit",
712     "      Edit",
713     "        Undo",
714     "        Redo",
715     "        Cut",
716     "        Copy",
717     "        Paste",
718     "      View",
719     "        Half Size",
720     "        Original Size",
721     "        Double Size",
722     "        Resize...",
723     "        Apply",
724     "        Refresh",
725     "        Restore",
726     "      Transform",
727     "        Crop",
728     "        Chop",
729     "        Flop",
730     "        Flip",
731     "        Rotate Right",
732     "        Rotate Left",
733     "        Rotate...",
734     "        Shear...",
735     "        Roll...",
736     "        Trim Edges",
737     "      Enhance",
738     "        Brightness...",
739     "        Saturation...",
740     "        Hue...",
741     "        Gamma...",
742     "        Sharpen...",
743     "        Dull",
744     "        Contrast Stretch...",
745     "        Sigmoidal Contrast...",
746     "        Normalize",
747     "        Equalize",
748     "        Negate",
749     "        Grayscale",
750     "        Map...",
751     "        Quantize...",
752     "      Effects",
753     "        Despeckle",
754     "        Emboss",
755     "        Reduce Noise",
756     "        Add Noise",
757     "        Sharpen...",
758     "        Blur...",
759     "        Threshold...",
760     "        Edge Detect...",
761     "        Spread...",
762     "        Shade...",
763     "        Painting...",
764     "        Segment...",
765     "      F/X",
766     "        Solarize...",
767     "        Sepia Tone...",
768     "        Swirl...",
769     "        Implode...",
770     "        Vignette...",
771     "        Wave...",
772     "        Oil Painting...",
773     "        Charcoal Drawing...",
774     "      Image Edit",
775     "        Annotate...",
776     "        Draw...",
777     "        Color...",
778     "        Matte...",
779     "        Composite...",
780     "        Add Border...",
781     "        Add Frame...",
782     "        Comment...",
783     "        Launch...",
784     "        Region of Interest...",
785     "      Miscellany",
786     "        Image Info",
787     "        Zoom Image",
788     "        Show Preview...",
789     "        Show Histogram",
790     "        Show Matte",
791     "        Background...",
792     "        Slide Show",
793     "        Preferences...",
794     "      Help",
795     "        Overview",
796     "        Browse Documentation",
797     "        About Display",
798     "",
799     "  Menu items with a indented triangle have a sub-menu.  They",
800     "  are represented above as the indented items.  To access a",
801     "  sub-menu item, move the pointer to the appropriate menu and",
802     "  press a button and drag.  When you find the desired sub-menu",
803     "  item, release the button and the command is executed.  Move",
804     "  the pointer away from the sub-menu if you decide not to",
805     "  execute a particular command.",
806     "",
807     "KEYBOARD ACCELERATORS",
808     "  Accelerators are one or two key presses that effect a",
809     "  particular command.  The keyboard accelerators that",
810     "  display(1) understands is:",
811     "",
812     "  Ctl+O     Press to open an image from a file.",
813     "",
814     "  space     Press to display the next image.",
815     "",
816     "            If the image is a multi-paged document such as a Postscript",
817     "            document, you can skip ahead several pages by preceding",
818     "            this command with a number.  For example to display the",
819     "            third page beyond the current page, press 3<space>.",
820     "",
821     "  backspace Press to display the former image.",
822     "",
823     "            If the image is a multi-paged document such as a Postscript",
824     "            document, you can skip behind several pages by preceding",
825     "            this command with a number.  For example to display the",
826     "            third page preceding the current page, press 3<backspace>.",
827     "",
828     "  Ctl+S     Press to write the image to a file.",
829     "",
830     "  Ctl+P     Press to print the image to a Postscript printer.",
831     "",
832     "  Ctl+D     Press to delete an image file.",
833     "",
834     "  Ctl+N     Press to create a blank canvas.",
835     "",
836     "  Ctl+Q     Press to discard all images and exit program.",
837     "",
838     "  Ctl+Z     Press to undo last image transformation.",
839     "",
840     "  Ctl+R     Press to redo last image transformation.",
841     "",
842     "  Ctl+X     Press to cut a region of the image.",
843     "",
844     "  Ctl+C     Press to copy a region of the image.",
845     "",
846     "  Ctl+V     Press to paste a region to the image.",
847     "",
848     "  <         Press to half the image size.",
849     "",
850     "  -         Press to return to the original image size.",
851     "",
852     "  >         Press to double the image size.",
853     "",
854     "  %         Press to resize the image to a width and height you",
855     "            specify.",
856     "",
857     "Cmd-A       Press to make any image transformations permanent."
858     "",
859     "            By default, any image size transformations are applied",
860     "            to the original image to create the image displayed on",
861     "            the X server.  However, the transformations are not",
862     "            permanent (i.e. the original image does not change",
863     "            size only the X image does).  For example, if you",
864     "            press > the X image will appear to double in size,",
865     "            but the original image will in fact remain the same size.",
866     "            To force the original image to double in size, press >",
867     "            followed by Cmd-A.",
868     "",
869     "  @         Press to refresh the image window.",
870     "",
871     "  C         Press to cut out a rectangular region of the image.",
872     "",
873     "  [         Press to chop the image.",
874     "",
875     "  H         Press to flop image in the horizontal direction.",
876     "",
877     "  V         Press to flip image in the vertical direction.",
878     "",
879     "  /         Press to rotate the image 90 degrees clockwise.",
880     "",
881     " \\         Press to rotate the image 90 degrees counter-clockwise.",
882     "",
883     "  *         Press to rotate the image the number of degrees you",
884     "            specify.",
885     "",
886     "  S         Press to shear the image the number of degrees you",
887     "            specify.",
888     "",
889     "  R         Press to roll the image.",
890     "",
891     "  T         Press to trim the image edges.",
892     "",
893     "  Shft-H    Press to vary the image hue.",
894     "",
895     "  Shft-S    Press to vary the color saturation.",
896     "",
897     "  Shft-L    Press to vary the color brightness.",
898     "",
899     "  Shft-G    Press to gamma correct the image.",
900     "",
901     "  Shft-C    Press to sharpen the image contrast.",
902     "",
903     "  Shft-Z    Press to dull the image contrast.",
904     "",
905     "  =         Press to perform histogram equalization on the image.",
906     "",
907     "  Shft-N    Press to perform histogram normalization on the image.",
908     "",
909     "  Shft-~    Press to negate the colors of the image.",
910     "",
911     "  .         Press to convert the image colors to gray.",
912     "",
913     "  Shft-#    Press to set the maximum number of unique colors in the",
914     "            image.",
915     "",
916     "  F2        Press to reduce the speckles in an image.",
917     "",
918     "  F3        Press to eliminate peak noise from an image.",
919     "",
920     "  F4        Press to add noise to an image.",
921     "",
922     "  F5        Press to sharpen an image.",
923     "",
924     "  F6        Press to delete an image file.",
925     "",
926     "  F7        Press to threshold the image.",
927     "",
928     "  F8        Press to detect edges within an image.",
929     "",
930     "  F9        Press to emboss an image.",
931     "",
932     "  F10       Press to displace pixels by a random amount.",
933     "",
934     "  F11       Press to negate all pixels above the threshold level.",
935     "",
936     "  F12       Press to shade the image using a distant light source.",
937     "",
938     "  F13       Press to lighten or darken image edges to create a 3-D effect.",
939     "",
940     "  F14       Press to segment the image by color.",
941     "",
942     "  Meta-S    Press to swirl image pixels about the center.",
943     "",
944     "  Meta-I    Press to implode image pixels about the center.",
945     "",
946     "  Meta-W    Press to alter an image along a sine wave.",
947     "",
948     "  Meta-P    Press to simulate an oil painting.",
949     "",
950     "  Meta-C    Press to simulate a charcoal drawing.",
951     "",
952     "  Alt-A     Press to annotate the image with text.",
953     "",
954     "  Alt-D     Press to draw on an image.",
955     "",
956     "  Alt-P     Press to edit an image pixel color.",
957     "",
958     "  Alt-M     Press to edit the image matte information.",
959     "",
960     "  Alt-V     Press to composite the image with another.",
961     "",
962     "  Alt-B     Press to add a border to the image.",
963     "",
964     "  Alt-F     Press to add an ornamental border to the image.",
965     "",
966     "  Alt-Shft-!",
967     "            Press to add an image comment.",
968     "",
969     "  Ctl-A     Press to apply image processing techniques to a region",
970     "            of interest.",
971     "",
972     "  Shft-?    Press to display information about the image.",
973     "",
974     "  Shft-+    Press to map the zoom image window.",
975     "",
976     "  Shft-P    Press to preview an image enhancement, effect, or f/x.",
977     "",
978     "  F1        Press to display helpful information about display(1).",
979     "",
980     "  Find      Press to browse documentation about ImageMagick.",
981     "",
982     "  1-9       Press to change the level of magnification.",
983     "",
984     "  Use the arrow keys to move the image one pixel up, down,",
985     "  left, or right within the magnify window.  Be sure to first",
986     "  map the magnify window by pressing button 2.",
987     "",
988     "  Press ALT and one of the arrow keys to trim off one pixel",
989     "  from any side of the image.",
990     (char *) NULL,
991   },
992   *ImageMatteEditHelp[] =
993   {
994     "Matte information within an image is useful for some",
995     "operations such as image compositing (See IMAGE",
996     "COMPOSITING).  This extra channel usually defines a mask",
997     "which represents a sort of a cookie-cutter for the image.",
998     "This the case when matte is opaque (full coverage) for",
999     "pixels inside the shape, zero outside, and between 0 and",
1000     "QuantumRange on the boundary.",
1001     "",
1002     "A small window appears showing the location of the cursor in",
1003     "the image window. You are now in matte edit mode.  To exit",
1004     "immediately, press Dismiss.  In matte edit mode, the Command",
1005     "widget has these options:",
1006     "",
1007     "    Method",
1008     "      point",
1009     "      replace",
1010     "      floodfill",
1011     "      filltoborder",
1012     "      reset",
1013     "    Border Color",
1014     "      black",
1015     "      blue",
1016     "      cyan",
1017     "      green",
1018     "      gray",
1019     "      red",
1020     "      magenta",
1021     "      yellow",
1022     "      white",
1023     "      Browser...",
1024     "    Fuzz",
1025     "      0%",
1026     "      2%",
1027     "      5%",
1028     "      10%",
1029     "      15%",
1030     "      Dialog...",
1031     "    Matte",
1032     "      Opaque",
1033     "      Transparent",
1034     "      Dialog...",
1035     "    Undo",
1036     "    Help",
1037     "    Dismiss",
1038     "",
1039     "Choose a matte editing method from the Method sub-menu of",
1040     "the Command widget.  The point method changes the matte value",
1041     "of any pixel selected with the pointer until the button is",
1042     "is released.  The replace method changes the matte value of",
1043     "any pixel that matches the color of the pixel you select with",
1044     "a button press.  Floodfill changes the matte value of any pixel",
1045     "that matches the color of the pixel you select with a button",
1046     "press and is a neighbor.  Whereas filltoborder changes the matte",
1047     "value any neighbor pixel that is not the border color.  Finally",
1048     "reset changes the entire image to the designated matte value.",
1049     "",
1050     "Choose Matte Value and pick Opaque or Transarent.  For other values",
1051     "select the Dialog entry.  Here a dialog appears requesting a matte",
1052     "value.  The value you select is assigned as the opacity value of the",
1053     "selected pixel or pixels.",
1054     "",
1055     "Now, press any button to select a pixel within the image",
1056     "window to change its matte value.",
1057     "",
1058     "If the Magnify widget is mapped, it can be helpful in positioning",
1059     "your pointer within the image (refer to button 2).",
1060     "",
1061     "Matte information is only valid in a DirectClass image.",
1062     "Therefore, any PseudoClass image is promoted to DirectClass",
1063     "(see miff(5)).  Note that matte information for PseudoClass",
1064     "is not retained for colormapped X server visuals (e.g.",
1065     "StaticColor, StaticColor, GrayScale, PseudoColor) unless you",
1066     "immediately save your image to a file (refer to Write).",
1067     "Correct matte editing behavior may require a TrueColor or",
1068     "DirectColor visual or a Standard Colormap.",
1069     (char *) NULL,
1070   },
1071   *ImagePanHelp[] =
1072   {
1073     "When an image exceeds the width or height of the X server",
1074     "screen, display maps a small panning icon.  The rectangle",
1075     "within the panning icon shows the area that is currently",
1076     "displayed in the image window.  To pan about the image,",
1077     "press any button and drag the pointer within the panning",
1078     "icon.  The pan rectangle moves with the pointer and the",
1079     "image window is updated to reflect the location of the",
1080     "rectangle within the panning icon.  When you have selected",
1081     "the area of the image you wish to view, release the button.",
1082     "",
1083     "Use the arrow keys to pan the image one pixel up, down,",
1084     "left, or right within the image window.",
1085     "",
1086     "The panning icon is withdrawn if the image becomes smaller",
1087     "than the dimensions of the X server screen.",
1088     (char *) NULL,
1089   },
1090   *ImagePasteHelp[] =
1091   {
1092     "A small window appears showing the location of the cursor in",
1093     "the image window. You are now in paste mode.  To exit",
1094     "immediately, press Dismiss.  In paste mode, the Command",
1095     "widget has these options:",
1096     "",
1097     "    Operators",
1098     "      over",
1099     "      in",
1100     "      out",
1101     "      atop",
1102     "      xor",
1103     "      plus",
1104     "      minus",
1105     "      add",
1106     "      subtract",
1107     "      difference",
1108     "      replace",
1109     "    Help",
1110     "    Dismiss",
1111     "",
1112     "Choose a composite operation from the Operators sub-menu of",
1113     "the Command widget.  How each operator behaves is described",
1114     "below.  Image window is the image currently displayed on",
1115     "your X server and image is the image obtained with the File",
1116     "Browser widget.",
1117     "",
1118     "Over     The result is the union of the two image shapes,",
1119     "         with image obscuring image window in the region of",
1120     "         overlap.",
1121     "",
1122     "In       The result is simply image cut by the shape of",
1123     "         image window.  None of the image data of image",
1124     "         window is in the result.",
1125     "",
1126     "Out      The resulting image is image with the shape of",
1127     "         image window cut out.",
1128     "",
1129     "Atop     The result is the same shape as image image window,",
1130     "         with image obscuring image window where the image",
1131     "         shapes overlap.  Note this differs from over",
1132     "         because the portion of image outside image window's",
1133     "         shape does not appear in the result.",
1134     "",
1135     "Xor      The result is the image data from both image and",
1136     "         image window that is outside the overlap region.",
1137     "         The overlap region is blank.",
1138     "",
1139     "Plus     The result is just the sum of the image data.",
1140     "         Output values are cropped to QuantumRange (no overflow).",
1141     "         This operation is independent of the matte",
1142     "         channels.",
1143     "",
1144     "Minus    The result of image - image window, with underflow",
1145     "         cropped to zero.",
1146     "",
1147     "Add      The result of image + image window, with overflow",
1148     "         wrapping around (mod 256).",
1149     "",
1150     "Subtract The result of image - image window, with underflow",
1151     "         wrapping around (mod 256).  The add and subtract",
1152     "         operators can be used to perform reversible",
1153     "         transformations.",
1154     "",
1155     "Difference",
1156     "         The result of abs(image - image window).  This",
1157     "         useful for comparing two very similar images.",
1158     "",
1159     "Copy     The resulting image is image window replaced with",
1160     "         image.  Here the matte information is ignored.",
1161     "",
1162     "CopyRed  The red layer of the image window is replace with",
1163     "         the red layer of the image.  The other layers are",
1164     "         untouched.",
1165     "",
1166     "CopyGreen",
1167     "         The green layer of the image window is replace with",
1168     "         the green layer of the image.  The other layers are",
1169     "         untouched.",
1170     "",
1171     "CopyBlue The blue layer of the image window is replace with",
1172     "         the blue layer of the image.  The other layers are",
1173     "         untouched.",
1174     "",
1175     "CopyOpacity",
1176     "         The matte layer of the image window is replace with",
1177     "         the matte layer of the image.  The other layers are",
1178     "         untouched.",
1179     "",
1180     "The image compositor requires a matte, or alpha channel in",
1181     "the image for some operations.  This extra channel usually",
1182     "defines a mask which represents a sort of a cookie-cutter",
1183     "for the image.  This the case when matte is opaque (full",
1184     "coverage) for pixels inside the shape, zero outside, and",
1185     "between 0 and QuantumRange on the boundary.  If image does not",
1186     "have a matte channel, it is initialized with 0 for any pixel",
1187     "matching in color to pixel location (0,0), otherwise QuantumRange.",
1188     "",
1189     "Note that matte information for image window is not retained",
1190     "for colormapped X server visuals (e.g. StaticColor,",
1191     "StaticColor, GrayScale, PseudoColor).  Correct compositing",
1192     "behavior may require a TrueColor or DirectColor visual or a",
1193     "Standard Colormap.",
1194     "",
1195     "Choosing a composite operator is optional.  The default",
1196     "operator is replace.  However, you must choose a location to",
1197     "paste your image and press button 1.  Press and hold the",
1198     "button before releasing and an outline of the image will",
1199     "appear to help you identify your location.",
1200     "",
1201     "The actual colors of the pasted image is saved.  However,",
1202     "the color that appears in image window may be different.",
1203     "For example, on a monochrome screen image window will appear",
1204     "black or white even though your pasted image may have",
1205     "many colors.  If the image is saved to a file it is written",
1206     "with the correct colors.  To assure the correct colors are",
1207     "saved in the final image, any PseudoClass image is promoted",
1208     "to DirectClass (see miff(5)).  To force a PseudoClass image",
1209     "to remain PseudoClass, use -colors.",
1210     (char *) NULL,
1211   },
1212   *ImageROIHelp[] =
1213   {
1214     "In region of interest mode, the Command widget has these",
1215     "options:",
1216     "",
1217     "    Help",
1218     "    Dismiss",
1219     "",
1220     "To define a region of interest, press button 1 and drag.",
1221     "The region of interest is defined by a highlighted rectangle",
1222     "that expands or contracts as it follows the pointer.  Once",
1223     "you are satisfied with the region of interest, release the",
1224     "button.  You are now in apply mode.  In apply mode the",
1225     "Command widget has these options:",
1226     "",
1227     "      File",
1228     "        Save...",
1229     "        Print...",
1230     "      Edit",
1231     "        Undo",
1232     "        Redo",
1233     "      Transform",
1234     "        Flop",
1235     "        Flip",
1236     "        Rotate Right",
1237     "        Rotate Left",
1238     "      Enhance",
1239     "        Hue...",
1240     "        Saturation...",
1241     "        Brightness...",
1242     "        Gamma...",
1243     "        Spiff",
1244     "        Dull",
1245     "        Contrast Stretch",
1246     "        Sigmoidal Contrast...",
1247     "        Normalize",
1248     "        Equalize",
1249     "        Negate",
1250     "        Grayscale",
1251     "        Map...",
1252     "        Quantize...",
1253     "      Effects",
1254     "        Despeckle",
1255     "        Emboss",
1256     "        Reduce Noise",
1257     "        Sharpen...",
1258     "        Blur...",
1259     "        Threshold...",
1260     "        Edge Detect...",
1261     "        Spread...",
1262     "        Shade...",
1263     "        Raise...",
1264     "        Segment...",
1265     "      F/X",
1266     "        Solarize...",
1267     "        Sepia Tone...",
1268     "        Swirl...",
1269     "        Implode...",
1270     "        Vignette...",
1271     "        Wave...",
1272     "        Oil Painting...",
1273     "        Charcoal Drawing...",
1274     "      Miscellany",
1275     "        Image Info",
1276     "        Zoom Image",
1277     "        Show Preview...",
1278     "        Show Histogram",
1279     "        Show Matte",
1280     "      Help",
1281     "      Dismiss",
1282     "",
1283     "You can make adjustments to the region of interest by moving",
1284     "the pointer to one of the rectangle corners, pressing a",
1285     "button, and dragging.  Finally, choose an image processing",
1286     "technique from the Command widget.  You can choose more than",
1287     "one image processing technique to apply to an area.",
1288     "Alternatively, you can move the region of interest before",
1289     "applying another image processing technique.  To exit, press",
1290     "Dismiss.",
1291     (char *) NULL,
1292   },
1293   *ImageRotateHelp[] =
1294   {
1295     "In rotate mode, the Command widget has these options:",
1296     "",
1297     "    Pixel Color",
1298     "      black",
1299     "      blue",
1300     "      cyan",
1301     "      green",
1302     "      gray",
1303     "      red",
1304     "      magenta",
1305     "      yellow",
1306     "      white",
1307     "      Browser...",
1308     "    Direction",
1309     "      horizontal",
1310     "      vertical",
1311     "    Help",
1312     "    Dismiss",
1313     "",
1314     "Choose a background color from the Pixel Color sub-menu.",
1315     "Additional background colors can be specified with the color",
1316     "browser.  You can change the menu colors by setting the X",
1317     "resources pen1 through pen9.",
1318     "",
1319     "If you choose the color browser and press Grab, you can",
1320     "select the background color by moving the pointer to the",
1321     "desired color on the screen and press any button.",
1322     "",
1323     "Choose a point in the image window and press this button and",
1324     "hold.  Next, move the pointer to another location in the",
1325     "image.  As you move a line connects the initial location and",
1326     "the pointer.  When you release the button, the degree of",
1327     "image rotation is determined by the slope of the line you",
1328     "just drew.  The slope is relative to the direction you",
1329     "choose from the Direction sub-menu of the Command widget.",
1330     "",
1331     "To cancel the image rotation, move the pointer back to the",
1332     "starting point of the line and release the button.",
1333     (char *) NULL,
1334   };
1335 \f
1336 /*
1337   Enumeration declarations.
1338 */
1339 typedef enum
1340 {
1341   CopyMode,
1342   CropMode,
1343   CutMode
1344 } ClipboardMode;
1345
1346 typedef enum
1347 {
1348   OpenCommand,
1349   NextCommand,
1350   FormerCommand,
1351   SelectCommand,
1352   SaveCommand,
1353   PrintCommand,
1354   DeleteCommand,
1355   NewCommand,
1356   VisualDirectoryCommand,
1357   QuitCommand,
1358   UndoCommand,
1359   RedoCommand,
1360   CutCommand,
1361   CopyCommand,
1362   PasteCommand,
1363   HalfSizeCommand,
1364   OriginalSizeCommand,
1365   DoubleSizeCommand,
1366   ResizeCommand,
1367   ApplyCommand,
1368   RefreshCommand,
1369   RestoreCommand,
1370   CropCommand,
1371   ChopCommand,
1372   FlopCommand,
1373   FlipCommand,
1374   RotateRightCommand,
1375   RotateLeftCommand,
1376   RotateCommand,
1377   ShearCommand,
1378   RollCommand,
1379   TrimCommand,
1380   HueCommand,
1381   SaturationCommand,
1382   BrightnessCommand,
1383   GammaCommand,
1384   SpiffCommand,
1385   DullCommand,
1386   ContrastStretchCommand,
1387   SigmoidalContrastCommand,
1388   NormalizeCommand,
1389   EqualizeCommand,
1390   NegateCommand,
1391   GrayscaleCommand,
1392   MapCommand,
1393   QuantizeCommand,
1394   DespeckleCommand,
1395   EmbossCommand,
1396   ReduceNoiseCommand,
1397   AddNoiseCommand,
1398   SharpenCommand,
1399   BlurCommand,
1400   ThresholdCommand,
1401   EdgeDetectCommand,
1402   SpreadCommand,
1403   ShadeCommand,
1404   RaiseCommand,
1405   SegmentCommand,
1406   SolarizeCommand,
1407   SepiaToneCommand,
1408   SwirlCommand,
1409   ImplodeCommand,
1410   VignetteCommand,
1411   WaveCommand,
1412   OilPaintCommand,
1413   CharcoalDrawCommand,
1414   AnnotateCommand,
1415   DrawCommand,
1416   ColorCommand,
1417   MatteCommand,
1418   CompositeCommand,
1419   AddBorderCommand,
1420   AddFrameCommand,
1421   CommentCommand,
1422   LaunchCommand,
1423   RegionofInterestCommand,
1424   ROIHelpCommand,
1425   ROIDismissCommand,
1426   InfoCommand,
1427   ZoomCommand,
1428   ShowPreviewCommand,
1429   ShowHistogramCommand,
1430   ShowMatteCommand,
1431   BackgroundCommand,
1432   SlideShowCommand,
1433   PreferencesCommand,
1434   HelpCommand,
1435   BrowseDocumentationCommand,
1436   VersionCommand,
1437   SaveToUndoBufferCommand,
1438   FreeBuffersCommand,
1439   NullCommand
1440 } CommandType;
1441
1442 typedef enum
1443 {
1444   AnnotateNameCommand,
1445   AnnotateFontColorCommand,
1446   AnnotateBackgroundColorCommand,
1447   AnnotateRotateCommand,
1448   AnnotateHelpCommand,
1449   AnnotateDismissCommand,
1450   TextHelpCommand,
1451   TextApplyCommand,
1452   ChopDirectionCommand,
1453   ChopHelpCommand,
1454   ChopDismissCommand,
1455   HorizontalChopCommand,
1456   VerticalChopCommand,
1457   ColorEditMethodCommand,
1458   ColorEditColorCommand,
1459   ColorEditBorderCommand,
1460   ColorEditFuzzCommand,
1461   ColorEditUndoCommand,
1462   ColorEditHelpCommand,
1463   ColorEditDismissCommand,
1464   CompositeOperatorsCommand,
1465   CompositeDissolveCommand,
1466   CompositeDisplaceCommand,
1467   CompositeHelpCommand,
1468   CompositeDismissCommand,
1469   CropHelpCommand,
1470   CropDismissCommand,
1471   RectifyCopyCommand,
1472   RectifyHelpCommand,
1473   RectifyDismissCommand,
1474   DrawElementCommand,
1475   DrawColorCommand,
1476   DrawStippleCommand,
1477   DrawWidthCommand,
1478   DrawUndoCommand,
1479   DrawHelpCommand,
1480   DrawDismissCommand,
1481   MatteEditMethod,
1482   MatteEditBorderCommand,
1483   MatteEditFuzzCommand,
1484   MatteEditValueCommand,
1485   MatteEditUndoCommand,
1486   MatteEditHelpCommand,
1487   MatteEditDismissCommand,
1488   PasteOperatorsCommand,
1489   PasteHelpCommand,
1490   PasteDismissCommand,
1491   RotateColorCommand,
1492   RotateDirectionCommand,
1493   RotateCropCommand,
1494   RotateSharpenCommand,
1495   RotateHelpCommand,
1496   RotateDismissCommand,
1497   HorizontalRotateCommand,
1498   VerticalRotateCommand,
1499   TileLoadCommand,
1500   TileNextCommand,
1501   TileFormerCommand,
1502   TileDeleteCommand,
1503   TileUpdateCommand
1504 } ModeType;
1505 \f
1506 /*
1507   Stipples.
1508 */
1509 #define BricksWidth  20
1510 #define BricksHeight  20
1511 #define DiagonalWidth  16
1512 #define DiagonalHeight  16
1513 #define HighlightWidth  8
1514 #define HighlightHeight  8
1515 #define OpaqueWidth  8
1516 #define OpaqueHeight  8
1517 #define ScalesWidth  16
1518 #define ScalesHeight  16
1519 #define ShadowWidth  8
1520 #define ShadowHeight  8
1521 #define VerticalWidth  16
1522 #define VerticalHeight  16
1523 #define WavyWidth  16
1524 #define WavyHeight  16
1525 \f
1526 /*
1527   Constant declaration.
1528 */
1529 static const int
1530   RoiDelta = 8;
1531
1532 static const unsigned char
1533   BricksBitmap[] =
1534   {
1535     0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00,
1536     0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01,
1537     0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00,
1538     0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f,
1539     0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01
1540   },
1541   DiagonalBitmap[] =
1542   {
1543     0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88,
1544     0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22,
1545     0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22
1546   },
1547   ScalesBitmap[] =
1548   {
1549     0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3, 0x80, 0x80, 0x80, 0x80,
1550     0x41, 0x41, 0x3e, 0x3e, 0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3,
1551     0x80, 0x80, 0x80, 0x80, 0x41, 0x41, 0x3e, 0x3e
1552   },
1553   VerticalBitmap[] =
1554   {
1555     0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1556     0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1557     0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11
1558   },
1559   WavyBitmap[] =
1560   {
1561     0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfb, 0xff,
1562     0xe7, 0xff, 0x1f, 0xff, 0xff, 0xf8, 0xff, 0xe7, 0xff, 0xdf, 0xff, 0xbf,
1563     0xff, 0xbf, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f
1564   };
1565 \f
1566 /*
1567   Function prototypes.
1568 */
1569 static CommandType
1570   XImageWindowCommand(Display *,XResourceInfo *,XWindows *,
1571     const MagickStatusType,KeySym,Image **,ExceptionInfo *);
1572
1573 static Image
1574   *XMagickCommand(Display *,XResourceInfo *,XWindows *,const CommandType,
1575     Image **,ExceptionInfo *),
1576   *XOpenImage(Display *,XResourceInfo *,XWindows *,const MagickBooleanType),
1577   *XTileImage(Display *,XResourceInfo *,XWindows *,Image *,XEvent *,
1578     ExceptionInfo *),
1579   *XVisualDirectoryImage(Display *,XResourceInfo *,XWindows *,
1580     ExceptionInfo *);
1581
1582 static MagickBooleanType
1583   XAnnotateEditImage(Display *,XResourceInfo *,XWindows *,Image *,
1584     ExceptionInfo *),
1585   XBackgroundImage(Display *,XResourceInfo *,XWindows *,Image **,
1586     ExceptionInfo *),
1587   XChopImage(Display *,XResourceInfo *,XWindows *,Image **,
1588     ExceptionInfo *),
1589   XCropImage(Display *,XResourceInfo *,XWindows *,Image *,const ClipboardMode,
1590     ExceptionInfo *),
1591   XColorEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1592     ExceptionInfo *),
1593   XCompositeImage(Display *,XResourceInfo *,XWindows *,Image *,
1594     ExceptionInfo *),
1595   XConfigureImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1596   XDrawEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1597     ExceptionInfo *),
1598   XMatteEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1599     ExceptionInfo *),
1600   XPasteImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1601   XPrintImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1602   XRotateImage(Display *,XResourceInfo *,XWindows *,double,Image **,
1603     ExceptionInfo *),
1604   XROIImage(Display *,XResourceInfo *,XWindows *,Image **,ExceptionInfo *),
1605   XSaveImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1606   XTrimImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *);
1607
1608 static void
1609   XDrawPanRectangle(Display *,XWindows *),
1610   XImageCache(Display *,XResourceInfo *,XWindows *,const CommandType,Image **,
1611     ExceptionInfo *),
1612   XMagnifyImage(Display *,XWindows *,XEvent *,ExceptionInfo *),
1613   XMakePanImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1614   XPanImage(Display *,XWindows *,XEvent *,ExceptionInfo *),
1615   XMagnifyWindowCommand(Display *,XWindows *,const MagickStatusType,
1616     const KeySym,ExceptionInfo *),
1617   XSetCropGeometry(Display *,XWindows *,RectangleInfo *,Image *),
1618   XScreenEvent(Display *,XWindows *,XEvent *,ExceptionInfo *),
1619   XTranslateImage(Display *,XWindows *,Image *,const KeySym);
1620 \f
1621 /*
1622 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1623 %                                                                             %
1624 %                                                                             %
1625 %                                                                             %
1626 %   D i s p l a y I m a g e s                                                 %
1627 %                                                                             %
1628 %                                                                             %
1629 %                                                                             %
1630 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1631 %
1632 %  DisplayImages() displays an image sequence to any X window screen.  It
1633 %  returns a value other than 0 if successful.  Check the exception member
1634 %  of image to determine the reason for any failure.
1635 %
1636 %  The format of the DisplayImages method is:
1637 %
1638 %      MagickBooleanType DisplayImages(const ImageInfo *image_info,
1639 %        Image *images,ExceptionInfo *exception)
1640 %
1641 %  A description of each parameter follows:
1642 %
1643 %    o image_info: the image info.
1644 %
1645 %    o image: the image.
1646 %
1647 %    o exception: return any errors or warnings in this structure.
1648 %
1649 */
1650 MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
1651   Image *images,ExceptionInfo *exception)
1652 {
1653   char
1654     *argv[1];
1655
1656   Display
1657     *display;
1658
1659   Image
1660     *image;
1661
1662   register ssize_t
1663     i;
1664
1665   size_t
1666     state;
1667
1668   XrmDatabase
1669     resource_database;
1670
1671   XResourceInfo
1672     resource_info;
1673
1674   assert(image_info != (const ImageInfo *) NULL);
1675   assert(image_info->signature == MagickSignature);
1676   assert(images != (Image *) NULL);
1677   assert(images->signature == MagickSignature);
1678   if( IfMagickTrue(images->debug) )
1679     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
1680   display=XOpenDisplay(image_info->server_name);
1681   if (display == (Display *) NULL)
1682     {
1683       (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
1684         "UnableToOpenXServer","'%s'",XDisplayName(image_info->server_name));
1685       return(MagickFalse);
1686     }
1687   if (exception->severity != UndefinedException)
1688     CatchException(exception);
1689   (void) XSetErrorHandler(XError);
1690   resource_database=XGetResourceDatabase(display,GetClientName());
1691   (void) ResetMagickMemory(&resource_info,0,sizeof(resource_info));
1692   XGetResourceInfo(image_info,resource_database,GetClientName(),&resource_info);
1693   if (image_info->page != (char *) NULL)
1694     resource_info.image_geometry=AcquireString(image_info->page);
1695   resource_info.immutable=MagickTrue;
1696   argv[0]=AcquireString(GetClientName());
1697   state=DefaultState;
1698   for (i=0; (state & ExitState) == 0; i++)
1699   {
1700     if ((images->iterations != 0) && (i >= (ssize_t) images->iterations))
1701       break;
1702     image=GetImageFromList(images,i % GetImageListLength(images));
1703     (void) XDisplayImage(display,&resource_info,argv,1,&image,&state,exception);
1704   }
1705   (void) SetErrorHandler((ErrorHandler) NULL);
1706   (void) SetWarningHandler((WarningHandler) NULL);
1707   argv[0]=DestroyString(argv[0]);
1708   (void) XCloseDisplay(display);
1709   XDestroyResourceInfo(&resource_info);
1710   if (exception->severity != UndefinedException)
1711     return(MagickFalse);
1712   return(MagickTrue);
1713 }
1714 \f
1715 /*
1716 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1717 %                                                                             %
1718 %                                                                             %
1719 %                                                                             %
1720 %   R e m o t e D i s p l a y C o m m a n d                                   %
1721 %                                                                             %
1722 %                                                                             %
1723 %                                                                             %
1724 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1725 %
1726 %  RemoteDisplayCommand() encourages a remote display program to display the
1727 %  specified image filename.
1728 %
1729 %  The format of the RemoteDisplayCommand method is:
1730 %
1731 %      MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1732 %        const char *window,const char *filename,ExceptionInfo *exception)
1733 %
1734 %  A description of each parameter follows:
1735 %
1736 %    o image_info: the image info.
1737 %
1738 %    o window: Specifies the name or id of an X window.
1739 %
1740 %    o filename: the name of the image filename to display.
1741 %
1742 %    o exception: return any errors or warnings in this structure.
1743 %
1744 */
1745 MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1746   const char *window,const char *filename,ExceptionInfo *exception)
1747 {
1748   Display
1749     *display;
1750
1751   MagickStatusType
1752     status;
1753
1754   assert(image_info != (const ImageInfo *) NULL);
1755   assert(image_info->signature == MagickSignature);
1756   assert(filename != (char *) NULL);
1757   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
1758   display=XOpenDisplay(image_info->server_name);
1759   if (display == (Display *) NULL)
1760     {
1761       (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
1762         "UnableToOpenXServer","'%s'",XDisplayName(image_info->server_name));
1763       return(MagickFalse);
1764     }
1765   (void) XSetErrorHandler(XError);
1766   status=XRemoteCommand(display,window,filename);
1767   (void) XCloseDisplay(display);
1768   return(IsMagickTrue(status));
1769 }
1770 \f
1771 /*
1772 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1773 %                                                                             %
1774 %                                                                             %
1775 %                                                                             %
1776 +   X A n n o t a t e E d i t I m a g e                                       %
1777 %                                                                             %
1778 %                                                                             %
1779 %                                                                             %
1780 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1781 %
1782 %  XAnnotateEditImage() annotates the image with text.
1783 %
1784 %  The format of the XAnnotateEditImage method is:
1785 %
1786 %      MagickBooleanType XAnnotateEditImage(Display *display,
1787 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
1788 %        ExceptionInfo *exception)
1789 %
1790 %  A description of each parameter follows:
1791 %
1792 %    o display: Specifies a connection to an X server;  returned from
1793 %      XOpenDisplay.
1794 %
1795 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
1796 %
1797 %    o windows: Specifies a pointer to a XWindows structure.
1798 %
1799 %    o image: the image; returned from ReadImage.
1800 %
1801 */
1802
1803 static inline ssize_t MagickMax(const ssize_t x,const ssize_t y)
1804 {
1805   if (x > y)
1806     return(x);
1807   return(y);
1808 }
1809
1810 static inline ssize_t MagickMin(const ssize_t x,const ssize_t y)
1811 {
1812   if (x < y)
1813     return(x);
1814   return(y);
1815 }
1816
1817 static MagickBooleanType XAnnotateEditImage(Display *display,
1818   XResourceInfo *resource_info,XWindows *windows,Image *image,
1819   ExceptionInfo *exception)
1820 {
1821   static const char
1822     *AnnotateMenu[] =
1823     {
1824       "Font Name",
1825       "Font Color",
1826       "Box Color",
1827       "Rotate Text",
1828       "Help",
1829       "Dismiss",
1830       (char *) NULL
1831     },
1832     *TextMenu[] =
1833     {
1834       "Help",
1835       "Apply",
1836       (char *) NULL
1837     };
1838
1839   static const ModeType
1840     AnnotateCommands[] =
1841     {
1842       AnnotateNameCommand,
1843       AnnotateFontColorCommand,
1844       AnnotateBackgroundColorCommand,
1845       AnnotateRotateCommand,
1846       AnnotateHelpCommand,
1847       AnnotateDismissCommand
1848     },
1849     TextCommands[] =
1850     {
1851       TextHelpCommand,
1852       TextApplyCommand
1853     };
1854
1855   static MagickBooleanType
1856     transparent_box = MagickTrue,
1857     transparent_pen = MagickFalse;
1858
1859   static double
1860     degrees = 0.0;
1861
1862   static unsigned int
1863     box_id = MaxNumberPens-2,
1864     font_id = 0,
1865     pen_id = 0;
1866
1867   char
1868     command[MaxTextExtent],
1869     text[MaxTextExtent];
1870
1871   const char
1872     *ColorMenu[MaxNumberPens+1];
1873
1874   Cursor
1875     cursor;
1876
1877   GC
1878     annotate_context;
1879
1880   int
1881     id,
1882     pen_number,
1883     status,
1884     x,
1885     y;
1886
1887   KeySym
1888     key_symbol;
1889
1890   register char
1891     *p;
1892
1893   register ssize_t
1894     i;
1895
1896   unsigned int
1897     height,
1898     width;
1899
1900   size_t
1901     state;
1902
1903   XAnnotateInfo
1904     *annotate_info,
1905     *previous_info;
1906
1907   XColor
1908     color;
1909
1910   XFontStruct
1911     *font_info;
1912
1913   XEvent
1914     event,
1915     text_event;
1916
1917   /*
1918     Map Command widget.
1919   */
1920   (void) CloneString(&windows->command.name,"Annotate");
1921   windows->command.data=4;
1922   (void) XCommandWidget(display,windows,AnnotateMenu,(XEvent *) NULL);
1923   (void) XMapRaised(display,windows->command.id);
1924   XClientMessage(display,windows->image.id,windows->im_protocols,
1925     windows->im_update_widget,CurrentTime);
1926   /*
1927     Track pointer until button 1 is pressed.
1928   */
1929   XQueryPosition(display,windows->image.id,&x,&y);
1930   (void) XSelectInput(display,windows->image.id,
1931     windows->image.attributes.event_mask | PointerMotionMask);
1932   cursor=XCreateFontCursor(display,XC_left_side);
1933   (void) XCheckDefineCursor(display,windows->image.id,cursor);
1934   state=DefaultState;
1935   do
1936   {
1937     if( IfMagickTrue(windows->info.mapped) )
1938       {
1939         /*
1940           Display pointer position.
1941         */
1942         (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
1943           x+windows->image.x,y+windows->image.y);
1944         XInfoWidget(display,windows,text);
1945       }
1946     /*
1947       Wait for next event.
1948     */
1949     XScreenEvent(display,windows,&event,exception);
1950     if (event.xany.window == windows->command.id)
1951       {
1952         /*
1953           Select a command from the Command widget.
1954         */
1955         id=XCommandWidget(display,windows,AnnotateMenu,&event);
1956         (void) XCheckDefineCursor(display,windows->image.id,cursor);
1957         if (id < 0)
1958           continue;
1959         switch (AnnotateCommands[id])
1960         {
1961           case AnnotateNameCommand:
1962           {
1963             const char
1964               *FontMenu[MaxNumberFonts];
1965
1966             int
1967               font_number;
1968
1969             /*
1970               Initialize menu selections.
1971             */
1972             for (i=0; i < MaxNumberFonts; i++)
1973               FontMenu[i]=resource_info->font_name[i];
1974             FontMenu[MaxNumberFonts-2]="Browser...";
1975             FontMenu[MaxNumberFonts-1]=(const char *) NULL;
1976             /*
1977               Select a font name from the pop-up menu.
1978             */
1979             font_number=XMenuWidget(display,windows,AnnotateMenu[id],
1980               (const char **) FontMenu,command);
1981             if (font_number < 0)
1982               break;
1983             if (font_number == (MaxNumberFonts-2))
1984               {
1985                 static char
1986                   font_name[MaxTextExtent] = "fixed";
1987
1988                 /*
1989                   Select a font name from a browser.
1990                 */
1991                 resource_info->font_name[font_number]=font_name;
1992                 XFontBrowserWidget(display,windows,"Select",font_name);
1993                 if (*font_name == '\0')
1994                   break;
1995               }
1996             /*
1997               Initialize font info.
1998             */
1999             font_info=XLoadQueryFont(display,resource_info->font_name[
2000               font_number]);
2001             if (font_info == (XFontStruct *) NULL)
2002               {
2003                 XNoticeWidget(display,windows,"Unable to load font:",
2004                   resource_info->font_name[font_number]);
2005                 break;
2006               }
2007             font_id=(unsigned int) font_number;
2008             (void) XFreeFont(display,font_info);
2009             break;
2010           }
2011           case AnnotateFontColorCommand:
2012           {
2013             /*
2014               Initialize menu selections.
2015             */
2016             for (i=0; i < (int) (MaxNumberPens-2); i++)
2017               ColorMenu[i]=resource_info->pen_colors[i];
2018             ColorMenu[MaxNumberPens-2]="transparent";
2019             ColorMenu[MaxNumberPens-1]="Browser...";
2020             ColorMenu[MaxNumberPens]=(const char *) NULL;
2021             /*
2022               Select a pen color from the pop-up menu.
2023             */
2024             pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
2025               (const char **) ColorMenu,command);
2026             if (pen_number < 0)
2027               break;
2028             transparent_pen=pen_number == (MaxNumberPens-2) ? MagickTrue :
2029               MagickFalse;
2030             if( IfMagickTrue(transparent_pen) )
2031               break;
2032             if (pen_number == (MaxNumberPens-1))
2033               {
2034                 static char
2035                   color_name[MaxTextExtent] = "gray";
2036
2037                 /*
2038                   Select a pen color from a dialog.
2039                 */
2040                 resource_info->pen_colors[pen_number]=color_name;
2041                 XColorBrowserWidget(display,windows,"Select",color_name);
2042                 if (*color_name == '\0')
2043                   break;
2044               }
2045             /*
2046               Set pen color.
2047             */
2048             (void) XParseColor(display,windows->map_info->colormap,
2049               resource_info->pen_colors[pen_number],&color);
2050             XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
2051               (unsigned int) MaxColors,&color);
2052             windows->pixel_info->pen_colors[pen_number]=color;
2053             pen_id=(unsigned int) pen_number;
2054             break;
2055           }
2056           case AnnotateBackgroundColorCommand:
2057           {
2058             /*
2059               Initialize menu selections.
2060             */
2061             for (i=0; i < (int) (MaxNumberPens-2); i++)
2062               ColorMenu[i]=resource_info->pen_colors[i];
2063             ColorMenu[MaxNumberPens-2]="transparent";
2064             ColorMenu[MaxNumberPens-1]="Browser...";
2065             ColorMenu[MaxNumberPens]=(const char *) NULL;
2066             /*
2067               Select a pen color from the pop-up menu.
2068             */
2069             pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
2070               (const char **) ColorMenu,command);
2071             if (pen_number < 0)
2072               break;
2073             transparent_box=pen_number == (MaxNumberPens-2) ? MagickTrue :
2074               MagickFalse;
2075             if( IfMagickTrue(transparent_box) )
2076               break;
2077             if (pen_number == (MaxNumberPens-1))
2078               {
2079                 static char
2080                   color_name[MaxTextExtent] = "gray";
2081
2082                 /*
2083                   Select a pen color from a dialog.
2084                 */
2085                 resource_info->pen_colors[pen_number]=color_name;
2086                 XColorBrowserWidget(display,windows,"Select",color_name);
2087                 if (*color_name == '\0')
2088                   break;
2089               }
2090             /*
2091               Set pen color.
2092             */
2093             (void) XParseColor(display,windows->map_info->colormap,
2094               resource_info->pen_colors[pen_number],&color);
2095             XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
2096               (unsigned int) MaxColors,&color);
2097             windows->pixel_info->pen_colors[pen_number]=color;
2098             box_id=(unsigned int) pen_number;
2099             break;
2100           }
2101           case AnnotateRotateCommand:
2102           {
2103             int
2104               entry;
2105
2106             static char
2107               angle[MaxTextExtent] = "30.0";
2108
2109             static const char
2110               *RotateMenu[] =
2111               {
2112                 "-90",
2113                 "-45",
2114                 "-30",
2115                 "0",
2116                 "30",
2117                 "45",
2118                 "90",
2119                 "180",
2120                 "Dialog...",
2121                 (char *) NULL,
2122               };
2123
2124             /*
2125               Select a command from the pop-up menu.
2126             */
2127             entry=XMenuWidget(display,windows,AnnotateMenu[id],RotateMenu,
2128               command);
2129             if (entry < 0)
2130               break;
2131             if (entry != 8)
2132               {
2133                 degrees=StringToDouble(RotateMenu[entry],(char **) NULL);
2134                 break;
2135               }
2136             (void) XDialogWidget(display,windows,"OK","Enter rotation angle:",
2137               angle);
2138             if (*angle == '\0')
2139               break;
2140             degrees=StringToDouble(angle,(char **) NULL);
2141             break;
2142           }
2143           case AnnotateHelpCommand:
2144           {
2145             XTextViewWidget(display,resource_info,windows,MagickFalse,
2146               "Help Viewer - Image Annotation",ImageAnnotateHelp);
2147             break;
2148           }
2149           case AnnotateDismissCommand:
2150           {
2151             /*
2152               Prematurely exit.
2153             */
2154             state|=EscapeState;
2155             state|=ExitState;
2156             break;
2157           }
2158           default:
2159             break;
2160         }
2161         continue;
2162       }
2163     switch (event.type)
2164     {
2165       case ButtonPress:
2166       {
2167         if (event.xbutton.button != Button1)
2168           break;
2169         if (event.xbutton.window != windows->image.id)
2170           break;
2171         /*
2172           Change to text entering mode.
2173         */
2174         x=event.xbutton.x;
2175         y=event.xbutton.y;
2176         state|=ExitState;
2177         break;
2178       }
2179       case ButtonRelease:
2180         break;
2181       case Expose:
2182         break;
2183       case KeyPress:
2184       {
2185         if (event.xkey.window != windows->image.id)
2186           break;
2187         /*
2188           Respond to a user key press.
2189         */
2190         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2191           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2192         switch ((int) key_symbol)
2193         {
2194           case XK_Escape:
2195           case XK_F20:
2196           {
2197             /*
2198               Prematurely exit.
2199             */
2200             state|=EscapeState;
2201             state|=ExitState;
2202             break;
2203           }
2204           case XK_F1:
2205           case XK_Help:
2206           {
2207             XTextViewWidget(display,resource_info,windows,MagickFalse,
2208               "Help Viewer - Image Annotation",ImageAnnotateHelp);
2209             break;
2210           }
2211           default:
2212           {
2213             (void) XBell(display,0);
2214             break;
2215           }
2216         }
2217         break;
2218       }
2219       case MotionNotify:
2220       {
2221         /*
2222           Map and unmap Info widget as cursor crosses its boundaries.
2223         */
2224         x=event.xmotion.x;
2225         y=event.xmotion.y;
2226         if( IfMagickTrue(windows->info.mapped) )
2227           {
2228             if ((x < (int) (windows->info.x+windows->info.width)) &&
2229                 (y < (int) (windows->info.y+windows->info.height)))
2230               (void) XWithdrawWindow(display,windows->info.id,
2231                 windows->info.screen);
2232           }
2233         else
2234           if ((x > (int) (windows->info.x+windows->info.width)) ||
2235               (y > (int) (windows->info.y+windows->info.height)))
2236             (void) XMapWindow(display,windows->info.id);
2237         break;
2238       }
2239       default:
2240         break;
2241     }
2242   } while ((state & ExitState) == 0);
2243   (void) XSelectInput(display,windows->image.id,
2244     windows->image.attributes.event_mask);
2245   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
2246   if ((state & EscapeState) != 0)
2247     return(MagickTrue);
2248   /*
2249     Set font info and check boundary conditions.
2250   */
2251   font_info=XLoadQueryFont(display,resource_info->font_name[font_id]);
2252   if (font_info == (XFontStruct *) NULL)
2253     {
2254       XNoticeWidget(display,windows,"Unable to load font:",
2255         resource_info->font_name[font_id]);
2256       font_info=windows->font_info;
2257     }
2258   if ((x+font_info->max_bounds.width) >= (int) windows->image.width)
2259     x=(int) windows->image.width-font_info->max_bounds.width;
2260   if (y < (int) (font_info->ascent+font_info->descent))
2261     y=(int) font_info->ascent+font_info->descent;
2262   if (((int) font_info->max_bounds.width > (int) windows->image.width) ||
2263       ((font_info->ascent+font_info->descent) >= (int) windows->image.height))
2264     return(MagickFalse);
2265   /*
2266     Initialize annotate structure.
2267   */
2268   annotate_info=(XAnnotateInfo *) AcquireMagickMemory(sizeof(*annotate_info));
2269   if (annotate_info == (XAnnotateInfo *) NULL)
2270     return(MagickFalse);
2271   XGetAnnotateInfo(annotate_info);
2272   annotate_info->x=x;
2273   annotate_info->y=y;
2274   if( IfMagickFalse(transparent_box) && IfMagickFalse(transparent_pen))
2275     annotate_info->stencil=OpaqueStencil;
2276   else
2277     if( IfMagickFalse(transparent_box) )
2278       annotate_info->stencil=BackgroundStencil;
2279     else
2280       annotate_info->stencil=ForegroundStencil;
2281   annotate_info->height=(unsigned int) font_info->ascent+font_info->descent;
2282   annotate_info->degrees=degrees;
2283   annotate_info->font_info=font_info;
2284   annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2285     windows->image.width/MagickMax((ssize_t) font_info->min_bounds.width,1)+2UL,
2286     sizeof(*annotate_info->text));
2287   if (annotate_info->text == (char *) NULL)
2288     return(MagickFalse);
2289   /*
2290     Create cursor and set graphic context.
2291   */
2292   cursor=XCreateFontCursor(display,XC_pencil);
2293   (void) XCheckDefineCursor(display,windows->image.id,cursor);
2294   annotate_context=windows->image.annotate_context;
2295   (void) XSetFont(display,annotate_context,font_info->fid);
2296   (void) XSetBackground(display,annotate_context,
2297     windows->pixel_info->pen_colors[box_id].pixel);
2298   (void) XSetForeground(display,annotate_context,
2299     windows->pixel_info->pen_colors[pen_id].pixel);
2300   /*
2301     Begin annotating the image with text.
2302   */
2303   (void) CloneString(&windows->command.name,"Text");
2304   windows->command.data=0;
2305   (void) XCommandWidget(display,windows,TextMenu,(XEvent *) NULL);
2306   state=DefaultState;
2307   (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2308   text_event.xexpose.width=(int) font_info->max_bounds.width;
2309   text_event.xexpose.height=font_info->max_bounds.ascent+
2310     font_info->max_bounds.descent;
2311   p=annotate_info->text;
2312   do
2313   {
2314     /*
2315       Display text cursor.
2316     */
2317     *p='\0';
2318     (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2319     /*
2320       Wait for next event.
2321     */
2322     XScreenEvent(display,windows,&event,exception);
2323     if (event.xany.window == windows->command.id)
2324       {
2325         /*
2326           Select a command from the Command widget.
2327         */
2328         (void) XSetBackground(display,annotate_context,
2329           windows->pixel_info->background_color.pixel);
2330         (void) XSetForeground(display,annotate_context,
2331           windows->pixel_info->foreground_color.pixel);
2332         id=XCommandWidget(display,windows,AnnotateMenu,&event);
2333         (void) XSetBackground(display,annotate_context,
2334           windows->pixel_info->pen_colors[box_id].pixel);
2335         (void) XSetForeground(display,annotate_context,
2336           windows->pixel_info->pen_colors[pen_id].pixel);
2337         if (id < 0)
2338           continue;
2339         switch (TextCommands[id])
2340         {
2341           case TextHelpCommand:
2342           {
2343             XTextViewWidget(display,resource_info,windows,MagickFalse,
2344               "Help Viewer - Image Annotation",ImageAnnotateHelp);
2345             (void) XCheckDefineCursor(display,windows->image.id,cursor);
2346             break;
2347           }
2348           case TextApplyCommand:
2349           {
2350             /*
2351               Finished annotating.
2352             */
2353             annotate_info->width=(unsigned int) XTextWidth(font_info,
2354               annotate_info->text,(int) strlen(annotate_info->text));
2355             XRefreshWindow(display,&windows->image,&text_event);
2356             state|=ExitState;
2357             break;
2358           }
2359           default:
2360             break;
2361         }
2362         continue;
2363       }
2364     /*
2365       Erase text cursor.
2366     */
2367     text_event.xexpose.x=x;
2368     text_event.xexpose.y=y-font_info->max_bounds.ascent;
2369     (void) XClearArea(display,windows->image.id,x,text_event.xexpose.y,
2370       (unsigned int) text_event.xexpose.width,(unsigned int)
2371       text_event.xexpose.height,MagickFalse);
2372     XRefreshWindow(display,&windows->image,&text_event);
2373     switch (event.type)
2374     {
2375       case ButtonPress:
2376       {
2377         if (event.xbutton.window != windows->image.id)
2378           break;
2379         if (event.xbutton.button == Button2)
2380           {
2381             /*
2382               Request primary selection.
2383             */
2384             (void) XConvertSelection(display,XA_PRIMARY,XA_STRING,XA_STRING,
2385               windows->image.id,CurrentTime);
2386             break;
2387           }
2388         break;
2389       }
2390       case Expose:
2391       {
2392         if (event.xexpose.count == 0)
2393           {
2394             XAnnotateInfo
2395               *text_info;
2396
2397             /*
2398               Refresh Image window.
2399             */
2400             XRefreshWindow(display,&windows->image,(XEvent *) NULL);
2401             text_info=annotate_info;
2402             while (text_info != (XAnnotateInfo *) NULL)
2403             {
2404               if (annotate_info->stencil == ForegroundStencil)
2405                 (void) XDrawString(display,windows->image.id,annotate_context,
2406                   text_info->x,text_info->y,text_info->text,
2407                   (int) strlen(text_info->text));
2408               else
2409                 (void) XDrawImageString(display,windows->image.id,
2410                   annotate_context,text_info->x,text_info->y,text_info->text,
2411                   (int) strlen(text_info->text));
2412               text_info=text_info->previous;
2413             }
2414             (void) XDrawString(display,windows->image.id,annotate_context,
2415               x,y,"_",1);
2416           }
2417         break;
2418       }
2419       case KeyPress:
2420       {
2421         int
2422           length;
2423
2424         if (event.xkey.window != windows->image.id)
2425           break;
2426         /*
2427           Respond to a user key press.
2428         */
2429         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
2430           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2431         *(command+length)='\0';
2432         if (((event.xkey.state & ControlMask) != 0) ||
2433             ((event.xkey.state & Mod1Mask) != 0))
2434           state|=ModifierState;
2435         if ((state & ModifierState) != 0)
2436           switch ((int) key_symbol)
2437           {
2438             case XK_u:
2439             case XK_U:
2440             {
2441               key_symbol=DeleteCommand;
2442               break;
2443             }
2444             default:
2445               break;
2446           }
2447         switch ((int) key_symbol)
2448         {
2449           case XK_BackSpace:
2450           {
2451             /*
2452               Erase one character.
2453             */
2454             if (p == annotate_info->text)
2455               {
2456                 if (annotate_info->previous == (XAnnotateInfo *) NULL)
2457                   break;
2458                 else
2459                   {
2460                     /*
2461                       Go to end of the previous line of text.
2462                     */
2463                     annotate_info=annotate_info->previous;
2464                     p=annotate_info->text;
2465                     x=annotate_info->x+annotate_info->width;
2466                     y=annotate_info->y;
2467                     if (annotate_info->width != 0)
2468                       p+=strlen(annotate_info->text);
2469                     break;
2470                   }
2471               }
2472             p--;
2473             x-=XTextWidth(font_info,p,1);
2474             text_event.xexpose.x=x;
2475             text_event.xexpose.y=y-font_info->max_bounds.ascent;
2476             XRefreshWindow(display,&windows->image,&text_event);
2477             break;
2478           }
2479           case XK_bracketleft:
2480           {
2481             key_symbol=XK_Escape;
2482             break;
2483           }
2484           case DeleteCommand:
2485           {
2486             /*
2487               Erase the entire line of text.
2488             */
2489             while (p != annotate_info->text)
2490             {
2491               p--;
2492               x-=XTextWidth(font_info,p,1);
2493               text_event.xexpose.x=x;
2494               XRefreshWindow(display,&windows->image,&text_event);
2495             }
2496             break;
2497           }
2498           case XK_Escape:
2499           case XK_F20:
2500           {
2501             /*
2502               Finished annotating.
2503             */
2504             annotate_info->width=(unsigned int) XTextWidth(font_info,
2505               annotate_info->text,(int) strlen(annotate_info->text));
2506             XRefreshWindow(display,&windows->image,&text_event);
2507             state|=ExitState;
2508             break;
2509           }
2510           default:
2511           {
2512             /*
2513               Draw a single character on the Image window.
2514             */
2515             if ((state & ModifierState) != 0)
2516               break;
2517             if (*command == '\0')
2518               break;
2519             *p=(*command);
2520             if (annotate_info->stencil == ForegroundStencil)
2521               (void) XDrawString(display,windows->image.id,annotate_context,
2522                 x,y,p,1);
2523             else
2524               (void) XDrawImageString(display,windows->image.id,
2525                 annotate_context,x,y,p,1);
2526             x+=XTextWidth(font_info,p,1);
2527             p++;
2528             if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2529               break;
2530           }
2531           case XK_Return:
2532           case XK_KP_Enter:
2533           {
2534             /*
2535               Advance to the next line of text.
2536             */
2537             *p='\0';
2538             annotate_info->width=(unsigned int) XTextWidth(font_info,
2539               annotate_info->text,(int) strlen(annotate_info->text));
2540             if (annotate_info->next != (XAnnotateInfo *) NULL)
2541               {
2542                 /*
2543                   Line of text already exists.
2544                 */
2545                 annotate_info=annotate_info->next;
2546                 x=annotate_info->x;
2547                 y=annotate_info->y;
2548                 p=annotate_info->text;
2549                 break;
2550               }
2551             annotate_info->next=(XAnnotateInfo *) AcquireMagickMemory(
2552               sizeof(*annotate_info->next));
2553             if (annotate_info->next == (XAnnotateInfo *) NULL)
2554               return(MagickFalse);
2555             *annotate_info->next=(*annotate_info);
2556             annotate_info->next->previous=annotate_info;
2557             annotate_info=annotate_info->next;
2558             annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2559               windows->image.width/MagickMax((ssize_t)
2560               font_info->min_bounds.width,1)+2UL,sizeof(*annotate_info->text));
2561             if (annotate_info->text == (char *) NULL)
2562               return(MagickFalse);
2563             annotate_info->y+=annotate_info->height;
2564             if (annotate_info->y > (int) windows->image.height)
2565               annotate_info->y=(int) annotate_info->height;
2566             annotate_info->next=(XAnnotateInfo *) NULL;
2567             x=annotate_info->x;
2568             y=annotate_info->y;
2569             p=annotate_info->text;
2570             break;
2571           }
2572         }
2573         break;
2574       }
2575       case KeyRelease:
2576       {
2577         /*
2578           Respond to a user key release.
2579         */
2580         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2581           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2582         state&=(~ModifierState);
2583         break;
2584       }
2585       case SelectionNotify:
2586       {
2587         Atom
2588           type;
2589
2590         int
2591           format;
2592
2593         unsigned char
2594           *data;
2595
2596         unsigned long
2597           after,
2598           length;
2599
2600         /*
2601           Obtain response from primary selection.
2602         */
2603         if (event.xselection.property == (Atom) None)
2604           break;
2605         status=XGetWindowProperty(display,event.xselection.requestor,
2606           event.xselection.property,0L,(long) MaxTextExtent,True,XA_STRING,
2607           &type,&format,&length,&after,&data);
2608         if ((status != Success) || (type != XA_STRING) || (format == 32) ||
2609             (length == 0))
2610           break;
2611         /*
2612           Annotate Image window with primary selection.
2613         */
2614         for (i=0; i < (ssize_t) length; i++)
2615         {
2616           if ((char) data[i] != '\n')
2617             {
2618               /*
2619                 Draw a single character on the Image window.
2620               */
2621               *p=(char) data[i];
2622               (void) XDrawString(display,windows->image.id,annotate_context,
2623                 x,y,p,1);
2624               x+=XTextWidth(font_info,p,1);
2625               p++;
2626               if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2627                 continue;
2628             }
2629           /*
2630             Advance to the next line of text.
2631           */
2632           *p='\0';
2633           annotate_info->width=(unsigned int) XTextWidth(font_info,
2634             annotate_info->text,(int) strlen(annotate_info->text));
2635           if (annotate_info->next != (XAnnotateInfo *) NULL)
2636             {
2637               /*
2638                 Line of text already exists.
2639               */
2640               annotate_info=annotate_info->next;
2641               x=annotate_info->x;
2642               y=annotate_info->y;
2643               p=annotate_info->text;
2644               continue;
2645             }
2646           annotate_info->next=(XAnnotateInfo *) AcquireMagickMemory(
2647             sizeof(*annotate_info->next));
2648           if (annotate_info->next == (XAnnotateInfo *) NULL)
2649             return(MagickFalse);
2650           *annotate_info->next=(*annotate_info);
2651           annotate_info->next->previous=annotate_info;
2652           annotate_info=annotate_info->next;
2653           annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2654             windows->image.width/MagickMax((ssize_t)
2655             font_info->min_bounds.width,1)+2UL,sizeof(*annotate_info->text));
2656           if (annotate_info->text == (char *) NULL)
2657             return(MagickFalse);
2658           annotate_info->y+=annotate_info->height;
2659           if (annotate_info->y > (int) windows->image.height)
2660             annotate_info->y=(int) annotate_info->height;
2661           annotate_info->next=(XAnnotateInfo *) NULL;
2662           x=annotate_info->x;
2663           y=annotate_info->y;
2664           p=annotate_info->text;
2665         }
2666         (void) XFree((void *) data);
2667         break;
2668       }
2669       default:
2670         break;
2671     }
2672   } while ((state & ExitState) == 0);
2673   (void) XFreeCursor(display,cursor);
2674   /*
2675     Annotation is relative to image configuration.
2676   */
2677   width=(unsigned int) image->columns;
2678   height=(unsigned int) image->rows;
2679   x=0;
2680   y=0;
2681   if (windows->image.crop_geometry != (char *) NULL)
2682     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
2683   /*
2684     Initialize annotated image.
2685   */
2686   XSetCursorState(display,windows,MagickTrue);
2687   XCheckRefreshWindows(display,windows);
2688   while (annotate_info != (XAnnotateInfo *) NULL)
2689   {
2690     if (annotate_info->width == 0)
2691       {
2692         /*
2693           No text on this line--  go to the next line of text.
2694         */
2695         previous_info=annotate_info->previous;
2696         annotate_info->text=(char *)
2697           RelinquishMagickMemory(annotate_info->text);
2698         annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2699         annotate_info=previous_info;
2700         continue;
2701       }
2702     /*
2703       Determine pixel index for box and pen color.
2704     */
2705     windows->pixel_info->box_color=windows->pixel_info->pen_colors[box_id];
2706     if (windows->pixel_info->colors != 0)
2707       for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
2708         if (windows->pixel_info->pixels[i] ==
2709             windows->pixel_info->pen_colors[box_id].pixel)
2710           {
2711             windows->pixel_info->box_index=(unsigned short) i;
2712             break;
2713           }
2714     windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
2715     if (windows->pixel_info->colors != 0)
2716       for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
2717         if (windows->pixel_info->pixels[i] ==
2718             windows->pixel_info->pen_colors[pen_id].pixel)
2719           {
2720             windows->pixel_info->pen_index=(unsigned short) i;
2721             break;
2722           }
2723     /*
2724       Define the annotate geometry string.
2725     */
2726     annotate_info->x=(int)
2727       width*(annotate_info->x+windows->image.x)/windows->image.ximage->width;
2728     annotate_info->y=(int) height*(annotate_info->y-font_info->ascent+
2729       windows->image.y)/windows->image.ximage->height;
2730     (void) FormatLocaleString(annotate_info->geometry,MaxTextExtent,
2731       "%ux%u%+d%+d",width*annotate_info->width/windows->image.ximage->width,
2732       height*annotate_info->height/windows->image.ximage->height,
2733       annotate_info->x+x,annotate_info->y+y);
2734     /*
2735       Annotate image with text.
2736     */
2737     status=XAnnotateImage(display,windows->pixel_info,annotate_info,image,
2738       exception);
2739     if (status == 0)
2740       return(MagickFalse);
2741     /*
2742       Free up memory.
2743     */
2744     previous_info=annotate_info->previous;
2745     annotate_info->text=DestroyString(annotate_info->text);
2746     annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2747     annotate_info=previous_info;
2748   }
2749   (void) XSetForeground(display,annotate_context,
2750     windows->pixel_info->foreground_color.pixel);
2751   (void) XSetBackground(display,annotate_context,
2752     windows->pixel_info->background_color.pixel);
2753   (void) XSetFont(display,annotate_context,windows->font_info->fid);
2754   XSetCursorState(display,windows,MagickFalse);
2755   (void) XFreeFont(display,font_info);
2756   /*
2757     Update image configuration.
2758   */
2759   XConfigureImageColormap(display,resource_info,windows,image,exception);
2760   (void) XConfigureImage(display,resource_info,windows,image,exception);
2761   return(MagickTrue);
2762 }
2763 \f
2764 /*
2765 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2766 %                                                                             %
2767 %                                                                             %
2768 %                                                                             %
2769 +   X B a c k g r o u n d I m a g e                                           %
2770 %                                                                             %
2771 %                                                                             %
2772 %                                                                             %
2773 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2774 %
2775 %  XBackgroundImage() displays the image in the background of a window.
2776 %
2777 %  The format of the XBackgroundImage method is:
2778 %
2779 %      MagickBooleanType XBackgroundImage(Display *display,
2780 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
2781 %        ExceptionInfo *exception)
2782 %
2783 %  A description of each parameter follows:
2784 %
2785 %    o display: Specifies a connection to an X server; returned from
2786 %      XOpenDisplay.
2787 %
2788 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2789 %
2790 %    o windows: Specifies a pointer to a XWindows structure.
2791 %
2792 %    o image: the image.
2793 %
2794 %    o exception: return any errors or warnings in this structure.
2795 %
2796 */
2797 static MagickBooleanType XBackgroundImage(Display *display,
2798   XResourceInfo *resource_info,XWindows *windows,Image **image,
2799   ExceptionInfo *exception)
2800 {
2801 #define BackgroundImageTag  "Background/Image"
2802
2803   int
2804     status;
2805
2806   static char
2807     window_id[MaxTextExtent] = "root";
2808
2809   XResourceInfo
2810     background_resources;
2811
2812   /*
2813     Put image in background.
2814   */
2815   status=XDialogWidget(display,windows,"Background",
2816     "Enter window id (id 0x00 selects window with pointer):",window_id);
2817   if (*window_id == '\0')
2818     return(MagickFalse);
2819   (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
2820     exception);
2821   XInfoWidget(display,windows,BackgroundImageTag);
2822   XSetCursorState(display,windows,MagickTrue);
2823   XCheckRefreshWindows(display,windows);
2824   background_resources=(*resource_info);
2825   background_resources.window_id=window_id;
2826   background_resources.backdrop=IsMagickTrue(status);
2827   status=XDisplayBackgroundImage(display,&background_resources,*image,
2828     exception);
2829   if (IfMagickTrue(status))
2830     XClientMessage(display,windows->image.id,windows->im_protocols,
2831       windows->im_retain_colors,CurrentTime);
2832   XSetCursorState(display,windows,MagickFalse);
2833   (void) XMagickCommand(display,resource_info,windows,UndoCommand,image,
2834     exception);
2835   return(MagickTrue);
2836 }
2837 \f
2838 /*
2839 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2840 %                                                                             %
2841 %                                                                             %
2842 %                                                                             %
2843 +   X C h o p I m a g e                                                       %
2844 %                                                                             %
2845 %                                                                             %
2846 %                                                                             %
2847 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2848 %
2849 %  XChopImage() chops the X image.
2850 %
2851 %  The format of the XChopImage method is:
2852 %
2853 %    MagickBooleanType XChopImage(Display *display,XResourceInfo *resource_info,
2854 %      XWindows *windows,Image **image,ExceptionInfo *exception)
2855 %
2856 %  A description of each parameter follows:
2857 %
2858 %    o display: Specifies a connection to an X server; returned from
2859 %      XOpenDisplay.
2860 %
2861 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2862 %
2863 %    o windows: Specifies a pointer to a XWindows structure.
2864 %
2865 %    o image: the image.
2866 %
2867 %    o exception: return any errors or warnings in this structure.
2868 %
2869 */
2870 static MagickBooleanType XChopImage(Display *display,
2871   XResourceInfo *resource_info,XWindows *windows,Image **image,
2872   ExceptionInfo *exception)
2873 {
2874   static const char
2875     *ChopMenu[] =
2876     {
2877       "Direction",
2878       "Help",
2879       "Dismiss",
2880       (char *) NULL
2881     };
2882
2883   static ModeType
2884     direction = HorizontalChopCommand;
2885
2886   static const ModeType
2887     ChopCommands[] =
2888     {
2889       ChopDirectionCommand,
2890       ChopHelpCommand,
2891       ChopDismissCommand
2892     },
2893     DirectionCommands[] =
2894     {
2895       HorizontalChopCommand,
2896       VerticalChopCommand
2897     };
2898
2899   char
2900     text[MaxTextExtent];
2901
2902   Image
2903     *chop_image;
2904
2905   int
2906     id,
2907     x,
2908     y;
2909
2910   double
2911     scale_factor;
2912
2913   RectangleInfo
2914     chop_info;
2915
2916   unsigned int
2917     distance,
2918     height,
2919     width;
2920
2921   size_t
2922     state;
2923
2924   XEvent
2925     event;
2926
2927   XSegment
2928     segment_info;
2929
2930   /*
2931     Map Command widget.
2932   */
2933   (void) CloneString(&windows->command.name,"Chop");
2934   windows->command.data=1;
2935   (void) XCommandWidget(display,windows,ChopMenu,(XEvent *) NULL);
2936   (void) XMapRaised(display,windows->command.id);
2937   XClientMessage(display,windows->image.id,windows->im_protocols,
2938     windows->im_update_widget,CurrentTime);
2939   /*
2940     Track pointer until button 1 is pressed.
2941   */
2942   XQueryPosition(display,windows->image.id,&x,&y);
2943   (void) XSelectInput(display,windows->image.id,
2944     windows->image.attributes.event_mask | PointerMotionMask);
2945   state=DefaultState;
2946   do
2947   {
2948     if( IfMagickTrue(windows->info.mapped) )
2949       {
2950         /*
2951           Display pointer position.
2952         */
2953         (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
2954           x+windows->image.x,y+windows->image.y);
2955         XInfoWidget(display,windows,text);
2956       }
2957     /*
2958       Wait for next event.
2959     */
2960     XScreenEvent(display,windows,&event,exception);
2961     if (event.xany.window == windows->command.id)
2962       {
2963         /*
2964           Select a command from the Command widget.
2965         */
2966         id=XCommandWidget(display,windows,ChopMenu,&event);
2967         if (id < 0)
2968           continue;
2969         switch (ChopCommands[id])
2970         {
2971           case ChopDirectionCommand:
2972           {
2973             char
2974               command[MaxTextExtent];
2975
2976             static const char
2977               *Directions[] =
2978               {
2979                 "horizontal",
2980                 "vertical",
2981                 (char *) NULL,
2982               };
2983
2984             /*
2985               Select a command from the pop-up menu.
2986             */
2987             id=XMenuWidget(display,windows,ChopMenu[id],Directions,command);
2988             if (id >= 0)
2989               direction=DirectionCommands[id];
2990             break;
2991           }
2992           case ChopHelpCommand:
2993           {
2994             XTextViewWidget(display,resource_info,windows,MagickFalse,
2995               "Help Viewer - Image Chop",ImageChopHelp);
2996             break;
2997           }
2998           case ChopDismissCommand:
2999           {
3000             /*
3001               Prematurely exit.
3002             */
3003             state|=EscapeState;
3004             state|=ExitState;
3005             break;
3006           }
3007           default:
3008             break;
3009         }
3010         continue;
3011       }
3012     switch (event.type)
3013     {
3014       case ButtonPress:
3015       {
3016         if (event.xbutton.button != Button1)
3017           break;
3018         if (event.xbutton.window != windows->image.id)
3019           break;
3020         /*
3021           User has committed to start point of chopping line.
3022         */
3023         segment_info.x1=(short int) event.xbutton.x;
3024         segment_info.x2=(short int) event.xbutton.x;
3025         segment_info.y1=(short int) event.xbutton.y;
3026         segment_info.y2=(short int) event.xbutton.y;
3027         state|=ExitState;
3028         break;
3029       }
3030       case ButtonRelease:
3031         break;
3032       case Expose:
3033         break;
3034       case KeyPress:
3035       {
3036         char
3037           command[MaxTextExtent];
3038
3039         KeySym
3040           key_symbol;
3041
3042         if (event.xkey.window != windows->image.id)
3043           break;
3044         /*
3045           Respond to a user key press.
3046         */
3047         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3048           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3049         switch ((int) key_symbol)
3050         {
3051           case XK_Escape:
3052           case XK_F20:
3053           {
3054             /*
3055               Prematurely exit.
3056             */
3057             state|=EscapeState;
3058             state|=ExitState;
3059             break;
3060           }
3061           case XK_F1:
3062           case XK_Help:
3063           {
3064             (void) XSetFunction(display,windows->image.highlight_context,
3065               GXcopy);
3066             XTextViewWidget(display,resource_info,windows,MagickFalse,
3067               "Help Viewer - Image Chop",ImageChopHelp);
3068             (void) XSetFunction(display,windows->image.highlight_context,
3069               GXinvert);
3070             break;
3071           }
3072           default:
3073           {
3074             (void) XBell(display,0);
3075             break;
3076           }
3077         }
3078         break;
3079       }
3080       case MotionNotify:
3081       {
3082         /*
3083           Map and unmap Info widget as text cursor crosses its boundaries.
3084         */
3085         x=event.xmotion.x;
3086         y=event.xmotion.y;
3087         if( IfMagickTrue(windows->info.mapped) )
3088           {
3089             if ((x < (int) (windows->info.x+windows->info.width)) &&
3090                 (y < (int) (windows->info.y+windows->info.height)))
3091               (void) XWithdrawWindow(display,windows->info.id,
3092                 windows->info.screen);
3093           }
3094         else
3095           if ((x > (int) (windows->info.x+windows->info.width)) ||
3096               (y > (int) (windows->info.y+windows->info.height)))
3097             (void) XMapWindow(display,windows->info.id);
3098       }
3099     }
3100   } while ((state & ExitState) == 0);
3101   (void) XSelectInput(display,windows->image.id,
3102     windows->image.attributes.event_mask);
3103   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3104   if ((state & EscapeState) != 0)
3105     return(MagickTrue);
3106   /*
3107     Draw line as pointer moves until the mouse button is released.
3108   */
3109   chop_info.width=0;
3110   chop_info.height=0;
3111   chop_info.x=0;
3112   chop_info.y=0;
3113   distance=0;
3114   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
3115   state=DefaultState;
3116   do
3117   {
3118     if (distance > 9)
3119       {
3120         /*
3121           Display info and draw chopping line.
3122         */
3123         if( IfMagickFalse(windows->info.mapped) )
3124           (void) XMapWindow(display,windows->info.id);
3125         (void) FormatLocaleString(text,MaxTextExtent,
3126           " %.20gx%.20g%+.20g%+.20g",(double) chop_info.width,(double)
3127           chop_info.height,(double) chop_info.x,(double) chop_info.y);
3128         XInfoWidget(display,windows,text);
3129         XHighlightLine(display,windows->image.id,
3130           windows->image.highlight_context,&segment_info);
3131       }
3132     else
3133       if( IfMagickTrue(windows->info.mapped) )
3134         (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3135     /*
3136       Wait for next event.
3137     */
3138     XScreenEvent(display,windows,&event,exception);
3139     if (distance > 9)
3140       XHighlightLine(display,windows->image.id,
3141         windows->image.highlight_context,&segment_info);
3142     switch (event.type)
3143     {
3144       case ButtonPress:
3145       {
3146         segment_info.x2=(short int) event.xmotion.x;
3147         segment_info.y2=(short int) event.xmotion.y;
3148         break;
3149       }
3150       case ButtonRelease:
3151       {
3152         /*
3153           User has committed to chopping line.
3154         */
3155         segment_info.x2=(short int) event.xbutton.x;
3156         segment_info.y2=(short int) event.xbutton.y;
3157         state|=ExitState;
3158         break;
3159       }
3160       case Expose:
3161         break;
3162       case MotionNotify:
3163       {
3164         segment_info.x2=(short int) event.xmotion.x;
3165         segment_info.y2=(short int) event.xmotion.y;
3166       }
3167       default:
3168         break;
3169     }
3170     /*
3171       Check boundary conditions.
3172     */
3173     if (segment_info.x2 < 0)
3174       segment_info.x2=0;
3175     else
3176       if (segment_info.x2 > windows->image.ximage->width)
3177         segment_info.x2=windows->image.ximage->width;
3178     if (segment_info.y2 < 0)
3179       segment_info.y2=0;
3180     else
3181       if (segment_info.y2 > windows->image.ximage->height)
3182         segment_info.y2=windows->image.ximage->height;
3183     distance=(unsigned int)
3184       (((segment_info.x2-segment_info.x1)*(segment_info.x2-segment_info.x1))+
3185        ((segment_info.y2-segment_info.y1)*(segment_info.y2-segment_info.y1)));
3186     /*
3187       Compute chopping geometry.
3188     */
3189     if (direction == HorizontalChopCommand)
3190       {
3191         chop_info.width=(size_t) (segment_info.x2-segment_info.x1+1);
3192         chop_info.x=(ssize_t) windows->image.x+segment_info.x1;
3193         chop_info.height=0;
3194         chop_info.y=0;
3195         if (segment_info.x1 > (int) segment_info.x2)
3196           {
3197             chop_info.width=(size_t) (segment_info.x1-segment_info.x2+1);
3198             chop_info.x=(ssize_t) windows->image.x+segment_info.x2;
3199           }
3200       }
3201     else
3202       {
3203         chop_info.width=0;
3204         chop_info.height=(size_t) (segment_info.y2-segment_info.y1+1);
3205         chop_info.x=0;
3206         chop_info.y=(ssize_t) windows->image.y+segment_info.y1;
3207         if (segment_info.y1 > segment_info.y2)
3208           {
3209             chop_info.height=(size_t) (segment_info.y1-segment_info.y2+1);
3210             chop_info.y=(ssize_t) windows->image.y+segment_info.y2;
3211           }
3212       }
3213   } while ((state & ExitState) == 0);
3214   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
3215   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3216   if (distance <= 9)
3217     return(MagickTrue);
3218   /*
3219     Image chopping is relative to image configuration.
3220   */
3221   (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
3222     exception);
3223   XSetCursorState(display,windows,MagickTrue);
3224   XCheckRefreshWindows(display,windows);
3225   windows->image.window_changes.width=windows->image.ximage->width-
3226     (unsigned int) chop_info.width;
3227   windows->image.window_changes.height=windows->image.ximage->height-
3228     (unsigned int) chop_info.height;
3229   width=(unsigned int) (*image)->columns;
3230   height=(unsigned int) (*image)->rows;
3231   x=0;
3232   y=0;
3233   if (windows->image.crop_geometry != (char *) NULL)
3234     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
3235   scale_factor=(double) width/windows->image.ximage->width;
3236   chop_info.x+=x;
3237   chop_info.x=(ssize_t) (scale_factor*chop_info.x+0.5);
3238   chop_info.width=(unsigned int) (scale_factor*chop_info.width+0.5);
3239   scale_factor=(double) height/windows->image.ximage->height;
3240   chop_info.y+=y;
3241   chop_info.y=(ssize_t) (scale_factor*chop_info.y+0.5);
3242   chop_info.height=(unsigned int) (scale_factor*chop_info.height+0.5);
3243   /*
3244     Chop image.
3245   */
3246   chop_image=ChopImage(*image,&chop_info,exception);
3247   XSetCursorState(display,windows,MagickFalse);
3248   if (chop_image == (Image *) NULL)
3249     return(MagickFalse);
3250   *image=DestroyImage(*image);
3251   *image=chop_image;
3252   /*
3253     Update image configuration.
3254   */
3255   XConfigureImageColormap(display,resource_info,windows,*image,exception);
3256   (void) XConfigureImage(display,resource_info,windows,*image,exception);
3257   return(MagickTrue);
3258 }
3259 \f
3260 /*
3261 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3262 %                                                                             %
3263 %                                                                             %
3264 %                                                                             %
3265 +   X C o l o r E d i t I m a g e                                             %
3266 %                                                                             %
3267 %                                                                             %
3268 %                                                                             %
3269 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3270 %
3271 %  XColorEditImage() allows the user to interactively change the color of one
3272 %  pixel for a DirectColor image or one colormap entry for a PseudoClass image.
3273 %
3274 %  The format of the XColorEditImage method is:
3275 %
3276 %      MagickBooleanType XColorEditImage(Display *display,
3277 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
3278 %          ExceptionInfo *exception)
3279 %
3280 %  A description of each parameter follows:
3281 %
3282 %    o display: Specifies a connection to an X server;  returned from
3283 %      XOpenDisplay.
3284 %
3285 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3286 %
3287 %    o windows: Specifies a pointer to a XWindows structure.
3288 %
3289 %    o image: the image; returned from ReadImage.
3290 %
3291 %    o exception: return any errors or warnings in this structure.
3292 %
3293 */
3294 static MagickBooleanType XColorEditImage(Display *display,
3295   XResourceInfo *resource_info,XWindows *windows,Image **image,
3296   ExceptionInfo *exception)
3297 {
3298   static const char
3299     *ColorEditMenu[] =
3300     {
3301       "Method",
3302       "Pixel Color",
3303       "Border Color",
3304       "Fuzz",
3305       "Undo",
3306       "Help",
3307       "Dismiss",
3308       (char *) NULL
3309     };
3310
3311   static const ModeType
3312     ColorEditCommands[] =
3313     {
3314       ColorEditMethodCommand,
3315       ColorEditColorCommand,
3316       ColorEditBorderCommand,
3317       ColorEditFuzzCommand,
3318       ColorEditUndoCommand,
3319       ColorEditHelpCommand,
3320       ColorEditDismissCommand
3321     };
3322
3323   static PaintMethod
3324     method = PointMethod;
3325
3326   static unsigned int
3327     pen_id = 0;
3328
3329   static XColor
3330     border_color = { 0, 0, 0, 0, 0, 0 };
3331
3332   char
3333     command[MaxTextExtent],
3334     text[MaxTextExtent];
3335
3336   Cursor
3337     cursor;
3338
3339   int
3340     entry,
3341     id,
3342     x,
3343     x_offset,
3344     y,
3345     y_offset;
3346
3347   register Quantum
3348     *q;
3349
3350   register ssize_t
3351     i;
3352
3353   unsigned int
3354     height,
3355     width;
3356
3357   size_t
3358     state;
3359
3360   XColor
3361     color;
3362
3363   XEvent
3364     event;
3365
3366   /*
3367     Map Command widget.
3368   */
3369   (void) CloneString(&windows->command.name,"Color Edit");
3370   windows->command.data=4;
3371   (void) XCommandWidget(display,windows,ColorEditMenu,(XEvent *) NULL);
3372   (void) XMapRaised(display,windows->command.id);
3373   XClientMessage(display,windows->image.id,windows->im_protocols,
3374     windows->im_update_widget,CurrentTime);
3375   /*
3376     Make cursor.
3377   */
3378   cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
3379     resource_info->background_color,resource_info->foreground_color);
3380   (void) XCheckDefineCursor(display,windows->image.id,cursor);
3381   /*
3382     Track pointer until button 1 is pressed.
3383   */
3384   XQueryPosition(display,windows->image.id,&x,&y);
3385   (void) XSelectInput(display,windows->image.id,
3386     windows->image.attributes.event_mask | PointerMotionMask);
3387   state=DefaultState;
3388   do
3389   {
3390     if( IfMagickTrue(windows->info.mapped) )
3391       {
3392         /*
3393           Display pointer position.
3394         */
3395         (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
3396           x+windows->image.x,y+windows->image.y);
3397         XInfoWidget(display,windows,text);
3398       }
3399     /*
3400       Wait for next event.
3401     */
3402     XScreenEvent(display,windows,&event,exception);
3403     if (event.xany.window == windows->command.id)
3404       {
3405         /*
3406           Select a command from the Command widget.
3407         */
3408         id=XCommandWidget(display,windows,ColorEditMenu,&event);
3409         if (id < 0)
3410           {
3411             (void) XCheckDefineCursor(display,windows->image.id,cursor);
3412             continue;
3413           }
3414         switch (ColorEditCommands[id])
3415         {
3416           case ColorEditMethodCommand:
3417           {
3418             char
3419               **methods;
3420
3421             /*
3422               Select a method from the pop-up menu.
3423             */
3424             methods=(char **) GetCommandOptions(MagickMethodOptions);
3425             if (methods == (char **) NULL)
3426               break;
3427             entry=XMenuWidget(display,windows,ColorEditMenu[id],
3428               (const char **) methods,command);
3429             if (entry >= 0)
3430               method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
3431                 MagickFalse,methods[entry]);
3432             methods=DestroyStringList(methods);
3433             break;
3434           }
3435           case ColorEditColorCommand:
3436           {
3437             const char
3438               *ColorMenu[MaxNumberPens];
3439
3440             int
3441               pen_number;
3442
3443             /*
3444               Initialize menu selections.
3445             */
3446             for (i=0; i < (int) (MaxNumberPens-2); i++)
3447               ColorMenu[i]=resource_info->pen_colors[i];
3448             ColorMenu[MaxNumberPens-2]="Browser...";
3449             ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3450             /*
3451               Select a pen color from the pop-up menu.
3452             */
3453             pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3454               (const char **) ColorMenu,command);
3455             if (pen_number < 0)
3456               break;
3457             if (pen_number == (MaxNumberPens-2))
3458               {
3459                 static char
3460                   color_name[MaxTextExtent] = "gray";
3461
3462                 /*
3463                   Select a pen color from a dialog.
3464                 */
3465                 resource_info->pen_colors[pen_number]=color_name;
3466                 XColorBrowserWidget(display,windows,"Select",color_name);
3467                 if (*color_name == '\0')
3468                   break;
3469               }
3470             /*
3471               Set pen color.
3472             */
3473             (void) XParseColor(display,windows->map_info->colormap,
3474               resource_info->pen_colors[pen_number],&color);
3475             XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
3476               (unsigned int) MaxColors,&color);
3477             windows->pixel_info->pen_colors[pen_number]=color;
3478             pen_id=(unsigned int) pen_number;
3479             break;
3480           }
3481           case ColorEditBorderCommand:
3482           {
3483             const char
3484               *ColorMenu[MaxNumberPens];
3485
3486             int
3487               pen_number;
3488
3489             /*
3490               Initialize menu selections.
3491             */
3492             for (i=0; i < (int) (MaxNumberPens-2); i++)
3493               ColorMenu[i]=resource_info->pen_colors[i];
3494             ColorMenu[MaxNumberPens-2]="Browser...";
3495             ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3496             /*
3497               Select a pen color from the pop-up menu.
3498             */
3499             pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3500               (const char **) ColorMenu,command);
3501             if (pen_number < 0)
3502               break;
3503             if (pen_number == (MaxNumberPens-2))
3504               {
3505                 static char
3506                   color_name[MaxTextExtent] = "gray";
3507
3508                 /*
3509                   Select a pen color from a dialog.
3510                 */
3511                 resource_info->pen_colors[pen_number]=color_name;
3512                 XColorBrowserWidget(display,windows,"Select",color_name);
3513                 if (*color_name == '\0')
3514                   break;
3515               }
3516             /*
3517               Set border color.
3518             */
3519             (void) XParseColor(display,windows->map_info->colormap,
3520               resource_info->pen_colors[pen_number],&border_color);
3521             break;
3522           }
3523           case ColorEditFuzzCommand:
3524           {
3525             static char
3526               fuzz[MaxTextExtent];
3527
3528             static const char
3529               *FuzzMenu[] =
3530               {
3531                 "0%",
3532                 "2%",
3533                 "5%",
3534                 "10%",
3535                 "15%",
3536                 "Dialog...",
3537                 (char *) NULL,
3538               };
3539
3540             /*
3541               Select a command from the pop-up menu.
3542             */
3543             entry=XMenuWidget(display,windows,ColorEditMenu[id],FuzzMenu,
3544               command);
3545             if (entry < 0)
3546               break;
3547             if (entry != 5)
3548               {
3549                 (*image)->fuzz=StringToDoubleInterval(FuzzMenu[entry],(double)
3550                   QuantumRange+1.0);
3551                 break;
3552               }
3553             (void) (void) CopyMagickString(fuzz,"20%",MaxTextExtent);
3554             (void) XDialogWidget(display,windows,"Ok",
3555               "Enter fuzz factor (0.0 - 99.9%):",fuzz);
3556             if (*fuzz == '\0')
3557               break;
3558             (void) ConcatenateMagickString(fuzz,"%",MaxTextExtent);
3559             (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+
3560               1.0);
3561             break;
3562           }
3563           case ColorEditUndoCommand:
3564           {
3565             (void) XMagickCommand(display,resource_info,windows,UndoCommand,
3566               image,exception);
3567             break;
3568           }
3569           case ColorEditHelpCommand:
3570           default:
3571           {
3572             XTextViewWidget(display,resource_info,windows,MagickFalse,
3573               "Help Viewer - Image Annotation",ImageColorEditHelp);
3574             break;
3575           }
3576           case ColorEditDismissCommand:
3577           {
3578             /*
3579               Prematurely exit.
3580             */
3581             state|=EscapeState;
3582             state|=ExitState;
3583             break;
3584           }
3585         }
3586         (void) XCheckDefineCursor(display,windows->image.id,cursor);
3587         continue;
3588       }
3589     switch (event.type)
3590     {
3591       case ButtonPress:
3592       {
3593         if (event.xbutton.button != Button1)
3594           break;
3595         if ((event.xbutton.window != windows->image.id) &&
3596             (event.xbutton.window != windows->magnify.id))
3597           break;
3598         /*
3599           exit loop.
3600         */
3601         x=event.xbutton.x;
3602         y=event.xbutton.y;
3603         (void) XMagickCommand(display,resource_info,windows,
3604           SaveToUndoBufferCommand,image,exception);
3605         state|=UpdateConfigurationState;
3606         break;
3607       }
3608       case ButtonRelease:
3609       {
3610         if (event.xbutton.button != Button1)
3611           break;
3612         if ((event.xbutton.window != windows->image.id) &&
3613             (event.xbutton.window != windows->magnify.id))
3614           break;
3615         /*
3616           Update colormap information.
3617         */
3618         x=event.xbutton.x;
3619         y=event.xbutton.y;
3620         XConfigureImageColormap(display,resource_info,windows,*image,exception);
3621         (void) XConfigureImage(display,resource_info,windows,*image,exception);
3622         XInfoWidget(display,windows,text);
3623         (void) XCheckDefineCursor(display,windows->image.id,cursor);
3624         state&=(~UpdateConfigurationState);
3625         break;
3626       }
3627       case Expose:
3628         break;
3629       case KeyPress:
3630       {
3631         KeySym
3632           key_symbol;
3633
3634         if (event.xkey.window == windows->magnify.id)
3635           {
3636             Window
3637               window;
3638
3639             window=windows->magnify.id;
3640             while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
3641           }
3642         if (event.xkey.window != windows->image.id)
3643           break;
3644         /*
3645           Respond to a user key press.
3646         */
3647         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3648           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3649         switch ((int) key_symbol)
3650         {
3651           case XK_Escape:
3652           case XK_F20:
3653           {
3654             /*
3655               Prematurely exit.
3656             */
3657             state|=ExitState;
3658             break;
3659           }
3660           case XK_F1:
3661           case XK_Help:
3662           {
3663             XTextViewWidget(display,resource_info,windows,MagickFalse,
3664               "Help Viewer - Image Annotation",ImageColorEditHelp);
3665             break;
3666           }
3667           default:
3668           {
3669             (void) XBell(display,0);
3670             break;
3671           }
3672         }
3673         break;
3674       }
3675       case MotionNotify:
3676       {
3677         /*
3678           Map and unmap Info widget as cursor crosses its boundaries.
3679         */
3680         x=event.xmotion.x;
3681         y=event.xmotion.y;
3682         if( IfMagickTrue(windows->info.mapped) )
3683           {
3684             if ((x < (int) (windows->info.x+windows->info.width)) &&
3685                 (y < (int) (windows->info.y+windows->info.height)))
3686               (void) XWithdrawWindow(display,windows->info.id,
3687                 windows->info.screen);
3688           }
3689         else
3690           if ((x > (int) (windows->info.x+windows->info.width)) ||
3691               (y > (int) (windows->info.y+windows->info.height)))
3692             (void) XMapWindow(display,windows->info.id);
3693         break;
3694       }
3695       default:
3696         break;
3697     }
3698     if (event.xany.window == windows->magnify.id)
3699       {
3700         x=windows->magnify.x-windows->image.x;
3701         y=windows->magnify.y-windows->image.y;
3702       }
3703     x_offset=x;
3704     y_offset=y;
3705     if ((state & UpdateConfigurationState) != 0)
3706       {
3707         CacheView
3708           *image_view;
3709
3710         int
3711           x,
3712           y;
3713
3714         /*
3715           Pixel edit is relative to image configuration.
3716         */
3717         (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
3718           MagickTrue);
3719         color=windows->pixel_info->pen_colors[pen_id];
3720         XPutPixel(windows->image.ximage,x_offset,y_offset,color.pixel);
3721         width=(unsigned int) (*image)->columns;
3722         height=(unsigned int) (*image)->rows;
3723         x=0;
3724         y=0;
3725         if (windows->image.crop_geometry != (char *) NULL)
3726           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
3727             &width,&height);
3728         x_offset=(int)
3729           (width*(windows->image.x+x_offset)/windows->image.ximage->width+x);
3730         y_offset=(int)
3731           (height*(windows->image.y+y_offset)/windows->image.ximage->height+y);
3732         if ((x_offset < 0) || (y_offset < 0))
3733           continue;
3734         if ((x_offset >= (int) (*image)->columns) ||
3735             (y_offset >= (int) (*image)->rows))
3736           continue;
3737         image_view=AcquireAuthenticCacheView(*image,exception);
3738         switch (method)
3739         {
3740           case PointMethod:
3741           default:
3742           {
3743             /*
3744               Update color information using point algorithm.
3745             */
3746             if( IfMagickFalse(SetImageStorageClass(*image,DirectClass,exception)) )
3747               return(MagickFalse);
3748             q=GetCacheViewAuthenticPixels(image_view,(ssize_t)x_offset,
3749               (ssize_t) y_offset,1,1,exception);
3750             if (q == (Quantum *) NULL)
3751               break;
3752             SetPixelRed(*image,ScaleShortToQuantum(color.red),q);
3753             SetPixelGreen(*image,ScaleShortToQuantum(color.green),q);
3754             SetPixelBlue(*image,ScaleShortToQuantum(color.blue),q);
3755             (void) SyncCacheViewAuthenticPixels(image_view,exception);
3756             break;
3757           }
3758           case ReplaceMethod:
3759           {
3760             PixelInfo
3761               pixel,
3762               target;
3763
3764             /*
3765               Update color information using replace algorithm.
3766             */
3767             (void) GetOneCacheViewVirtualPixelInfo(image_view,(ssize_t)
3768               x_offset,(ssize_t) y_offset,&target,exception);
3769             if ((*image)->storage_class == DirectClass)
3770               {
3771                 for (y=0; y < (int) (*image)->rows; y++)
3772                 {
3773                   q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3774                     (*image)->columns,1,exception);
3775                   if (q == (Quantum *) NULL)
3776                     break;
3777                   for (x=0; x < (int) (*image)->columns; x++)
3778                   {
3779                     GetPixelInfoPixel(*image,q,&pixel);
3780                     if (IsFuzzyEquivalencePixelInfo(&pixel,&target))
3781                       {
3782                         SetPixelRed(*image,ScaleShortToQuantum(
3783                           color.red),q);
3784                         SetPixelGreen(*image,ScaleShortToQuantum(
3785                           color.green),q);
3786                         SetPixelBlue(*image,ScaleShortToQuantum(
3787                           color.blue),q);
3788                       }
3789                     q+=GetPixelChannels(*image);
3790                   }
3791                   if( IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
3792                     break;
3793                 }
3794               }
3795             else
3796               {
3797                 for (i=0; i < (ssize_t) (*image)->colors; i++)
3798                   if (IsFuzzyEquivalencePixelInfo((*image)->colormap+i,&target))
3799                     {
3800                       (*image)->colormap[i].red=(double) ScaleShortToQuantum(
3801                         color.red);
3802                       (*image)->colormap[i].green=(double) ScaleShortToQuantum(
3803                         color.green);
3804                       (*image)->colormap[i].blue=(double) ScaleShortToQuantum(
3805                         color.blue);
3806                     }
3807                 (void) SyncImage(*image,exception);
3808               }
3809             break;
3810           }
3811           case FloodfillMethod:
3812           case FillToBorderMethod:
3813           {
3814             DrawInfo
3815               *draw_info;
3816
3817             PixelInfo
3818               target;
3819
3820             /*
3821               Update color information using floodfill algorithm.
3822             */
3823             (void) GetOneVirtualPixelInfo(*image,
3824               GetPixelCacheVirtualMethod(*image),(ssize_t) x_offset,(ssize_t)
3825               y_offset,&target,exception);
3826             if (method == FillToBorderMethod)
3827               {
3828                 target.red=(double)
3829                   ScaleShortToQuantum(border_color.red);
3830                 target.green=(double)
3831                   ScaleShortToQuantum(border_color.green);
3832                 target.blue=(double)
3833                   ScaleShortToQuantum(border_color.blue);
3834               }
3835             draw_info=CloneDrawInfo(resource_info->image_info,
3836               (DrawInfo *) NULL);
3837             (void) QueryColorCompliance(resource_info->pen_colors[pen_id],
3838               AllCompliance,&draw_info->fill,exception);
3839             (void) FloodfillPaintImage(*image,draw_info,&target,
3840               (ssize_t)x_offset,(ssize_t)y_offset,
3841               IsMagickFalse(method == FloodfillMethod),exception);
3842             draw_info=DestroyDrawInfo(draw_info);
3843             break;
3844           }
3845           case ResetMethod:
3846           {
3847             /*
3848               Update color information using reset algorithm.
3849             */
3850             if( IfMagickFalse(SetImageStorageClass(*image,DirectClass,exception)) )
3851               return(MagickFalse);
3852             for (y=0; y < (int) (*image)->rows; y++)
3853             {
3854               q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3855                 (*image)->columns,1,exception);
3856               if (q == (Quantum *) NULL)
3857                 break;
3858               for (x=0; x < (int) (*image)->columns; x++)
3859               {
3860                 SetPixelRed(*image,ScaleShortToQuantum(color.red),q);
3861                 SetPixelGreen(*image,ScaleShortToQuantum(color.green),q);
3862                 SetPixelBlue(*image,ScaleShortToQuantum(color.blue),q);
3863                 q+=GetPixelChannels(*image);
3864               }
3865               if( IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
3866                 break;
3867             }
3868             break;
3869           }
3870         }
3871         image_view=DestroyCacheView(image_view);
3872         state&=(~UpdateConfigurationState);
3873       }
3874   } while ((state & ExitState) == 0);
3875   (void) XSelectInput(display,windows->image.id,
3876     windows->image.attributes.event_mask);
3877   XSetCursorState(display,windows,MagickFalse);
3878   (void) XFreeCursor(display,cursor);
3879   return(MagickTrue);
3880 }
3881 \f
3882 /*
3883 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3884 %                                                                             %
3885 %                                                                             %
3886 %                                                                             %
3887 +   X C o m p o s i t e I m a g e                                             %
3888 %                                                                             %
3889 %                                                                             %
3890 %                                                                             %
3891 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3892 %
3893 %  XCompositeImage() requests an image name from the user, reads the image and
3894 %  composites it with the X window image at a location the user chooses with
3895 %  the pointer.
3896 %
3897 %  The format of the XCompositeImage method is:
3898 %
3899 %      MagickBooleanType XCompositeImage(Display *display,
3900 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
3901 %        ExceptionInfo *exception)
3902 %
3903 %  A description of each parameter follows:
3904 %
3905 %    o display: Specifies a connection to an X server;  returned from
3906 %      XOpenDisplay.
3907 %
3908 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3909 %
3910 %    o windows: Specifies a pointer to a XWindows structure.
3911 %
3912 %    o image: the image; returned from ReadImage.
3913 %
3914 %    o exception: return any errors or warnings in this structure.
3915 %
3916 */
3917 static MagickBooleanType XCompositeImage(Display *display,
3918   XResourceInfo *resource_info,XWindows *windows,Image *image,
3919   ExceptionInfo *exception)
3920 {
3921   static char
3922     displacement_geometry[MaxTextExtent] = "30x30",
3923     filename[MaxTextExtent] = "\0";
3924
3925   static const char
3926     *CompositeMenu[] =
3927     {
3928       "Operators",
3929       "Dissolve",
3930       "Displace",
3931       "Help",
3932       "Dismiss",
3933       (char *) NULL
3934     };
3935
3936   static CompositeOperator
3937     compose = CopyCompositeOp;
3938
3939   static const ModeType
3940     CompositeCommands[] =
3941     {
3942       CompositeOperatorsCommand,
3943       CompositeDissolveCommand,
3944       CompositeDisplaceCommand,
3945       CompositeHelpCommand,
3946       CompositeDismissCommand
3947     };
3948
3949   char
3950     text[MaxTextExtent];
3951
3952   Cursor
3953     cursor;
3954
3955   Image
3956     *composite_image;
3957
3958   int
3959     entry,
3960     id,
3961     x,
3962     y;
3963
3964   double
3965     blend,
3966     scale_factor;
3967
3968   RectangleInfo
3969     highlight_info,
3970     composite_info;
3971
3972   unsigned int
3973     height,
3974     width;
3975
3976   size_t
3977     state;
3978
3979   XEvent
3980     event;
3981
3982   /*
3983     Request image file name from user.
3984   */
3985   XFileBrowserWidget(display,windows,"Composite",filename);
3986   if (*filename == '\0')
3987     return(MagickTrue);
3988   /*
3989     Read image.
3990   */
3991   XSetCursorState(display,windows,MagickTrue);
3992   XCheckRefreshWindows(display,windows);
3993   (void) CopyMagickString(resource_info->image_info->filename,filename,
3994     MaxTextExtent);
3995   composite_image=ReadImage(resource_info->image_info,exception);
3996   CatchException(exception);
3997   XSetCursorState(display,windows,MagickFalse);
3998   if (composite_image == (Image *) NULL)
3999     return(MagickFalse);
4000   /*
4001     Map Command widget.
4002   */
4003   (void) CloneString(&windows->command.name,"Composite");
4004   windows->command.data=1;
4005   (void) XCommandWidget(display,windows,CompositeMenu,(XEvent *) NULL);
4006   (void) XMapRaised(display,windows->command.id);
4007   XClientMessage(display,windows->image.id,windows->im_protocols,
4008     windows->im_update_widget,CurrentTime);
4009   /*
4010     Track pointer until button 1 is pressed.
4011   */
4012   XQueryPosition(display,windows->image.id,&x,&y);
4013   (void) XSelectInput(display,windows->image.id,
4014     windows->image.attributes.event_mask | PointerMotionMask);
4015   composite_info.x=(ssize_t) windows->image.x+x;
4016   composite_info.y=(ssize_t) windows->image.y+y;
4017   composite_info.width=0;
4018   composite_info.height=0;
4019   cursor=XCreateFontCursor(display,XC_ul_angle);
4020   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
4021   blend=0.0;
4022   state=DefaultState;
4023   do
4024   {
4025     if( IfMagickTrue(windows->info.mapped) )
4026       {
4027         /*
4028           Display pointer position.
4029         */
4030         (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
4031           (long) composite_info.x,(long) composite_info.y);
4032         XInfoWidget(display,windows,text);
4033       }
4034     highlight_info=composite_info;
4035     highlight_info.x=composite_info.x-windows->image.x;
4036     highlight_info.y=composite_info.y-windows->image.y;
4037     XHighlightRectangle(display,windows->image.id,
4038       windows->image.highlight_context,&highlight_info);
4039     /*
4040       Wait for next event.
4041     */
4042     XScreenEvent(display,windows,&event,exception);
4043     XHighlightRectangle(display,windows->image.id,
4044       windows->image.highlight_context,&highlight_info);
4045     if (event.xany.window == windows->command.id)
4046       {
4047         /*
4048           Select a command from the Command widget.
4049         */
4050         id=XCommandWidget(display,windows,CompositeMenu,&event);
4051         if (id < 0)
4052           continue;
4053         switch (CompositeCommands[id])
4054         {
4055           case CompositeOperatorsCommand:
4056           {
4057             char
4058               command[MaxTextExtent],
4059               **operators;
4060
4061             /*
4062               Select a command from the pop-up menu.
4063             */
4064             operators=GetCommandOptions(MagickComposeOptions);
4065             if (operators == (char **) NULL)
4066               break;
4067             entry=XMenuWidget(display,windows,CompositeMenu[id],
4068               (const char **) operators,command);
4069             if (entry >= 0)
4070               compose=(CompositeOperator) ParseCommandOption(
4071                 MagickComposeOptions,MagickFalse,operators[entry]);
4072             operators=DestroyStringList(operators);
4073             break;
4074           }
4075           case CompositeDissolveCommand:
4076           {
4077             static char
4078               factor[MaxTextExtent] = "20.0";
4079
4080             /*
4081               Dissolve the two images a given percent.
4082             */
4083             (void) XSetFunction(display,windows->image.highlight_context,
4084               GXcopy);
4085             (void) XDialogWidget(display,windows,"Dissolve",
4086               "Enter the blend factor (0.0 - 99.9%):",factor);
4087             (void) XSetFunction(display,windows->image.highlight_context,
4088               GXinvert);
4089             if (*factor == '\0')
4090               break;
4091             blend=StringToDouble(factor,(char **) NULL);
4092             compose=DissolveCompositeOp;
4093             break;
4094           }
4095           case CompositeDisplaceCommand:
4096           {
4097             /*
4098               Get horizontal and vertical scale displacement geometry.
4099             */
4100             (void) XSetFunction(display,windows->image.highlight_context,
4101               GXcopy);
4102             (void) XDialogWidget(display,windows,"Displace",
4103               "Enter the horizontal and vertical scale:",displacement_geometry);
4104             (void) XSetFunction(display,windows->image.highlight_context,
4105               GXinvert);
4106             if (*displacement_geometry == '\0')
4107               break;
4108             compose=DisplaceCompositeOp;
4109             break;
4110           }
4111           case CompositeHelpCommand:
4112           {
4113             (void) XSetFunction(display,windows->image.highlight_context,
4114               GXcopy);
4115             XTextViewWidget(display,resource_info,windows,MagickFalse,
4116               "Help Viewer - Image Composite",ImageCompositeHelp);
4117             (void) XSetFunction(display,windows->image.highlight_context,
4118               GXinvert);
4119             break;
4120           }
4121           case CompositeDismissCommand:
4122           {
4123             /*
4124               Prematurely exit.
4125             */
4126             state|=EscapeState;
4127             state|=ExitState;
4128             break;
4129           }
4130           default:
4131             break;
4132         }
4133         continue;
4134       }
4135     switch (event.type)
4136     {
4137       case ButtonPress:
4138       {
4139         if( IfMagickTrue(image->debug) )
4140           (void) LogMagickEvent(X11Event,GetMagickModule(),
4141             "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
4142             event.xbutton.button,event.xbutton.x,event.xbutton.y);
4143         if (event.xbutton.button != Button1)
4144           break;
4145         if (event.xbutton.window != windows->image.id)
4146           break;
4147         /*
4148           Change cursor.
4149         */
4150         composite_info.width=composite_image->columns;
4151         composite_info.height=composite_image->rows;
4152         (void) XCheckDefineCursor(display,windows->image.id,cursor);
4153         composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4154         composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4155         break;
4156       }
4157       case ButtonRelease:
4158       {
4159         if( IfMagickTrue(image->debug) )
4160           (void) LogMagickEvent(X11Event,GetMagickModule(),
4161             "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
4162             event.xbutton.button,event.xbutton.x,event.xbutton.y);
4163         if (event.xbutton.button != Button1)
4164           break;
4165         if (event.xbutton.window != windows->image.id)
4166           break;
4167         if ((composite_info.width != 0) && (composite_info.height != 0))
4168           {
4169             /*
4170               User has selected the location of the composite image.
4171             */
4172             composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4173             composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4174             state|=ExitState;
4175           }
4176         break;
4177       }
4178       case Expose:
4179         break;
4180       case KeyPress:
4181       {
4182         char
4183           command[MaxTextExtent];
4184
4185         KeySym
4186           key_symbol;
4187
4188         int
4189           length;
4190
4191         if (event.xkey.window != windows->image.id)
4192           break;
4193         /*
4194           Respond to a user key press.
4195         */
4196         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
4197           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4198         *(command+length)='\0';
4199         if( IfMagickTrue(image->debug) )
4200           (void) LogMagickEvent(X11Event,GetMagickModule(),
4201             "Key press: 0x%lx (%s)",(unsigned long) key_symbol,command);
4202         switch ((int) key_symbol)
4203         {
4204           case XK_Escape:
4205           case XK_F20:
4206           {
4207             /*
4208               Prematurely exit.
4209             */
4210             composite_image=DestroyImage(composite_image);
4211             state|=EscapeState;
4212             state|=ExitState;
4213             break;
4214           }
4215           case XK_F1:
4216           case XK_Help:
4217           {
4218             (void) XSetFunction(display,windows->image.highlight_context,
4219               GXcopy);
4220             XTextViewWidget(display,resource_info,windows,MagickFalse,
4221               "Help Viewer - Image Composite",ImageCompositeHelp);
4222             (void) XSetFunction(display,windows->image.highlight_context,
4223               GXinvert);
4224             break;
4225           }
4226           default:
4227           {
4228             (void) XBell(display,0);
4229             break;
4230           }
4231         }
4232         break;
4233       }
4234       case MotionNotify:
4235       {
4236         /*
4237           Map and unmap Info widget as text cursor crosses its boundaries.
4238         */
4239         x=event.xmotion.x;
4240         y=event.xmotion.y;
4241         if( IfMagickTrue(windows->info.mapped) )
4242           {
4243             if ((x < (int) (windows->info.x+windows->info.width)) &&
4244                 (y < (int) (windows->info.y+windows->info.height)))
4245               (void) XWithdrawWindow(display,windows->info.id,
4246                 windows->info.screen);
4247           }
4248         else
4249           if ((x > (int) (windows->info.x+windows->info.width)) ||
4250               (y > (int) (windows->info.y+windows->info.height)))
4251             (void) XMapWindow(display,windows->info.id);
4252         composite_info.x=(ssize_t) windows->image.x+x;
4253         composite_info.y=(ssize_t) windows->image.y+y;
4254         break;
4255       }
4256       default:
4257       {
4258         if( IfMagickTrue(image->debug) )
4259           (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
4260             event.type);
4261         break;
4262       }
4263     }
4264   } while ((state & ExitState) == 0);
4265   (void) XSelectInput(display,windows->image.id,
4266     windows->image.attributes.event_mask);
4267   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
4268   XSetCursorState(display,windows,MagickFalse);
4269   (void) XFreeCursor(display,cursor);
4270   if ((state & EscapeState) != 0)
4271     return(MagickTrue);
4272   /*
4273     Image compositing is relative to image configuration.
4274   */
4275   XSetCursorState(display,windows,MagickTrue);
4276   XCheckRefreshWindows(display,windows);
4277   width=(unsigned int) image->columns;
4278   height=(unsigned int) image->rows;
4279   x=0;
4280   y=0;
4281   if (windows->image.crop_geometry != (char *) NULL)
4282     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
4283   scale_factor=(double) width/windows->image.ximage->width;
4284   composite_info.x+=x;
4285   composite_info.x=(ssize_t) (scale_factor*composite_info.x+0.5);
4286   composite_info.width=(unsigned int) (scale_factor*composite_info.width+0.5);
4287   scale_factor=(double) height/windows->image.ximage->height;
4288   composite_info.y+=y;
4289   composite_info.y=(ssize_t) (scale_factor*composite_info.y+0.5);
4290   composite_info.height=(unsigned int) (scale_factor*composite_info.height+0.5);
4291   if ((composite_info.width != composite_image->columns) ||
4292       (composite_info.height != composite_image->rows))
4293     {
4294       Image
4295         *resize_image;
4296
4297       /*
4298         Scale composite image.
4299       */
4300       resize_image=ResizeImage(composite_image,composite_info.width,
4301         composite_info.height,composite_image->filter,exception);
4302       composite_image=DestroyImage(composite_image);
4303       if (resize_image == (Image *) NULL)
4304         {
4305           XSetCursorState(display,windows,MagickFalse);
4306           return(MagickFalse);
4307         }
4308       composite_image=resize_image;
4309     }
4310   if (compose == DisplaceCompositeOp)
4311     (void) SetImageArtifact(composite_image,"compose:args",
4312       displacement_geometry);
4313   if (blend != 0.0)
4314     {
4315       CacheView
4316         *image_view;
4317
4318       int
4319         y;
4320
4321       Quantum
4322         opacity;
4323
4324       register int
4325         x;
4326
4327       register Quantum
4328         *q;
4329
4330       /*
4331         Create mattes for blending.
4332       */
4333       (void) SetImageAlphaChannel(composite_image,OpaqueAlphaChannel,exception);
4334       opacity=(Quantum) (ScaleQuantumToChar(QuantumRange)-
4335         ((ssize_t) ScaleQuantumToChar(QuantumRange)*blend)/100);
4336       if( IfMagickFalse(SetImageStorageClass(image,DirectClass,exception)) )
4337         return(MagickFalse);
4338       image->alpha_trait=BlendPixelTrait;
4339       image_view=AcquireAuthenticCacheView(image,exception);
4340       for (y=0; y < (int) image->rows; y++)
4341       {
4342         q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,image->columns,1,
4343           exception);
4344         if (q == (Quantum *) NULL)
4345           break;
4346         for (x=0; x < (int) image->columns; x++)
4347         {
4348           SetPixelAlpha(image,opacity,q);
4349           q+=GetPixelChannels(image);
4350         }
4351         if( IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
4352           break;
4353       }
4354       image_view=DestroyCacheView(image_view);
4355     }
4356   /*
4357     Composite image with X Image window.
4358   */
4359   (void) CompositeImage(image,composite_image,compose,MagickTrue,
4360     composite_info.x,composite_info.y,exception);
4361   composite_image=DestroyImage(composite_image);
4362   XSetCursorState(display,windows,MagickFalse);
4363   /*
4364     Update image configuration.
4365   */
4366   XConfigureImageColormap(display,resource_info,windows,image,exception);
4367   (void) XConfigureImage(display,resource_info,windows,image,exception);
4368   return(MagickTrue);
4369 }
4370 \f
4371 /*
4372 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4373 %                                                                             %
4374 %                                                                             %
4375 %                                                                             %
4376 +   X C o n f i g u r e I m a g e                                             %
4377 %                                                                             %
4378 %                                                                             %
4379 %                                                                             %
4380 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4381 %
4382 %  XConfigureImage() creates a new X image.  It also notifies the window
4383 %  manager of the new image size and configures the transient widows.
4384 %
4385 %  The format of the XConfigureImage method is:
4386 %
4387 %      MagickBooleanType XConfigureImage(Display *display,
4388 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
4389 %        ExceptionInfo *exception)
4390 %
4391 %  A description of each parameter follows:
4392 %
4393 %    o display: Specifies a connection to an X server; returned from
4394 %      XOpenDisplay.
4395 %
4396 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4397 %
4398 %    o windows: Specifies a pointer to a XWindows structure.
4399 %
4400 %    o image: the image.
4401 %
4402 %    o exception: return any errors or warnings in this structure.
4403 %
4404 %    o exception: return any errors or warnings in this structure.
4405 %
4406 */
4407 static MagickBooleanType XConfigureImage(Display *display,
4408   XResourceInfo *resource_info,XWindows *windows,Image *image,
4409   ExceptionInfo *exception)
4410 {
4411   char
4412     geometry[MaxTextExtent];
4413
4414   MagickStatusType
4415     status;
4416
4417   size_t
4418     mask,
4419     height,
4420     width;
4421
4422   ssize_t
4423     x,
4424     y;
4425
4426   XSizeHints
4427     *size_hints;
4428
4429   XWindowChanges
4430     window_changes;
4431
4432   /*
4433     Dismiss if window dimensions are zero.
4434   */
4435   width=(unsigned int) windows->image.window_changes.width;
4436   height=(unsigned int) windows->image.window_changes.height;
4437   if( IfMagickTrue(image->debug) )
4438     (void) LogMagickEvent(X11Event,GetMagickModule(),
4439       "Configure Image: %dx%d=>%.20gx%.20g",windows->image.ximage->width,
4440       windows->image.ximage->height,(double) width,(double) height);
4441   if ((width*height) == 0)
4442     return(MagickTrue);
4443   x=0;
4444   y=0;
4445   /*
4446     Resize image to fit Image window dimensions.
4447   */
4448   XSetCursorState(display,windows,MagickTrue);
4449   (void) XFlush(display);
4450   if (((int) width != windows->image.ximage->width) ||
4451       ((int) height != windows->image.ximage->height))
4452     image->taint=MagickTrue;
4453   windows->magnify.x=(int)
4454     width*windows->magnify.x/windows->image.ximage->width;
4455   windows->magnify.y=(int)
4456     height*windows->magnify.y/windows->image.ximage->height;
4457   windows->image.x=(int) (width*windows->image.x/windows->image.ximage->width);
4458   windows->image.y=(int)
4459     (height*windows->image.y/windows->image.ximage->height);
4460   status=XMakeImage(display,resource_info,&windows->image,image,
4461     (unsigned int) width,(unsigned int) height,exception);
4462   if( IfMagickFalse(status) )
4463     XNoticeWidget(display,windows,"Unable to configure X image:",
4464       windows->image.name);
4465   /*
4466     Notify window manager of the new configuration.
4467   */
4468   if (resource_info->image_geometry != (char *) NULL)
4469     (void) FormatLocaleString(geometry,MaxTextExtent,"%s>!",
4470       resource_info->image_geometry);
4471   else
4472     (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>!",
4473       XDisplayWidth(display,windows->image.screen),
4474       XDisplayHeight(display,windows->image.screen));
4475   (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
4476   window_changes.width=(int) width;
4477   if (window_changes.width > XDisplayWidth(display,windows->image.screen))
4478     window_changes.width=XDisplayWidth(display,windows->image.screen);
4479   window_changes.height=(int) height;
4480   if (window_changes.height > XDisplayHeight(display,windows->image.screen))
4481     window_changes.height=XDisplayHeight(display,windows->image.screen);
4482   mask=(size_t) (CWWidth | CWHeight);
4483   if (resource_info->backdrop)
4484     {
4485       mask|=CWX | CWY;
4486       window_changes.x=(int)
4487         ((XDisplayWidth(display,windows->image.screen)/2)-(width/2));
4488       window_changes.y=(int)
4489         ((XDisplayHeight(display,windows->image.screen)/2)-(height/2));
4490     }
4491   (void) XReconfigureWMWindow(display,windows->image.id,windows->image.screen,
4492     (unsigned int) mask,&window_changes);
4493   (void) XClearWindow(display,windows->image.id);
4494   XRefreshWindow(display,&windows->image,(XEvent *) NULL);
4495   /*
4496     Update Magnify window configuration.
4497   */
4498   if( IfMagickTrue(windows->magnify.mapped) )
4499     XMakeMagnifyImage(display,windows,exception);
4500   windows->pan.crop_geometry=windows->image.crop_geometry;
4501   XBestIconSize(display,&windows->pan,image);
4502   while (((windows->pan.width << 1) < MaxIconSize) &&
4503          ((windows->pan.height << 1) < MaxIconSize))
4504   {
4505     windows->pan.width<<=1;
4506     windows->pan.height<<=1;
4507   }
4508   if (windows->pan.geometry != (char *) NULL)
4509     (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
4510       &windows->pan.width,&windows->pan.height);
4511   window_changes.width=(int) windows->pan.width;
4512   window_changes.height=(int) windows->pan.height;
4513   size_hints=XAllocSizeHints();
4514   if (size_hints != (XSizeHints *) NULL)
4515     {
4516       /*
4517         Set new size hints.
4518       */
4519       size_hints->flags=PSize | PMinSize | PMaxSize;
4520       size_hints->width=window_changes.width;
4521       size_hints->height=window_changes.height;
4522       size_hints->min_width=size_hints->width;
4523       size_hints->min_height=size_hints->height;
4524       size_hints->max_width=size_hints->width;
4525       size_hints->max_height=size_hints->height;
4526       (void) XSetNormalHints(display,windows->pan.id,size_hints);
4527       (void) XFree((void *) size_hints);
4528     }
4529   (void) XReconfigureWMWindow(display,windows->pan.id,windows->pan.screen,
4530     (unsigned int) (CWWidth | CWHeight),&window_changes);
4531   /*
4532     Update icon window configuration.
4533   */
4534   windows->icon.crop_geometry=windows->image.crop_geometry;
4535   XBestIconSize(display,&windows->icon,image);
4536   window_changes.width=(int) windows->icon.width;
4537   window_changes.height=(int) windows->icon.height;
4538   (void) XReconfigureWMWindow(display,windows->icon.id,windows->icon.screen,
4539     (unsigned int) (CWWidth | CWHeight),&window_changes);
4540   XSetCursorState(display,windows,MagickFalse);
4541   return(IsMagickTrue(status));
4542 }
4543 \f
4544 /*
4545 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4546 %                                                                             %
4547 %                                                                             %
4548 %                                                                             %
4549 +   X C r o p I m a g e                                                       %
4550 %                                                                             %
4551 %                                                                             %
4552 %                                                                             %
4553 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4554 %
4555 %  XCropImage() allows the user to select a region of the image and crop, copy,
4556 %  or cut it.  For copy or cut, the image can subsequently be composited onto
4557 %  the image with XPasteImage.
4558 %
4559 %  The format of the XCropImage method is:
4560 %
4561 %      MagickBooleanType XCropImage(Display *display,
4562 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
4563 %        const ClipboardMode mode,ExceptionInfo *exception)
4564 %
4565 %  A description of each parameter follows:
4566 %
4567 %    o display: Specifies a connection to an X server; returned from
4568 %      XOpenDisplay.
4569 %
4570 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4571 %
4572 %    o windows: Specifies a pointer to a XWindows structure.
4573 %
4574 %    o image: the image; returned from ReadImage.
4575 %
4576 %    o mode: This unsigned value specified whether the image should be
4577 %      cropped, copied, or cut.
4578 %
4579 %    o exception: return any errors or warnings in this structure.
4580 %
4581 */
4582 static MagickBooleanType XCropImage(Display *display,
4583   XResourceInfo *resource_info,XWindows *windows,Image *image,
4584   const ClipboardMode mode,ExceptionInfo *exception)
4585 {
4586   static const char
4587     *CropModeMenu[] =
4588     {
4589       "Help",
4590       "Dismiss",
4591       (char *) NULL
4592     },
4593     *RectifyModeMenu[] =
4594     {
4595       "Crop",
4596       "Help",
4597       "Dismiss",
4598       (char *) NULL
4599     };
4600
4601   static const ModeType
4602     CropCommands[] =
4603     {
4604       CropHelpCommand,
4605       CropDismissCommand
4606     },
4607     RectifyCommands[] =
4608     {
4609       RectifyCopyCommand,
4610       RectifyHelpCommand,
4611       RectifyDismissCommand
4612     };
4613
4614   CacheView
4615     *image_view;
4616
4617   char
4618     command[MaxTextExtent],
4619     text[MaxTextExtent];
4620
4621   Cursor
4622     cursor;
4623
4624   int
4625     id,
4626     x,
4627     y;
4628
4629   KeySym
4630     key_symbol;
4631
4632   Image
4633     *crop_image;
4634
4635   double
4636     scale_factor;
4637
4638   RectangleInfo
4639     crop_info,
4640     highlight_info;
4641
4642   register Quantum
4643     *q;
4644
4645   unsigned int
4646     height,
4647     width;
4648
4649   size_t
4650     state;
4651
4652   XEvent
4653     event;
4654
4655   /*
4656     Map Command widget.
4657   */
4658   switch (mode)
4659   {
4660     case CopyMode:
4661     {
4662       (void) CloneString(&windows->command.name,"Copy");
4663       break;
4664     }
4665     case CropMode:
4666     {
4667       (void) CloneString(&windows->command.name,"Crop");
4668       break;
4669     }
4670     case CutMode:
4671     {
4672       (void) CloneString(&windows->command.name,"Cut");
4673       break;
4674     }
4675   }
4676   RectifyModeMenu[0]=windows->command.name;
4677   windows->command.data=0;
4678   (void) XCommandWidget(display,windows,CropModeMenu,(XEvent *) NULL);
4679   (void) XMapRaised(display,windows->command.id);
4680   XClientMessage(display,windows->image.id,windows->im_protocols,
4681     windows->im_update_widget,CurrentTime);
4682   /*
4683     Track pointer until button 1 is pressed.
4684   */
4685   XQueryPosition(display,windows->image.id,&x,&y);
4686   (void) XSelectInput(display,windows->image.id,
4687     windows->image.attributes.event_mask | PointerMotionMask);
4688   crop_info.x=(ssize_t) windows->image.x+x;
4689   crop_info.y=(ssize_t) windows->image.y+y;
4690   crop_info.width=0;
4691   crop_info.height=0;
4692   cursor=XCreateFontCursor(display,XC_fleur);
4693   state=DefaultState;
4694   do
4695   {
4696     if( IfMagickTrue(windows->info.mapped) )
4697       {
4698         /*
4699           Display pointer position.
4700         */
4701         (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
4702           (long) crop_info.x,(long) crop_info.y);
4703         XInfoWidget(display,windows,text);
4704       }
4705     /*
4706       Wait for next event.
4707     */
4708     XScreenEvent(display,windows,&event,exception);
4709     if (event.xany.window == windows->command.id)
4710       {
4711         /*
4712           Select a command from the Command widget.
4713         */
4714         id=XCommandWidget(display,windows,CropModeMenu,&event);
4715         if (id < 0)
4716           continue;
4717         switch (CropCommands[id])
4718         {
4719           case CropHelpCommand:
4720           {
4721             switch (mode)
4722             {
4723               case CopyMode:
4724               {
4725                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4726                   "Help Viewer - Image Copy",ImageCopyHelp);
4727                 break;
4728               }
4729               case CropMode:
4730               {
4731                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4732                   "Help Viewer - Image Crop",ImageCropHelp);
4733                 break;
4734               }
4735               case CutMode:
4736               {
4737                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4738                   "Help Viewer - Image Cut",ImageCutHelp);
4739                 break;
4740               }
4741             }
4742             break;
4743           }
4744           case CropDismissCommand:
4745           {
4746             /*
4747               Prematurely exit.
4748             */
4749             state|=EscapeState;
4750             state|=ExitState;
4751             break;
4752           }
4753           default:
4754             break;
4755         }
4756         continue;
4757       }
4758     switch (event.type)
4759     {
4760       case ButtonPress:
4761       {
4762         if (event.xbutton.button != Button1)
4763           break;
4764         if (event.xbutton.window != windows->image.id)
4765           break;
4766         /*
4767           Note first corner of cropping rectangle-- exit loop.
4768         */
4769         (void) XCheckDefineCursor(display,windows->image.id,cursor);
4770         crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4771         crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4772         state|=ExitState;
4773         break;
4774       }
4775       case ButtonRelease:
4776         break;
4777       case Expose:
4778         break;
4779       case KeyPress:
4780       {
4781         if (event.xkey.window != windows->image.id)
4782           break;
4783         /*
4784           Respond to a user key press.
4785         */
4786         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
4787           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4788         switch ((int) key_symbol)
4789         {
4790           case XK_Escape:
4791           case XK_F20:
4792           {
4793             /*
4794               Prematurely exit.
4795             */
4796             state|=EscapeState;
4797             state|=ExitState;
4798             break;
4799           }
4800           case XK_F1:
4801           case XK_Help:
4802           {
4803             switch (mode)
4804             {
4805               case CopyMode:
4806               {
4807                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4808                   "Help Viewer - Image Copy",ImageCopyHelp);
4809                 break;
4810               }
4811               case CropMode:
4812               {
4813                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4814                   "Help Viewer - Image Crop",ImageCropHelp);
4815                 break;
4816               }
4817               case CutMode:
4818               {
4819                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4820                   "Help Viewer - Image Cut",ImageCutHelp);
4821                 break;
4822               }
4823             }
4824             break;
4825           }
4826           default:
4827           {
4828             (void) XBell(display,0);
4829             break;
4830           }
4831         }
4832         break;
4833       }
4834       case MotionNotify:
4835       {
4836         if (event.xmotion.window != windows->image.id)
4837           break;
4838         /*
4839           Map and unmap Info widget as text cursor crosses its boundaries.
4840         */
4841         x=event.xmotion.x;
4842         y=event.xmotion.y;
4843         if( IfMagickTrue(windows->info.mapped) )
4844           {
4845             if ((x < (int) (windows->info.x+windows->info.width)) &&
4846                 (y < (int) (windows->info.y+windows->info.height)))
4847               (void) XWithdrawWindow(display,windows->info.id,
4848                 windows->info.screen);
4849           }
4850         else
4851           if ((x > (int) (windows->info.x+windows->info.width)) ||
4852               (y > (int) (windows->info.y+windows->info.height)))
4853             (void) XMapWindow(display,windows->info.id);
4854         crop_info.x=(ssize_t) windows->image.x+x;
4855         crop_info.y=(ssize_t) windows->image.y+y;
4856         break;
4857       }
4858       default:
4859         break;
4860     }
4861   } while ((state & ExitState) == 0);
4862   (void) XSelectInput(display,windows->image.id,
4863     windows->image.attributes.event_mask);
4864   if ((state & EscapeState) != 0)
4865     {
4866       /*
4867         User want to exit without cropping.
4868       */
4869       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4870       (void) XFreeCursor(display,cursor);
4871       return(MagickTrue);
4872     }
4873   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
4874   do
4875   {
4876     /*
4877       Size rectangle as pointer moves until the mouse button is released.
4878     */
4879     x=(int) crop_info.x;
4880     y=(int) crop_info.y;
4881     crop_info.width=0;
4882     crop_info.height=0;
4883     state=DefaultState;
4884     do
4885     {
4886       highlight_info=crop_info;
4887       highlight_info.x=crop_info.x-windows->image.x;
4888       highlight_info.y=crop_info.y-windows->image.y;
4889       if ((highlight_info.width > 3) && (highlight_info.height > 3))
4890         {
4891           /*
4892             Display info and draw cropping rectangle.
4893           */
4894           if( IfMagickFalse(windows->info.mapped) )
4895             (void) XMapWindow(display,windows->info.id);
4896           (void) FormatLocaleString(text,MaxTextExtent,
4897             " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
4898             crop_info.height,(double) crop_info.x,(double) crop_info.y);
4899           XInfoWidget(display,windows,text);
4900           XHighlightRectangle(display,windows->image.id,
4901             windows->image.highlight_context,&highlight_info);
4902         }
4903       else
4904         if( IfMagickTrue(windows->info.mapped) )
4905           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4906       /*
4907         Wait for next event.
4908       */
4909       XScreenEvent(display,windows,&event,exception);
4910       if ((highlight_info.width > 3) && (highlight_info.height > 3))
4911         XHighlightRectangle(display,windows->image.id,
4912           windows->image.highlight_context,&highlight_info);
4913       switch (event.type)
4914       {
4915         case ButtonPress:
4916         {
4917           crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4918           crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4919           break;
4920         }
4921         case ButtonRelease:
4922         {
4923           /*
4924             User has committed to cropping rectangle.
4925           */
4926           crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4927           crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4928           XSetCursorState(display,windows,MagickFalse);
4929           state|=ExitState;
4930           windows->command.data=0;
4931           (void) XCommandWidget(display,windows,RectifyModeMenu,
4932             (XEvent *) NULL);
4933           break;
4934         }
4935         case Expose:
4936           break;
4937         case MotionNotify:
4938         {
4939           crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
4940           crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
4941         }
4942         default:
4943           break;
4944       }
4945       if ((((int) crop_info.x != x) && ((int) crop_info.y != y)) ||
4946           ((state & ExitState) != 0))
4947         {
4948           /*
4949             Check boundary conditions.
4950           */
4951           if (crop_info.x < 0)
4952             crop_info.x=0;
4953           else
4954             if (crop_info.x > (ssize_t) windows->image.ximage->width)
4955               crop_info.x=(ssize_t) windows->image.ximage->width;
4956           if ((int) crop_info.x < x)
4957             crop_info.width=(unsigned int) (x-crop_info.x);
4958           else
4959             {
4960               crop_info.width=(unsigned int) (crop_info.x-x);
4961               crop_info.x=(ssize_t) x;
4962             }
4963           if (crop_info.y < 0)
4964             crop_info.y=0;
4965           else
4966             if (crop_info.y > (ssize_t) windows->image.ximage->height)
4967               crop_info.y=(ssize_t) windows->image.ximage->height;
4968           if ((int) crop_info.y < y)
4969             crop_info.height=(unsigned int) (y-crop_info.y);
4970           else
4971             {
4972               crop_info.height=(unsigned int) (crop_info.y-y);
4973               crop_info.y=(ssize_t) y;
4974             }
4975         }
4976     } while ((state & ExitState) == 0);
4977     /*
4978       Wait for user to grab a corner of the rectangle or press return.
4979     */
4980     state=DefaultState;
4981     (void) XMapWindow(display,windows->info.id);
4982     do
4983     {
4984       if( IfMagickTrue(windows->info.mapped) )
4985         {
4986           /*
4987             Display pointer position.
4988           */
4989           (void) FormatLocaleString(text,MaxTextExtent,
4990             " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
4991             crop_info.height,(double) crop_info.x,(double) crop_info.y);
4992           XInfoWidget(display,windows,text);
4993         }
4994       highlight_info=crop_info;
4995       highlight_info.x=crop_info.x-windows->image.x;
4996       highlight_info.y=crop_info.y-windows->image.y;
4997       if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
4998         {
4999           state|=EscapeState;
5000           state|=ExitState;
5001           break;
5002         }
5003       XHighlightRectangle(display,windows->image.id,
5004         windows->image.highlight_context,&highlight_info);
5005       XScreenEvent(display,windows,&event,exception);
5006       if (event.xany.window == windows->command.id)
5007         {
5008           /*
5009             Select a command from the Command widget.
5010           */
5011           (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
5012           id=XCommandWidget(display,windows,RectifyModeMenu,&event);
5013           (void) XSetFunction(display,windows->image.highlight_context,
5014             GXinvert);
5015           XHighlightRectangle(display,windows->image.id,
5016             windows->image.highlight_context,&highlight_info);
5017           if (id >= 0)
5018             switch (RectifyCommands[id])
5019             {
5020               case RectifyCopyCommand:
5021               {
5022                 state|=ExitState;
5023                 break;
5024               }
5025               case RectifyHelpCommand:
5026               {
5027                 (void) XSetFunction(display,windows->image.highlight_context,
5028                   GXcopy);
5029                 switch (mode)
5030                 {
5031                   case CopyMode:
5032                   {
5033                     XTextViewWidget(display,resource_info,windows,MagickFalse,
5034                       "Help Viewer - Image Copy",ImageCopyHelp);
5035                     break;
5036                   }
5037                   case CropMode:
5038                   {
5039                     XTextViewWidget(display,resource_info,windows,MagickFalse,
5040                       "Help Viewer - Image Crop",ImageCropHelp);
5041                     break;
5042                   }
5043                   case CutMode:
5044                   {
5045                     XTextViewWidget(display,resource_info,windows,MagickFalse,
5046                       "Help Viewer - Image Cut",ImageCutHelp);
5047                     break;
5048                   }
5049                 }
5050                 (void) XSetFunction(display,windows->image.highlight_context,
5051                   GXinvert);
5052                 break;
5053               }
5054               case RectifyDismissCommand:
5055               {
5056                 /*
5057                   Prematurely exit.
5058                 */
5059                 state|=EscapeState;
5060                 state|=ExitState;
5061                 break;
5062               }
5063               default:
5064                 break;
5065             }
5066           continue;
5067         }
5068       XHighlightRectangle(display,windows->image.id,
5069         windows->image.highlight_context,&highlight_info);
5070       switch (event.type)
5071       {
5072         case ButtonPress:
5073         {
5074           if (event.xbutton.button != Button1)
5075             break;
5076           if (event.xbutton.window != windows->image.id)
5077             break;
5078           x=windows->image.x+event.xbutton.x;
5079           y=windows->image.y+event.xbutton.y;
5080           if ((x < (int) (crop_info.x+RoiDelta)) &&
5081               (x > (int) (crop_info.x-RoiDelta)) &&
5082               (y < (int) (crop_info.y+RoiDelta)) &&
5083               (y > (int) (crop_info.y-RoiDelta)))
5084             {
5085               crop_info.x=(ssize_t) (crop_info.x+crop_info.width);
5086               crop_info.y=(ssize_t) (crop_info.y+crop_info.height);
5087               state|=UpdateConfigurationState;
5088               break;
5089             }
5090           if ((x < (int) (crop_info.x+RoiDelta)) &&
5091               (x > (int) (crop_info.x-RoiDelta)) &&
5092               (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5093               (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5094             {
5095               crop_info.x=(ssize_t) (crop_info.x+crop_info.width);
5096               state|=UpdateConfigurationState;
5097               break;
5098             }
5099           if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5100               (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5101               (y < (int) (crop_info.y+RoiDelta)) &&
5102               (y > (int) (crop_info.y-RoiDelta)))
5103             {
5104               crop_info.y=(ssize_t) (crop_info.y+crop_info.height);
5105               state|=UpdateConfigurationState;
5106               break;
5107             }
5108           if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5109               (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5110               (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5111               (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5112             {
5113               state|=UpdateConfigurationState;
5114               break;
5115             }
5116         }
5117         case ButtonRelease:
5118         {
5119           if (event.xbutton.window == windows->pan.id)
5120             if ((highlight_info.x != crop_info.x-windows->image.x) ||
5121                 (highlight_info.y != crop_info.y-windows->image.y))
5122               XHighlightRectangle(display,windows->image.id,
5123                 windows->image.highlight_context,&highlight_info);
5124           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5125             event.xbutton.time);
5126           break;
5127         }
5128         case Expose:
5129         {
5130           if (event.xexpose.window == windows->image.id)
5131             if (event.xexpose.count == 0)
5132               {
5133                 event.xexpose.x=(int) highlight_info.x;
5134                 event.xexpose.y=(int) highlight_info.y;
5135                 event.xexpose.width=(int) highlight_info.width;
5136                 event.xexpose.height=(int) highlight_info.height;
5137                 XRefreshWindow(display,&windows->image,&event);
5138               }
5139           if (event.xexpose.window == windows->info.id)
5140             if (event.xexpose.count == 0)
5141               XInfoWidget(display,windows,text);
5142           break;
5143         }
5144         case KeyPress:
5145         {
5146           if (event.xkey.window != windows->image.id)
5147             break;
5148           /*
5149             Respond to a user key press.
5150           */
5151           (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5152             sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5153           switch ((int) key_symbol)
5154           {
5155             case XK_Escape:
5156             case XK_F20:
5157               state|=EscapeState;
5158             case XK_Return:
5159             {
5160               state|=ExitState;
5161               break;
5162             }
5163             case XK_Home:
5164             case XK_KP_Home:
5165             {
5166               crop_info.x=(ssize_t) (windows->image.width/2L-
5167                 crop_info.width/2L);
5168               crop_info.y=(ssize_t) (windows->image.height/2L-
5169                 crop_info.height/2L);
5170               break;
5171             }
5172             case XK_Left:
5173             case XK_KP_Left:
5174             {
5175               crop_info.x--;
5176               break;
5177             }
5178             case XK_Up:
5179             case XK_KP_Up:
5180             case XK_Next:
5181             {
5182               crop_info.y--;
5183               break;
5184             }
5185             case XK_Right:
5186             case XK_KP_Right:
5187             {
5188               crop_info.x++;
5189               break;
5190             }
5191             case XK_Prior:
5192             case XK_Down:
5193             case XK_KP_Down:
5194             {
5195               crop_info.y++;
5196               break;
5197             }
5198             case XK_F1:
5199             case XK_Help:
5200             {
5201               (void) XSetFunction(display,windows->image.highlight_context,
5202                 GXcopy);
5203               switch (mode)
5204               {
5205                 case CopyMode:
5206                 {
5207                   XTextViewWidget(display,resource_info,windows,MagickFalse,
5208                     "Help Viewer - Image Copy",ImageCopyHelp);
5209                   break;
5210                 }
5211                 case CropMode:
5212                 {
5213                   XTextViewWidget(display,resource_info,windows,MagickFalse,
5214                     "Help Viewer - Image Cropg",ImageCropHelp);
5215                   break;
5216                 }
5217                 case CutMode:
5218                 {
5219                   XTextViewWidget(display,resource_info,windows,MagickFalse,
5220                     "Help Viewer - Image Cutg",ImageCutHelp);
5221                   break;
5222                 }
5223               }
5224               (void) XSetFunction(display,windows->image.highlight_context,
5225                 GXinvert);
5226               break;
5227             }
5228             default:
5229             {
5230               (void) XBell(display,0);
5231               break;
5232             }
5233           }
5234           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5235             event.xkey.time);
5236           break;
5237         }
5238         case KeyRelease:
5239           break;
5240         case MotionNotify:
5241         {
5242           if (event.xmotion.window != windows->image.id)
5243             break;
5244           /*
5245             Map and unmap Info widget as text cursor crosses its boundaries.
5246           */
5247           x=event.xmotion.x;
5248           y=event.xmotion.y;
5249           if( IfMagickTrue(windows->info.mapped) )
5250             {
5251               if ((x < (int) (windows->info.x+windows->info.width)) &&
5252                   (y < (int) (windows->info.y+windows->info.height)))
5253                 (void) XWithdrawWindow(display,windows->info.id,
5254                   windows->info.screen);
5255             }
5256           else
5257             if ((x > (int) (windows->info.x+windows->info.width)) ||
5258                 (y > (int) (windows->info.y+windows->info.height)))
5259               (void) XMapWindow(display,windows->info.id);
5260           crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
5261           crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
5262           break;
5263         }
5264         case SelectionRequest:
5265         {
5266           XSelectionEvent
5267             notify;
5268
5269           XSelectionRequestEvent
5270             *request;
5271
5272           /*
5273             Set primary selection.
5274           */
5275           (void) FormatLocaleString(text,MaxTextExtent,
5276             "%.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
5277             crop_info.height,(double) crop_info.x,(double) crop_info.y);
5278           request=(&(event.xselectionrequest));
5279           (void) XChangeProperty(request->display,request->requestor,
5280             request->property,request->target,8,PropModeReplace,
5281             (unsigned char *) text,(int) strlen(text));
5282           notify.type=SelectionNotify;
5283           notify.display=request->display;
5284           notify.requestor=request->requestor;
5285           notify.selection=request->selection;
5286           notify.target=request->target;
5287           notify.time=request->time;
5288           if (request->property == None)
5289             notify.property=request->target;
5290           else
5291             notify.property=request->property;
5292           (void) XSendEvent(request->display,request->requestor,False,0,
5293             (XEvent *) &notify);
5294         }
5295         default:
5296           break;
5297       }
5298       if ((state & UpdateConfigurationState) != 0)
5299         {
5300           (void) XPutBackEvent(display,&event);
5301           (void) XCheckDefineCursor(display,windows->image.id,cursor);
5302           break;
5303         }
5304     } while ((state & ExitState) == 0);
5305   } while ((state & ExitState) == 0);
5306   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
5307   XSetCursorState(display,windows,MagickFalse);
5308   if ((state & EscapeState) != 0)
5309     return(MagickTrue);
5310   if (mode == CropMode)
5311     if (((int) crop_info.width != windows->image.ximage->width) ||
5312         ((int) crop_info.height != windows->image.ximage->height))
5313       {
5314         /*
5315           Reconfigure Image window as defined by cropping rectangle.
5316         */
5317         XSetCropGeometry(display,windows,&crop_info,image);
5318         windows->image.window_changes.width=(int) crop_info.width;
5319         windows->image.window_changes.height=(int) crop_info.height;
5320         (void) XConfigureImage(display,resource_info,windows,image,exception);
5321         return(MagickTrue);
5322       }
5323   /*
5324     Copy image before applying image transforms.
5325   */
5326   XSetCursorState(display,windows,MagickTrue);
5327   XCheckRefreshWindows(display,windows);
5328   width=(unsigned int) image->columns;
5329   height=(unsigned int) image->rows;
5330   x=0;
5331   y=0;
5332   if (windows->image.crop_geometry != (char *) NULL)
5333     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
5334   scale_factor=(double) width/windows->image.ximage->width;
5335   crop_info.x+=x;
5336   crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
5337   crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
5338   scale_factor=(double) height/windows->image.ximage->height;
5339   crop_info.y+=y;
5340   crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
5341   crop_info.height=(unsigned int) (scale_factor*crop_info.height+0.5);
5342   crop_image=CropImage(image,&crop_info,exception);
5343   XSetCursorState(display,windows,MagickFalse);
5344   if (crop_image == (Image *) NULL)
5345     return(MagickFalse);
5346   if (resource_info->copy_image != (Image *) NULL)
5347     resource_info->copy_image=DestroyImage(resource_info->copy_image);
5348   resource_info->copy_image=crop_image;
5349   if (mode == CopyMode)
5350     {
5351       (void) XConfigureImage(display,resource_info,windows,image,exception);
5352       return(MagickTrue);
5353     }
5354   /*
5355     Cut image.
5356   */
5357   if( IfMagickFalse(SetImageStorageClass(image,DirectClass,exception)) )
5358     return(MagickFalse);
5359   image->alpha_trait=BlendPixelTrait;
5360   image_view=AcquireAuthenticCacheView(image,exception);
5361   for (y=0; y < (int) crop_info.height; y++)
5362   {
5363     q=GetCacheViewAuthenticPixels(image_view,crop_info.x,y+crop_info.y,
5364       crop_info.width,1,exception);
5365     if (q == (Quantum *) NULL)
5366       break;
5367     for (x=0; x < (int) crop_info.width; x++)
5368     {
5369       SetPixelAlpha(image,TransparentAlpha,q);
5370       q+=GetPixelChannels(image);
5371     }
5372     if( IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
5373       break;
5374   }
5375   image_view=DestroyCacheView(image_view);
5376   /*
5377     Update image configuration.
5378   */
5379   XConfigureImageColormap(display,resource_info,windows,image,exception);
5380   (void) XConfigureImage(display,resource_info,windows,image,exception);
5381   return(MagickTrue);
5382 }
5383 \f
5384 /*
5385 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5386 %                                                                             %
5387 %                                                                             %
5388 %                                                                             %
5389 +   X D r a w I m a g e                                                       %
5390 %                                                                             %
5391 %                                                                             %
5392 %                                                                             %
5393 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5394 %
5395 %  XDrawEditImage() draws a graphic element (point, line, rectangle, etc.) on
5396 %  the image.
5397 %
5398 %  The format of the XDrawEditImage method is:
5399 %
5400 %      MagickBooleanType XDrawEditImage(Display *display,
5401 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
5402 %        ExceptionInfo *exception)
5403 %
5404 %  A description of each parameter follows:
5405 %
5406 %    o display: Specifies a connection to an X server; returned from
5407 %      XOpenDisplay.
5408 %
5409 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
5410 %
5411 %    o windows: Specifies a pointer to a XWindows structure.
5412 %
5413 %    o image: the image.
5414 %
5415 %    o exception: return any errors or warnings in this structure.
5416 %
5417 */
5418 static MagickBooleanType XDrawEditImage(Display *display,
5419   XResourceInfo *resource_info,XWindows *windows,Image **image,
5420   ExceptionInfo *exception)
5421 {
5422   static const char
5423     *DrawMenu[] =
5424     {
5425       "Element",
5426       "Color",
5427       "Stipple",
5428       "Width",
5429       "Undo",
5430       "Help",
5431       "Dismiss",
5432       (char *) NULL
5433     };
5434
5435   static ElementType
5436     element = PointElement;
5437
5438   static const ModeType
5439     DrawCommands[] =
5440     {
5441       DrawElementCommand,
5442       DrawColorCommand,
5443       DrawStippleCommand,
5444       DrawWidthCommand,
5445       DrawUndoCommand,
5446       DrawHelpCommand,
5447       DrawDismissCommand
5448     };
5449
5450   static Pixmap
5451     stipple = (Pixmap) NULL;
5452
5453   static unsigned int
5454     pen_id = 0,
5455     line_width = 1;
5456
5457   char
5458     command[MaxTextExtent],
5459     text[MaxTextExtent];
5460
5461   Cursor
5462     cursor;
5463
5464   int
5465     entry,
5466     id,
5467     number_coordinates,
5468     x,
5469     y;
5470
5471   double
5472     degrees;
5473
5474   MagickStatusType
5475     status;
5476
5477   RectangleInfo
5478     rectangle_info;
5479
5480   register int
5481     i;
5482
5483   unsigned int
5484     distance,
5485     height,
5486     max_coordinates,
5487     width;
5488
5489   size_t
5490     state;
5491
5492   Window
5493     root_window;
5494
5495   XDrawInfo
5496     draw_info;
5497
5498   XEvent
5499     event;
5500
5501   XPoint
5502     *coordinate_info;
5503
5504   XSegment
5505     line_info;
5506
5507   /*
5508     Allocate polygon info.
5509   */
5510   max_coordinates=2048;
5511   coordinate_info=(XPoint *) AcquireQuantumMemory((size_t) max_coordinates,
5512     sizeof(*coordinate_info));
5513   if (coordinate_info == (XPoint *) NULL)
5514     {
5515       (void) ThrowMagickException(exception,GetMagickModule(),
5516         ResourceLimitError,"MemoryAllocationFailed","'%s'","...");
5517       return(MagickFalse);
5518     }
5519   /*
5520     Map Command widget.
5521   */
5522   (void) CloneString(&windows->command.name,"Draw");
5523   windows->command.data=4;
5524   (void) XCommandWidget(display,windows,DrawMenu,(XEvent *) NULL);
5525   (void) XMapRaised(display,windows->command.id);
5526   XClientMessage(display,windows->image.id,windows->im_protocols,
5527     windows->im_update_widget,CurrentTime);
5528   /*
5529     Wait for first button press.
5530   */
5531   root_window=XRootWindow(display,XDefaultScreen(display));
5532   draw_info.stencil=OpaqueStencil;
5533   status=MagickTrue;
5534   cursor=XCreateFontCursor(display,XC_tcross);
5535   for ( ; ; )
5536   {
5537     XQueryPosition(display,windows->image.id,&x,&y);
5538     (void) XSelectInput(display,windows->image.id,
5539       windows->image.attributes.event_mask | PointerMotionMask);
5540     (void) XCheckDefineCursor(display,windows->image.id,cursor);
5541     state=DefaultState;
5542     do
5543     {
5544       if( IfMagickTrue(windows->info.mapped) )
5545         {
5546           /*
5547             Display pointer position.
5548           */
5549           (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
5550             x+windows->image.x,y+windows->image.y);
5551           XInfoWidget(display,windows,text);
5552         }
5553       /*
5554         Wait for next event.
5555       */
5556       XScreenEvent(display,windows,&event,exception);
5557       if (event.xany.window == windows->command.id)
5558         {
5559           /*
5560             Select a command from the Command widget.
5561           */
5562           id=XCommandWidget(display,windows,DrawMenu,&event);
5563           if (id < 0)
5564             continue;
5565           switch (DrawCommands[id])
5566           {
5567             case DrawElementCommand:
5568             {
5569               static const char
5570                 *Elements[] =
5571                 {
5572                   "point",
5573                   "line",
5574                   "rectangle",
5575                   "fill rectangle",
5576                   "circle",
5577                   "fill circle",
5578                   "ellipse",
5579                   "fill ellipse",
5580                   "polygon",
5581                   "fill polygon",
5582                   (char *) NULL,
5583                 };
5584
5585               /*
5586                 Select a command from the pop-up menu.
5587               */
5588               element=(ElementType) (XMenuWidget(display,windows,
5589                 DrawMenu[id],Elements,command)+1);
5590               break;
5591             }
5592             case DrawColorCommand:
5593             {
5594               const char
5595                 *ColorMenu[MaxNumberPens+1];
5596
5597               int
5598                 pen_number;
5599
5600               MagickBooleanType
5601                 transparent;
5602
5603               XColor
5604                 color;
5605
5606               /*
5607                 Initialize menu selections.
5608               */
5609               for (i=0; i < (int) (MaxNumberPens-2); i++)
5610                 ColorMenu[i]=resource_info->pen_colors[i];
5611               ColorMenu[MaxNumberPens-2]="transparent";
5612               ColorMenu[MaxNumberPens-1]="Browser...";
5613               ColorMenu[MaxNumberPens]=(char *) NULL;
5614               /*
5615                 Select a pen color from the pop-up menu.
5616               */
5617               pen_number=XMenuWidget(display,windows,DrawMenu[id],
5618                 (const char **) ColorMenu,command);
5619               if (pen_number < 0)
5620                 break;
5621               transparent=pen_number == (MaxNumberPens-2) ? MagickTrue :
5622                 MagickFalse;
5623               if( IfMagickTrue(transparent) )
5624                 {
5625                   draw_info.stencil=TransparentStencil;
5626                   break;
5627                 }
5628               if (pen_number == (MaxNumberPens-1))
5629                 {
5630                   static char
5631                     color_name[MaxTextExtent] = "gray";
5632
5633                   /*
5634                     Select a pen color from a dialog.
5635                   */
5636                   resource_info->pen_colors[pen_number]=color_name;
5637                   XColorBrowserWidget(display,windows,"Select",color_name);
5638                   if (*color_name == '\0')
5639                     break;
5640                 }
5641               /*
5642                 Set pen color.
5643               */
5644               (void) XParseColor(display,windows->map_info->colormap,
5645                 resource_info->pen_colors[pen_number],&color);
5646               XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
5647                 (unsigned int) MaxColors,&color);
5648               windows->pixel_info->pen_colors[pen_number]=color;
5649               pen_id=(unsigned int) pen_number;
5650               draw_info.stencil=OpaqueStencil;
5651               break;
5652             }
5653             case DrawStippleCommand:
5654             {
5655               Image
5656                 *stipple_image;
5657
5658               ImageInfo
5659                 *image_info;
5660
5661               int
5662                 status;
5663
5664               static char
5665                 filename[MaxTextExtent] = "\0";
5666
5667               static const char
5668                 *StipplesMenu[] =
5669                 {
5670                   "Brick",
5671                   "Diagonal",
5672                   "Scales",
5673                   "Vertical",
5674                   "Wavy",
5675                   "Translucent",
5676                   "Opaque",
5677                   (char *) NULL,
5678                   (char *) NULL,
5679                 };
5680
5681               /*
5682                 Select a command from the pop-up menu.
5683               */
5684               StipplesMenu[7]="Open...";
5685               entry=XMenuWidget(display,windows,DrawMenu[id],StipplesMenu,
5686                 command);
5687               if (entry < 0)
5688                 break;
5689               if (stipple != (Pixmap) NULL)
5690                 (void) XFreePixmap(display,stipple);
5691               stipple=(Pixmap) NULL;
5692               if (entry != 7)
5693                 {
5694                   switch (entry)
5695                   {
5696                     case 0:
5697                     {
5698                       stipple=XCreateBitmapFromData(display,root_window,
5699                         (char *) BricksBitmap,BricksWidth,BricksHeight);
5700                       break;
5701                     }
5702                     case 1:
5703                     {
5704                       stipple=XCreateBitmapFromData(display,root_window,
5705                         (char *) DiagonalBitmap,DiagonalWidth,DiagonalHeight);
5706                       break;
5707                     }
5708                     case 2:
5709                     {
5710                       stipple=XCreateBitmapFromData(display,root_window,
5711                         (char *) ScalesBitmap,ScalesWidth,ScalesHeight);
5712                       break;
5713                     }
5714                     case 3:
5715                     {
5716                       stipple=XCreateBitmapFromData(display,root_window,
5717                         (char *) VerticalBitmap,VerticalWidth,VerticalHeight);
5718                       break;
5719                     }
5720                     case 4:
5721                     {
5722                       stipple=XCreateBitmapFromData(display,root_window,
5723                         (char *) WavyBitmap,WavyWidth,WavyHeight);
5724                       break;
5725                     }
5726                     case 5:
5727                     {
5728                       stipple=XCreateBitmapFromData(display,root_window,
5729                         (char *) HighlightBitmap,HighlightWidth,
5730                         HighlightHeight);
5731                       break;
5732                     }
5733                     case 6:
5734                     default:
5735                     {
5736                       stipple=XCreateBitmapFromData(display,root_window,
5737                         (char *) OpaqueBitmap,OpaqueWidth,OpaqueHeight);
5738                       break;
5739                     }
5740                   }
5741                   break;
5742                 }
5743               XFileBrowserWidget(display,windows,"Stipple",filename);
5744               if (*filename == '\0')
5745                 break;
5746               /*
5747                 Read image.
5748               */
5749               XSetCursorState(display,windows,MagickTrue);
5750               XCheckRefreshWindows(display,windows);
5751               image_info=AcquireImageInfo();
5752               (void) CopyMagickString(image_info->filename,filename,
5753                 MaxTextExtent);
5754               stipple_image=ReadImage(image_info,exception);
5755               CatchException(exception);
5756               XSetCursorState(display,windows,MagickFalse);
5757               if (stipple_image == (Image *) NULL)
5758                 break;
5759               (void) AcquireUniqueFileResource(filename);
5760               (void) FormatLocaleString(stipple_image->filename,MaxTextExtent,
5761                 "xbm:%s",filename);
5762               (void) WriteImage(image_info,stipple_image,exception);
5763               stipple_image=DestroyImage(stipple_image);
5764               image_info=DestroyImageInfo(image_info);
5765               status=XReadBitmapFile(display,root_window,filename,&width,
5766                 &height,&stipple,&x,&y);
5767               (void) RelinquishUniqueFileResource(filename);
5768               if ((status != BitmapSuccess) != 0)
5769                 XNoticeWidget(display,windows,"Unable to read X bitmap image:",
5770                   filename);
5771               break;
5772             }
5773             case DrawWidthCommand:
5774             {
5775               static char
5776                 width[MaxTextExtent] = "0";
5777
5778               static const char
5779                 *WidthsMenu[] =
5780                 {
5781                   "1",
5782                   "2",
5783                   "4",
5784                   "8",
5785                   "16",
5786                   "Dialog...",
5787                   (char *) NULL,
5788                 };
5789
5790               /*
5791                 Select a command from the pop-up menu.
5792               */
5793               entry=XMenuWidget(display,windows,DrawMenu[id],WidthsMenu,
5794                 command);
5795               if (entry < 0)
5796                 break;
5797               if (entry != 5)
5798                 {
5799                   line_width=(unsigned int) StringToUnsignedLong(
5800                     WidthsMenu[entry]);
5801                   break;
5802                 }
5803               (void) XDialogWidget(display,windows,"Ok","Enter line width:",
5804                 width);
5805               if (*width == '\0')
5806                 break;
5807               line_width=(unsigned int) StringToUnsignedLong(width);
5808               break;
5809             }
5810             case DrawUndoCommand:
5811             {
5812               (void) XMagickCommand(display,resource_info,windows,UndoCommand,
5813                 image,exception);
5814               break;
5815             }
5816             case DrawHelpCommand:
5817             {
5818               XTextViewWidget(display,resource_info,windows,MagickFalse,
5819                 "Help Viewer - Image Rotation",ImageDrawHelp);
5820               (void) XCheckDefineCursor(display,windows->image.id,cursor);
5821               break;
5822             }
5823             case DrawDismissCommand:
5824             {
5825               /*
5826                 Prematurely exit.
5827               */
5828               state|=EscapeState;
5829               state|=ExitState;
5830               break;
5831             }
5832             default:
5833               break;
5834           }
5835           (void) XCheckDefineCursor(display,windows->image.id,cursor);
5836           continue;
5837         }
5838       switch (event.type)
5839       {
5840         case ButtonPress:
5841         {
5842           if (event.xbutton.button != Button1)
5843             break;
5844           if (event.xbutton.window != windows->image.id)
5845             break;
5846           /*
5847             exit loop.
5848           */
5849           x=event.xbutton.x;
5850           y=event.xbutton.y;
5851           state|=ExitState;
5852           break;
5853         }
5854         case ButtonRelease:
5855           break;
5856         case Expose:
5857           break;
5858         case KeyPress:
5859         {
5860           KeySym
5861             key_symbol;
5862
5863           if (event.xkey.window != windows->image.id)
5864             break;
5865           /*
5866             Respond to a user key press.
5867           */
5868           (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5869             sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5870           switch ((int) key_symbol)
5871           {
5872             case XK_Escape:
5873             case XK_F20:
5874             {
5875               /*
5876                 Prematurely exit.
5877               */
5878               state|=EscapeState;
5879               state|=ExitState;
5880               break;
5881             }
5882             case XK_F1:
5883             case XK_Help:
5884             {
5885               XTextViewWidget(display,resource_info,windows,MagickFalse,
5886                 "Help Viewer - Image Rotation",ImageDrawHelp);
5887               break;
5888             }
5889             default:
5890             {
5891               (void) XBell(display,0);
5892               break;
5893             }
5894           }
5895           break;
5896         }
5897         case MotionNotify:
5898         {
5899           /*
5900             Map and unmap Info widget as text cursor crosses its boundaries.
5901           */
5902           x=event.xmotion.x;
5903           y=event.xmotion.y;
5904           if( IfMagickTrue(windows->info.mapped) )
5905             {
5906               if ((x < (int) (windows->info.x+windows->info.width)) &&
5907                   (y < (int) (windows->info.y+windows->info.height)))
5908                 (void) XWithdrawWindow(display,windows->info.id,
5909                   windows->info.screen);
5910             }
5911           else
5912             if ((x > (int) (windows->info.x+windows->info.width)) ||
5913                 (y > (int) (windows->info.y+windows->info.height)))
5914               (void) XMapWindow(display,windows->info.id);
5915           break;
5916         }
5917       }
5918     } while ((state & ExitState) == 0);
5919     (void) XSelectInput(display,windows->image.id,
5920       windows->image.attributes.event_mask);
5921     (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
5922     if ((state & EscapeState) != 0)
5923       break;
5924     /*
5925       Draw element as pointer moves until the button is released.
5926     */
5927     distance=0;
5928     degrees=0.0;
5929     line_info.x1=x;
5930     line_info.y1=y;
5931     line_info.x2=x;
5932     line_info.y2=y;
5933     rectangle_info.x=(ssize_t) x;
5934     rectangle_info.y=(ssize_t) y;
5935     rectangle_info.width=0;
5936     rectangle_info.height=0;
5937     number_coordinates=1;
5938     coordinate_info->x=x;
5939     coordinate_info->y=y;
5940     (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
5941     state=DefaultState;
5942     do
5943     {
5944       switch (element)
5945       {
5946         case PointElement:
5947         default:
5948         {
5949           if (number_coordinates > 1)
5950             {
5951               (void) XDrawLines(display,windows->image.id,
5952                 windows->image.highlight_context,coordinate_info,
5953                 number_coordinates,CoordModeOrigin);
5954               (void) FormatLocaleString(text,MaxTextExtent," %+d%+d",
5955                 coordinate_info[number_coordinates-1].x,
5956                 coordinate_info[number_coordinates-1].y);
5957               XInfoWidget(display,windows,text);
5958             }
5959           break;
5960         }
5961         case LineElement:
5962         {
5963           if (distance > 9)
5964             {
5965               /*
5966                 Display angle of the line.
5967               */
5968               degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
5969                 line_info.y1),(double) (line_info.x2-line_info.x1)));
5970               (void) FormatLocaleString(text,MaxTextExtent," %g",
5971                 (double) degrees);
5972               XInfoWidget(display,windows,text);
5973               XHighlightLine(display,windows->image.id,
5974                 windows->image.highlight_context,&line_info);
5975             }
5976           else
5977             if( IfMagickTrue(windows->info.mapped) )
5978               (void) XWithdrawWindow(display,windows->info.id,
5979                 windows->info.screen);
5980           break;
5981         }
5982         case RectangleElement:
5983         case FillRectangleElement:
5984         {
5985           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
5986             {
5987               /*
5988                 Display info and draw drawing rectangle.
5989               */
5990               (void) FormatLocaleString(text,MaxTextExtent,
5991                 " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
5992                 (double) rectangle_info.height,(double) rectangle_info.x,
5993                 (double) rectangle_info.y);
5994               XInfoWidget(display,windows,text);
5995               XHighlightRectangle(display,windows->image.id,
5996                 windows->image.highlight_context,&rectangle_info);
5997             }
5998           else
5999             if( IfMagickTrue(windows->info.mapped) )
6000               (void) XWithdrawWindow(display,windows->info.id,
6001                 windows->info.screen);
6002           break;
6003         }
6004         case CircleElement:
6005         case FillCircleElement:
6006         case EllipseElement:
6007         case FillEllipseElement:
6008         {
6009           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6010             {
6011               /*
6012                 Display info and draw drawing rectangle.
6013               */
6014               (void) FormatLocaleString(text,MaxTextExtent,
6015                 " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
6016                 (double) rectangle_info.height,(double) rectangle_info.x,
6017                 (double) rectangle_info.y);
6018               XInfoWidget(display,windows,text);
6019               XHighlightEllipse(display,windows->image.id,
6020                 windows->image.highlight_context,&rectangle_info);
6021             }
6022           else
6023             if( IfMagickTrue(windows->info.mapped) )
6024               (void) XWithdrawWindow(display,windows->info.id,
6025                 windows->info.screen);
6026           break;
6027         }
6028         case PolygonElement:
6029         case FillPolygonElement:
6030         {
6031           if (number_coordinates > 1)
6032             (void) XDrawLines(display,windows->image.id,
6033               windows->image.highlight_context,coordinate_info,
6034               number_coordinates,CoordModeOrigin);
6035           if (distance > 9)
6036             {
6037               /*
6038                 Display angle of the line.
6039               */
6040               degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
6041                 line_info.y1),(double) (line_info.x2-line_info.x1)));
6042               (void) FormatLocaleString(text,MaxTextExtent," %g",
6043                 (double) degrees);
6044               XInfoWidget(display,windows,text);
6045               XHighlightLine(display,windows->image.id,
6046                 windows->image.highlight_context,&line_info);
6047             }
6048           else
6049             if( IfMagickTrue(windows->info.mapped) )
6050               (void) XWithdrawWindow(display,windows->info.id,
6051                 windows->info.screen);
6052           break;
6053         }
6054       }
6055       /*
6056         Wait for next event.
6057       */
6058       XScreenEvent(display,windows,&event,exception);
6059       switch (element)
6060       {
6061         case PointElement:
6062         default:
6063         {
6064           if (number_coordinates > 1)
6065             (void) XDrawLines(display,windows->image.id,
6066               windows->image.highlight_context,coordinate_info,
6067               number_coordinates,CoordModeOrigin);
6068           break;
6069         }
6070         case LineElement:
6071         {
6072           if (distance > 9)
6073             XHighlightLine(display,windows->image.id,
6074               windows->image.highlight_context,&line_info);
6075           break;
6076         }
6077         case RectangleElement:
6078         case FillRectangleElement:
6079         {
6080           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6081             XHighlightRectangle(display,windows->image.id,
6082               windows->image.highlight_context,&rectangle_info);
6083           break;
6084         }
6085         case CircleElement:
6086         case FillCircleElement:
6087         case EllipseElement:
6088         case FillEllipseElement:
6089         {
6090           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6091             XHighlightEllipse(display,windows->image.id,
6092               windows->image.highlight_context,&rectangle_info);
6093           break;
6094         }
6095         case PolygonElement:
6096         case FillPolygonElement:
6097         {
6098           if (number_coordinates > 1)
6099             (void) XDrawLines(display,windows->image.id,
6100               windows->image.highlight_context,coordinate_info,
6101               number_coordinates,CoordModeOrigin);
6102           if (distance > 9)
6103             XHighlightLine(display,windows->image.id,
6104               windows->image.highlight_context,&line_info);
6105           break;
6106         }
6107       }
6108       switch (event.type)
6109       {
6110         case ButtonPress:
6111           break;
6112         case ButtonRelease:
6113         {
6114           /*
6115             User has committed to element.
6116           */
6117           line_info.x2=event.xbutton.x;
6118           line_info.y2=event.xbutton.y;
6119           rectangle_info.x=(ssize_t) event.xbutton.x;
6120           rectangle_info.y=(ssize_t) event.xbutton.y;
6121           coordinate_info[number_coordinates].x=event.xbutton.x;
6122           coordinate_info[number_coordinates].y=event.xbutton.y;
6123           if (((element != PolygonElement) &&
6124                (element != FillPolygonElement)) || (distance <= 9))
6125             {
6126               state|=ExitState;
6127               break;
6128             }
6129           number_coordinates++;
6130           if (number_coordinates < (int) max_coordinates)
6131             {
6132               line_info.x1=event.xbutton.x;
6133               line_info.y1=event.xbutton.y;
6134               break;
6135             }
6136           max_coordinates<<=1;
6137           coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6138             max_coordinates,sizeof(*coordinate_info));
6139           if (coordinate_info == (XPoint *) NULL)
6140             (void) ThrowMagickException(exception,GetMagickModule(),
6141               ResourceLimitError,"MemoryAllocationFailed","'%s'","...");
6142           break;
6143         }
6144         case Expose:
6145           break;
6146         case MotionNotify:
6147         {
6148           if (event.xmotion.window != windows->image.id)
6149             break;
6150           if (element != PointElement)
6151             {
6152               line_info.x2=event.xmotion.x;
6153               line_info.y2=event.xmotion.y;
6154               rectangle_info.x=(ssize_t) event.xmotion.x;
6155               rectangle_info.y=(ssize_t) event.xmotion.y;
6156               break;
6157             }
6158           coordinate_info[number_coordinates].x=event.xbutton.x;
6159           coordinate_info[number_coordinates].y=event.xbutton.y;
6160           number_coordinates++;
6161           if (number_coordinates < (int) max_coordinates)
6162             break;
6163           max_coordinates<<=1;
6164           coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6165             max_coordinates,sizeof(*coordinate_info));
6166           if (coordinate_info == (XPoint *) NULL)
6167             (void) ThrowMagickException(exception,GetMagickModule(),
6168               ResourceLimitError,"MemoryAllocationFailed","'%s'","...");
6169           break;
6170         }
6171         default:
6172           break;
6173       }
6174       /*
6175         Check boundary conditions.
6176       */
6177       if (line_info.x2 < 0)
6178         line_info.x2=0;
6179       else
6180         if (line_info.x2 > (int) windows->image.width)
6181           line_info.x2=(short) windows->image.width;
6182       if (line_info.y2 < 0)
6183         line_info.y2=0;
6184       else
6185         if (line_info.y2 > (int) windows->image.height)
6186           line_info.y2=(short) windows->image.height;
6187       distance=(unsigned int)
6188         (((line_info.x2-line_info.x1+1)*(line_info.x2-line_info.x1+1))+
6189          ((line_info.y2-line_info.y1+1)*(line_info.y2-line_info.y1+1)));
6190       if ((((int) rectangle_info.x != x) && ((int) rectangle_info.y != y)) ||
6191           ((state & ExitState) != 0))
6192         {
6193           if (rectangle_info.x < 0)
6194             rectangle_info.x=0;
6195           else
6196             if (rectangle_info.x > (ssize_t) windows->image.width)
6197               rectangle_info.x=(ssize_t) windows->image.width;
6198           if ((int) rectangle_info.x < x)
6199             rectangle_info.width=(unsigned int) (x-rectangle_info.x);
6200           else
6201             {
6202               rectangle_info.width=(unsigned int) (rectangle_info.x-x);
6203               rectangle_info.x=(ssize_t) x;
6204             }
6205           if (rectangle_info.y < 0)
6206             rectangle_info.y=0;
6207           else
6208             if (rectangle_info.y > (ssize_t) windows->image.height)
6209               rectangle_info.y=(ssize_t) windows->image.height;
6210           if ((int) rectangle_info.y < y)
6211             rectangle_info.height=(unsigned int) (y-rectangle_info.y);
6212           else
6213             {
6214               rectangle_info.height=(unsigned int) (rectangle_info.y-y);
6215               rectangle_info.y=(ssize_t) y;
6216             }
6217         }
6218     } while ((state & ExitState) == 0);
6219     (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
6220     if ((element == PointElement) || (element == PolygonElement) ||
6221         (element == FillPolygonElement))
6222       {
6223         /*
6224           Determine polygon bounding box.
6225         */
6226         rectangle_info.x=(ssize_t) coordinate_info->x;
6227         rectangle_info.y=(ssize_t) coordinate_info->y;
6228         x=coordinate_info->x;
6229         y=coordinate_info->y;
6230         for (i=1; i < number_coordinates; i++)
6231         {
6232           if (coordinate_info[i].x > x)
6233             x=coordinate_info[i].x;
6234           if (coordinate_info[i].y > y)
6235             y=coordinate_info[i].y;
6236           if ((ssize_t) coordinate_info[i].x < rectangle_info.x)
6237             rectangle_info.x=MagickMax((ssize_t) coordinate_info[i].x,0);
6238           if ((ssize_t) coordinate_info[i].y < rectangle_info.y)
6239             rectangle_info.y=MagickMax((ssize_t) coordinate_info[i].y,0);
6240         }
6241         rectangle_info.width=(size_t) (x-rectangle_info.x);
6242         rectangle_info.height=(size_t) (y-rectangle_info.y);
6243         for (i=0; i < number_coordinates; i++)
6244         {
6245           coordinate_info[i].x-=rectangle_info.x;
6246           coordinate_info[i].y-=rectangle_info.y;
6247         }
6248       }
6249     else
6250       if (distance <= 9)
6251         continue;
6252       else
6253         if ((element == RectangleElement) ||
6254             (element == CircleElement) || (element == EllipseElement))
6255           {
6256             rectangle_info.width--;
6257             rectangle_info.height--;
6258           }
6259     /*
6260       Drawing is relative to image configuration.
6261     */
6262     draw_info.x=(int) rectangle_info.x;
6263     draw_info.y=(int) rectangle_info.y;
6264     (void) XMagickCommand(display,resource_info,windows,SaveToUndoBufferCommand,
6265       image,exception);
6266     width=(unsigned int) (*image)->columns;
6267     height=(unsigned int) (*image)->rows;
6268     x=0;
6269     y=0;
6270     if (windows->image.crop_geometry != (char *) NULL)
6271       (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
6272     draw_info.x+=windows->image.x-(line_width/2);
6273     if (draw_info.x < 0)
6274       draw_info.x=0;
6275     draw_info.x=(int) (width*draw_info.x/windows->image.ximage->width);
6276     draw_info.y+=windows->image.y-(line_width/2);
6277     if (draw_info.y < 0)
6278       draw_info.y=0;
6279     draw_info.y=(int) height*draw_info.y/windows->image.ximage->height;
6280     draw_info.width=(unsigned int) rectangle_info.width+(line_width << 1);
6281     if (draw_info.width > (unsigned int) (*image)->columns)
6282       draw_info.width=(unsigned int) (*image)->columns;
6283     draw_info.height=(unsigned int) rectangle_info.height+(line_width << 1);
6284     if (draw_info.height > (unsigned int) (*image)->rows)
6285       draw_info.height=(unsigned int) (*image)->rows;
6286     (void) FormatLocaleString(draw_info.geometry,MaxTextExtent,"%ux%u%+d%+d",
6287       width*draw_info.width/windows->image.ximage->width,
6288       height*draw_info.height/windows->image.ximage->height,
6289       draw_info.x+x,draw_info.y+y);
6290     /*
6291       Initialize drawing attributes.
6292     */
6293     draw_info.degrees=0.0;
6294     draw_info.element=element;
6295     draw_info.stipple=stipple;
6296     draw_info.line_width=line_width;
6297     draw_info.line_info=line_info;
6298     if (line_info.x1 > (int) (line_width/2))
6299       draw_info.line_info.x1=(short) line_width/2;
6300     if (line_info.y1 > (int) (line_width/2))
6301       draw_info.line_info.y1=(short) line_width/2;
6302     draw_info.line_info.x2=(short) (line_info.x2-line_info.x1+(line_width/2));
6303     draw_info.line_info.y2=(short) (line_info.y2-line_info.y1+(line_width/2));
6304     if ((draw_info.line_info.x2 < 0) && (draw_info.line_info.y2 < 0))
6305       {
6306         draw_info.line_info.x2=(-draw_info.line_info.x2);
6307         draw_info.line_info.y2=(-draw_info.line_info.y2);
6308       }
6309     if (draw_info.line_info.x2 < 0)
6310       {
6311         draw_info.line_info.x2=(-draw_info.line_info.x2);
6312         Swap(draw_info.line_info.x1,draw_info.line_info.x2);
6313       }
6314     if (draw_info.line_info.y2 < 0)
6315       {
6316         draw_info.line_info.y2=(-draw_info.line_info.y2);
6317         Swap(draw_info.line_info.y1,draw_info.line_info.y2);
6318       }
6319     draw_info.rectangle_info=rectangle_info;
6320     if (draw_info.rectangle_info.x > (ssize_t) (line_width/2))
6321       draw_info.rectangle_info.x=(ssize_t) line_width/2;
6322     if (draw_info.rectangle_info.y > (ssize_t) (line_width/2))
6323       draw_info.rectangle_info.y=(ssize_t) line_width/2;
6324     draw_info.number_coordinates=(unsigned int) number_coordinates;
6325     draw_info.coordinate_info=coordinate_info;
6326     windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
6327     /*
6328       Draw element on image.
6329     */
6330     XSetCursorState(display,windows,MagickTrue);
6331     XCheckRefreshWindows(display,windows);
6332     status=XDrawImage(display,windows->pixel_info,&draw_info,*image,exception);
6333     XSetCursorState(display,windows,MagickFalse);
6334     /*
6335       Update image colormap and return to image drawing.
6336     */
6337     XConfigureImageColormap(display,resource_info,windows,*image,exception);
6338     (void) XConfigureImage(display,resource_info,windows,*image,exception);
6339   }
6340   XSetCursorState(display,windows,MagickFalse);
6341   coordinate_info=(XPoint *) RelinquishMagickMemory(coordinate_info);
6342   return(IsMagickTrue(status));
6343 }
6344 \f
6345 /*
6346 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6347 %                                                                             %
6348 %                                                                             %
6349 %                                                                             %
6350 +   X D r a w P a n R e c t a n g l e                                         %
6351 %                                                                             %
6352 %                                                                             %
6353 %                                                                             %
6354 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6355 %
6356 %  XDrawPanRectangle() draws a rectangle in the pan window.  The pan window
6357 %  displays a zoom image and the rectangle shows which portion of the image is
6358 %  displayed in the Image window.
6359 %
6360 %  The format of the XDrawPanRectangle method is:
6361 %
6362 %      XDrawPanRectangle(Display *display,XWindows *windows)
6363 %
6364 %  A description of each parameter follows:
6365 %
6366 %    o display: Specifies a connection to an X server;  returned from
6367 %      XOpenDisplay.
6368 %
6369 %    o windows: Specifies a pointer to a XWindows structure.
6370 %
6371 */
6372 static void XDrawPanRectangle(Display *display,XWindows *windows)
6373 {
6374   double
6375     scale_factor;
6376
6377   RectangleInfo
6378     highlight_info;
6379
6380   /*
6381     Determine dimensions of the panning rectangle.
6382   */
6383   scale_factor=(double) windows->pan.width/windows->image.ximage->width;
6384   highlight_info.x=(ssize_t) (scale_factor*windows->image.x+0.5);
6385   highlight_info.width=(unsigned int) (scale_factor*windows->image.width+0.5);
6386   scale_factor=(double)
6387     windows->pan.height/windows->image.ximage->height;
6388   highlight_info.y=(ssize_t) (scale_factor*windows->image.y+0.5);
6389   highlight_info.height=(unsigned int) (scale_factor*windows->image.height+0.5);
6390   /*
6391     Display the panning rectangle.
6392   */
6393   (void) XClearWindow(display,windows->pan.id);
6394   XHighlightRectangle(display,windows->pan.id,windows->pan.annotate_context,
6395     &highlight_info);
6396 }
6397 \f
6398 /*
6399 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6400 %                                                                             %
6401 %                                                                             %
6402 %                                                                             %
6403 +   X I m a g e C a c h e                                                     %
6404 %                                                                             %
6405 %                                                                             %
6406 %                                                                             %
6407 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6408 %
6409 %  XImageCache() handles the creation, manipulation, and destruction of the
6410 %  image cache (undo and redo buffers).
6411 %
6412 %  The format of the XImageCache method is:
6413 %
6414 %      void XImageCache(Display *display,XResourceInfo *resource_info,
6415 %        XWindows *windows,const CommandType command,Image **image,
6416 %        ExceptionInfo *exception)
6417 %
6418 %  A description of each parameter follows:
6419 %
6420 %    o display: Specifies a connection to an X server; returned from
6421 %      XOpenDisplay.
6422 %
6423 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6424 %
6425 %    o windows: Specifies a pointer to a XWindows structure.
6426 %
6427 %    o command: Specifies a command to perform.
6428 %
6429 %    o image: the image;  XImageCache may transform the image and return a new
6430 %      image pointer.
6431 %
6432 %    o exception: return any errors or warnings in this structure.
6433 %
6434 */
6435 static void XImageCache(Display *display,XResourceInfo *resource_info,
6436   XWindows *windows,const CommandType command,Image **image,
6437   ExceptionInfo *exception)
6438 {
6439   Image
6440     *cache_image;
6441
6442   static Image
6443     *redo_image = (Image *) NULL,
6444     *undo_image = (Image *) NULL;
6445
6446   switch (command)
6447   {
6448     case FreeBuffersCommand:
6449     {
6450       /*
6451         Free memory from the undo and redo cache.
6452       */
6453       while (undo_image != (Image *) NULL)
6454       {
6455         cache_image=undo_image;
6456         undo_image=GetPreviousImageInList(undo_image);
6457         cache_image->list=DestroyImage(cache_image->list);
6458         cache_image=DestroyImage(cache_image);
6459       }
6460       undo_image=NewImageList();
6461       if (redo_image != (Image *) NULL)
6462         redo_image=DestroyImage(redo_image);
6463       redo_image=NewImageList();
6464       return;
6465     }
6466     case UndoCommand:
6467     {
6468       char
6469         image_geometry[MaxTextExtent];
6470
6471       /*
6472         Undo the last image transformation.
6473       */
6474       if (undo_image == (Image *) NULL)
6475         {
6476           (void) XBell(display,0);
6477           return;
6478         }
6479       cache_image=undo_image;
6480       undo_image=GetPreviousImageInList(undo_image);
6481       windows->image.window_changes.width=(int) cache_image->columns;
6482       windows->image.window_changes.height=(int) cache_image->rows;
6483       (void) FormatLocaleString(image_geometry,MaxTextExtent,"%dx%d!",
6484         windows->image.ximage->width,windows->image.ximage->height);
6485       (void) TransformImage(image,windows->image.crop_geometry,image_geometry,
6486         exception);
6487       if (windows->image.crop_geometry != (char *) NULL)
6488         windows->image.crop_geometry=(char *) RelinquishMagickMemory(
6489           windows->image.crop_geometry);
6490       windows->image.crop_geometry=cache_image->geometry;
6491       if (redo_image != (Image *) NULL)
6492         redo_image=DestroyImage(redo_image);
6493       redo_image=(*image);
6494       *image=cache_image->list;
6495       cache_image=DestroyImage(cache_image);
6496       if( IfMagickTrue(windows->image.orphan) )
6497         return;
6498       XConfigureImageColormap(display,resource_info,windows,*image,exception);
6499       (void) XConfigureImage(display,resource_info,windows,*image,exception);
6500       return;
6501     }
6502     case CutCommand:
6503     case PasteCommand:
6504     case ApplyCommand:
6505     case HalfSizeCommand:
6506     case OriginalSizeCommand:
6507     case DoubleSizeCommand:
6508     case ResizeCommand:
6509     case TrimCommand:
6510     case CropCommand:
6511     case ChopCommand:
6512     case FlipCommand:
6513     case FlopCommand:
6514     case RotateRightCommand:
6515     case RotateLeftCommand:
6516     case RotateCommand:
6517     case ShearCommand:
6518     case RollCommand:
6519     case NegateCommand:
6520     case ContrastStretchCommand:
6521     case SigmoidalContrastCommand:
6522     case NormalizeCommand:
6523     case EqualizeCommand:
6524     case HueCommand:
6525     case SaturationCommand:
6526     case BrightnessCommand:
6527     case GammaCommand:
6528     case SpiffCommand:
6529     case DullCommand:
6530     case GrayscaleCommand:
6531     case MapCommand:
6532     case QuantizeCommand:
6533     case DespeckleCommand:
6534     case EmbossCommand:
6535     case ReduceNoiseCommand:
6536     case AddNoiseCommand:
6537     case SharpenCommand:
6538     case BlurCommand:
6539     case ThresholdCommand:
6540     case EdgeDetectCommand:
6541     case SpreadCommand:
6542     case ShadeCommand:
6543     case RaiseCommand:
6544     case SegmentCommand:
6545     case SolarizeCommand:
6546     case SepiaToneCommand:
6547     case SwirlCommand:
6548     case ImplodeCommand:
6549     case VignetteCommand:
6550     case WaveCommand:
6551     case OilPaintCommand:
6552     case CharcoalDrawCommand:
6553     case AnnotateCommand:
6554     case AddBorderCommand:
6555     case AddFrameCommand:
6556     case CompositeCommand:
6557     case CommentCommand:
6558     case LaunchCommand:
6559     case RegionofInterestCommand:
6560     case SaveToUndoBufferCommand:
6561     case RedoCommand:
6562     {
6563       Image
6564         *previous_image;
6565
6566       ssize_t
6567         bytes;
6568
6569       bytes=(ssize_t) ((*image)->columns*(*image)->rows*sizeof(PixelInfo));
6570       if (undo_image != (Image *) NULL)
6571         {
6572           /*
6573             Ensure the undo cache has enough memory available.
6574           */
6575           previous_image=undo_image;
6576           while (previous_image != (Image *) NULL)
6577           {
6578             bytes+=previous_image->list->columns*previous_image->list->rows*
6579               sizeof(PixelInfo);
6580             if (bytes <= (ssize_t) (resource_info->undo_cache << 20))
6581               {
6582                 previous_image=GetPreviousImageInList(previous_image);
6583                 continue;
6584               }
6585             bytes-=previous_image->list->columns*previous_image->list->rows*
6586               sizeof(PixelInfo);
6587             if (previous_image == undo_image)
6588               undo_image=NewImageList();
6589             else
6590               previous_image->next->previous=NewImageList();
6591             break;
6592           }
6593           while (previous_image != (Image *) NULL)
6594           {
6595             /*
6596               Delete any excess memory from undo cache.
6597             */
6598             cache_image=previous_image;
6599             previous_image=GetPreviousImageInList(previous_image);
6600             cache_image->list=DestroyImage(cache_image->list);
6601             cache_image=DestroyImage(cache_image);
6602           }
6603         }
6604       if (bytes > (ssize_t) (resource_info->undo_cache << 20))
6605         break;
6606       /*
6607         Save image before transformations are applied.
6608       */
6609       cache_image=AcquireImage((ImageInfo *) NULL,exception);
6610       if (cache_image == (Image *) NULL)
6611         break;
6612       XSetCursorState(display,windows,MagickTrue);
6613       XCheckRefreshWindows(display,windows);
6614       cache_image->list=CloneImage(*image,0,0,MagickTrue,exception);
6615       XSetCursorState(display,windows,MagickFalse);
6616       if (cache_image->list == (Image *) NULL)
6617         {
6618           cache_image=DestroyImage(cache_image);
6619           break;
6620         }
6621       cache_image->columns=(size_t) windows->image.ximage->width;
6622       cache_image->rows=(size_t) windows->image.ximage->height;
6623       cache_image->geometry=windows->image.crop_geometry;
6624       if (windows->image.crop_geometry != (char *) NULL)
6625         {
6626           cache_image->geometry=AcquireString((char *) NULL);
6627           (void) CopyMagickString(cache_image->geometry,
6628             windows->image.crop_geometry,MaxTextExtent);
6629         }
6630       if (undo_image == (Image *) NULL)
6631         {
6632           undo_image=cache_image;
6633           break;
6634         }
6635       undo_image->next=cache_image;
6636       undo_image->next->previous=undo_image;
6637       undo_image=undo_image->next;
6638       break;
6639     }
6640     default:
6641       break;
6642   }
6643   if (command == RedoCommand)
6644     {
6645       /*
6646         Redo the last image transformation.
6647       */
6648       if (redo_image == (Image *) NULL)
6649         {
6650           (void) XBell(display,0);
6651           return;
6652         }
6653       windows->image.window_changes.width=(int) redo_image->columns;
6654       windows->image.window_changes.height=(int) redo_image->rows;
6655       if (windows->image.crop_geometry != (char *) NULL)
6656         windows->image.crop_geometry=(char *)
6657           RelinquishMagickMemory(windows->image.crop_geometry);
6658       windows->image.crop_geometry=redo_image->geometry;
6659       *image=DestroyImage(*image);
6660       *image=redo_image;
6661       redo_image=NewImageList();
6662       if( IfMagickTrue(windows->image.orphan) )
6663         return;
6664       XConfigureImageColormap(display,resource_info,windows,*image,exception);
6665       (void) XConfigureImage(display,resource_info,windows,*image,exception);
6666       return;
6667     }
6668   if (command != InfoCommand)
6669     return;
6670   /*
6671     Display image info.
6672   */
6673   XSetCursorState(display,windows,MagickTrue);
6674   XCheckRefreshWindows(display,windows);
6675   XDisplayImageInfo(display,resource_info,windows,undo_image,*image,exception);
6676   XSetCursorState(display,windows,MagickFalse);
6677 }
6678 \f
6679 /*
6680 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6681 %                                                                             %
6682 %                                                                             %
6683 %                                                                             %
6684 +   X I m a g e W i n d o w C o m m a n d                                     %
6685 %                                                                             %
6686 %                                                                             %
6687 %                                                                             %
6688 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6689 %
6690 %  XImageWindowCommand() makes a transform to the image or Image window as
6691 %  specified by a user menu button or keyboard command.
6692 %
6693 %  The format of the XImageWindowCommand method is:
6694 %
6695 %      CommandType XImageWindowCommand(Display *display,
6696 %        XResourceInfo *resource_info,XWindows *windows,
6697 %        const MagickStatusType state,KeySym key_symbol,Image **image,
6698 %        ExceptionInfo *exception)
6699 %
6700 %  A description of each parameter follows:
6701 %
6702 %    o nexus:  Method XImageWindowCommand returns an image when the
6703 %      user chooses 'Open Image' from the command menu.  Otherwise a null
6704 %      image is returned.
6705 %
6706 %    o display: Specifies a connection to an X server; returned from
6707 %      XOpenDisplay.
6708 %
6709 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6710 %
6711 %    o windows: Specifies a pointer to a XWindows structure.
6712 %
6713 %    o state: key mask.
6714 %
6715 %    o key_symbol: Specifies a command to perform.
6716 %
6717 %    o image: the image;  XImageWIndowCommand may transform the image and
6718 %      return a new image pointer.
6719 %
6720 %    o exception: return any errors or warnings in this structure.
6721 %
6722 */
6723 static CommandType XImageWindowCommand(Display *display,
6724   XResourceInfo *resource_info,XWindows *windows,const MagickStatusType state,
6725   KeySym key_symbol,Image **image,ExceptionInfo *exception)
6726 {
6727   static char
6728     delta[MaxTextExtent] = "";
6729
6730   static const char
6731     Digits[] = "01234567890";
6732
6733   static KeySym
6734     last_symbol = XK_0;
6735
6736   if ((key_symbol >= XK_0) && (key_symbol <= XK_9))
6737     {
6738       if (((last_symbol < XK_0) || (last_symbol > XK_9)))
6739         {
6740           *delta='\0';
6741           resource_info->quantum=1;
6742         }
6743       last_symbol=key_symbol;
6744       delta[strlen(delta)+1]='\0';
6745       delta[strlen(delta)]=Digits[key_symbol-XK_0];
6746       resource_info->quantum=StringToLong(delta);
6747       return(NullCommand);
6748     }
6749   last_symbol=key_symbol;
6750   if (resource_info->immutable)
6751     {
6752       /*
6753         Virtual image window has a restricted command set.
6754       */
6755       switch (key_symbol)
6756       {
6757         case XK_question:
6758           return(InfoCommand);
6759         case XK_p:
6760         case XK_Print:
6761           return(PrintCommand);
6762         case XK_space:
6763           return(NextCommand);
6764         case XK_q:
6765         case XK_Escape:
6766           return(QuitCommand);
6767         default:
6768           break;
6769       }
6770       return(NullCommand);
6771     }
6772   switch ((int) key_symbol)
6773   {
6774     case XK_o:
6775     {
6776       if ((state & ControlMask) == 0)
6777         break;
6778       return(OpenCommand);
6779     }
6780     case XK_space:
6781       return(NextCommand);
6782     case XK_BackSpace:
6783       return(FormerCommand);
6784     case XK_s:
6785     {
6786       if ((state & Mod1Mask) != 0)
6787         return(SwirlCommand);
6788       if ((state & ControlMask) == 0)
6789         return(ShearCommand);
6790       return(SaveCommand);
6791     }
6792     case XK_p:
6793     case XK_Print:
6794     {
6795       if ((state & Mod1Mask) != 0)
6796         return(OilPaintCommand);
6797       if ((state & Mod4Mask) != 0)
6798         return(ColorCommand);
6799       if ((state & ControlMask) == 0)
6800         return(NullCommand);
6801       return(PrintCommand);
6802     }
6803     case XK_d:
6804     {
6805       if ((state & Mod4Mask) != 0)
6806         return(DrawCommand);
6807       if ((state & ControlMask) == 0)
6808         return(NullCommand);
6809       return(DeleteCommand);
6810     }
6811     case XK_Select:
6812     {
6813       if ((state & ControlMask) == 0)
6814         return(NullCommand);
6815       return(SelectCommand);
6816     }
6817     case XK_n:
6818     {
6819       if ((state & ControlMask) == 0)
6820         return(NullCommand);
6821       return(NewCommand);
6822     }
6823     case XK_q:
6824     case XK_Escape:
6825       return(QuitCommand);
6826     case XK_z:
6827     case XK_Undo:
6828     {
6829       if ((state & ControlMask) == 0)
6830         return(NullCommand);
6831       return(UndoCommand);
6832     }
6833     case XK_r:
6834     case XK_Redo:
6835     {
6836       if ((state & ControlMask) == 0)
6837         return(RollCommand);
6838       return(RedoCommand);
6839     }
6840     case XK_x:
6841     {
6842       if ((state & ControlMask) == 0)
6843         return(NullCommand);
6844       return(CutCommand);
6845     }
6846     case XK_c:
6847     {
6848       if ((state & Mod1Mask) != 0)
6849         return(CharcoalDrawCommand);
6850       if ((state & ControlMask) == 0)
6851         return(CropCommand);
6852       return(CopyCommand);
6853     }
6854     case XK_v:
6855     case XK_Insert:
6856     {
6857       if ((state & Mod4Mask) != 0)
6858         return(CompositeCommand);
6859       if ((state & ControlMask) == 0)
6860         return(FlipCommand);
6861       return(PasteCommand);
6862     }
6863     case XK_less:
6864       return(HalfSizeCommand);
6865     case XK_minus:
6866       return(OriginalSizeCommand);
6867     case XK_greater:
6868       return(DoubleSizeCommand);
6869     case XK_percent:
6870       return(ResizeCommand);
6871     case XK_at:
6872       return(RefreshCommand);
6873     case XK_bracketleft:
6874       return(ChopCommand);
6875     case XK_h:
6876       return(FlopCommand);
6877     case XK_slash:
6878       return(RotateRightCommand);
6879     case XK_backslash:
6880       return(RotateLeftCommand);
6881     case XK_asterisk:
6882       return(RotateCommand);
6883     case XK_t:
6884       return(TrimCommand);
6885     case XK_H:
6886       return(HueCommand);
6887     case XK_S:
6888       return(SaturationCommand);
6889     case XK_L:
6890       return(BrightnessCommand);
6891     case XK_G:
6892       return(GammaCommand);
6893     case XK_C:
6894       return(SpiffCommand);
6895     case XK_Z:
6896       return(DullCommand);
6897     case XK_N:
6898       return(NormalizeCommand);
6899     case XK_equal:
6900       return(EqualizeCommand);
6901     case XK_asciitilde:
6902       return(NegateCommand);
6903     case XK_period:
6904       return(GrayscaleCommand);
6905     case XK_numbersign:
6906       return(QuantizeCommand);
6907     case XK_F2:
6908       return(DespeckleCommand);
6909     case XK_F3:
6910       return(EmbossCommand);
6911     case XK_F4:
6912       return(ReduceNoiseCommand);
6913     case XK_F5:
6914       return(AddNoiseCommand);
6915     case XK_F6:
6916       return(SharpenCommand);
6917     case XK_F7:
6918       return(BlurCommand);
6919     case XK_F8:
6920       return(ThresholdCommand);
6921     case XK_F9:
6922       return(EdgeDetectCommand);
6923     case XK_F10:
6924       return(SpreadCommand);
6925     case XK_F11:
6926       return(ShadeCommand);
6927     case XK_F12:
6928       return(RaiseCommand);
6929     case XK_F13:
6930       return(SegmentCommand);
6931     case XK_i:
6932     {
6933       if ((state & Mod1Mask) == 0)
6934         return(NullCommand);
6935       return(ImplodeCommand);
6936     }
6937     case XK_w:
6938     {
6939       if ((state & Mod1Mask) == 0)
6940         return(NullCommand);
6941       return(WaveCommand);
6942     }
6943     case XK_m:
6944     {
6945       if ((state & Mod4Mask) == 0)
6946         return(NullCommand);
6947       return(MatteCommand);
6948     }
6949     case XK_b:
6950     {
6951       if ((state & Mod4Mask) == 0)
6952         return(NullCommand);
6953       return(AddBorderCommand);
6954     }
6955     case XK_f:
6956     {
6957       if ((state & Mod4Mask) == 0)
6958         return(NullCommand);
6959       return(AddFrameCommand);
6960     }
6961     case XK_exclam:
6962     {
6963       if ((state & Mod4Mask) == 0)
6964         return(NullCommand);
6965       return(CommentCommand);
6966     }
6967     case XK_a:
6968     {
6969       if ((state & Mod1Mask) != 0)
6970         return(ApplyCommand);
6971       if ((state & Mod4Mask) != 0)
6972         return(AnnotateCommand);
6973       if ((state & ControlMask) == 0)
6974         return(NullCommand);
6975       return(RegionofInterestCommand);
6976     }
6977     case XK_question:
6978       return(InfoCommand);
6979     case XK_plus:
6980       return(ZoomCommand);
6981     case XK_P:
6982     {
6983       if ((state & ShiftMask) == 0)
6984         return(NullCommand);
6985       return(ShowPreviewCommand);
6986     }
6987     case XK_Execute:
6988       return(LaunchCommand);
6989     case XK_F1:
6990       return(HelpCommand);
6991     case XK_Find:
6992       return(BrowseDocumentationCommand);
6993     case XK_Menu:
6994     {
6995       (void) XMapRaised(display,windows->command.id);
6996       return(NullCommand);
6997     }
6998     case XK_Next:
6999     case XK_Prior:
7000     case XK_Home:
7001     case XK_KP_Home:
7002     {
7003       XTranslateImage(display,windows,*image,key_symbol);
7004       return(NullCommand);
7005     }
7006     case XK_Up:
7007     case XK_KP_Up:
7008     case XK_Down:
7009     case XK_KP_Down:
7010     case XK_Left:
7011     case XK_KP_Left:
7012     case XK_Right:
7013     case XK_KP_Right:
7014     {
7015       if ((state & Mod1Mask) != 0)
7016         {
7017           RectangleInfo
7018             crop_info;
7019
7020           /*
7021             Trim one pixel from edge of image.
7022           */
7023           crop_info.x=0;
7024           crop_info.y=0;
7025           crop_info.width=(size_t) windows->image.ximage->width;
7026           crop_info.height=(size_t) windows->image.ximage->height;
7027           if ((key_symbol == XK_Up) || (key_symbol == XK_KP_Up))
7028             {
7029               if (resource_info->quantum >= (int) crop_info.height)
7030                 resource_info->quantum=(int) crop_info.height-1;
7031               crop_info.height-=resource_info->quantum;
7032             }
7033           if ((key_symbol == XK_Down) || (key_symbol == XK_KP_Down))
7034             {
7035               if (resource_info->quantum >= (int) (crop_info.height-crop_info.y))
7036                 resource_info->quantum=(int) (crop_info.height-crop_info.y-1);
7037               crop_info.y+=resource_info->quantum;
7038               crop_info.height-=resource_info->quantum;
7039             }
7040           if ((key_symbol == XK_Left) || (key_symbol == XK_KP_Left))
7041             {
7042               if (resource_info->quantum >= (int) crop_info.width)
7043                 resource_info->quantum=(int) crop_info.width-1;
7044               crop_info.width-=resource_info->quantum;
7045             }
7046           if ((key_symbol == XK_Right) || (key_symbol == XK_KP_Right))
7047             {
7048               if (resource_info->quantum >= (int) (crop_info.width-crop_info.x))
7049                 resource_info->quantum=(int) (crop_info.width-crop_info.x-1);
7050               crop_info.x+=resource_info->quantum;
7051               crop_info.width-=resource_info->quantum;
7052             }
7053           if ((int) (windows->image.x+windows->image.width) >
7054               (int) crop_info.width)
7055             windows->image.x=(int) (crop_info.width-windows->image.width);
7056           if ((int) (windows->image.y+windows->image.height) >
7057               (int) crop_info.height)
7058             windows->image.y=(int) (crop_info.height-windows->image.height);
7059           XSetCropGeometry(display,windows,&crop_info,*image);
7060           windows->image.window_changes.width=(int) crop_info.width;
7061           windows->image.window_changes.height=(int) crop_info.height;
7062           (void) XSetWindowBackgroundPixmap(display,windows->image.id,None);
7063           (void) XConfigureImage(display,resource_info,windows,*image,
7064             exception);
7065           return(NullCommand);
7066         }
7067       XTranslateImage(display,windows,*image,key_symbol);
7068       return(NullCommand);
7069     }
7070     default:
7071       return(NullCommand);
7072   }
7073   return(NullCommand);
7074 }
7075 \f
7076 /*
7077 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7078 %                                                                             %
7079 %                                                                             %
7080 %                                                                             %
7081 +   X M a g i c k C o m m a n d                                               %
7082 %                                                                             %
7083 %                                                                             %
7084 %                                                                             %
7085 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7086 %
7087 %  XMagickCommand() makes a transform to the image or Image window as
7088 %  specified by a user menu button or keyboard command.
7089 %
7090 %  The format of the XMagickCommand method is:
7091 %
7092 %      Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7093 %        XWindows *windows,const CommandType command,Image **image,
7094 %        ExceptionInfo *exception)
7095 %
7096 %  A description of each parameter follows:
7097 %
7098 %    o display: Specifies a connection to an X server; returned from
7099 %      XOpenDisplay.
7100 %
7101 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
7102 %
7103 %    o windows: Specifies a pointer to a XWindows structure.
7104 %
7105 %    o command: Specifies a command to perform.
7106 %
7107 %    o image: the image;  XMagickCommand may transform the image and return a
7108 %      new image pointer.
7109 %
7110 %    o exception: return any errors or warnings in this structure.
7111 %
7112 */
7113 static Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7114   XWindows *windows,const CommandType command,Image **image,
7115   ExceptionInfo *exception)
7116 {
7117   char
7118     filename[MaxTextExtent],
7119     geometry[MaxTextExtent],
7120     modulate_factors[MaxTextExtent];
7121
7122   GeometryInfo
7123     geometry_info;
7124
7125   Image
7126     *nexus;
7127
7128   ImageInfo
7129     *image_info;
7130
7131   int
7132     x,
7133     y;
7134
7135   MagickStatusType
7136     flags,
7137     status;
7138
7139   QuantizeInfo
7140     quantize_info;
7141
7142   RectangleInfo
7143     page_geometry;
7144
7145   register int
7146     i;
7147
7148   static char
7149     color[MaxTextExtent] = "gray";
7150
7151   unsigned int
7152     height,
7153     width;
7154
7155   /*
7156     Process user command.
7157   */
7158   XCheckRefreshWindows(display,windows);
7159   XImageCache(display,resource_info,windows,command,image,exception);
7160   nexus=NewImageList();
7161   windows->image.window_changes.width=windows->image.ximage->width;
7162   windows->image.window_changes.height=windows->image.ximage->height;
7163   image_info=CloneImageInfo(resource_info->image_info);
7164   SetGeometryInfo(&geometry_info);
7165   GetQuantizeInfo(&quantize_info);
7166   switch (command)
7167   {
7168     case OpenCommand:
7169     {
7170       /*
7171         Load image.
7172       */
7173       nexus=XOpenImage(display,resource_info,windows,MagickFalse);
7174       break;
7175     }
7176     case NextCommand:
7177     {
7178       /*
7179         Display next image.
7180       */
7181       for (i=0; i < resource_info->quantum; i++)
7182         XClientMessage(display,windows->image.id,windows->im_protocols,
7183           windows->im_next_image,CurrentTime);
7184       break;
7185     }
7186     case FormerCommand:
7187     {
7188       /*
7189         Display former image.
7190       */
7191       for (i=0; i < resource_info->quantum; i++)
7192         XClientMessage(display,windows->image.id,windows->im_protocols,
7193           windows->im_former_image,CurrentTime);
7194       break;
7195     }
7196     case SelectCommand:
7197     {
7198       int
7199         status;
7200
7201       /*
7202         Select image.
7203       */
7204       status=chdir(resource_info->home_directory);
7205       if (status == -1)
7206         (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
7207           "UnableToOpenFile","%s",resource_info->home_directory);
7208       nexus=XOpenImage(display,resource_info,windows,MagickTrue);
7209       break;
7210     }
7211     case SaveCommand:
7212     {
7213       /*
7214         Save image.
7215       */
7216       status=XSaveImage(display,resource_info,windows,*image,exception);
7217       if( IfMagickFalse(status) )
7218         {
7219           char
7220             message[MaxTextExtent];
7221
7222           (void) FormatLocaleString(message,MaxTextExtent,"%s:%s",
7223             exception->reason != (char *) NULL ? exception->reason : "",
7224             exception->description != (char *) NULL ? exception->description :
7225             "");
7226           XNoticeWidget(display,windows,"Unable to save file:",message);
7227           break;
7228         }
7229       break;
7230     }
7231     case PrintCommand:
7232     {
7233       /*
7234         Print image.
7235       */
7236       status=XPrintImage(display,resource_info,windows,*image,exception);
7237       if( IfMagickFalse(status) )
7238         {
7239           char
7240             message[MaxTextExtent];
7241
7242           (void) FormatLocaleString(message,MaxTextExtent,"%s:%s",
7243             exception->reason != (char *) NULL ? exception->reason : "",
7244             exception->description != (char *) NULL ? exception->description :
7245             "");
7246           XNoticeWidget(display,windows,"Unable to print file:",message);
7247           break;
7248         }
7249       break;
7250     }
7251     case DeleteCommand:
7252     {
7253       static char
7254         filename[MaxTextExtent] = "\0";
7255
7256       /*
7257         Delete image file.
7258       */
7259       XFileBrowserWidget(display,windows,"Delete",filename);
7260       if (*filename == '\0')
7261         break;
7262       status=IsMagickTrue(remove_utf8(filename));
7263       if( IfMagickTrue(status) )
7264         XNoticeWidget(display,windows,"Unable to delete image file:",filename);
7265       break;
7266     }
7267     case NewCommand:
7268     {
7269       int
7270         status;
7271
7272       static char
7273         color[MaxTextExtent] = "gray",
7274         geometry[MaxTextExtent] = "640x480";
7275
7276       static const char
7277         *format = "gradient";
7278
7279       /*
7280         Query user for canvas geometry.
7281       */
7282       status=XDialogWidget(display,windows,"New","Enter image geometry:",
7283         geometry);
7284       if (*geometry == '\0')
7285         break;
7286       if (status == 0)
7287         format="xc";
7288       XColorBrowserWidget(display,windows,"Select",color);
7289       if (*color == '\0')
7290         break;
7291       /*
7292         Create canvas.
7293       */
7294       (void) FormatLocaleString(image_info->filename,MaxTextExtent,
7295         "%s:%s",format,color);
7296       (void) CloneString(&image_info->size,geometry);
7297       nexus=ReadImage(image_info,exception);
7298       CatchException(exception);
7299       XClientMessage(display,windows->image.id,windows->im_protocols,
7300         windows->im_next_image,CurrentTime);
7301       break;
7302     }
7303     case VisualDirectoryCommand:
7304     {
7305       /*
7306         Visual Image directory.
7307       */
7308       nexus=XVisualDirectoryImage(display,resource_info,windows,exception);
7309       break;
7310     }
7311     case QuitCommand:
7312     {
7313       /*
7314         exit program.
7315       */
7316       if( IfMagickFalse(resource_info->confirm_exit) )
7317         XClientMessage(display,windows->image.id,windows->im_protocols,
7318           windows->im_exit,CurrentTime);
7319       else
7320         {
7321           int
7322             status;
7323
7324           /*
7325             Confirm program exit.
7326           */
7327           status=XConfirmWidget(display,windows,"Do you really want to exit",
7328             resource_info->client_name);
7329           if (status > 0)
7330             XClientMessage(display,windows->image.id,windows->im_protocols,
7331               windows->im_exit,CurrentTime);
7332         }
7333       break;
7334     }
7335     case CutCommand:
7336     {
7337       /*
7338         Cut image.
7339       */
7340       (void) XCropImage(display,resource_info,windows,*image,CutMode,exception);
7341       break;
7342     }
7343     case CopyCommand:
7344     {
7345       /*
7346         Copy image.
7347       */
7348       (void) XCropImage(display,resource_info,windows,*image,CopyMode,
7349         exception);
7350       break;
7351     }
7352     case PasteCommand:
7353     {
7354       /*
7355         Paste image.
7356       */
7357       status=XPasteImage(display,resource_info,windows,*image,exception);
7358       if( IfMagickFalse(status) )
7359         {
7360           XNoticeWidget(display,windows,"Unable to paste X image",
7361             (*image)->filename);
7362           break;
7363         }
7364       break;
7365     }
7366     case HalfSizeCommand:
7367     {
7368       /*
7369         Half image size.
7370       */
7371       windows->image.window_changes.width=windows->image.ximage->width/2;
7372       windows->image.window_changes.height=windows->image.ximage->height/2;
7373       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7374       break;
7375     }
7376     case OriginalSizeCommand:
7377     {
7378       /*
7379         Original image size.
7380       */
7381       windows->image.window_changes.width=(int) (*image)->columns;
7382       windows->image.window_changes.height=(int) (*image)->rows;
7383       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7384       break;
7385     }
7386     case DoubleSizeCommand:
7387     {
7388       /*
7389         Double the image size.
7390       */
7391       windows->image.window_changes.width=windows->image.ximage->width << 1;
7392       windows->image.window_changes.height=windows->image.ximage->height << 1;
7393       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7394       break;
7395     }
7396     case ResizeCommand:
7397     {
7398       int
7399         status;
7400
7401       size_t
7402         height,
7403         width;
7404
7405       ssize_t
7406         x,
7407         y;
7408
7409       /*
7410         Resize image.
7411       */
7412       width=(size_t) windows->image.ximage->width;
7413       height=(size_t) windows->image.ximage->height;
7414       x=0;
7415       y=0;
7416       (void) FormatLocaleString(geometry,MaxTextExtent,"%.20gx%.20g+0+0",
7417         (double) width,(double) height);
7418       status=XDialogWidget(display,windows,"Resize",
7419         "Enter resize geometry (e.g. 640x480, 200%):",geometry);
7420       if (*geometry == '\0')
7421         break;
7422       if (status == 0)
7423         (void) ConcatenateMagickString(geometry,"!",MaxTextExtent);
7424       (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
7425       windows->image.window_changes.width=(int) width;
7426       windows->image.window_changes.height=(int) height;
7427       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7428       break;
7429     }
7430     case ApplyCommand:
7431     {
7432       char
7433         image_geometry[MaxTextExtent];
7434
7435       if ((windows->image.crop_geometry == (char *) NULL) &&
7436           ((int) (*image)->columns == windows->image.ximage->width) &&
7437           ((int) (*image)->rows == windows->image.ximage->height))
7438         break;
7439       /*
7440         Apply size transforms to image.
7441       */
7442       XSetCursorState(display,windows,MagickTrue);
7443       XCheckRefreshWindows(display,windows);
7444       /*
7445         Crop and/or scale displayed image.
7446       */
7447       (void) FormatLocaleString(image_geometry,MaxTextExtent,"%dx%d!",
7448         windows->image.ximage->width,windows->image.ximage->height);
7449       (void) TransformImage(image,windows->image.crop_geometry,image_geometry,
7450         exception);
7451       if (windows->image.crop_geometry != (char *) NULL)
7452         windows->image.crop_geometry=(char *) RelinquishMagickMemory(
7453           windows->image.crop_geometry);
7454       windows->image.x=0;
7455       windows->image.y=0;
7456       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7457       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7458       break;
7459     }
7460     case RefreshCommand:
7461     {
7462       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7463       break;
7464     }
7465     case RestoreCommand:
7466     {
7467       /*
7468         Restore Image window to its original size.
7469       */
7470       if ((windows->image.width == (unsigned int) (*image)->columns) &&
7471           (windows->image.height == (unsigned int) (*image)->rows) &&
7472           (windows->image.crop_geometry == (char *) NULL))
7473         {
7474           (void) XBell(display,0);
7475           break;
7476         }
7477       windows->image.window_changes.width=(int) (*image)->columns;
7478       windows->image.window_changes.height=(int) (*image)->rows;
7479       if (windows->image.crop_geometry != (char *) NULL)
7480         {
7481           windows->image.crop_geometry=(char *)
7482             RelinquishMagickMemory(windows->image.crop_geometry);
7483           windows->image.crop_geometry=(char *) NULL;
7484           windows->image.x=0;
7485           windows->image.y=0;
7486         }
7487       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7488       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7489       break;
7490     }
7491     case CropCommand:
7492     {
7493       /*
7494         Crop image.
7495       */
7496       (void) XCropImage(display,resource_info,windows,*image,CropMode,
7497         exception);
7498       break;
7499     }
7500     case ChopCommand:
7501     {
7502       /*
7503         Chop image.
7504       */
7505       status=XChopImage(display,resource_info,windows,image,exception);
7506       if( IfMagickFalse(status) )
7507         {
7508           XNoticeWidget(display,windows,"Unable to cut X image",
7509             (*image)->filename);
7510           break;
7511         }
7512       break;
7513     }
7514     case FlopCommand:
7515     {
7516       Image
7517         *flop_image;
7518
7519       /*
7520         Flop image scanlines.
7521       */
7522       XSetCursorState(display,windows,MagickTrue);
7523       XCheckRefreshWindows(display,windows);
7524       flop_image=FlopImage(*image,exception);
7525       if (flop_image != (Image *) NULL)
7526         {
7527           *image=DestroyImage(*image);
7528           *image=flop_image;
7529         }
7530       CatchException(exception);
7531       XSetCursorState(display,windows,MagickFalse);
7532       if (windows->image.crop_geometry != (char *) NULL)
7533         {
7534           /*
7535             Flop crop geometry.
7536           */
7537           width=(unsigned int) (*image)->columns;
7538           height=(unsigned int) (*image)->rows;
7539           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7540             &width,&height);
7541           (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
7542             "%ux%u%+d%+d",width,height,(int) (*image)->columns-(int) width-x,y);
7543         }
7544       if( IfMagickTrue(windows->image.orphan) )
7545         break;
7546       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7547       break;
7548     }
7549     case FlipCommand:
7550     {
7551       Image
7552         *flip_image;
7553
7554       /*
7555         Flip image scanlines.
7556       */
7557       XSetCursorState(display,windows,MagickTrue);
7558       XCheckRefreshWindows(display,windows);
7559       flip_image=FlipImage(*image,exception);
7560       if (flip_image != (Image *) NULL)
7561         {
7562           *image=DestroyImage(*image);
7563           *image=flip_image;
7564         }
7565       CatchException(exception);
7566       XSetCursorState(display,windows,MagickFalse);
7567       if (windows->image.crop_geometry != (char *) NULL)
7568         {
7569           /*
7570             Flip crop geometry.
7571           */
7572           width=(unsigned int) (*image)->columns;
7573           height=(unsigned int) (*image)->rows;
7574           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7575             &width,&height);
7576           (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
7577             "%ux%u%+d%+d",width,height,x,(int) (*image)->rows-(int) height-y);
7578         }
7579       if( IfMagickTrue(windows->image.orphan) )
7580         break;
7581       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7582       break;
7583     }
7584     case RotateRightCommand:
7585     {
7586       /*
7587         Rotate image 90 degrees clockwise.
7588       */
7589       status=XRotateImage(display,resource_info,windows,90.0,image,exception);
7590       if( IfMagickFalse(status) )
7591         {
7592           XNoticeWidget(display,windows,"Unable to rotate X image",
7593             (*image)->filename);
7594           break;
7595         }
7596       break;
7597     }
7598     case RotateLeftCommand:
7599     {
7600       /*
7601         Rotate image 90 degrees counter-clockwise.
7602       */
7603       status=XRotateImage(display,resource_info,windows,-90.0,image,exception);
7604       if( IfMagickFalse(status) )
7605         {
7606           XNoticeWidget(display,windows,"Unable to rotate X image",
7607             (*image)->filename);
7608           break;
7609         }
7610       break;
7611     }
7612     case RotateCommand:
7613     {
7614       /*
7615         Rotate image.
7616       */
7617       status=XRotateImage(display,resource_info,windows,0.0,image,exception);
7618       if( IfMagickFalse(status) )
7619         {
7620           XNoticeWidget(display,windows,"Unable to rotate X image",
7621             (*image)->filename);
7622           break;
7623         }
7624       break;
7625     }
7626     case ShearCommand:
7627     {
7628       Image
7629         *shear_image;
7630
7631       static char
7632         geometry[MaxTextExtent] = "45.0x45.0";
7633
7634       /*
7635         Query user for shear color and geometry.
7636       */
7637       XColorBrowserWidget(display,windows,"Select",color);
7638       if (*color == '\0')
7639         break;
7640       (void) XDialogWidget(display,windows,"Shear","Enter shear geometry:",
7641         geometry);
7642       if (*geometry == '\0')
7643         break;
7644       /*
7645         Shear image.
7646       */
7647       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
7648         exception);
7649       XSetCursorState(display,windows,MagickTrue);
7650       XCheckRefreshWindows(display,windows);
7651       (void) QueryColorCompliance(color,AllCompliance,
7652         &(*image)->background_color,exception);
7653       flags=ParseGeometry(geometry,&geometry_info);
7654       if ((flags & SigmaValue) == 0)
7655         geometry_info.sigma=geometry_info.rho;
7656       shear_image=ShearImage(*image,geometry_info.rho,geometry_info.sigma,
7657         exception);
7658       if (shear_image != (Image *) NULL)
7659         {
7660           *image=DestroyImage(*image);
7661           *image=shear_image;
7662         }
7663       CatchException(exception);
7664       XSetCursorState(display,windows,MagickFalse);
7665       if( IfMagickTrue(windows->image.orphan) )
7666         break;
7667       windows->image.window_changes.width=(int) (*image)->columns;
7668       windows->image.window_changes.height=(int) (*image)->rows;
7669       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7670       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7671       break;
7672     }
7673     case RollCommand:
7674     {
7675       Image
7676         *roll_image;
7677
7678       static char
7679         geometry[MaxTextExtent] = "+2+2";
7680
7681       /*
7682         Query user for the roll geometry.
7683       */
7684       (void) XDialogWidget(display,windows,"Roll","Enter roll geometry:",
7685         geometry);
7686       if (*geometry == '\0')
7687         break;
7688       /*
7689         Roll image.
7690       */
7691       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
7692         exception);
7693       XSetCursorState(display,windows,MagickTrue);
7694       XCheckRefreshWindows(display,windows);
7695       (void) ParsePageGeometry(*image,geometry,&page_geometry,
7696         exception);
7697       roll_image=RollImage(*image,page_geometry.x,page_geometry.y,
7698         exception);
7699       if (roll_image != (Image *) NULL)
7700         {
7701           *image=DestroyImage(*image);
7702           *image=roll_image;
7703         }
7704       CatchException(exception);
7705       XSetCursorState(display,windows,MagickFalse);
7706       if( IfMagickTrue(windows->image.orphan) )
7707         break;
7708       windows->image.window_changes.width=(int) (*image)->columns;
7709       windows->image.window_changes.height=(int) (*image)->rows;
7710       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7711       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7712       break;
7713     }
7714     case TrimCommand:
7715     {
7716       static char
7717         fuzz[MaxTextExtent];
7718
7719       /*
7720         Query user for the fuzz factor.
7721       */
7722       (void) FormatLocaleString(fuzz,MaxTextExtent,"%g%%",100.0*
7723         (*image)->fuzz/(QuantumRange+1.0));
7724       (void) XDialogWidget(display,windows,"Trim","Enter fuzz factor:",fuzz);
7725       if (*fuzz == '\0')
7726         break;
7727       (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+1.0);
7728       /*
7729         Trim image.
7730       */
7731       status=XTrimImage(display,resource_info,windows,*image,exception);
7732       if( IfMagickFalse(status) )
7733         {
7734           XNoticeWidget(display,windows,"Unable to trim X image",
7735             (*image)->filename);
7736           break;
7737         }
7738       break;
7739     }
7740     case HueCommand:
7741     {
7742       static char
7743         hue_percent[MaxTextExtent] = "110";
7744
7745       /*
7746         Query user for percent hue change.
7747       */
7748       (void) XDialogWidget(display,windows,"Apply",
7749         "Enter percent change in image hue (0-200):",hue_percent);
7750       if (*hue_percent == '\0')
7751         break;
7752       /*
7753         Vary the image hue.
7754       */
7755       XSetCursorState(display,windows,MagickTrue);
7756       XCheckRefreshWindows(display,windows);
7757       (void) CopyMagickString(modulate_factors,"100.0/100.0/",MaxTextExtent);
7758       (void) ConcatenateMagickString(modulate_factors,hue_percent,
7759         MaxTextExtent);
7760       (void) ModulateImage(*image,modulate_factors,exception);
7761       XSetCursorState(display,windows,MagickFalse);
7762       if( IfMagickTrue(windows->image.orphan) )
7763         break;
7764       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7765       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7766       break;
7767     }
7768     case SaturationCommand:
7769     {
7770       static char
7771         saturation_percent[MaxTextExtent] = "110";
7772
7773       /*
7774         Query user for percent saturation change.
7775       */
7776       (void) XDialogWidget(display,windows,"Apply",
7777         "Enter percent change in color saturation (0-200):",saturation_percent);
7778       if (*saturation_percent == '\0')
7779         break;
7780       /*
7781         Vary color saturation.
7782       */
7783       XSetCursorState(display,windows,MagickTrue);
7784       XCheckRefreshWindows(display,windows);
7785       (void) CopyMagickString(modulate_factors,"100.0/",MaxTextExtent);
7786       (void) ConcatenateMagickString(modulate_factors,saturation_percent,
7787         MaxTextExtent);
7788       (void) ModulateImage(*image,modulate_factors,exception);
7789       XSetCursorState(display,windows,MagickFalse);
7790       if( IfMagickTrue(windows->image.orphan) )
7791         break;
7792       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7793       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7794       break;
7795     }
7796     case BrightnessCommand:
7797     {
7798       static char
7799         brightness_percent[MaxTextExtent] = "110";
7800
7801       /*
7802         Query user for percent brightness change.
7803       */
7804       (void) XDialogWidget(display,windows,"Apply",
7805         "Enter percent change in color brightness (0-200):",brightness_percent);
7806       if (*brightness_percent == '\0')
7807         break;
7808       /*
7809         Vary the color brightness.
7810       */
7811       XSetCursorState(display,windows,MagickTrue);
7812       XCheckRefreshWindows(display,windows);
7813       (void) CopyMagickString(modulate_factors,brightness_percent,
7814         MaxTextExtent);
7815       (void) ModulateImage(*image,modulate_factors,exception);
7816       XSetCursorState(display,windows,MagickFalse);
7817       if( IfMagickTrue(windows->image.orphan) )
7818         break;
7819       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7820       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7821       break;
7822     }
7823     case GammaCommand:
7824     {
7825       static char
7826         factor[MaxTextExtent] = "1.6";
7827
7828       /*
7829         Query user for gamma value.
7830       */
7831       (void) XDialogWidget(display,windows,"Gamma",
7832         "Enter gamma value (e.g. 1.2):",factor);
7833       if (*factor == '\0')
7834         break;
7835       /*
7836         Gamma correct image.
7837       */
7838       XSetCursorState(display,windows,MagickTrue);
7839       XCheckRefreshWindows(display,windows);
7840       (void) GammaImage(*image,atof(factor),exception);
7841       XSetCursorState(display,windows,MagickFalse);
7842       if( IfMagickTrue(windows->image.orphan) )
7843         break;
7844       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7845       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7846       break;
7847     }
7848     case SpiffCommand:
7849     {
7850       /*
7851         Sharpen the image contrast.
7852       */
7853       XSetCursorState(display,windows,MagickTrue);
7854       XCheckRefreshWindows(display,windows);
7855       (void) ContrastImage(*image,MagickTrue,exception);
7856       XSetCursorState(display,windows,MagickFalse);
7857       if( IfMagickTrue(windows->image.orphan) )
7858         break;
7859       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7860       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7861       break;
7862     }
7863     case DullCommand:
7864     {
7865       /*
7866         Dull the image contrast.
7867       */
7868       XSetCursorState(display,windows,MagickTrue);
7869       XCheckRefreshWindows(display,windows);
7870       (void) ContrastImage(*image,MagickFalse,exception);
7871       XSetCursorState(display,windows,MagickFalse);
7872       if( IfMagickTrue(windows->image.orphan) )
7873         break;
7874       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7875       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7876       break;
7877     }
7878     case ContrastStretchCommand:
7879     {
7880       double
7881         black_point,
7882         white_point;
7883
7884       static char
7885         levels[MaxTextExtent] = "1%";
7886
7887       /*
7888         Query user for gamma value.
7889       */
7890       (void) XDialogWidget(display,windows,"Contrast Stretch",
7891         "Enter black and white points:",levels);
7892       if (*levels == '\0')
7893         break;
7894       /*
7895         Contrast stretch image.
7896       */
7897       XSetCursorState(display,windows,MagickTrue);
7898       XCheckRefreshWindows(display,windows);
7899       flags=ParseGeometry(levels,&geometry_info);
7900       black_point=geometry_info.rho;
7901       white_point=(flags & SigmaValue) != 0 ? geometry_info.sigma : black_point;
7902       if ((flags & PercentValue) != 0)
7903         {
7904           black_point*=(double) (*image)->columns*(*image)->rows/100.0;
7905           white_point*=(double) (*image)->columns*(*image)->rows/100.0;
7906         }
7907       white_point=(double) (*image)->columns*(*image)->rows-white_point;
7908       (void) ContrastStretchImage(*image,black_point,white_point,
7909         exception);
7910       XSetCursorState(display,windows,MagickFalse);
7911       if( IfMagickTrue(windows->image.orphan) )
7912         break;
7913       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7914       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7915       break;
7916     }
7917     case SigmoidalContrastCommand:
7918     {
7919       GeometryInfo
7920         geometry_info;
7921
7922       MagickStatusType
7923         flags;
7924
7925       static char
7926         levels[MaxTextExtent] = "3x50%";
7927
7928       /*
7929         Query user for gamma value.
7930       */
7931       (void) XDialogWidget(display,windows,"Sigmoidal Contrast",
7932         "Enter contrast and midpoint:",levels);
7933       if (*levels == '\0')
7934         break;
7935       /*
7936         Contrast stretch image.
7937       */
7938       XSetCursorState(display,windows,MagickTrue);
7939       XCheckRefreshWindows(display,windows);
7940       flags=ParseGeometry(levels,&geometry_info);
7941       if ((flags & SigmaValue) == 0)
7942         geometry_info.sigma=1.0*QuantumRange/2.0;
7943       if ((flags & PercentValue) != 0)
7944         geometry_info.sigma=1.0*QuantumRange*geometry_info.sigma/100.0;
7945       (void) SigmoidalContrastImage(*image,MagickTrue,geometry_info.rho,
7946         geometry_info.sigma,exception);
7947       XSetCursorState(display,windows,MagickFalse);
7948       if( IfMagickTrue(windows->image.orphan) )
7949         break;
7950       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7951       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7952       break;
7953     }
7954     case NormalizeCommand:
7955     {
7956       /*
7957         Perform histogram normalization on the image.
7958       */
7959       XSetCursorState(display,windows,MagickTrue);
7960       XCheckRefreshWindows(display,windows);
7961       (void) NormalizeImage(*image,exception);
7962       XSetCursorState(display,windows,MagickFalse);
7963       if( IfMagickTrue(windows->image.orphan) )
7964         break;
7965       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7966       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7967       break;
7968     }
7969     case EqualizeCommand:
7970     {
7971       /*
7972         Perform histogram equalization on the image.
7973       */
7974       XSetCursorState(display,windows,MagickTrue);
7975       XCheckRefreshWindows(display,windows);
7976       (void) EqualizeImage(*image,exception);
7977       XSetCursorState(display,windows,MagickFalse);
7978       if( IfMagickTrue(windows->image.orphan) )
7979         break;
7980       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7981       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7982       break;
7983     }
7984     case NegateCommand:
7985     {
7986       /*
7987         Negate colors in image.
7988       */
7989       XSetCursorState(display,windows,MagickTrue);
7990       XCheckRefreshWindows(display,windows);
7991       (void) NegateImage(*image,MagickFalse,exception);
7992       XSetCursorState(display,windows,MagickFalse);
7993       if( IfMagickTrue(windows->image.orphan) )
7994         break;
7995       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7996       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7997       break;
7998     }
7999     case GrayscaleCommand:
8000     {
8001       /*
8002         Convert image to grayscale.
8003       */
8004       XSetCursorState(display,windows,MagickTrue);
8005       XCheckRefreshWindows(display,windows);
8006       (void) SetImageType(*image,(*image)->alpha_trait != BlendPixelTrait ?
8007         GrayscaleType : GrayscaleMatteType,exception);
8008       XSetCursorState(display,windows,MagickFalse);
8009       if( IfMagickTrue(windows->image.orphan) )
8010         break;
8011       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8012       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8013       break;
8014     }
8015     case MapCommand:
8016     {
8017       Image
8018         *affinity_image;
8019
8020       static char
8021         filename[MaxTextExtent] = "\0";
8022
8023       /*
8024         Request image file name from user.
8025       */
8026       XFileBrowserWidget(display,windows,"Map",filename);
8027       if (*filename == '\0')
8028         break;
8029       /*
8030         Map image.
8031       */
8032       XSetCursorState(display,windows,MagickTrue);
8033       XCheckRefreshWindows(display,windows);
8034       (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
8035       affinity_image=ReadImage(image_info,exception);
8036       if (affinity_image != (Image *) NULL)
8037         {
8038           (void) RemapImage(&quantize_info,*image,affinity_image,exception);
8039           affinity_image=DestroyImage(affinity_image);
8040         }
8041       CatchException(exception);
8042       XSetCursorState(display,windows,MagickFalse);
8043       if( IfMagickTrue(windows->image.orphan) )
8044         break;
8045       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8046       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8047       break;
8048     }
8049     case QuantizeCommand:
8050     {
8051       int
8052         status;
8053
8054       static char
8055         colors[MaxTextExtent] = "256";
8056
8057       /*
8058         Query user for maximum number of colors.
8059       */
8060       status=XDialogWidget(display,windows,"Quantize",
8061         "Maximum number of colors:",colors);
8062       if (*colors == '\0')
8063         break;
8064       /*
8065         Color reduce the image.
8066       */
8067       XSetCursorState(display,windows,MagickTrue);
8068       XCheckRefreshWindows(display,windows);
8069       quantize_info.number_colors=StringToUnsignedLong(colors);
8070       quantize_info.dither_method=status != 0 ? RiemersmaDitherMethod :
8071         NoDitherMethod;
8072       (void) QuantizeImage(&quantize_info,*image,exception);
8073       XSetCursorState(display,windows,MagickFalse);
8074       if( IfMagickTrue(windows->image.orphan) )
8075         break;
8076       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8077       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8078       break;
8079     }
8080     case DespeckleCommand:
8081     {
8082       Image
8083         *despeckle_image;
8084
8085       /*
8086         Despeckle image.
8087       */
8088       XSetCursorState(display,windows,MagickTrue);
8089       XCheckRefreshWindows(display,windows);
8090       despeckle_image=DespeckleImage(*image,exception);
8091       if (despeckle_image != (Image *) NULL)
8092         {
8093           *image=DestroyImage(*image);
8094           *image=despeckle_image;
8095         }
8096       CatchException(exception);
8097       XSetCursorState(display,windows,MagickFalse);
8098       if( IfMagickTrue(windows->image.orphan) )
8099         break;
8100       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8101       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8102       break;
8103     }
8104     case EmbossCommand:
8105     {
8106       Image
8107         *emboss_image;
8108
8109       static char
8110         radius[MaxTextExtent] = "0.0x1.0";
8111
8112       /*
8113         Query user for emboss radius.
8114       */
8115       (void) XDialogWidget(display,windows,"Emboss",
8116         "Enter the emboss radius and standard deviation:",radius);
8117       if (*radius == '\0')
8118         break;
8119       /*
8120         Reduce noise in the image.
8121       */
8122       XSetCursorState(display,windows,MagickTrue);
8123       XCheckRefreshWindows(display,windows);
8124       flags=ParseGeometry(radius,&geometry_info);
8125       if ((flags & SigmaValue) == 0)
8126         geometry_info.sigma=1.0;
8127       emboss_image=EmbossImage(*image,geometry_info.rho,geometry_info.sigma,
8128         exception);
8129       if (emboss_image != (Image *) NULL)
8130         {
8131           *image=DestroyImage(*image);
8132           *image=emboss_image;
8133         }
8134       CatchException(exception);
8135       XSetCursorState(display,windows,MagickFalse);
8136       if( IfMagickTrue(windows->image.orphan) )
8137         break;
8138       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8139       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8140       break;
8141     }
8142     case ReduceNoiseCommand:
8143     {
8144       Image
8145         *noise_image;
8146
8147       static char
8148         radius[MaxTextExtent] = "0";
8149
8150       /*
8151         Query user for noise radius.
8152       */
8153       (void) XDialogWidget(display,windows,"Reduce Noise",
8154         "Enter the noise radius:",radius);
8155       if (*radius == '\0')
8156         break;
8157       /*
8158         Reduce noise in the image.
8159       */
8160       XSetCursorState(display,windows,MagickTrue);
8161       XCheckRefreshWindows(display,windows);
8162       flags=ParseGeometry(radius,&geometry_info);
8163       noise_image=StatisticImage(*image,NonpeakStatistic,(size_t)
8164         geometry_info.rho,(size_t) geometry_info.rho,exception);
8165       if (noise_image != (Image *) NULL)
8166         {
8167           *image=DestroyImage(*image);
8168           *image=noise_image;
8169         }
8170       CatchException(exception);
8171       XSetCursorState(display,windows,MagickFalse);
8172       if( IfMagickTrue(windows->image.orphan) )
8173         break;
8174       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8175       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8176       break;
8177     }
8178     case AddNoiseCommand:
8179     {
8180       char
8181         **noises;
8182
8183       Image
8184         *noise_image;
8185
8186       static char
8187         noise_type[MaxTextExtent] = "Gaussian";
8188
8189       /*
8190         Add noise to the image.
8191       */
8192       noises=GetCommandOptions(MagickNoiseOptions);
8193       if (noises == (char **) NULL)
8194         break;
8195       XListBrowserWidget(display,windows,&windows->widget,
8196         (const char **) noises,"Add Noise",
8197         "Select a type of noise to add to your image:",noise_type);
8198       noises=DestroyStringList(noises);
8199       if (*noise_type == '\0')
8200         break;
8201       XSetCursorState(display,windows,MagickTrue);
8202       XCheckRefreshWindows(display,windows);
8203       noise_image=AddNoiseImage(*image,(NoiseType) ParseCommandOption(
8204         MagickNoiseOptions,MagickFalse,noise_type),1.0,exception);
8205       if (noise_image != (Image *) NULL)
8206         {
8207           *image=DestroyImage(*image);
8208           *image=noise_image;
8209         }
8210       CatchException(exception);
8211       XSetCursorState(display,windows,MagickFalse);
8212       if( IfMagickTrue(windows->image.orphan) )
8213         break;
8214       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8215       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8216       break;
8217     }
8218     case SharpenCommand:
8219     {
8220       Image
8221         *sharp_image;
8222
8223       static char
8224         radius[MaxTextExtent] = "0.0x1.0";
8225
8226       /*
8227         Query user for sharpen radius.
8228       */
8229       (void) XDialogWidget(display,windows,"Sharpen",
8230         "Enter the sharpen radius and standard deviation:",radius);
8231       if (*radius == '\0')
8232         break;
8233       /*
8234         Sharpen image scanlines.
8235       */
8236       XSetCursorState(display,windows,MagickTrue);
8237       XCheckRefreshWindows(display,windows);
8238       flags=ParseGeometry(radius,&geometry_info);
8239       sharp_image=SharpenImage(*image,geometry_info.rho,geometry_info.sigma,
8240         exception);
8241       if (sharp_image != (Image *) NULL)
8242         {
8243           *image=DestroyImage(*image);
8244           *image=sharp_image;
8245         }
8246       CatchException(exception);
8247       XSetCursorState(display,windows,MagickFalse);
8248       if( IfMagickTrue(windows->image.orphan) )
8249         break;
8250       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8251       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8252       break;
8253     }
8254     case BlurCommand:
8255     {
8256       Image
8257         *blur_image;
8258
8259       static char
8260         radius[MaxTextExtent] = "0.0x1.0";
8261
8262       /*
8263         Query user for blur radius.
8264       */
8265       (void) XDialogWidget(display,windows,"Blur",
8266         "Enter the blur radius and standard deviation:",radius);
8267       if (*radius == '\0')
8268         break;
8269       /*
8270         Blur an image.
8271       */
8272       XSetCursorState(display,windows,MagickTrue);
8273       XCheckRefreshWindows(display,windows);
8274       flags=ParseGeometry(radius,&geometry_info);
8275       blur_image=BlurImage(*image,geometry_info.rho,geometry_info.sigma,
8276         exception);
8277       if (blur_image != (Image *) NULL)
8278         {
8279           *image=DestroyImage(*image);
8280           *image=blur_image;
8281         }
8282       CatchException(exception);
8283       XSetCursorState(display,windows,MagickFalse);
8284       if( IfMagickTrue(windows->image.orphan) )
8285         break;
8286       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8287       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8288       break;
8289     }
8290     case ThresholdCommand:
8291     {
8292       double
8293         threshold;
8294
8295       static char
8296         factor[MaxTextExtent] = "128";
8297
8298       /*
8299         Query user for threshold value.
8300       */
8301       (void) XDialogWidget(display,windows,"Threshold",
8302         "Enter threshold value:",factor);
8303       if (*factor == '\0')
8304         break;
8305       /*
8306         Gamma correct image.
8307       */
8308       XSetCursorState(display,windows,MagickTrue);
8309       XCheckRefreshWindows(display,windows);
8310       threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8311       (void) BilevelImage(*image,threshold,exception);
8312       XSetCursorState(display,windows,MagickFalse);
8313       if( IfMagickTrue(windows->image.orphan) )
8314         break;
8315       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8316       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8317       break;
8318     }
8319     case EdgeDetectCommand:
8320     {
8321       Image
8322         *edge_image;
8323
8324       static char
8325         radius[MaxTextExtent] = "0";
8326
8327       /*
8328         Query user for edge factor.
8329       */
8330       (void) XDialogWidget(display,windows,"Detect Edges",
8331         "Enter the edge detect radius:",radius);
8332       if (*radius == '\0')
8333         break;
8334       /*
8335         Detect edge in image.
8336       */
8337       XSetCursorState(display,windows,MagickTrue);
8338       XCheckRefreshWindows(display,windows);
8339       flags=ParseGeometry(radius,&geometry_info);
8340       edge_image=EdgeImage(*image,geometry_info.rho,geometry_info.sigma,
8341         exception);
8342       if (edge_image != (Image *) NULL)
8343         {
8344           *image=DestroyImage(*image);
8345           *image=edge_image;
8346         }
8347       CatchException(exception);
8348       XSetCursorState(display,windows,MagickFalse);
8349       if( IfMagickTrue(windows->image.orphan) )
8350         break;
8351       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8352       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8353       break;
8354     }
8355     case SpreadCommand:
8356     {
8357       Image
8358         *spread_image;
8359
8360       static char
8361         amount[MaxTextExtent] = "2";
8362
8363       /*
8364         Query user for spread amount.
8365       */
8366       (void) XDialogWidget(display,windows,"Spread",
8367         "Enter the displacement amount:",amount);
8368       if (*amount == '\0')
8369         break;
8370       /*
8371         Displace image pixels by a random amount.
8372       */
8373       XSetCursorState(display,windows,MagickTrue);
8374       XCheckRefreshWindows(display,windows);
8375       flags=ParseGeometry(amount,&geometry_info);
8376       spread_image=EdgeImage(*image,geometry_info.rho,geometry_info.sigma,
8377         exception);
8378       if (spread_image != (Image *) NULL)
8379         {
8380           *image=DestroyImage(*image);
8381           *image=spread_image;
8382         }
8383       CatchException(exception);
8384       XSetCursorState(display,windows,MagickFalse);
8385       if( IfMagickTrue(windows->image.orphan) )
8386         break;
8387       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8388       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8389       break;
8390     }
8391     case ShadeCommand:
8392     {
8393       Image
8394         *shade_image;
8395
8396       int
8397         status;
8398
8399       static char
8400         geometry[MaxTextExtent] = "30x30";
8401
8402       /*
8403         Query user for the shade geometry.
8404       */
8405       status=XDialogWidget(display,windows,"Shade",
8406         "Enter the azimuth and elevation of the light source:",geometry);
8407       if (*geometry == '\0')
8408         break;
8409       /*
8410         Shade image pixels.
8411       */
8412       XSetCursorState(display,windows,MagickTrue);
8413       XCheckRefreshWindows(display,windows);
8414       flags=ParseGeometry(geometry,&geometry_info);
8415       if ((flags & SigmaValue) == 0)
8416         geometry_info.sigma=1.0;
8417       shade_image=ShadeImage(*image,IsMagickTrue(status),
8418         geometry_info.rho,geometry_info.sigma,exception);
8419       if (shade_image != (Image *) NULL)
8420         {
8421           *image=DestroyImage(*image);
8422           *image=shade_image;
8423         }
8424       CatchException(exception);
8425       XSetCursorState(display,windows,MagickFalse);
8426       if( IfMagickTrue(windows->image.orphan) )
8427         break;
8428       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8429       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8430       break;
8431     }
8432     case RaiseCommand:
8433     {
8434       static char
8435         bevel_width[MaxTextExtent] = "10";
8436
8437       /*
8438         Query user for bevel width.
8439       */
8440       (void) XDialogWidget(display,windows,"Raise","Bevel width:",bevel_width);
8441       if (*bevel_width == '\0')
8442         break;
8443       /*
8444         Raise an image.
8445       */
8446       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8447         exception);
8448       XSetCursorState(display,windows,MagickTrue);
8449       XCheckRefreshWindows(display,windows);
8450       (void) ParsePageGeometry(*image,bevel_width,&page_geometry,
8451         exception);
8452       (void) RaiseImage(*image,&page_geometry,MagickTrue,exception);
8453       XSetCursorState(display,windows,MagickFalse);
8454       if( IfMagickTrue(windows->image.orphan) )
8455         break;
8456       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8457       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8458       break;
8459     }
8460     case SegmentCommand:
8461     {
8462       static char
8463         threshold[MaxTextExtent] = "1.0x1.5";
8464
8465       /*
8466         Query user for smoothing threshold.
8467       */
8468       (void) XDialogWidget(display,windows,"Segment","Smooth threshold:",
8469         threshold);
8470       if (*threshold == '\0')
8471         break;
8472       /*
8473         Segment an image.
8474       */
8475       XSetCursorState(display,windows,MagickTrue);
8476       XCheckRefreshWindows(display,windows);
8477       flags=ParseGeometry(threshold,&geometry_info);
8478       if ((flags & SigmaValue) == 0)
8479         geometry_info.sigma=1.0;
8480       (void) SegmentImage(*image,sRGBColorspace,MagickFalse,geometry_info.rho,
8481         geometry_info.sigma,exception);
8482       XSetCursorState(display,windows,MagickFalse);
8483       if( IfMagickTrue(windows->image.orphan) )
8484         break;
8485       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8486       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8487       break;
8488     }
8489     case SepiaToneCommand:
8490     {
8491       double
8492         threshold;
8493
8494       Image
8495         *sepia_image;
8496
8497       static char
8498         factor[MaxTextExtent] = "80%";
8499
8500       /*
8501         Query user for sepia-tone factor.
8502       */
8503       (void) XDialogWidget(display,windows,"Sepia Tone",
8504         "Enter the sepia tone factor (0 - 99.9%):",factor);
8505       if (*factor == '\0')
8506         break;
8507       /*
8508         Sepia tone image pixels.
8509       */
8510       XSetCursorState(display,windows,MagickTrue);
8511       XCheckRefreshWindows(display,windows);
8512       threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8513       sepia_image=SepiaToneImage(*image,threshold,exception);
8514       if (sepia_image != (Image *) NULL)
8515         {
8516           *image=DestroyImage(*image);
8517           *image=sepia_image;
8518         }
8519       CatchException(exception);
8520       XSetCursorState(display,windows,MagickFalse);
8521       if( IfMagickTrue(windows->image.orphan) )
8522         break;
8523       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8524       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8525       break;
8526     }
8527     case SolarizeCommand:
8528     {
8529       double
8530         threshold;
8531
8532       static char
8533         factor[MaxTextExtent] = "60%";
8534
8535       /*
8536         Query user for solarize factor.
8537       */
8538       (void) XDialogWidget(display,windows,"Solarize",
8539         "Enter the solarize factor (0 - 99.9%):",factor);
8540       if (*factor == '\0')
8541         break;
8542       /*
8543         Solarize image pixels.
8544       */
8545       XSetCursorState(display,windows,MagickTrue);
8546       XCheckRefreshWindows(display,windows);
8547       threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8548       (void) SolarizeImage(*image,threshold,exception);
8549       XSetCursorState(display,windows,MagickFalse);
8550       if( IfMagickTrue(windows->image.orphan) )
8551         break;
8552       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8553       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8554       break;
8555     }
8556     case SwirlCommand:
8557     {
8558       Image
8559         *swirl_image;
8560
8561       static char
8562         degrees[MaxTextExtent] = "60";
8563
8564       /*
8565         Query user for swirl angle.
8566       */
8567       (void) XDialogWidget(display,windows,"Swirl","Enter the swirl angle:",
8568         degrees);
8569       if (*degrees == '\0')
8570         break;
8571       /*
8572         Swirl image pixels about the center.
8573       */
8574       XSetCursorState(display,windows,MagickTrue);
8575       XCheckRefreshWindows(display,windows);
8576       flags=ParseGeometry(degrees,&geometry_info);
8577       swirl_image=SwirlImage(*image,geometry_info.rho,(*image)->interpolate,
8578         exception);
8579       if (swirl_image != (Image *) NULL)
8580         {
8581           *image=DestroyImage(*image);
8582           *image=swirl_image;
8583         }
8584       CatchException(exception);
8585       XSetCursorState(display,windows,MagickFalse);
8586       if( IfMagickTrue(windows->image.orphan) )
8587         break;
8588       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8589       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8590       break;
8591     }
8592     case ImplodeCommand:
8593     {
8594       Image
8595         *implode_image;
8596
8597       static char
8598         factor[MaxTextExtent] = "0.3";
8599
8600       /*
8601         Query user for implode factor.
8602       */
8603       (void) XDialogWidget(display,windows,"Implode",
8604         "Enter the implosion/explosion factor (-1.0 - 1.0):",factor);
8605       if (*factor == '\0')
8606         break;
8607       /*
8608         Implode image pixels about the center.
8609       */
8610       XSetCursorState(display,windows,MagickTrue);
8611       XCheckRefreshWindows(display,windows);
8612       flags=ParseGeometry(factor,&geometry_info);
8613       implode_image=ImplodeImage(*image,geometry_info.rho,(*image)->interpolate,
8614         exception);
8615       if (implode_image != (Image *) NULL)
8616         {
8617           *image=DestroyImage(*image);
8618           *image=implode_image;
8619         }
8620       CatchException(exception);
8621       XSetCursorState(display,windows,MagickFalse);
8622       if( IfMagickTrue(windows->image.orphan) )
8623         break;
8624       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8625       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8626       break;
8627     }
8628     case VignetteCommand:
8629     {
8630       Image
8631         *vignette_image;
8632
8633       static char
8634         geometry[MaxTextExtent] = "0x20";
8635
8636       /*
8637         Query user for the vignette geometry.
8638       */
8639       (void) XDialogWidget(display,windows,"Vignette",
8640         "Enter the radius, sigma, and x and y offsets:",geometry);
8641       if (*geometry == '\0')
8642         break;
8643       /*
8644         Soften the edges of the image in vignette style
8645       */
8646       XSetCursorState(display,windows,MagickTrue);
8647       XCheckRefreshWindows(display,windows);
8648       flags=ParseGeometry(geometry,&geometry_info);
8649       if ((flags & SigmaValue) == 0)
8650         geometry_info.sigma=1.0;
8651       if ((flags & XiValue) == 0)
8652         geometry_info.xi=0.1*(*image)->columns;
8653       if ((flags & PsiValue) == 0)
8654         geometry_info.psi=0.1*(*image)->rows;
8655       vignette_image=VignetteImage(*image,geometry_info.rho,0.0,(ssize_t)
8656         ceil(geometry_info.xi-0.5),(ssize_t) ceil(geometry_info.psi-0.5),
8657         exception);
8658       if (vignette_image != (Image *) NULL)
8659         {
8660           *image=DestroyImage(*image);
8661           *image=vignette_image;
8662         }
8663       CatchException(exception);
8664       XSetCursorState(display,windows,MagickFalse);
8665       if( IfMagickTrue(windows->image.orphan) )
8666         break;
8667       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8668       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8669       break;
8670     }
8671     case WaveCommand:
8672     {
8673       Image
8674         *wave_image;
8675
8676       static char
8677         geometry[MaxTextExtent] = "25x150";
8678
8679       /*
8680         Query user for the wave geometry.
8681       */
8682       (void) XDialogWidget(display,windows,"Wave",
8683         "Enter the amplitude and length of the wave:",geometry);
8684       if (*geometry == '\0')
8685         break;
8686       /*
8687         Alter an image along a sine wave.
8688       */
8689       XSetCursorState(display,windows,MagickTrue);
8690       XCheckRefreshWindows(display,windows);
8691       flags=ParseGeometry(geometry,&geometry_info);
8692       if ((flags & SigmaValue) == 0)
8693         geometry_info.sigma=1.0;
8694       wave_image=WaveImage(*image,geometry_info.rho,geometry_info.sigma,
8695         (*image)->interpolate,exception);
8696       if (wave_image != (Image *) NULL)
8697         {
8698           *image=DestroyImage(*image);
8699           *image=wave_image;
8700         }
8701       CatchException(exception);
8702       XSetCursorState(display,windows,MagickFalse);
8703       if( IfMagickTrue(windows->image.orphan) )
8704         break;
8705       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8706       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8707       break;
8708     }
8709     case OilPaintCommand:
8710     {
8711       Image
8712         *paint_image;
8713
8714       static char
8715         radius[MaxTextExtent] = "0";
8716
8717       /*
8718         Query user for circular neighborhood radius.
8719       */
8720       (void) XDialogWidget(display,windows,"Oil Paint",
8721         "Enter the mask radius:",radius);
8722       if (*radius == '\0')
8723         break;
8724       /*
8725         OilPaint image scanlines.
8726       */
8727       XSetCursorState(display,windows,MagickTrue);
8728       XCheckRefreshWindows(display,windows);
8729       flags=ParseGeometry(radius,&geometry_info);
8730       paint_image=OilPaintImage(*image,geometry_info.rho,geometry_info.sigma,
8731         exception);
8732       if (paint_image != (Image *) NULL)
8733         {
8734           *image=DestroyImage(*image);
8735           *image=paint_image;
8736         }
8737       CatchException(exception);
8738       XSetCursorState(display,windows,MagickFalse);
8739       if( IfMagickTrue(windows->image.orphan) )
8740         break;
8741       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8742       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8743       break;
8744     }
8745     case CharcoalDrawCommand:
8746     {
8747       Image
8748         *charcoal_image;
8749
8750       static char
8751         radius[MaxTextExtent] = "0x1";
8752
8753       /*
8754         Query user for charcoal radius.
8755       */
8756       (void) XDialogWidget(display,windows,"Charcoal Draw",
8757         "Enter the charcoal radius and sigma:",radius);
8758       if (*radius == '\0')
8759         break;
8760       /*
8761         Charcoal the image.
8762       */
8763       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8764         exception);
8765       XSetCursorState(display,windows,MagickTrue);
8766       XCheckRefreshWindows(display,windows);
8767       flags=ParseGeometry(radius,&geometry_info);
8768       if ((flags & SigmaValue) == 0)
8769         geometry_info.sigma=geometry_info.rho;
8770       charcoal_image=CharcoalImage(*image,geometry_info.rho,geometry_info.sigma,
8771         exception);
8772       if (charcoal_image != (Image *) NULL)
8773         {
8774           *image=DestroyImage(*image);
8775           *image=charcoal_image;
8776         }
8777       CatchException(exception);
8778       XSetCursorState(display,windows,MagickFalse);
8779       if( IfMagickTrue(windows->image.orphan) )
8780         break;
8781       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8782       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8783       break;
8784     }
8785     case AnnotateCommand:
8786     {
8787       /*
8788         Annotate the image with text.
8789       */
8790       status=XAnnotateEditImage(display,resource_info,windows,*image,exception);
8791       if( IfMagickFalse(status) )
8792         {
8793           XNoticeWidget(display,windows,"Unable to annotate X image",
8794             (*image)->filename);
8795           break;
8796         }
8797       break;
8798     }
8799     case DrawCommand:
8800     {
8801       /*
8802         Draw image.
8803       */
8804       status=XDrawEditImage(display,resource_info,windows,image,exception);
8805       if( IfMagickFalse(status) )
8806         {
8807           XNoticeWidget(display,windows,"Unable to draw on the X image",
8808             (*image)->filename);
8809           break;
8810         }
8811       break;
8812     }
8813     case ColorCommand:
8814     {
8815       /*
8816         Color edit.
8817       */
8818       status=XColorEditImage(display,resource_info,windows,image,exception);
8819       if( IfMagickFalse(status) )
8820         {
8821           XNoticeWidget(display,windows,"Unable to pixel edit X image",
8822             (*image)->filename);
8823           break;
8824         }
8825       break;
8826     }
8827     case MatteCommand:
8828     {
8829       /*
8830         Matte edit.
8831       */
8832       status=XMatteEditImage(display,resource_info,windows,image,exception);
8833       if( IfMagickFalse(status) )
8834         {
8835           XNoticeWidget(display,windows,"Unable to matte edit X image",
8836             (*image)->filename);
8837           break;
8838         }
8839       break;
8840     }
8841     case CompositeCommand:
8842     {
8843       /*
8844         Composite image.
8845       */
8846       status=XCompositeImage(display,resource_info,windows,*image,
8847         exception);
8848       if( IfMagickFalse(status) )
8849         {
8850           XNoticeWidget(display,windows,"Unable to composite X image",
8851             (*image)->filename);
8852           break;
8853         }
8854       break;
8855     }
8856     case AddBorderCommand:
8857     {
8858       Image
8859         *border_image;
8860
8861       static char
8862         geometry[MaxTextExtent] = "6x6";
8863
8864       /*
8865         Query user for border color and geometry.
8866       */
8867       XColorBrowserWidget(display,windows,"Select",color);
8868       if (*color == '\0')
8869         break;
8870       (void) XDialogWidget(display,windows,"Add Border",
8871         "Enter border geometry:",geometry);
8872       if (*geometry == '\0')
8873         break;
8874       /*
8875         Add a border to the image.
8876       */
8877       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8878         exception);
8879       XSetCursorState(display,windows,MagickTrue);
8880       XCheckRefreshWindows(display,windows);
8881       (void) QueryColorCompliance(color,AllCompliance,&(*image)->border_color,
8882         exception);
8883       (void) ParsePageGeometry(*image,geometry,&page_geometry,
8884         exception);
8885       border_image=BorderImage(*image,&page_geometry,(*image)->compose,
8886         exception);
8887       if (border_image != (Image *) NULL)
8888         {
8889           *image=DestroyImage(*image);
8890           *image=border_image;
8891         }
8892       CatchException(exception);
8893       XSetCursorState(display,windows,MagickFalse);
8894       if( IfMagickTrue(windows->image.orphan) )
8895         break;
8896       windows->image.window_changes.width=(int) (*image)->columns;
8897       windows->image.window_changes.height=(int) (*image)->rows;
8898       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8899       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8900       break;
8901     }
8902     case AddFrameCommand:
8903     {
8904       FrameInfo
8905         frame_info;
8906
8907       Image
8908         *frame_image;
8909
8910       static char
8911         geometry[MaxTextExtent] = "6x6";
8912
8913       /*
8914         Query user for frame color and geometry.
8915       */
8916       XColorBrowserWidget(display,windows,"Select",color);
8917       if (*color == '\0')
8918         break;
8919       (void) XDialogWidget(display,windows,"Add Frame","Enter frame geometry:",
8920         geometry);
8921       if (*geometry == '\0')
8922         break;
8923       /*
8924         Surround image with an ornamental border.
8925       */
8926       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8927         exception);
8928       XSetCursorState(display,windows,MagickTrue);
8929       XCheckRefreshWindows(display,windows);
8930       (void) QueryColorCompliance(color,AllCompliance,&(*image)->matte_color,
8931         exception);
8932       (void) ParsePageGeometry(*image,geometry,&page_geometry,
8933         exception);
8934       frame_info.width=page_geometry.width;
8935       frame_info.height=page_geometry.height;
8936       frame_info.outer_bevel=page_geometry.x;
8937       frame_info.inner_bevel=page_geometry.y;
8938       frame_info.x=(ssize_t) frame_info.width;
8939       frame_info.y=(ssize_t) frame_info.height;
8940       frame_info.width=(*image)->columns+2*frame_info.width;
8941       frame_info.height=(*image)->rows+2*frame_info.height;
8942       frame_image=FrameImage(*image,&frame_info,(*image)->compose,exception);
8943       if (frame_image != (Image *) NULL)
8944         {
8945           *image=DestroyImage(*image);
8946           *image=frame_image;
8947         }
8948       CatchException(exception);
8949       XSetCursorState(display,windows,MagickFalse);
8950       if( IfMagickTrue(windows->image.orphan) )
8951         break;
8952       windows->image.window_changes.width=(int) (*image)->columns;
8953       windows->image.window_changes.height=(int) (*image)->rows;
8954       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8955       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8956       break;
8957     }
8958     case CommentCommand:
8959     {
8960       const char
8961         *value;
8962
8963       FILE
8964         *file;
8965
8966       int
8967         unique_file;
8968
8969       /*
8970         Edit image comment.
8971       */
8972       unique_file=AcquireUniqueFileResource(image_info->filename);
8973       if (unique_file == -1)
8974         XNoticeWidget(display,windows,"Unable to edit image comment",
8975           image_info->filename);
8976       value=GetImageProperty(*image,"comment",exception);
8977       if (value == (char *) NULL)
8978         unique_file=close(unique_file)-1;
8979       else
8980         {
8981           register const char
8982             *p;
8983
8984           file=fdopen(unique_file,"w");
8985           if (file == (FILE *) NULL)
8986             {
8987               XNoticeWidget(display,windows,"Unable to edit image comment",
8988                 image_info->filename);
8989               break;
8990             }
8991           for (p=value; *p != '\0'; p++)
8992             (void) fputc((int) *p,file);
8993           (void) fputc('\n',file);
8994           (void) fclose(file);
8995         }
8996       XSetCursorState(display,windows,MagickTrue);
8997       XCheckRefreshWindows(display,windows);
8998       status=InvokeDelegate(image_info,*image,"edit",(char *) NULL,
8999         exception);
9000       if( IfMagickFalse(status) )
9001         XNoticeWidget(display,windows,"Unable to edit image comment",
9002           (char *) NULL);
9003       else
9004         {
9005           char
9006             *comment;
9007
9008           comment=FileToString(image_info->filename,~0UL,exception);
9009           if (comment != (char *) NULL)
9010             {
9011               (void) SetImageProperty(*image,"comment",comment,exception);
9012               (*image)->taint=MagickTrue;
9013             }
9014         }
9015       (void) RelinquishUniqueFileResource(image_info->filename);
9016       XSetCursorState(display,windows,MagickFalse);
9017       break;
9018     }
9019     case LaunchCommand:
9020     {
9021       /*
9022         Launch program.
9023       */
9024       XSetCursorState(display,windows,MagickTrue);
9025       XCheckRefreshWindows(display,windows);
9026       (void) AcquireUniqueFilename(filename);
9027       (void) FormatLocaleString((*image)->filename,MaxTextExtent,"launch:%s",
9028         filename);
9029       status=WriteImage(image_info,*image,exception);
9030       if( IfMagickFalse(status) )
9031         XNoticeWidget(display,windows,"Unable to launch image editor",
9032           (char *) NULL);
9033       else
9034         {
9035           nexus=ReadImage(resource_info->image_info,exception);
9036           CatchException(exception);
9037           XClientMessage(display,windows->image.id,windows->im_protocols,
9038             windows->im_next_image,CurrentTime);
9039         }
9040       (void) RelinquishUniqueFileResource(filename);
9041       XSetCursorState(display,windows,MagickFalse);
9042       break;
9043     }
9044     case RegionofInterestCommand:
9045     {
9046       /*
9047         Apply an image processing technique to a region of interest.
9048       */
9049       (void) XROIImage(display,resource_info,windows,image,exception);
9050       break;
9051     }
9052     case InfoCommand:
9053       break;
9054     case ZoomCommand:
9055     {
9056       /*
9057         Zoom image.
9058       */
9059       if( IfMagickTrue(windows->magnify.mapped) )
9060         (void) XRaiseWindow(display,windows->magnify.id);
9061       else
9062         {
9063           /*
9064             Make magnify image.
9065           */
9066           XSetCursorState(display,windows,MagickTrue);
9067           (void) XMapRaised(display,windows->magnify.id);
9068           XSetCursorState(display,windows,MagickFalse);
9069         }
9070       break;
9071     }
9072     case ShowPreviewCommand:
9073     {
9074       char
9075         **previews;
9076
9077       Image
9078         *preview_image;
9079
9080       static char
9081         preview_type[MaxTextExtent] = "Gamma";
9082
9083       /*
9084         Select preview type from menu.
9085       */
9086       previews=GetCommandOptions(MagickPreviewOptions);
9087       if (previews == (char **) NULL)
9088         break;
9089       XListBrowserWidget(display,windows,&windows->widget,
9090         (const char **) previews,"Preview",
9091         "Select an enhancement, effect, or F/X:",preview_type);
9092       previews=DestroyStringList(previews);
9093       if (*preview_type == '\0')
9094         break;
9095       /*
9096         Show image preview.
9097       */
9098       XSetCursorState(display,windows,MagickTrue);
9099       XCheckRefreshWindows(display,windows);
9100       image_info->preview_type=(PreviewType)
9101         ParseCommandOption(MagickPreviewOptions,MagickFalse,preview_type);
9102       image_info->group=(ssize_t) windows->image.id;
9103       (void) DeleteImageProperty(*image,"label");
9104       (void) SetImageProperty(*image,"label","Preview",exception);
9105       (void) AcquireUniqueFilename(filename);
9106       (void) FormatLocaleString((*image)->filename,MaxTextExtent,"preview:%s",
9107         filename);
9108       status=WriteImage(image_info,*image,exception);
9109       (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9110       preview_image=ReadImage(image_info,exception);
9111       (void) RelinquishUniqueFileResource(filename);
9112       if (preview_image == (Image *) NULL)
9113         break;
9114       (void) FormatLocaleString(preview_image->filename,MaxTextExtent,"show:%s",
9115         filename);
9116       status=WriteImage(image_info,preview_image,exception);
9117       preview_image=DestroyImage(preview_image);
9118       if( IfMagickFalse(status) )
9119         XNoticeWidget(display,windows,"Unable to show image preview",
9120           (*image)->filename);
9121       XDelay(display,1500);
9122       XSetCursorState(display,windows,MagickFalse);
9123       break;
9124     }
9125     case ShowHistogramCommand:
9126     {
9127       Image
9128         *histogram_image;
9129
9130       /*
9131         Show image histogram.
9132       */
9133       XSetCursorState(display,windows,MagickTrue);
9134       XCheckRefreshWindows(display,windows);
9135       image_info->group=(ssize_t) windows->image.id;
9136       (void) DeleteImageProperty(*image,"label");
9137       (void) SetImageProperty(*image,"label","Histogram",exception);
9138       (void) AcquireUniqueFilename(filename);
9139       (void) FormatLocaleString((*image)->filename,MaxTextExtent,"histogram:%s",
9140         filename);
9141       status=WriteImage(image_info,*image,exception);
9142       (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9143       histogram_image=ReadImage(image_info,exception);
9144       (void) RelinquishUniqueFileResource(filename);
9145       if (histogram_image == (Image *) NULL)
9146         break;
9147       (void) FormatLocaleString(histogram_image->filename,MaxTextExtent,
9148         "show:%s",filename);
9149       status=WriteImage(image_info,histogram_image,exception);
9150       histogram_image=DestroyImage(histogram_image);
9151       if( IfMagickFalse(status) )
9152         XNoticeWidget(display,windows,"Unable to show histogram",
9153           (*image)->filename);
9154       XDelay(display,1500);
9155       XSetCursorState(display,windows,MagickFalse);
9156       break;
9157     }
9158     case ShowMatteCommand:
9159     {
9160       Image
9161         *matte_image;
9162
9163       if ((*image)->alpha_trait != BlendPixelTrait)
9164         {
9165           XNoticeWidget(display,windows,
9166             "Image does not have any matte information",(*image)->filename);
9167           break;
9168         }
9169       /*
9170         Show image matte.
9171       */
9172       XSetCursorState(display,windows,MagickTrue);
9173       XCheckRefreshWindows(display,windows);
9174       image_info->group=(ssize_t) windows->image.id;
9175       (void) DeleteImageProperty(*image,"label");
9176       (void) SetImageProperty(*image,"label","Matte",exception);
9177       (void) AcquireUniqueFilename(filename);
9178       (void) FormatLocaleString((*image)->filename,MaxTextExtent,"matte:%s",
9179         filename);
9180       status=WriteImage(image_info,*image,exception);
9181       (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9182       matte_image=ReadImage(image_info,exception);
9183       (void) RelinquishUniqueFileResource(filename);
9184       if (matte_image == (Image *) NULL)
9185         break;
9186       (void) FormatLocaleString(matte_image->filename,MaxTextExtent,"show:%s",
9187         filename);
9188       status=WriteImage(image_info,matte_image,exception);
9189       matte_image=DestroyImage(matte_image);
9190       if( IfMagickFalse(status) )
9191         XNoticeWidget(display,windows,"Unable to show matte",
9192           (*image)->filename);
9193       XDelay(display,1500);
9194       XSetCursorState(display,windows,MagickFalse);
9195       break;
9196     }
9197     case BackgroundCommand:
9198     {
9199       /*
9200         Background image.
9201       */
9202       status=XBackgroundImage(display,resource_info,windows,image,exception);
9203       if( IfMagickFalse(status) )
9204         break;
9205       nexus=CloneImage(*image,0,0,MagickTrue,exception);
9206       if (nexus != (Image *) NULL)
9207         XClientMessage(display,windows->image.id,windows->im_protocols,
9208           windows->im_next_image,CurrentTime);
9209       break;
9210     }
9211     case SlideShowCommand:
9212     {
9213       static char
9214         delay[MaxTextExtent] = "5";
9215
9216       /*
9217         Display next image after pausing.
9218       */
9219       (void) XDialogWidget(display,windows,"Slide Show",
9220         "Pause how many 1/100ths of a second between images:",delay);
9221       if (*delay == '\0')
9222         break;
9223       resource_info->delay=StringToUnsignedLong(delay);
9224       XClientMessage(display,windows->image.id,windows->im_protocols,
9225         windows->im_next_image,CurrentTime);
9226       break;
9227     }
9228     case PreferencesCommand:
9229     {
9230       /*
9231         Set user preferences.
9232       */
9233       status=XPreferencesWidget(display,resource_info,windows);
9234       if( IfMagickFalse(status) )
9235         break;
9236       nexus=CloneImage(*image,0,0,MagickTrue,exception);
9237       if (nexus != (Image *) NULL)
9238         XClientMessage(display,windows->image.id,windows->im_protocols,
9239           windows->im_next_image,CurrentTime);
9240       break;
9241     }
9242     case HelpCommand:
9243     {
9244       /*
9245         User requested help.
9246       */
9247       XTextViewWidget(display,resource_info,windows,MagickFalse,
9248         "Help Viewer - Display",DisplayHelp);
9249       break;
9250     }
9251     case BrowseDocumentationCommand:
9252     {
9253       Atom
9254         mozilla_atom;
9255
9256       Window
9257         mozilla_window,
9258         root_window;
9259
9260       /*
9261         Browse the ImageMagick documentation.
9262       */
9263       root_window=XRootWindow(display,XDefaultScreen(display));
9264       mozilla_atom=XInternAtom(display,"_MOZILLA_VERSION",MagickFalse);
9265       mozilla_window=XWindowByProperty(display,root_window,mozilla_atom);
9266       if (mozilla_window != (Window) NULL)
9267         {
9268           char
9269             command[MaxTextExtent],
9270             *url;
9271
9272           /*
9273             Display documentation using Netscape remote control.
9274           */
9275           url=GetMagickHomeURL();
9276           (void) FormatLocaleString(command,MaxTextExtent,
9277             "openurl(%s,new-tab)",url);
9278           url=DestroyString(url);
9279           mozilla_atom=XInternAtom(display,"_MOZILLA_COMMAND",MagickFalse);
9280           (void) XChangeProperty(display,mozilla_window,mozilla_atom,XA_STRING,
9281             8,PropModeReplace,(unsigned char *) command,(int) strlen(command));
9282           XSetCursorState(display,windows,MagickFalse);
9283           break;
9284         }
9285       XSetCursorState(display,windows,MagickTrue);
9286       XCheckRefreshWindows(display,windows);
9287       status=InvokeDelegate(image_info,*image,"browse",(char *) NULL,
9288         exception);
9289       if( IfMagickFalse(status) )
9290         XNoticeWidget(display,windows,"Unable to browse documentation",
9291           (char *) NULL);
9292       XDelay(display,1500);
9293       XSetCursorState(display,windows,MagickFalse);
9294       break;
9295     }
9296     case VersionCommand:
9297     {
9298       XNoticeWidget(display,windows,GetMagickVersion((size_t *) NULL),
9299         GetMagickCopyright());
9300       break;
9301     }
9302     case SaveToUndoBufferCommand:
9303       break;
9304     default:
9305     {
9306       (void) XBell(display,0);
9307       break;
9308     }
9309   }
9310   image_info=DestroyImageInfo(image_info);
9311   return(nexus);
9312 }
9313 \f
9314 /*
9315 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9316 %                                                                             %
9317 %                                                                             %
9318 %                                                                             %
9319 +   X M a g n i f y I m a g e                                                 %
9320 %                                                                             %
9321 %                                                                             %
9322 %                                                                             %
9323 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9324 %
9325 %  XMagnifyImage() magnifies portions of the image as indicated by the pointer.
9326 %  The magnified portion is displayed in a separate window.
9327 %
9328 %  The format of the XMagnifyImage method is:
9329 %
9330 %      void XMagnifyImage(Display *display,XWindows *windows,XEvent *event,
9331 %        ExceptionInfo *exception)
9332 %
9333 %  A description of each parameter follows:
9334 %
9335 %    o display: Specifies a connection to an X server;  returned from
9336 %      XOpenDisplay.
9337 %
9338 %    o windows: Specifies a pointer to a XWindows structure.
9339 %
9340 %    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
9341 %      the entire image is refreshed.
9342 %
9343 %    o exception: return any errors or warnings in this structure.
9344 %
9345 */
9346 static void XMagnifyImage(Display *display,XWindows *windows,XEvent *event,
9347   ExceptionInfo *exception)
9348 {
9349   char
9350     text[MaxTextExtent];
9351
9352   register int
9353     x,
9354     y;
9355
9356   size_t
9357     state;
9358
9359   /*
9360     Update magnified image until the mouse button is released.
9361   */
9362   (void) XCheckDefineCursor(display,windows->image.id,windows->magnify.cursor);
9363   state=DefaultState;
9364   x=event->xbutton.x;
9365   y=event->xbutton.y;
9366   windows->magnify.x=(int) windows->image.x+x;
9367   windows->magnify.y=(int) windows->image.y+y;
9368   do
9369   {
9370     /*
9371       Map and unmap Info widget as text cursor crosses its boundaries.
9372     */
9373     if( IfMagickTrue(windows->info.mapped) )
9374       {
9375         if ((x < (int) (windows->info.x+windows->info.width)) &&
9376             (y < (int) (windows->info.y+windows->info.height)))
9377           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
9378       }
9379     else
9380       if ((x > (int) (windows->info.x+windows->info.width)) ||
9381           (y > (int) (windows->info.y+windows->info.height)))
9382         (void) XMapWindow(display,windows->info.id);
9383     if( IfMagickTrue(windows->info.mapped) )
9384       {
9385         /*
9386           Display pointer position.
9387         */
9388         (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
9389           windows->magnify.x,windows->magnify.y);
9390         XInfoWidget(display,windows,text);
9391       }
9392     /*
9393       Wait for next event.
9394     */
9395     XScreenEvent(display,windows,event,exception);
9396     switch (event->type)
9397     {
9398       case ButtonPress:
9399         break;
9400       case ButtonRelease:
9401       {
9402         /*
9403           User has finished magnifying image.
9404         */
9405         x=event->xbutton.x;
9406         y=event->xbutton.y;
9407         state|=ExitState;
9408         break;
9409       }
9410       case Expose:
9411         break;
9412       case MotionNotify:
9413       {
9414         x=event->xmotion.x;
9415         y=event->xmotion.y;
9416         break;
9417       }
9418       default:
9419         break;
9420     }
9421     /*
9422       Check boundary conditions.
9423     */
9424     if (x < 0)
9425       x=0;
9426     else
9427       if (x >= (int) windows->image.width)
9428         x=(int) windows->image.width-1;
9429     if (y < 0)
9430       y=0;
9431     else
9432      if (y >= (int) windows->image.height)
9433        y=(int) windows->image.height-1;
9434   } while ((state & ExitState) == 0);
9435   /*
9436     Display magnified image.
9437   */
9438   XSetCursorState(display,windows,MagickFalse);
9439 }
9440 \f
9441 /*
9442 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9443 %                                                                             %
9444 %                                                                             %
9445 %                                                                             %
9446 +   X M a g n i f y W i n d o w C o m m a n d                                 %
9447 %                                                                             %
9448 %                                                                             %
9449 %                                                                             %
9450 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9451 %
9452 %  XMagnifyWindowCommand() moves the image within an Magnify window by one
9453 %  pixel as specified by the key symbol.
9454 %
9455 %  The format of the XMagnifyWindowCommand method is:
9456 %
9457 %      void XMagnifyWindowCommand(Display *display,XWindows *windows,
9458 %        const MagickStatusType state,const KeySym key_symbol,
9459 %        ExceptionInfo *exception)
9460 %
9461 %  A description of each parameter follows:
9462 %
9463 %    o display: Specifies a connection to an X server; returned from
9464 %      XOpenDisplay.
9465 %
9466 %    o windows: Specifies a pointer to a XWindows structure.
9467 %
9468 %    o state: key mask.
9469 %
9470 %    o key_symbol: Specifies a KeySym which indicates which side of the image
9471 %      to trim.
9472 %
9473 %    o exception: return any errors or warnings in this structure.
9474 %
9475 */
9476 static void XMagnifyWindowCommand(Display *display,XWindows *windows,
9477   const MagickStatusType state,const KeySym key_symbol,ExceptionInfo *exception)
9478 {
9479   unsigned int
9480     quantum;
9481
9482   /*
9483     User specified a magnify factor or position.
9484   */
9485   quantum=1;
9486   if ((state & Mod1Mask) != 0)
9487     quantum=10;
9488   switch ((int) key_symbol)
9489   {
9490     case QuitCommand:
9491     {
9492       (void) XWithdrawWindow(display,windows->magnify.id,
9493         windows->magnify.screen);
9494       break;
9495     }
9496     case XK_Home:
9497     case XK_KP_Home:
9498     {
9499       windows->magnify.x=(int) windows->image.width/2;
9500       windows->magnify.y=(int) windows->image.height/2;
9501       break;
9502     }
9503     case XK_Left:
9504     case XK_KP_Left:
9505     {
9506       if (windows->magnify.x > 0)
9507         windows->magnify.x-=quantum;
9508       break;
9509     }
9510     case XK_Up:
9511     case XK_KP_Up:
9512     {
9513       if (windows->magnify.y > 0)
9514         windows->magnify.y-=quantum;
9515       break;
9516     }
9517     case XK_Right:
9518     case XK_KP_Right:
9519     {
9520       if (windows->magnify.x < (int) (windows->image.ximage->width-1))
9521         windows->magnify.x+=quantum;
9522       break;
9523     }
9524     case XK_Down:
9525     case XK_KP_Down:
9526     {
9527       if (windows->magnify.y < (int) (windows->image.ximage->height-1))
9528         windows->magnify.y+=quantum;
9529       break;
9530     }
9531     case XK_0:
9532     case XK_1:
9533     case XK_2:
9534     case XK_3:
9535     case XK_4:
9536     case XK_5:
9537     case XK_6:
9538     case XK_7:
9539     case XK_8:
9540     case XK_9:
9541     {
9542       windows->magnify.data=(key_symbol-XK_0);
9543       break;
9544     }
9545     case XK_KP_0:
9546     case XK_KP_1:
9547     case XK_KP_2:
9548     case XK_KP_3:
9549     case XK_KP_4:
9550     case XK_KP_5:
9551     case XK_KP_6:
9552     case XK_KP_7:
9553     case XK_KP_8:
9554     case XK_KP_9:
9555     {
9556       windows->magnify.data=(key_symbol-XK_KP_0);
9557       break;
9558     }
9559     default:
9560       break;
9561   }
9562   XMakeMagnifyImage(display,windows,exception);
9563 }
9564 \f
9565 /*
9566 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9567 %                                                                             %
9568 %                                                                             %
9569 %                                                                             %
9570 +   X M a k e P a n I m a g e                                                 %
9571 %                                                                             %
9572 %                                                                             %
9573 %                                                                             %
9574 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9575 %
9576 %  XMakePanImage() creates a thumbnail of the image and displays it in the Pan
9577 %  icon window.
9578 %
9579 %  The format of the XMakePanImage method is:
9580 %
9581 %        void XMakePanImage(Display *display,XResourceInfo *resource_info,
9582 %          XWindows *windows,Image *image,ExceptionInfo *exception)
9583 %
9584 %  A description of each parameter follows:
9585 %
9586 %    o display: Specifies a connection to an X server;  returned from
9587 %      XOpenDisplay.
9588 %
9589 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9590 %
9591 %    o windows: Specifies a pointer to a XWindows structure.
9592 %
9593 %    o image: the image.
9594 %
9595 %    o exception: return any errors or warnings in this structure.
9596 %
9597 */
9598 static void XMakePanImage(Display *display,XResourceInfo *resource_info,
9599   XWindows *windows,Image *image,ExceptionInfo *exception)
9600 {
9601   MagickStatusType
9602     status;
9603
9604   /*
9605     Create and display image for panning icon.
9606   */
9607   XSetCursorState(display,windows,MagickTrue);
9608   XCheckRefreshWindows(display,windows);
9609   windows->pan.x=(int) windows->image.x;
9610   windows->pan.y=(int) windows->image.y;
9611   status=XMakeImage(display,resource_info,&windows->pan,image,
9612     windows->pan.width,windows->pan.height,exception);
9613   if( IfMagickFalse(status) )
9614     ThrowXWindowFatalException(ResourceLimitError,
9615      "MemoryAllocationFailed",image->filename);
9616   (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
9617     windows->pan.pixmap);
9618   (void) XClearWindow(display,windows->pan.id);
9619   XDrawPanRectangle(display,windows);
9620   XSetCursorState(display,windows,MagickFalse);
9621 }
9622 \f
9623 /*
9624 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9625 %                                                                             %
9626 %                                                                             %
9627 %                                                                             %
9628 +   X M a t t a E d i t I m a g e                                             %
9629 %                                                                             %
9630 %                                                                             %
9631 %                                                                             %
9632 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9633 %
9634 %  XMatteEditImage() allows the user to interactively change the Matte channel
9635 %  of an image.  If the image is PseudoClass it is promoted to DirectClass
9636 %  before the matte information is stored.
9637 %
9638 %  The format of the XMatteEditImage method is:
9639 %
9640 %      MagickBooleanType XMatteEditImage(Display *display,
9641 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
9642 %        ExceptionInfo *exception)
9643 %
9644 %  A description of each parameter follows:
9645 %
9646 %    o display: Specifies a connection to an X server;  returned from
9647 %      XOpenDisplay.
9648 %
9649 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9650 %
9651 %    o windows: Specifies a pointer to a XWindows structure.
9652 %
9653 %    o image: the image; returned from ReadImage.
9654 %
9655 %    o exception: return any errors or warnings in this structure.
9656 %
9657 */
9658 static MagickBooleanType XMatteEditImage(Display *display,
9659   XResourceInfo *resource_info,XWindows *windows,Image **image,
9660   ExceptionInfo *exception)
9661 {
9662   static char
9663     matte[MaxTextExtent] = "0";
9664
9665   static const char
9666     *MatteEditMenu[] =
9667     {
9668       "Method",
9669       "Border Color",
9670       "Fuzz",
9671       "Matte Value",
9672       "Undo",
9673       "Help",
9674       "Dismiss",
9675       (char *) NULL
9676     };
9677
9678   static const ModeType
9679     MatteEditCommands[] =
9680     {
9681       MatteEditMethod,
9682       MatteEditBorderCommand,
9683       MatteEditFuzzCommand,
9684       MatteEditValueCommand,
9685       MatteEditUndoCommand,
9686       MatteEditHelpCommand,
9687       MatteEditDismissCommand
9688     };
9689
9690   static PaintMethod
9691     method = PointMethod;
9692
9693   static XColor
9694     border_color = { 0, 0, 0, 0, 0, 0 };
9695
9696   char
9697     command[MaxTextExtent],
9698     text[MaxTextExtent];
9699
9700   Cursor
9701     cursor;
9702
9703   int
9704     entry,
9705     id,
9706     x,
9707     x_offset,
9708     y,
9709     y_offset;
9710
9711   register int
9712     i;
9713
9714   register Quantum
9715     *q;
9716
9717   unsigned int
9718     height,
9719     width;
9720
9721   size_t
9722     state;
9723
9724   XEvent
9725     event;
9726
9727   /*
9728     Map Command widget.
9729   */
9730   (void) CloneString(&windows->command.name,"Matte Edit");
9731   windows->command.data=4;
9732   (void) XCommandWidget(display,windows,MatteEditMenu,(XEvent *) NULL);
9733   (void) XMapRaised(display,windows->command.id);
9734   XClientMessage(display,windows->image.id,windows->im_protocols,
9735     windows->im_update_widget,CurrentTime);
9736   /*
9737     Make cursor.
9738   */
9739   cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
9740     resource_info->background_color,resource_info->foreground_color);
9741   (void) XCheckDefineCursor(display,windows->image.id,cursor);
9742   /*
9743     Track pointer until button 1 is pressed.
9744   */
9745   XQueryPosition(display,windows->image.id,&x,&y);
9746   (void) XSelectInput(display,windows->image.id,
9747     windows->image.attributes.event_mask | PointerMotionMask);
9748   state=DefaultState;
9749   do
9750   {
9751     if( IfMagickTrue(windows->info.mapped) )
9752       {
9753         /*
9754           Display pointer position.
9755         */
9756         (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
9757           x+windows->image.x,y+windows->image.y);
9758         XInfoWidget(display,windows,text);
9759       }
9760     /*
9761       Wait for next event.
9762     */
9763     XScreenEvent(display,windows,&event,exception);
9764     if (event.xany.window == windows->command.id)
9765       {
9766         /*
9767           Select a command from the Command widget.
9768         */
9769         id=XCommandWidget(display,windows,MatteEditMenu,&event);
9770         if (id < 0)
9771           {
9772             (void) XCheckDefineCursor(display,windows->image.id,cursor);
9773             continue;
9774           }
9775         switch (MatteEditCommands[id])
9776         {
9777           case MatteEditMethod:
9778           {
9779             char
9780               **methods;
9781
9782             /*
9783               Select a method from the pop-up menu.
9784             */
9785             methods=GetCommandOptions(MagickMethodOptions);
9786             if (methods == (char **) NULL)
9787               break;
9788             entry=XMenuWidget(display,windows,MatteEditMenu[id],
9789               (const char **) methods,command);
9790             if (entry >= 0)
9791               method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
9792                 MagickFalse,methods[entry]);
9793             methods=DestroyStringList(methods);
9794             break;
9795           }
9796           case MatteEditBorderCommand:
9797           {
9798             const char
9799               *ColorMenu[MaxNumberPens];
9800
9801             int
9802               pen_number;
9803
9804             /*
9805               Initialize menu selections.
9806             */
9807             for (i=0; i < (int) (MaxNumberPens-2); i++)
9808               ColorMenu[i]=resource_info->pen_colors[i];
9809             ColorMenu[MaxNumberPens-2]="Browser...";
9810             ColorMenu[MaxNumberPens-1]=(const char *) NULL;
9811             /*
9812               Select a pen color from the pop-up menu.
9813             */
9814             pen_number=XMenuWidget(display,windows,MatteEditMenu[id],
9815               (const char **) ColorMenu,command);
9816             if (pen_number < 0)
9817               break;
9818             if (pen_number == (MaxNumberPens-2))
9819               {
9820                 static char
9821                   color_name[MaxTextExtent] = "gray";
9822
9823                 /*
9824                   Select a pen color from a dialog.
9825                 */
9826                 resource_info->pen_colors[pen_number]=color_name;
9827                 XColorBrowserWidget(display,windows,"Select",color_name);
9828                 if (*color_name == '\0')
9829                   break;
9830               }
9831             /*
9832               Set border color.
9833             */
9834             (void) XParseColor(display,windows->map_info->colormap,
9835               resource_info->pen_colors[pen_number],&border_color);
9836             break;
9837           }
9838           case MatteEditFuzzCommand:
9839           {
9840             static char
9841               fuzz[MaxTextExtent];
9842
9843             static const char
9844               *FuzzMenu[] =
9845               {
9846                 "0%",
9847                 "2%",
9848                 "5%",
9849                 "10%",
9850                 "15%",
9851                 "Dialog...",
9852                 (char *) NULL,
9853               };
9854
9855             /*
9856               Select a command from the pop-up menu.
9857             */
9858             entry=XMenuWidget(display,windows,MatteEditMenu[id],FuzzMenu,
9859               command);
9860             if (entry < 0)
9861               break;
9862             if (entry != 5)
9863               {
9864                 (*image)->fuzz=StringToDoubleInterval(FuzzMenu[entry],(double)
9865                   QuantumRange+1.0);
9866                 break;
9867               }
9868             (void) CopyMagickString(fuzz,"20%",MaxTextExtent);
9869             (void) XDialogWidget(display,windows,"Ok",
9870               "Enter fuzz factor (0.0 - 99.9%):",fuzz);
9871             if (*fuzz == '\0')
9872               break;
9873             (void) ConcatenateMagickString(fuzz,"%",MaxTextExtent);
9874             (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+
9875               1.0);
9876             break;
9877           }
9878           case MatteEditValueCommand:
9879           {
9880             static char
9881               message[MaxTextExtent];
9882
9883             static const char
9884               *MatteMenu[] =
9885               {
9886                 "Opaque",
9887                 "Transparent",
9888                 "Dialog...",
9889                 (char *) NULL,
9890               };
9891
9892             /*
9893               Select a command from the pop-up menu.
9894             */
9895             entry=XMenuWidget(display,windows,MatteEditMenu[id],MatteMenu,
9896               command);
9897             if (entry < 0)
9898               break;
9899             if (entry != 2)
9900               {
9901                 (void) FormatLocaleString(matte,MaxTextExtent,QuantumFormat,
9902                   OpaqueAlpha);
9903                 if (LocaleCompare(MatteMenu[entry],"Transparent") == 0)
9904                   (void) FormatLocaleString(matte,MaxTextExtent,QuantumFormat,
9905                     (Quantum) TransparentAlpha);
9906                 break;
9907               }
9908             (void) FormatLocaleString(message,MaxTextExtent,
9909               "Enter matte value (0 - " QuantumFormat "):",(Quantum)
9910               QuantumRange);
9911             (void) XDialogWidget(display,windows,"Matte",message,matte);
9912             if (*matte == '\0')
9913               break;
9914             break;
9915           }
9916           case MatteEditUndoCommand:
9917           {
9918             (void) XMagickCommand(display,resource_info,windows,UndoCommand,
9919               image,exception);
9920             break;
9921           }
9922           case MatteEditHelpCommand:
9923           {
9924             XTextViewWidget(display,resource_info,windows,MagickFalse,
9925               "Help Viewer - Matte Edit",ImageMatteEditHelp);
9926             break;
9927           }
9928           case MatteEditDismissCommand:
9929           {
9930             /*
9931               Prematurely exit.
9932             */
9933             state|=EscapeState;
9934             state|=ExitState;
9935             break;
9936           }
9937           default:
9938             break;
9939         }
9940         (void) XCheckDefineCursor(display,windows->image.id,cursor);
9941         continue;
9942       }
9943     switch (event.type)
9944     {
9945       case ButtonPress:
9946       {
9947         if (event.xbutton.button != Button1)
9948           break;
9949         if ((event.xbutton.window != windows->image.id) &&
9950             (event.xbutton.window != windows->magnify.id))
9951           break;
9952         /*
9953           Update matte data.
9954         */
9955         x=event.xbutton.x;
9956         y=event.xbutton.y;
9957         (void) XMagickCommand(display,resource_info,windows,
9958           SaveToUndoBufferCommand,image,exception);
9959         state|=UpdateConfigurationState;
9960         break;
9961       }
9962       case ButtonRelease:
9963       {
9964         if (event.xbutton.button != Button1)
9965           break;
9966         if ((event.xbutton.window != windows->image.id) &&
9967             (event.xbutton.window != windows->magnify.id))
9968           break;
9969         /*
9970           Update colormap information.
9971         */
9972         x=event.xbutton.x;
9973         y=event.xbutton.y;
9974         XConfigureImageColormap(display,resource_info,windows,*image,exception);
9975         (void) XConfigureImage(display,resource_info,windows,*image,exception);
9976         XInfoWidget(display,windows,text);
9977         (void) XCheckDefineCursor(display,windows->image.id,cursor);
9978         state&=(~UpdateConfigurationState);
9979         break;
9980       }
9981       case Expose:
9982         break;
9983       case KeyPress:
9984       {
9985         char
9986           command[MaxTextExtent];
9987
9988         KeySym
9989           key_symbol;
9990
9991         if (event.xkey.window == windows->magnify.id)
9992           {
9993             Window
9994               window;
9995
9996             window=windows->magnify.id;
9997             while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
9998           }
9999         if (event.xkey.window != windows->image.id)
10000           break;
10001         /*
10002           Respond to a user key press.
10003         */
10004         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
10005           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
10006         switch ((int) key_symbol)
10007         {
10008           case XK_Escape:
10009           case XK_F20:
10010           {
10011             /*
10012               Prematurely exit.
10013             */
10014             state|=ExitState;
10015             break;
10016           }
10017           case XK_F1:
10018           case XK_Help:
10019           {
10020             XTextViewWidget(display,resource_info,windows,MagickFalse,
10021               "Help Viewer - Matte Edit",ImageMatteEditHelp);
10022             break;
10023           }
10024           default:
10025           {
10026             (void) XBell(display,0);
10027             break;
10028           }
10029         }
10030         break;
10031       }
10032       case MotionNotify:
10033       {
10034         /*
10035           Map and unmap Info widget as cursor crosses its boundaries.
10036         */
10037         x=event.xmotion.x;
10038         y=event.xmotion.y;
10039         if( IfMagickTrue(windows->info.mapped) )
10040           {
10041             if ((x < (int) (windows->info.x+windows->info.width)) &&
10042                 (y < (int) (windows->info.y+windows->info.height)))
10043               (void) XWithdrawWindow(display,windows->info.id,
10044                 windows->info.screen);
10045           }
10046         else
10047           if ((x > (int) (windows->info.x+windows->info.width)) ||
10048               (y > (int) (windows->info.y+windows->info.height)))
10049             (void) XMapWindow(display,windows->info.id);
10050         break;
10051       }
10052       default:
10053         break;
10054     }
10055     if (event.xany.window == windows->magnify.id)
10056       {
10057         x=windows->magnify.x-windows->image.x;
10058         y=windows->magnify.y-windows->image.y;
10059       }
10060     x_offset=x;
10061     y_offset=y;
10062     if ((state & UpdateConfigurationState) != 0)
10063       {
10064         CacheView
10065           *image_view;
10066
10067         int
10068           x,
10069           y;
10070
10071         /*
10072           Matte edit is relative to image configuration.
10073         */
10074         (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
10075           MagickTrue);
10076         XPutPixel(windows->image.ximage,x_offset,y_offset,
10077           windows->pixel_info->background_color.pixel);
10078         width=(unsigned int) (*image)->columns;
10079         height=(unsigned int) (*image)->rows;
10080         x=0;
10081         y=0;
10082         if (windows->image.crop_geometry != (char *) NULL)
10083           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,
10084             &height);
10085         x_offset=(int) (width*(windows->image.x+x_offset)/
10086           windows->image.ximage->width+x);
10087         y_offset=(int) (height*(windows->image.y+y_offset)/
10088           windows->image.ximage->height+y);
10089         if ((x_offset < 0) || (y_offset < 0))
10090           continue;
10091         if ((x_offset >= (int) (*image)->columns) ||
10092             (y_offset >= (int) (*image)->rows))
10093           continue;
10094         if( IfMagickFalse(SetImageStorageClass(*image,DirectClass,exception)) )
10095           return(MagickFalse);
10096         if ((*image)->alpha_trait != BlendPixelTrait)
10097           (void) SetImageAlphaChannel(*image,OpaqueAlphaChannel,exception);
10098         image_view=AcquireAuthenticCacheView(*image,exception);
10099         switch (method)
10100         {
10101           case PointMethod:
10102           default:
10103           {
10104             /*
10105               Update matte information using point algorithm.
10106             */
10107             q=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,
10108               (ssize_t) y_offset,1,1,exception);
10109             if (q == (Quantum *) NULL)
10110               break;
10111             SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10112             (void) SyncCacheViewAuthenticPixels(image_view,exception);
10113             break;
10114           }
10115           case ReplaceMethod:
10116           {
10117             PixelInfo
10118               pixel,
10119               target;
10120
10121             /*
10122               Update matte information using replace algorithm.
10123             */
10124             (void) GetOneCacheViewVirtualPixelInfo(image_view,(ssize_t)
10125               x_offset,(ssize_t) y_offset,&target,exception);
10126             for (y=0; y < (int) (*image)->rows; y++)
10127             {
10128               q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10129                 (*image)->columns,1,exception);
10130               if (q == (Quantum *) NULL)
10131                 break;
10132               for (x=0; x < (int) (*image)->columns; x++)
10133               {
10134                 GetPixelInfoPixel(*image,q,&pixel);
10135                 if (IsFuzzyEquivalencePixelInfo(&pixel,&target))
10136                   SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10137                 q+=GetPixelChannels(*image);
10138               }
10139               if( IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
10140                 break;
10141             }
10142             break;
10143           }
10144           case FloodfillMethod:
10145           case FillToBorderMethod:
10146           {
10147             ChannelType
10148               channel_mask;
10149
10150             DrawInfo
10151               *draw_info;
10152
10153             PixelInfo
10154               target;
10155
10156             /*
10157               Update matte information using floodfill algorithm.
10158             */
10159             (void) GetOneVirtualPixelInfo(*image,
10160               GetPixelCacheVirtualMethod(*image),(ssize_t) x_offset,(ssize_t)
10161               y_offset,&target,exception);
10162             if (method == FillToBorderMethod)
10163               {
10164                 target.red=(double) ScaleShortToQuantum(
10165                   border_color.red);
10166                 target.green=(double) ScaleShortToQuantum(
10167                   border_color.green);
10168                 target.blue=(double) ScaleShortToQuantum(
10169                   border_color.blue);
10170               }
10171             draw_info=CloneDrawInfo(resource_info->image_info,
10172               (DrawInfo *) NULL);
10173             draw_info->fill.alpha=(double) ClampToQuantum(
10174               StringToDouble(matte,(char **) NULL));
10175             channel_mask=SetImageChannelMask(*image,AlphaChannel);
10176             (void) FloodfillPaintImage(*image,draw_info,&target,(ssize_t)
10177               x_offset,(ssize_t) y_offset,
10178               IsMagickFalse(method == FloodfillMethod),exception);
10179             (void) SetPixelChannelMask(*image,channel_mask);
10180             draw_info=DestroyDrawInfo(draw_info);
10181             break;
10182           }
10183           case ResetMethod:
10184           {
10185             /*
10186               Update matte information using reset algorithm.
10187             */
10188             if( IfMagickFalse(SetImageStorageClass(*image,DirectClass,exception)) )
10189               return(MagickFalse);
10190             for (y=0; y < (int) (*image)->rows; y++)
10191             {
10192               q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10193                 (*image)->columns,1,exception);
10194               if (q == (Quantum *) NULL)
10195                 break;
10196               for (x=0; x < (int) (*image)->columns; x++)
10197               {
10198                 SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10199                 q+=GetPixelChannels(*image);
10200               }
10201               if( IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
10202                 break;
10203             }
10204             if (StringToLong(matte) == (long) OpaqueAlpha)
10205               (*image)->alpha_trait=UndefinedPixelTrait;
10206             break;
10207           }
10208         }
10209         image_view=DestroyCacheView(image_view);
10210         state&=(~UpdateConfigurationState);
10211       }
10212   } while ((state & ExitState) == 0);
10213   (void) XSelectInput(display,windows->image.id,
10214     windows->image.attributes.event_mask);
10215   XSetCursorState(display,windows,MagickFalse);
10216   (void) XFreeCursor(display,cursor);
10217   return(MagickTrue);
10218 }
10219 \f
10220 /*
10221 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10222 %                                                                             %
10223 %                                                                             %
10224 %                                                                             %
10225 +   X O p e n I m a g e                                                       %
10226 %                                                                             %
10227 %                                                                             %
10228 %                                                                             %
10229 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10230 %
10231 %  XOpenImage() loads an image from a file.
10232 %
10233 %  The format of the XOpenImage method is:
10234 %
10235 %     Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10236 %       XWindows *windows,const unsigned int command)
10237 %
10238 %  A description of each parameter follows:
10239 %
10240 %    o display: Specifies a connection to an X server; returned from
10241 %      XOpenDisplay.
10242 %
10243 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10244 %
10245 %    o windows: Specifies a pointer to a XWindows structure.
10246 %
10247 %    o command: A value other than zero indicates that the file is selected
10248 %      from the command line argument list.
10249 %
10250 */
10251 static Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10252   XWindows *windows,const MagickBooleanType command)
10253 {
10254   const MagickInfo
10255     *magick_info;
10256
10257   ExceptionInfo
10258     *exception;
10259
10260   Image
10261     *nexus;
10262
10263   ImageInfo
10264     *image_info;
10265
10266   static char
10267     filename[MaxTextExtent] = "\0";
10268
10269   /*
10270     Request file name from user.
10271   */
10272   if( IfMagickFalse(command) )
10273     XFileBrowserWidget(display,windows,"Open",filename);
10274   else
10275     {
10276       char
10277         **filelist,
10278         **files;
10279
10280       int
10281         count,
10282         status;
10283
10284       register int
10285         i,
10286         j;
10287
10288       /*
10289         Select next image from the command line.
10290       */
10291       status=XGetCommand(display,windows->image.id,&files,&count);
10292       if (status == 0)
10293         {
10294           ThrowXWindowFatalException(XServerError,"UnableToGetProperty","...");
10295           return((Image *) NULL);
10296         }
10297       filelist=(char **) AcquireQuantumMemory((size_t) count,sizeof(*filelist));
10298       if (filelist == (char **) NULL)
10299         {
10300           ThrowXWindowFatalException(ResourceLimitError,
10301             "MemoryAllocationFailed","...");
10302           (void) XFreeStringList(files);
10303           return((Image *) NULL);
10304         }
10305       j=0;
10306       for (i=1; i < count; i++)
10307         if (*files[i] != '-')
10308           filelist[j++]=files[i];
10309       filelist[j]=(char *) NULL;
10310       XListBrowserWidget(display,windows,&windows->widget,
10311         (const char **) filelist,"Load","Select Image to Load:",filename);
10312       filelist=(char **) RelinquishMagickMemory(filelist);
10313       (void) XFreeStringList(files);
10314     }
10315   if (*filename == '\0')
10316     return((Image *) NULL);
10317   image_info=CloneImageInfo(resource_info->image_info);
10318   (void) SetImageInfoProgressMonitor(image_info,(MagickProgressMonitor) NULL,
10319     (void *) NULL);
10320   (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
10321   exception=AcquireExceptionInfo();
10322   (void) SetImageInfo(image_info,0,exception);
10323   if (LocaleCompare(image_info->magick,"X") == 0)
10324     {
10325       char
10326         seconds[MaxTextExtent];
10327
10328       /*
10329         User may want to delay the X server screen grab.
10330       */
10331       (void) CopyMagickString(seconds,"0",MaxTextExtent);
10332       (void) XDialogWidget(display,windows,"Grab","Enter any delay in seconds:",
10333         seconds);
10334       if (*seconds == '\0')
10335         return((Image *) NULL);
10336       XDelay(display,(size_t) (1000*StringToLong(seconds)));
10337     }
10338   magick_info=GetMagickInfo(image_info->magick,exception);
10339   if ((magick_info != (const MagickInfo *) NULL) &&
10340       IfMagickTrue(magick_info->raw))
10341     {
10342       char
10343         geometry[MaxTextExtent];
10344
10345       /*
10346         Request image size from the user.
10347       */
10348       (void) CopyMagickString(geometry,"512x512",MaxTextExtent);
10349       if (image_info->size != (char *) NULL)
10350         (void) CopyMagickString(geometry,image_info->size,MaxTextExtent);
10351       (void) XDialogWidget(display,windows,"Load","Enter the image geometry:",
10352         geometry);
10353       (void) CloneString(&image_info->size,geometry);
10354     }
10355   /*
10356     Load the image.
10357   */
10358   XSetCursorState(display,windows,MagickTrue);
10359   XCheckRefreshWindows(display,windows);
10360   (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
10361   nexus=ReadImage(image_info,exception);
10362   CatchException(exception);
10363   XSetCursorState(display,windows,MagickFalse);
10364   if (nexus != (Image *) NULL)
10365     XClientMessage(display,windows->image.id,windows->im_protocols,
10366       windows->im_next_image,CurrentTime);
10367   else
10368     {
10369       char
10370         *text,
10371         **textlist;
10372
10373       /*
10374         Unknown image format.
10375       */
10376       text=FileToString(filename,~0,exception);
10377       if (text == (char *) NULL)
10378         return((Image *) NULL);
10379       textlist=StringToList(text);
10380       if (textlist != (char **) NULL)
10381         {
10382           char
10383             title[MaxTextExtent];
10384
10385           register int
10386             i;
10387
10388           (void) FormatLocaleString(title,MaxTextExtent,
10389             "Unknown format: %s",filename);
10390           XTextViewWidget(display,resource_info,windows,MagickTrue,title,
10391             (const char **) textlist);
10392           for (i=0; textlist[i] != (char *) NULL; i++)
10393             textlist[i]=DestroyString(textlist[i]);
10394           textlist=(char **) RelinquishMagickMemory(textlist);
10395         }
10396       text=DestroyString(text);
10397     }
10398   exception=DestroyExceptionInfo(exception);
10399   image_info=DestroyImageInfo(image_info);
10400   return(nexus);
10401 }
10402 \f
10403 /*
10404 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10405 %                                                                             %
10406 %                                                                             %
10407 %                                                                             %
10408 +   X P a n I m a g e                                                         %
10409 %                                                                             %
10410 %                                                                             %
10411 %                                                                             %
10412 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10413 %
10414 %  XPanImage() pans the image until the mouse button is released.
10415 %
10416 %  The format of the XPanImage method is:
10417 %
10418 %      void XPanImage(Display *display,XWindows *windows,XEvent *event,
10419 %        ExceptionInfo *exception)
10420 %
10421 %  A description of each parameter follows:
10422 %
10423 %    o display: Specifies a connection to an X server;  returned from
10424 %      XOpenDisplay.
10425 %
10426 %    o windows: Specifies a pointer to a XWindows structure.
10427 %
10428 %    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
10429 %      the entire image is refreshed.
10430 %
10431 %    o exception: return any errors or warnings in this structure.
10432 %
10433 */
10434 static void XPanImage(Display *display,XWindows *windows,XEvent *event,
10435   ExceptionInfo *exception)
10436 {
10437   char
10438     text[MaxTextExtent];
10439
10440   Cursor
10441     cursor;
10442
10443   double
10444     x_factor,
10445     y_factor;
10446
10447   RectangleInfo
10448     pan_info;
10449
10450   size_t
10451     state;
10452
10453   /*
10454     Define cursor.
10455   */
10456   if ((windows->image.ximage->width > (int) windows->image.width) &&
10457       (windows->image.ximage->height > (int) windows->image.height))
10458     cursor=XCreateFontCursor(display,XC_fleur);
10459   else
10460     if (windows->image.ximage->width > (int) windows->image.width)
10461       cursor=XCreateFontCursor(display,XC_sb_h_double_arrow);
10462     else
10463       if (windows->image.ximage->height > (int) windows->image.height)
10464         cursor=XCreateFontCursor(display,XC_sb_v_double_arrow);
10465       else
10466         cursor=XCreateFontCursor(display,XC_arrow);
10467   (void) XCheckDefineCursor(display,windows->pan.id,cursor);
10468   /*
10469     Pan image as pointer moves until the mouse button is released.
10470   */
10471   x_factor=(double) windows->image.ximage->width/windows->pan.width;
10472   y_factor=(double) windows->image.ximage->height/windows->pan.height;
10473   pan_info.width=windows->pan.width*windows->image.width/
10474     windows->image.ximage->width;
10475   pan_info.height=windows->pan.height*windows->image.height/
10476     windows->image.ximage->height;
10477   pan_info.x=0;
10478   pan_info.y=0;
10479   state=UpdateConfigurationState;
10480   do
10481   {
10482     switch (event->type)
10483     {
10484       case ButtonPress:
10485       {
10486         /*
10487           User choose an initial pan location.
10488         */
10489         pan_info.x=(ssize_t) event->xbutton.x;
10490         pan_info.y=(ssize_t) event->xbutton.y;
10491         state|=UpdateConfigurationState;
10492         break;
10493       }
10494       case ButtonRelease:
10495       {
10496         /*
10497           User has finished panning the image.
10498         */
10499         pan_info.x=(ssize_t) event->xbutton.x;
10500         pan_info.y=(ssize_t) event->xbutton.y;
10501         state|=UpdateConfigurationState | ExitState;
10502         break;
10503       }
10504       case MotionNotify:
10505       {
10506         pan_info.x=(ssize_t) event->xmotion.x;
10507         pan_info.y=(ssize_t) event->xmotion.y;
10508         state|=UpdateConfigurationState;
10509       }
10510       default:
10511         break;
10512     }
10513     if ((state & UpdateConfigurationState) != 0)
10514       {
10515         /*
10516           Check boundary conditions.
10517         */
10518         if (pan_info.x < (ssize_t) (pan_info.width/2))
10519           pan_info.x=0;
10520         else
10521           pan_info.x=(ssize_t) (x_factor*(pan_info.x-(pan_info.width/2)));
10522         if (pan_info.x < 0)
10523           pan_info.x=0;
10524         else
10525           if ((int) (pan_info.x+windows->image.width) >
10526               windows->image.ximage->width)
10527             pan_info.x=(ssize_t)
10528               (windows->image.ximage->width-windows->image.width);
10529         if (pan_info.y < (ssize_t) (pan_info.height/2))
10530           pan_info.y=0;
10531         else
10532           pan_info.y=(ssize_t) (y_factor*(pan_info.y-(pan_info.height/2)));
10533         if (pan_info.y < 0)
10534           pan_info.y=0;
10535         else
10536           if ((int) (pan_info.y+windows->image.height) >
10537               windows->image.ximage->height)
10538             pan_info.y=(ssize_t)
10539               (windows->image.ximage->height-windows->image.height);
10540         if ((windows->image.x != (int) pan_info.x) ||
10541             (windows->image.y != (int) pan_info.y))
10542           {
10543             /*
10544               Display image pan offset.
10545             */
10546             windows->image.x=(int) pan_info.x;
10547             windows->image.y=(int) pan_info.y;
10548             (void) FormatLocaleString(text,MaxTextExtent," %ux%u%+d%+d ",
10549               windows->image.width,windows->image.height,windows->image.x,
10550               windows->image.y);
10551             XInfoWidget(display,windows,text);
10552             /*
10553               Refresh Image window.
10554             */
10555             XDrawPanRectangle(display,windows);
10556             XRefreshWindow(display,&windows->image,(XEvent *) NULL);
10557           }
10558         state&=(~UpdateConfigurationState);
10559       }
10560     /*
10561       Wait for next event.
10562     */
10563     if ((state & ExitState) == 0)
10564       XScreenEvent(display,windows,event,exception);
10565   } while ((state & ExitState) == 0);
10566   /*
10567     Restore cursor.
10568   */
10569   (void) XCheckDefineCursor(display,windows->pan.id,windows->pan.cursor);
10570   (void) XFreeCursor(display,cursor);
10571   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
10572 }
10573 \f
10574 /*
10575 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10576 %                                                                             %
10577 %                                                                             %
10578 %                                                                             %
10579 +   X P a s t e I m a g e                                                     %
10580 %                                                                             %
10581 %                                                                             %
10582 %                                                                             %
10583 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10584 %
10585 %  XPasteImage() pastes an image previously saved with XCropImage in the X
10586 %  window image at a location the user chooses with the pointer.
10587 %
10588 %  The format of the XPasteImage method is:
10589 %
10590 %      MagickBooleanType XPasteImage(Display *display,
10591 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
10592 %        ExceptionInfo *exception)
10593 %
10594 %  A description of each parameter follows:
10595 %
10596 %    o display: Specifies a connection to an X server;  returned from
10597 %      XOpenDisplay.
10598 %
10599 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10600 %
10601 %    o windows: Specifies a pointer to a XWindows structure.
10602 %
10603 %    o image: the image; returned from ReadImage.
10604 %
10605 %    o exception: return any errors or warnings in this structure.
10606 %
10607 */
10608 static MagickBooleanType XPasteImage(Display *display,
10609   XResourceInfo *resource_info,XWindows *windows,Image *image,
10610   ExceptionInfo *exception)
10611 {
10612   static const char
10613     *PasteMenu[] =
10614     {
10615       "Operator",
10616       "Help",
10617       "Dismiss",
10618       (char *) NULL
10619     };
10620
10621   static const ModeType
10622     PasteCommands[] =
10623     {
10624       PasteOperatorsCommand,
10625       PasteHelpCommand,
10626       PasteDismissCommand
10627     };
10628
10629   static CompositeOperator
10630     compose = CopyCompositeOp;
10631
10632   char
10633     text[MaxTextExtent];
10634
10635   Cursor
10636     cursor;
10637
10638   Image
10639     *paste_image;
10640
10641   int
10642     entry,
10643     id,
10644     x,
10645     y;
10646
10647   double
10648     scale_factor;
10649
10650   RectangleInfo
10651     highlight_info,
10652     paste_info;
10653
10654   unsigned int
10655     height,
10656     width;
10657
10658   size_t
10659     state;
10660
10661   XEvent
10662     event;
10663
10664   /*
10665     Copy image.
10666   */
10667   if (resource_info->copy_image == (Image *) NULL)
10668     return(MagickFalse);
10669   paste_image=CloneImage(resource_info->copy_image,0,0,MagickTrue,exception);
10670   /*
10671     Map Command widget.
10672   */
10673   (void) CloneString(&windows->command.name,"Paste");
10674   windows->command.data=1;
10675   (void) XCommandWidget(display,windows,PasteMenu,(XEvent *) NULL);
10676   (void) XMapRaised(display,windows->command.id);
10677   XClientMessage(display,windows->image.id,windows->im_protocols,
10678     windows->im_update_widget,CurrentTime);
10679   /*
10680     Track pointer until button 1 is pressed.
10681   */
10682   XSetCursorState(display,windows,MagickFalse);
10683   XQueryPosition(display,windows->image.id,&x,&y);
10684   (void) XSelectInput(display,windows->image.id,
10685     windows->image.attributes.event_mask | PointerMotionMask);
10686   paste_info.x=(ssize_t) windows->image.x+x;
10687   paste_info.y=(ssize_t) windows->image.y+y;
10688   paste_info.width=0;
10689   paste_info.height=0;
10690   cursor=XCreateFontCursor(display,XC_ul_angle);
10691   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
10692   state=DefaultState;
10693   do
10694   {
10695     if( IfMagickTrue(windows->info.mapped) )
10696       {
10697         /*
10698           Display pointer position.
10699         */
10700         (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
10701           (long) paste_info.x,(long) paste_info.y);
10702         XInfoWidget(display,windows,text);
10703       }
10704     highlight_info=paste_info;
10705     highlight_info.x=paste_info.x-windows->image.x;
10706     highlight_info.y=paste_info.y-windows->image.y;
10707     XHighlightRectangle(display,windows->image.id,
10708       windows->image.highlight_context,&highlight_info);
10709     /*
10710       Wait for next event.
10711     */
10712     XScreenEvent(display,windows,&event,exception);
10713     XHighlightRectangle(display,windows->image.id,
10714       windows->image.highlight_context,&highlight_info);
10715     if (event.xany.window == windows->command.id)
10716       {
10717         /*
10718           Select a command from the Command widget.
10719         */
10720         id=XCommandWidget(display,windows,PasteMenu,&event);
10721         if (id < 0)
10722           continue;
10723         switch (PasteCommands[id])
10724         {
10725           case PasteOperatorsCommand:
10726           {
10727             char
10728               command[MaxTextExtent],
10729               **operators;
10730
10731             /*
10732               Select a command from the pop-up menu.
10733             */
10734             operators=GetCommandOptions(MagickComposeOptions);
10735             if (operators == (char **) NULL)
10736               break;
10737             entry=XMenuWidget(display,windows,PasteMenu[id],
10738               (const char **) operators,command);
10739             if (entry >= 0)
10740               compose=(CompositeOperator) ParseCommandOption(
10741                 MagickComposeOptions,MagickFalse,operators[entry]);
10742             operators=DestroyStringList(operators);
10743             break;
10744           }
10745           case PasteHelpCommand:
10746           {
10747             XTextViewWidget(display,resource_info,windows,MagickFalse,
10748               "Help Viewer - Image Composite",ImagePasteHelp);
10749             break;
10750           }
10751           case PasteDismissCommand:
10752           {
10753             /*
10754               Prematurely exit.
10755             */
10756             state|=EscapeState;
10757             state|=ExitState;
10758             break;
10759           }
10760           default:
10761             break;
10762         }
10763         continue;
10764       }
10765     switch (event.type)
10766     {
10767       case ButtonPress:
10768       {
10769         if( IfMagickTrue(image->debug) )
10770           (void) LogMagickEvent(X11Event,GetMagickModule(),
10771             "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
10772             event.xbutton.button,event.xbutton.x,event.xbutton.y);
10773         if (event.xbutton.button != Button1)
10774           break;
10775         if (event.xbutton.window != windows->image.id)
10776           break;
10777         /*
10778           Paste rectangle is relative to image configuration.
10779         */
10780         width=(unsigned int) image->columns;
10781         height=(unsigned int) image->rows;
10782         x=0;
10783         y=0;
10784         if (windows->image.crop_geometry != (char *) NULL)
10785           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
10786             &width,&height);
10787         scale_factor=(double) windows->image.ximage->width/width;
10788         paste_info.width=(unsigned int) (scale_factor*paste_image->columns+0.5);
10789         scale_factor=(double) windows->image.ximage->height/height;
10790         paste_info.height=(unsigned int) (scale_factor*paste_image->rows+0.5);
10791         (void) XCheckDefineCursor(display,windows->image.id,cursor);
10792         paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10793         paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10794         break;
10795       }
10796       case ButtonRelease:
10797       {
10798         if( IfMagickTrue(image->debug) )
10799           (void) LogMagickEvent(X11Event,GetMagickModule(),
10800             "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
10801             event.xbutton.button,event.xbutton.x,event.xbutton.y);
10802         if (event.xbutton.button != Button1)
10803           break;
10804         if (event.xbutton.window != windows->image.id)
10805           break;
10806         if ((paste_info.width != 0) && (paste_info.height != 0))
10807           {
10808             /*
10809               User has selected the location of the paste image.
10810             */
10811             paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10812             paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10813             state|=ExitState;
10814           }
10815         break;
10816       }
10817       case Expose:
10818         break;
10819       case KeyPress:
10820       {
10821         char
10822           command[MaxTextExtent];
10823
10824         KeySym
10825           key_symbol;
10826
10827         int
10828           length;
10829
10830         if (event.xkey.window != windows->image.id)
10831           break;
10832         /*
10833           Respond to a user key press.
10834         */
10835         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
10836           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
10837         *(command+length)='\0';
10838         if( IfMagickTrue(image->debug) )
10839           (void) LogMagickEvent(X11Event,GetMagickModule(),
10840             "Key press: 0x%lx (%s)",(long) key_symbol,command);
10841         switch ((int) key_symbol)
10842         {
10843           case XK_Escape:
10844           case XK_F20:
10845           {
10846             /*
10847               Prematurely exit.
10848             */
10849             paste_image=DestroyImage(paste_image);
10850             state|=EscapeState;
10851             state|=ExitState;
10852             break;
10853           }
10854           case XK_F1:
10855           case XK_Help:
10856           {
10857             (void) XSetFunction(display,windows->image.highlight_context,
10858               GXcopy);
10859             XTextViewWidget(display,resource_info,windows,MagickFalse,
10860               "Help Viewer - Image Composite",ImagePasteHelp);
10861             (void) XSetFunction(display,windows->image.highlight_context,
10862               GXinvert);
10863             break;
10864           }
10865           default:
10866           {
10867             (void) XBell(display,0);
10868             break;
10869           }
10870         }
10871         break;
10872       }
10873       case MotionNotify:
10874       {
10875         /*
10876           Map and unmap Info widget as text cursor crosses its boundaries.
10877         */
10878         x=event.xmotion.x;
10879         y=event.xmotion.y;
10880         if( IfMagickTrue(windows->info.mapped) )
10881           {
10882             if ((x < (int) (windows->info.x+windows->info.width)) &&
10883                 (y < (int) (windows->info.y+windows->info.height)))
10884               (void) XWithdrawWindow(display,windows->info.id,
10885                 windows->info.screen);
10886           }
10887         else
10888           if ((x > (int) (windows->info.x+windows->info.width)) ||
10889               (y > (int) (windows->info.y+windows->info.height)))
10890             (void) XMapWindow(display,windows->info.id);
10891         paste_info.x=(ssize_t) windows->image.x+x;
10892         paste_info.y=(ssize_t) windows->image.y+y;
10893         break;
10894       }
10895       default:
10896       {
10897         if( IfMagickTrue(image->debug) )
10898           (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
10899             event.type);
10900         break;
10901       }
10902     }
10903   } while ((state & ExitState) == 0);
10904   (void) XSelectInput(display,windows->image.id,
10905     windows->image.attributes.event_mask);
10906   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
10907   XSetCursorState(display,windows,MagickFalse);
10908   (void) XFreeCursor(display,cursor);
10909   if ((state & EscapeState) != 0)
10910     return(MagickTrue);
10911   /*
10912     Image pasting is relative to image configuration.
10913   */
10914   XSetCursorState(display,windows,MagickTrue);
10915   XCheckRefreshWindows(display,windows);
10916   width=(unsigned int) image->columns;
10917   height=(unsigned int) image->rows;
10918   x=0;
10919   y=0;
10920   if (windows->image.crop_geometry != (char *) NULL)
10921     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
10922   scale_factor=(double) width/windows->image.ximage->width;
10923   paste_info.x+=x;
10924   paste_info.x=(ssize_t) (scale_factor*paste_info.x+0.5);
10925   paste_info.width=(unsigned int) (scale_factor*paste_info.width+0.5);
10926   scale_factor=(double) height/windows->image.ximage->height;
10927   paste_info.y+=y;
10928   paste_info.y=(ssize_t) (scale_factor*paste_info.y*scale_factor+0.5);
10929   paste_info.height=(unsigned int) (scale_factor*paste_info.height+0.5);
10930   /*
10931     Paste image with X Image window.
10932   */
10933   (void) CompositeImage(image,paste_image,compose,MagickTrue,paste_info.x,
10934     paste_info.y,exception);
10935   paste_image=DestroyImage(paste_image);
10936   XSetCursorState(display,windows,MagickFalse);
10937   /*
10938     Update image colormap.
10939   */
10940   XConfigureImageColormap(display,resource_info,windows,image,exception);
10941   (void) XConfigureImage(display,resource_info,windows,image,exception);
10942   return(MagickTrue);
10943 }
10944 \f
10945 /*
10946 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10947 %                                                                             %
10948 %                                                                             %
10949 %                                                                             %
10950 +   X P r i n t I m a g e                                                     %
10951 %                                                                             %
10952 %                                                                             %
10953 %                                                                             %
10954 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10955 %
10956 %  XPrintImage() prints an image to a Postscript printer.
10957 %
10958 %  The format of the XPrintImage method is:
10959 %
10960 %      MagickBooleanType XPrintImage(Display *display,
10961 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
10962 %        ExceptionInfo *exception)
10963 %
10964 %  A description of each parameter follows:
10965 %
10966 %    o display: Specifies a connection to an X server; returned from
10967 %      XOpenDisplay.
10968 %
10969 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10970 %
10971 %    o windows: Specifies a pointer to a XWindows structure.
10972 %
10973 %    o image: the image.
10974 %
10975 %    o exception: return any errors or warnings in this structure.
10976 %
10977 */
10978 static MagickBooleanType XPrintImage(Display *display,
10979   XResourceInfo *resource_info,XWindows *windows,Image *image,
10980   ExceptionInfo *exception)
10981 {
10982   char
10983     filename[MaxTextExtent],
10984     geometry[MaxTextExtent];
10985
10986   Image
10987     *print_image;
10988
10989   ImageInfo
10990     *image_info;
10991
10992   MagickStatusType
10993     status;
10994
10995   /*
10996     Request Postscript page geometry from user.
10997   */
10998   image_info=CloneImageInfo(resource_info->image_info);
10999   (void) FormatLocaleString(geometry,MaxTextExtent,"Letter");
11000   if (image_info->page != (char *) NULL)
11001     (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
11002   XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
11003     "Select Postscript Page Geometry:",geometry);
11004   if (*geometry == '\0')
11005     return(MagickTrue);
11006   image_info->page=GetPageGeometry(geometry);
11007   /*
11008     Apply image transforms.
11009   */
11010   XSetCursorState(display,windows,MagickTrue);
11011   XCheckRefreshWindows(display,windows);
11012   print_image=CloneImage(image,0,0,MagickTrue,exception);
11013   if (print_image == (Image *) NULL)
11014     return(MagickFalse);
11015   (void) FormatLocaleString(geometry,MaxTextExtent,"%dx%d!",
11016     windows->image.ximage->width,windows->image.ximage->height);
11017   (void) TransformImage(&print_image,windows->image.crop_geometry,geometry,
11018     exception);
11019   /*
11020     Print image.
11021   */
11022   (void) AcquireUniqueFilename(filename);
11023   (void) FormatLocaleString(print_image->filename,MaxTextExtent,"print:%s",
11024     filename);
11025   status=WriteImage(image_info,print_image,exception);
11026   (void) RelinquishUniqueFileResource(filename);
11027   print_image=DestroyImage(print_image);
11028   image_info=DestroyImageInfo(image_info);
11029   XSetCursorState(display,windows,MagickFalse);
11030   return(IsMagickTrue(status));
11031 }
11032 \f
11033 /*
11034 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11035 %                                                                             %
11036 %                                                                             %
11037 %                                                                             %
11038 +   X R O I I m a g e                                                         %
11039 %                                                                             %
11040 %                                                                             %
11041 %                                                                             %
11042 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11043 %
11044 %  XROIImage() applies an image processing technique to a region of interest.
11045 %
11046 %  The format of the XROIImage method is:
11047 %
11048 %      MagickBooleanType XROIImage(Display *display,
11049 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
11050 %        ExceptionInfo *exception)
11051 %
11052 %  A description of each parameter follows:
11053 %
11054 %    o display: Specifies a connection to an X server; returned from
11055 %      XOpenDisplay.
11056 %
11057 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
11058 %
11059 %    o windows: Specifies a pointer to a XWindows structure.
11060 %
11061 %    o image: the image; returned from ReadImage.
11062 %
11063 %    o exception: return any errors or warnings in this structure.
11064 %
11065 */
11066 static MagickBooleanType XROIImage(Display *display,
11067   XResourceInfo *resource_info,XWindows *windows,Image **image,
11068   ExceptionInfo *exception)
11069 {
11070 #define ApplyMenus  7
11071
11072   static const char
11073     *ROIMenu[] =
11074     {
11075       "Help",
11076       "Dismiss",
11077       (char *) NULL
11078     },
11079     *ApplyMenu[] =
11080     {
11081       "File",
11082       "Edit",
11083       "Transform",
11084       "Enhance",
11085       "Effects",
11086       "F/X",
11087       "Miscellany",
11088       "Help",
11089       "Dismiss",
11090       (char *) NULL
11091     },
11092     *FileMenu[] =
11093     {
11094       "Save...",
11095       "Print...",
11096       (char *) NULL
11097     },
11098     *EditMenu[] =
11099     {
11100       "Undo",
11101       "Redo",
11102       (char *) NULL
11103     },
11104     *TransformMenu[] =
11105     {
11106       "Flop",
11107       "Flip",
11108       "Rotate Right",
11109       "Rotate Left",
11110       (char *) NULL
11111     },
11112     *EnhanceMenu[] =
11113     {
11114       "Hue...",
11115       "Saturation...",
11116       "Brightness...",
11117       "Gamma...",
11118       "Spiff",
11119       "Dull",
11120       "Contrast Stretch...",
11121       "Sigmoidal Contrast...",
11122       "Normalize",
11123       "Equalize",
11124       "Negate",
11125       "Grayscale",
11126       "Map...",
11127       "Quantize...",
11128       (char *) NULL
11129     },
11130     *EffectsMenu[] =
11131     {
11132       "Despeckle",
11133       "Emboss",
11134       "Reduce Noise",
11135       "Add Noise",
11136       "Sharpen...",
11137       "Blur...",
11138       "Threshold...",
11139       "Edge Detect...",
11140       "Spread...",
11141       "Shade...",
11142       "Raise...",
11143       "Segment...",
11144       (char *) NULL
11145     },
11146     *FXMenu[] =
11147     {
11148       "Solarize...",
11149       "Sepia Tone...",
11150       "Swirl...",
11151       "Implode...",
11152       "Vignette...",
11153       "Wave...",
11154       "Oil Paint...",
11155       "Charcoal Draw...",
11156       (char *) NULL
11157     },
11158     *MiscellanyMenu[] =
11159     {
11160       "Image Info",
11161       "Zoom Image",
11162       "Show Preview...",
11163       "Show Histogram",
11164       "Show Matte",
11165       (char *) NULL
11166     };
11167
11168   static const char
11169     **Menus[ApplyMenus] =
11170     {
11171       FileMenu,
11172       EditMenu,
11173       TransformMenu,
11174       EnhanceMenu,
11175       EffectsMenu,
11176       FXMenu,
11177       MiscellanyMenu
11178     };
11179
11180   static const CommandType
11181     ApplyCommands[] =
11182     {
11183       NullCommand,
11184       NullCommand,
11185       NullCommand,
11186       NullCommand,
11187       NullCommand,
11188       NullCommand,
11189       NullCommand,
11190       HelpCommand,
11191       QuitCommand
11192     },
11193     FileCommands[] =
11194     {
11195       SaveCommand,
11196       PrintCommand
11197     },
11198     EditCommands[] =
11199     {
11200       UndoCommand,
11201       RedoCommand
11202     },
11203     TransformCommands[] =
11204     {
11205       FlopCommand,
11206       FlipCommand,
11207       RotateRightCommand,
11208       RotateLeftCommand
11209     },
11210     EnhanceCommands[] =
11211     {
11212       HueCommand,
11213       SaturationCommand,
11214       BrightnessCommand,
11215       GammaCommand,
11216       SpiffCommand,
11217       DullCommand,
11218       ContrastStretchCommand,
11219       SigmoidalContrastCommand,
11220       NormalizeCommand,
11221       EqualizeCommand,
11222       NegateCommand,
11223       GrayscaleCommand,
11224       MapCommand,
11225       QuantizeCommand
11226     },
11227     EffectsCommands[] =
11228     {
11229       DespeckleCommand,
11230       EmbossCommand,
11231       ReduceNoiseCommand,
11232       AddNoiseCommand,
11233       SharpenCommand,
11234       BlurCommand,
11235       EdgeDetectCommand,
11236       SpreadCommand,
11237       ShadeCommand,
11238       RaiseCommand,
11239       SegmentCommand
11240     },
11241     FXCommands[] =
11242     {
11243       SolarizeCommand,
11244       SepiaToneCommand,
11245       SwirlCommand,
11246       ImplodeCommand,
11247       VignetteCommand,
11248       WaveCommand,
11249       OilPaintCommand,
11250       CharcoalDrawCommand
11251     },
11252     MiscellanyCommands[] =
11253     {
11254       InfoCommand,
11255       ZoomCommand,
11256       ShowPreviewCommand,
11257       ShowHistogramCommand,
11258       ShowMatteCommand
11259     },
11260     ROICommands[] =
11261     {
11262       ROIHelpCommand,
11263       ROIDismissCommand
11264     };
11265
11266   static const CommandType
11267     *Commands[ApplyMenus] =
11268     {
11269       FileCommands,
11270       EditCommands,
11271       TransformCommands,
11272       EnhanceCommands,
11273       EffectsCommands,
11274       FXCommands,
11275       MiscellanyCommands
11276     };
11277
11278   char
11279     command[MaxTextExtent],
11280     text[MaxTextExtent];
11281
11282   CommandType
11283     command_type;
11284
11285   Cursor
11286     cursor;
11287
11288   Image
11289     *roi_image;
11290
11291   int
11292     entry,
11293     id,
11294     x,
11295     y;
11296
11297   double
11298     scale_factor;
11299
11300   MagickProgressMonitor
11301     progress_monitor;
11302
11303   RectangleInfo
11304     crop_info,
11305     highlight_info,
11306     roi_info;
11307
11308   unsigned int
11309     height,
11310     width;
11311
11312   size_t
11313     state;
11314
11315   XEvent
11316     event;
11317
11318   /*
11319     Map Command widget.
11320   */
11321   (void) CloneString(&windows->command.name,"ROI");
11322   windows->command.data=0;
11323   (void) XCommandWidget(display,windows,ROIMenu,(XEvent *) NULL);
11324   (void) XMapRaised(display,windows->command.id);
11325   XClientMessage(display,windows->image.id,windows->im_protocols,
11326     windows->im_update_widget,CurrentTime);
11327   /*
11328     Track pointer until button 1 is pressed.
11329   */
11330   XQueryPosition(display,windows->image.id,&x,&y);
11331   (void) XSelectInput(display,windows->image.id,
11332     windows->image.attributes.event_mask | PointerMotionMask);
11333   roi_info.x=(ssize_t) windows->image.x+x;
11334   roi_info.y=(ssize_t) windows->image.y+y;
11335   roi_info.width=0;
11336   roi_info.height=0;
11337   cursor=XCreateFontCursor(display,XC_fleur);
11338   state=DefaultState;
11339   do
11340   {
11341     if( IfMagickTrue(windows->info.mapped) )
11342       {
11343         /*
11344           Display pointer position.
11345         */
11346         (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
11347           (long) roi_info.x,(long) roi_info.y);
11348         XInfoWidget(display,windows,text);
11349       }
11350     /*
11351       Wait for next event.
11352     */
11353     XScreenEvent(display,windows,&event,exception);
11354     if (event.xany.window == windows->command.id)
11355       {
11356         /*
11357           Select a command from the Command widget.
11358         */
11359         id=XCommandWidget(display,windows,ROIMenu,&event);
11360         if (id < 0)
11361           continue;
11362         switch (ROICommands[id])
11363         {
11364           case ROIHelpCommand:
11365           {
11366             XTextViewWidget(display,resource_info,windows,MagickFalse,
11367               "Help Viewer - Region of Interest",ImageROIHelp);
11368             break;
11369           }
11370           case ROIDismissCommand:
11371           {
11372             /*
11373               Prematurely exit.
11374             */
11375             state|=EscapeState;
11376             state|=ExitState;
11377             break;
11378           }
11379           default:
11380             break;
11381         }
11382         continue;
11383       }
11384     switch (event.type)
11385     {
11386       case ButtonPress:
11387       {
11388         if (event.xbutton.button != Button1)
11389           break;
11390         if (event.xbutton.window != windows->image.id)
11391           break;
11392         /*
11393           Note first corner of region of interest rectangle-- exit loop.
11394         */
11395         (void) XCheckDefineCursor(display,windows->image.id,cursor);
11396         roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11397         roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11398         state|=ExitState;
11399         break;
11400       }
11401       case ButtonRelease:
11402         break;
11403       case Expose:
11404         break;
11405       case KeyPress:
11406       {
11407         KeySym
11408           key_symbol;
11409
11410         if (event.xkey.window != windows->image.id)
11411           break;
11412         /*
11413           Respond to a user key press.
11414         */
11415         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11416           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11417         switch ((int) key_symbol)
11418         {
11419           case XK_Escape:
11420           case XK_F20:
11421           {
11422             /*
11423               Prematurely exit.
11424             */
11425             state|=EscapeState;
11426             state|=ExitState;
11427             break;
11428           }
11429           case XK_F1:
11430           case XK_Help:
11431           {
11432             XTextViewWidget(display,resource_info,windows,MagickFalse,
11433               "Help Viewer - Region of Interest",ImageROIHelp);
11434             break;
11435           }
11436           default:
11437           {
11438             (void) XBell(display,0);
11439             break;
11440           }
11441         }
11442         break;
11443       }
11444       case MotionNotify:
11445       {
11446         /*
11447           Map and unmap Info widget as text cursor crosses its boundaries.
11448         */
11449         x=event.xmotion.x;
11450         y=event.xmotion.y;
11451         if( IfMagickTrue(windows->info.mapped) )
11452           {
11453             if ((x < (int) (windows->info.x+windows->info.width)) &&
11454                 (y < (int) (windows->info.y+windows->info.height)))
11455               (void) XWithdrawWindow(display,windows->info.id,
11456                 windows->info.screen);
11457           }
11458         else
11459           if ((x > (int) (windows->info.x+windows->info.width)) ||
11460               (y > (int) (windows->info.y+windows->info.height)))
11461             (void) XMapWindow(display,windows->info.id);
11462         roi_info.x=(ssize_t) windows->image.x+x;
11463         roi_info.y=(ssize_t) windows->image.y+y;
11464         break;
11465       }
11466       default:
11467         break;
11468     }
11469   } while ((state & ExitState) == 0);
11470   (void) XSelectInput(display,windows->image.id,
11471     windows->image.attributes.event_mask);
11472   if ((state & EscapeState) != 0)
11473     {
11474       /*
11475         User want to exit without region of interest.
11476       */
11477       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11478       (void) XFreeCursor(display,cursor);
11479       return(MagickTrue);
11480     }
11481   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
11482   do
11483   {
11484     /*
11485       Size rectangle as pointer moves until the mouse button is released.
11486     */
11487     x=(int) roi_info.x;
11488     y=(int) roi_info.y;
11489     roi_info.width=0;
11490     roi_info.height=0;
11491     state=DefaultState;
11492     do
11493     {
11494       highlight_info=roi_info;
11495       highlight_info.x=roi_info.x-windows->image.x;
11496       highlight_info.y=roi_info.y-windows->image.y;
11497       if ((highlight_info.width > 3) && (highlight_info.height > 3))
11498         {
11499           /*
11500             Display info and draw region of interest rectangle.
11501           */
11502           if( IfMagickFalse(windows->info.mapped) )
11503             (void) XMapWindow(display,windows->info.id);
11504           (void) FormatLocaleString(text,MaxTextExtent,
11505             " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11506             roi_info.height,(double) roi_info.x,(double) roi_info.y);
11507           XInfoWidget(display,windows,text);
11508           XHighlightRectangle(display,windows->image.id,
11509             windows->image.highlight_context,&highlight_info);
11510         }
11511       else
11512         if( IfMagickTrue(windows->info.mapped) )
11513           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11514       /*
11515         Wait for next event.
11516       */
11517       XScreenEvent(display,windows,&event,exception);
11518       if ((highlight_info.width > 3) && (highlight_info.height > 3))
11519         XHighlightRectangle(display,windows->image.id,
11520           windows->image.highlight_context,&highlight_info);
11521       switch (event.type)
11522       {
11523         case ButtonPress:
11524         {
11525           roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11526           roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11527           break;
11528         }
11529         case ButtonRelease:
11530         {
11531           /*
11532             User has committed to region of interest rectangle.
11533           */
11534           roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11535           roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11536           XSetCursorState(display,windows,MagickFalse);
11537           state|=ExitState;
11538           if (LocaleCompare(windows->command.name,"Apply") == 0)
11539             break;
11540           (void) CloneString(&windows->command.name,"Apply");
11541           windows->command.data=ApplyMenus;
11542           (void) XCommandWidget(display,windows,ApplyMenu,(XEvent *) NULL);
11543           break;
11544         }
11545         case Expose:
11546           break;
11547         case MotionNotify:
11548         {
11549           roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11550           roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11551         }
11552         default:
11553           break;
11554       }
11555       if ((((int) roi_info.x != x) && ((int) roi_info.y != y)) ||
11556           ((state & ExitState) != 0))
11557         {
11558           /*
11559             Check boundary conditions.
11560           */
11561           if (roi_info.x < 0)
11562             roi_info.x=0;
11563           else
11564             if (roi_info.x > (ssize_t) windows->image.ximage->width)
11565               roi_info.x=(ssize_t) windows->image.ximage->width;
11566           if ((int) roi_info.x < x)
11567             roi_info.width=(unsigned int) (x-roi_info.x);
11568           else
11569             {
11570               roi_info.width=(unsigned int) (roi_info.x-x);
11571               roi_info.x=(ssize_t) x;
11572             }
11573           if (roi_info.y < 0)
11574             roi_info.y=0;
11575           else
11576             if (roi_info.y > (ssize_t) windows->image.ximage->height)
11577               roi_info.y=(ssize_t) windows->image.ximage->height;
11578           if ((int) roi_info.y < y)
11579             roi_info.height=(unsigned int) (y-roi_info.y);
11580           else
11581             {
11582               roi_info.height=(unsigned int) (roi_info.y-y);
11583               roi_info.y=(ssize_t) y;
11584             }
11585         }
11586     } while ((state & ExitState) == 0);
11587     /*
11588       Wait for user to grab a corner of the rectangle or press return.
11589     */
11590     state=DefaultState;
11591     command_type=NullCommand;
11592     (void) XMapWindow(display,windows->info.id);
11593     do
11594     {
11595       if( IfMagickTrue(windows->info.mapped) )
11596         {
11597           /*
11598             Display pointer position.
11599           */
11600           (void) FormatLocaleString(text,MaxTextExtent,
11601             " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11602             roi_info.height,(double) roi_info.x,(double) roi_info.y);
11603           XInfoWidget(display,windows,text);
11604         }
11605       highlight_info=roi_info;
11606       highlight_info.x=roi_info.x-windows->image.x;
11607       highlight_info.y=roi_info.y-windows->image.y;
11608       if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
11609         {
11610           state|=EscapeState;
11611           state|=ExitState;
11612           break;
11613         }
11614       if ((state & UpdateRegionState) != 0)
11615         {
11616           (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11617           switch (command_type)
11618           {
11619             case UndoCommand:
11620             case RedoCommand:
11621             {
11622               (void) XMagickCommand(display,resource_info,windows,command_type,
11623                 image,exception);
11624               break;
11625             }
11626             default:
11627             {
11628               /*
11629                 Region of interest is relative to image configuration.
11630               */
11631               progress_monitor=SetImageProgressMonitor(*image,
11632                 (MagickProgressMonitor) NULL,(*image)->client_data);
11633               crop_info=roi_info;
11634               width=(unsigned int) (*image)->columns;
11635               height=(unsigned int) (*image)->rows;
11636               x=0;
11637               y=0;
11638               if (windows->image.crop_geometry != (char *) NULL)
11639                 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
11640                   &width,&height);
11641               scale_factor=(double) width/windows->image.ximage->width;
11642               crop_info.x+=x;
11643               crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
11644               crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
11645               scale_factor=(double)
11646                 height/windows->image.ximage->height;
11647               crop_info.y+=y;
11648               crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
11649               crop_info.height=(unsigned int)
11650                 (scale_factor*crop_info.height+0.5);
11651               roi_image=CropImage(*image,&crop_info,exception);
11652               (void) SetImageProgressMonitor(*image,progress_monitor,
11653                 (*image)->client_data);
11654               if (roi_image == (Image *) NULL)
11655                 continue;
11656               /*
11657                 Apply image processing technique to the region of interest.
11658               */
11659               windows->image.orphan=MagickTrue;
11660               (void) XMagickCommand(display,resource_info,windows,command_type,
11661                 &roi_image,exception);
11662               progress_monitor=SetImageProgressMonitor(*image,
11663                 (MagickProgressMonitor) NULL,(*image)->client_data);
11664               (void) XMagickCommand(display,resource_info,windows,
11665                 SaveToUndoBufferCommand,image,exception);
11666               windows->image.orphan=MagickFalse;
11667               (void) CompositeImage(*image,roi_image,CopyCompositeOp,
11668                 MagickTrue,crop_info.x,crop_info.y,exception);
11669               roi_image=DestroyImage(roi_image);
11670               (void) SetImageProgressMonitor(*image,progress_monitor,
11671                 (*image)->client_data);
11672               break;
11673             }
11674           }
11675           if (command_type != InfoCommand)
11676             {
11677               XConfigureImageColormap(display,resource_info,windows,*image,
11678                 exception);
11679               (void) XConfigureImage(display,resource_info,windows,*image,
11680                 exception);
11681             }
11682           XCheckRefreshWindows(display,windows);
11683           XInfoWidget(display,windows,text);
11684           (void) XSetFunction(display,windows->image.highlight_context,
11685             GXinvert);
11686           state&=(~UpdateRegionState);
11687         }
11688       XHighlightRectangle(display,windows->image.id,
11689         windows->image.highlight_context,&highlight_info);
11690       XScreenEvent(display,windows,&event,exception);
11691       if (event.xany.window == windows->command.id)
11692         {
11693           /*
11694             Select a command from the Command widget.
11695           */
11696           (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11697           command_type=NullCommand;
11698           id=XCommandWidget(display,windows,ApplyMenu,&event);
11699           if (id >= 0)
11700             {
11701               (void) CopyMagickString(command,ApplyMenu[id],MaxTextExtent);
11702               command_type=ApplyCommands[id];
11703               if (id < ApplyMenus)
11704                 {
11705                   /*
11706                     Select a command from a pop-up menu.
11707                   */
11708                   entry=XMenuWidget(display,windows,ApplyMenu[id],
11709                     (const char **) Menus[id],command);
11710                   if (entry >= 0)
11711                     {
11712                       (void) CopyMagickString(command,Menus[id][entry],
11713                         MaxTextExtent);
11714                       command_type=Commands[id][entry];
11715                     }
11716                 }
11717             }
11718           (void) XSetFunction(display,windows->image.highlight_context,
11719             GXinvert);
11720           XHighlightRectangle(display,windows->image.id,
11721             windows->image.highlight_context,&highlight_info);
11722           if (command_type == HelpCommand)
11723             {
11724               (void) XSetFunction(display,windows->image.highlight_context,
11725                 GXcopy);
11726               XTextViewWidget(display,resource_info,windows,MagickFalse,
11727                 "Help Viewer - Region of Interest",ImageROIHelp);
11728               (void) XSetFunction(display,windows->image.highlight_context,
11729                 GXinvert);
11730               continue;
11731             }
11732           if (command_type == QuitCommand)
11733             {
11734               /*
11735                 exit.
11736               */
11737               state|=EscapeState;
11738               state|=ExitState;
11739               continue;
11740             }
11741           if (command_type != NullCommand)
11742             state|=UpdateRegionState;
11743           continue;
11744         }
11745       XHighlightRectangle(display,windows->image.id,
11746         windows->image.highlight_context,&highlight_info);
11747       switch (event.type)
11748       {
11749         case ButtonPress:
11750         {
11751           x=windows->image.x;
11752           y=windows->image.y;
11753           if (event.xbutton.button != Button1)
11754             break;
11755           if (event.xbutton.window != windows->image.id)
11756             break;
11757           x=windows->image.x+event.xbutton.x;
11758           y=windows->image.y+event.xbutton.y;
11759           if ((x < (int) (roi_info.x+RoiDelta)) &&
11760               (x > (int) (roi_info.x-RoiDelta)) &&
11761               (y < (int) (roi_info.y+RoiDelta)) &&
11762               (y > (int) (roi_info.y-RoiDelta)))
11763             {
11764               roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11765               roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11766               state|=UpdateConfigurationState;
11767               break;
11768             }
11769           if ((x < (int) (roi_info.x+RoiDelta)) &&
11770               (x > (int) (roi_info.x-RoiDelta)) &&
11771               (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11772               (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11773             {
11774               roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
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+RoiDelta)) &&
11781               (y > (int) (roi_info.y-RoiDelta)))
11782             {
11783               roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11784               state|=UpdateConfigurationState;
11785               break;
11786             }
11787           if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11788               (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11789               (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11790               (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11791             {
11792               state|=UpdateConfigurationState;
11793               break;
11794             }
11795         }
11796         case ButtonRelease:
11797         {
11798           if (event.xbutton.window == windows->pan.id)
11799             if ((highlight_info.x != crop_info.x-windows->image.x) ||
11800                 (highlight_info.y != crop_info.y-windows->image.y))
11801               XHighlightRectangle(display,windows->image.id,
11802                 windows->image.highlight_context,&highlight_info);
11803           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11804             event.xbutton.time);
11805           break;
11806         }
11807         case Expose:
11808         {
11809           if (event.xexpose.window == windows->image.id)
11810             if (event.xexpose.count == 0)
11811               {
11812                 event.xexpose.x=(int) highlight_info.x;
11813                 event.xexpose.y=(int) highlight_info.y;
11814                 event.xexpose.width=(int) highlight_info.width;
11815                 event.xexpose.height=(int) highlight_info.height;
11816                 XRefreshWindow(display,&windows->image,&event);
11817               }
11818           if (event.xexpose.window == windows->info.id)
11819             if (event.xexpose.count == 0)
11820               XInfoWidget(display,windows,text);
11821           break;
11822         }
11823         case KeyPress:
11824         {
11825           KeySym
11826             key_symbol;
11827
11828           if (event.xkey.window != windows->image.id)
11829             break;
11830           /*
11831             Respond to a user key press.
11832           */
11833           (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11834             sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11835           switch ((int) key_symbol)
11836           {
11837             case XK_Shift_L:
11838             case XK_Shift_R:
11839               break;
11840             case XK_Escape:
11841             case XK_F20:
11842               state|=EscapeState;
11843             case XK_Return:
11844             {
11845               state|=ExitState;
11846               break;
11847             }
11848             case XK_Home:
11849             case XK_KP_Home:
11850             {
11851               roi_info.x=(ssize_t) (windows->image.width/2L-roi_info.width/2L);
11852               roi_info.y=(ssize_t) (windows->image.height/2L-
11853                 roi_info.height/2L);
11854               break;
11855             }
11856             case XK_Left:
11857             case XK_KP_Left:
11858             {
11859               roi_info.x--;
11860               break;
11861             }
11862             case XK_Up:
11863             case XK_KP_Up:
11864             case XK_Next:
11865             {
11866               roi_info.y--;
11867               break;
11868             }
11869             case XK_Right:
11870             case XK_KP_Right:
11871             {
11872               roi_info.x++;
11873               break;
11874             }
11875             case XK_Prior:
11876             case XK_Down:
11877             case XK_KP_Down:
11878             {
11879               roi_info.y++;
11880               break;
11881             }
11882             case XK_F1:
11883             case XK_Help:
11884             {
11885               (void) XSetFunction(display,windows->image.highlight_context,
11886                 GXcopy);
11887               XTextViewWidget(display,resource_info,windows,MagickFalse,
11888                 "Help Viewer - Region of Interest",ImageROIHelp);
11889               (void) XSetFunction(display,windows->image.highlight_context,
11890                 GXinvert);
11891               break;
11892             }
11893             default:
11894             {
11895               command_type=XImageWindowCommand(display,resource_info,windows,
11896                 event.xkey.state,key_symbol,image,exception);
11897               if (command_type != NullCommand)
11898                 state|=UpdateRegionState;
11899               break;
11900             }
11901           }
11902           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11903             event.xkey.time);
11904           break;
11905         }
11906         case KeyRelease:
11907           break;
11908         case MotionNotify:
11909         {
11910           if (event.xbutton.window != windows->image.id)
11911             break;
11912           /*
11913             Map and unmap Info widget as text cursor crosses its boundaries.
11914           */
11915           x=event.xmotion.x;
11916           y=event.xmotion.y;
11917           if( IfMagickTrue(windows->info.mapped) )
11918             {
11919               if ((x < (int) (windows->info.x+windows->info.width)) &&
11920                   (y < (int) (windows->info.y+windows->info.height)))
11921                 (void) XWithdrawWindow(display,windows->info.id,
11922                   windows->info.screen);
11923             }
11924           else
11925             if ((x > (int) (windows->info.x+windows->info.width)) ||
11926                 (y > (int) (windows->info.y+windows->info.height)))
11927               (void) XMapWindow(display,windows->info.id);
11928           roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11929           roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11930           break;
11931         }
11932         case SelectionRequest:
11933         {
11934           XSelectionEvent
11935             notify;
11936
11937           XSelectionRequestEvent
11938             *request;
11939
11940           /*
11941             Set primary selection.
11942           */
11943           (void) FormatLocaleString(text,MaxTextExtent,
11944             "%.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11945             roi_info.height,(double) roi_info.x,(double) roi_info.y);
11946           request=(&(event.xselectionrequest));
11947           (void) XChangeProperty(request->display,request->requestor,
11948             request->property,request->target,8,PropModeReplace,
11949             (unsigned char *) text,(int) strlen(text));
11950           notify.type=SelectionNotify;
11951           notify.display=request->display;
11952           notify.requestor=request->requestor;
11953           notify.selection=request->selection;
11954           notify.target=request->target;
11955           notify.time=request->time;
11956           if (request->property == None)
11957             notify.property=request->target;
11958           else
11959             notify.property=request->property;
11960           (void) XSendEvent(request->display,request->requestor,False,0,
11961             (XEvent *) &notify);
11962         }
11963         default:
11964           break;
11965       }
11966       if ((state & UpdateConfigurationState) != 0)
11967         {
11968           (void) XPutBackEvent(display,&event);
11969           (void) XCheckDefineCursor(display,windows->image.id,cursor);
11970           break;
11971         }
11972     } while ((state & ExitState) == 0);
11973   } while ((state & ExitState) == 0);
11974   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11975   XSetCursorState(display,windows,MagickFalse);
11976   if ((state & EscapeState) != 0)
11977     return(MagickTrue);
11978   return(MagickTrue);
11979 }
11980 \f
11981 /*
11982 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11983 %                                                                             %
11984 %                                                                             %
11985 %                                                                             %
11986 +   X R o t a t e I m a g e                                                   %
11987 %                                                                             %
11988 %                                                                             %
11989 %                                                                             %
11990 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11991 %
11992 %  XRotateImage() rotates the X image.  If the degrees parameter if zero, the
11993 %  rotation angle is computed from the slope of a line drawn by the user.
11994 %
11995 %  The format of the XRotateImage method is:
11996 %
11997 %      MagickBooleanType XRotateImage(Display *display,
11998 %        XResourceInfo *resource_info,XWindows *windows,double degrees,
11999 %        Image **image,ExceptionInfo *exception)
12000 %
12001 %  A description of each parameter follows:
12002 %
12003 %    o display: Specifies a connection to an X server; returned from
12004 %      XOpenDisplay.
12005 %
12006 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12007 %
12008 %    o windows: Specifies a pointer to a XWindows structure.
12009 %
12010 %    o degrees: Specifies the number of degrees to rotate the image.
12011 %
12012 %    o image: the image.
12013 %
12014 %    o exception: return any errors or warnings in this structure.
12015 %
12016 */
12017 static MagickBooleanType XRotateImage(Display *display,
12018   XResourceInfo *resource_info,XWindows *windows,double degrees,Image **image,
12019   ExceptionInfo *exception)
12020 {
12021   static const char
12022     *RotateMenu[] =
12023     {
12024       "Pixel Color",
12025       "Direction",
12026       "Help",
12027       "Dismiss",
12028       (char *) NULL
12029     };
12030
12031   static ModeType
12032     direction = HorizontalRotateCommand;
12033
12034   static const ModeType
12035     DirectionCommands[] =
12036     {
12037       HorizontalRotateCommand,
12038       VerticalRotateCommand
12039     },
12040     RotateCommands[] =
12041     {
12042       RotateColorCommand,
12043       RotateDirectionCommand,
12044       RotateHelpCommand,
12045       RotateDismissCommand
12046     };
12047
12048   static unsigned int
12049     pen_id = 0;
12050
12051   char
12052     command[MaxTextExtent],
12053     text[MaxTextExtent];
12054
12055   Image
12056     *rotate_image;
12057
12058   int
12059     id,
12060     x,
12061     y;
12062
12063   double
12064     normalized_degrees;
12065
12066   register int
12067     i;
12068
12069   unsigned int
12070     height,
12071     rotations,
12072     width;
12073
12074   if (degrees == 0.0)
12075     {
12076       unsigned int
12077         distance;
12078
12079       size_t
12080         state;
12081
12082       XEvent
12083         event;
12084
12085       XSegment
12086         rotate_info;
12087
12088       /*
12089         Map Command widget.
12090       */
12091       (void) CloneString(&windows->command.name,"Rotate");
12092       windows->command.data=2;
12093       (void) XCommandWidget(display,windows,RotateMenu,(XEvent *) NULL);
12094       (void) XMapRaised(display,windows->command.id);
12095       XClientMessage(display,windows->image.id,windows->im_protocols,
12096         windows->im_update_widget,CurrentTime);
12097       /*
12098         Wait for first button press.
12099       */
12100       (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12101       XQueryPosition(display,windows->image.id,&x,&y);
12102       rotate_info.x1=x;
12103       rotate_info.y1=y;
12104       rotate_info.x2=x;
12105       rotate_info.y2=y;
12106       state=DefaultState;
12107       do
12108       {
12109         XHighlightLine(display,windows->image.id,
12110           windows->image.highlight_context,&rotate_info);
12111         /*
12112           Wait for next event.
12113         */
12114         XScreenEvent(display,windows,&event,exception);
12115         XHighlightLine(display,windows->image.id,
12116           windows->image.highlight_context,&rotate_info);
12117         if (event.xany.window == windows->command.id)
12118           {
12119             /*
12120               Select a command from the Command widget.
12121             */
12122             id=XCommandWidget(display,windows,RotateMenu,&event);
12123             if (id < 0)
12124               continue;
12125             (void) XSetFunction(display,windows->image.highlight_context,
12126               GXcopy);
12127             switch (RotateCommands[id])
12128             {
12129               case RotateColorCommand:
12130               {
12131                 const char
12132                   *ColorMenu[MaxNumberPens];
12133
12134                 int
12135                   pen_number;
12136
12137                 XColor
12138                   color;
12139
12140                 /*
12141                   Initialize menu selections.
12142                 */
12143                 for (i=0; i < (int) (MaxNumberPens-2); i++)
12144                   ColorMenu[i]=resource_info->pen_colors[i];
12145                 ColorMenu[MaxNumberPens-2]="Browser...";
12146                 ColorMenu[MaxNumberPens-1]=(const char *) NULL;
12147                 /*
12148                   Select a pen color from the pop-up menu.
12149                 */
12150                 pen_number=XMenuWidget(display,windows,RotateMenu[id],
12151                   (const char **) ColorMenu,command);
12152                 if (pen_number < 0)
12153                   break;
12154                 if (pen_number == (MaxNumberPens-2))
12155                   {
12156                     static char
12157                       color_name[MaxTextExtent] = "gray";
12158
12159                     /*
12160                       Select a pen color from a dialog.
12161                     */
12162                     resource_info->pen_colors[pen_number]=color_name;
12163                     XColorBrowserWidget(display,windows,"Select",color_name);
12164                     if (*color_name == '\0')
12165                       break;
12166                   }
12167                 /*
12168                   Set pen color.
12169                 */
12170                 (void) XParseColor(display,windows->map_info->colormap,
12171                   resource_info->pen_colors[pen_number],&color);
12172                 XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
12173                   (unsigned int) MaxColors,&color);
12174                 windows->pixel_info->pen_colors[pen_number]=color;
12175                 pen_id=(unsigned int) pen_number;
12176                 break;
12177               }
12178               case RotateDirectionCommand:
12179               {
12180                 static const char
12181                   *Directions[] =
12182                   {
12183                     "horizontal",
12184                     "vertical",
12185                     (char *) NULL,
12186                   };
12187
12188                 /*
12189                   Select a command from the pop-up menu.
12190                 */
12191                 id=XMenuWidget(display,windows,RotateMenu[id],
12192                   Directions,command);
12193                 if (id >= 0)
12194                   direction=DirectionCommands[id];
12195                 break;
12196               }
12197               case RotateHelpCommand:
12198               {
12199                 XTextViewWidget(display,resource_info,windows,MagickFalse,
12200                   "Help Viewer - Image Rotation",ImageRotateHelp);
12201                 break;
12202               }
12203               case RotateDismissCommand:
12204               {
12205                 /*
12206                   Prematurely exit.
12207                 */
12208                 state|=EscapeState;
12209                 state|=ExitState;
12210                 break;
12211               }
12212               default:
12213                 break;
12214             }
12215             (void) XSetFunction(display,windows->image.highlight_context,
12216               GXinvert);
12217             continue;
12218           }
12219         switch (event.type)
12220         {
12221           case ButtonPress:
12222           {
12223             if (event.xbutton.button != Button1)
12224               break;
12225             if (event.xbutton.window != windows->image.id)
12226               break;
12227             /*
12228               exit loop.
12229             */
12230             (void) XSetFunction(display,windows->image.highlight_context,
12231               GXcopy);
12232             rotate_info.x1=event.xbutton.x;
12233             rotate_info.y1=event.xbutton.y;
12234             state|=ExitState;
12235             break;
12236           }
12237           case ButtonRelease:
12238             break;
12239           case Expose:
12240             break;
12241           case KeyPress:
12242           {
12243             char
12244               command[MaxTextExtent];
12245
12246             KeySym
12247               key_symbol;
12248
12249             if (event.xkey.window != windows->image.id)
12250               break;
12251             /*
12252               Respond to a user key press.
12253             */
12254             (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
12255               sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12256             switch ((int) key_symbol)
12257             {
12258               case XK_Escape:
12259               case XK_F20:
12260               {
12261                 /*
12262                   Prematurely exit.
12263                 */
12264                 state|=EscapeState;
12265                 state|=ExitState;
12266                 break;
12267               }
12268               case XK_F1:
12269               case XK_Help:
12270               {
12271                 (void) XSetFunction(display,windows->image.highlight_context,
12272                   GXcopy);
12273                 XTextViewWidget(display,resource_info,windows,MagickFalse,
12274                   "Help Viewer - Image Rotation",ImageRotateHelp);
12275                 (void) XSetFunction(display,windows->image.highlight_context,
12276                   GXinvert);
12277                 break;
12278               }
12279               default:
12280               {
12281                 (void) XBell(display,0);
12282                 break;
12283               }
12284             }
12285             break;
12286           }
12287           case MotionNotify:
12288           {
12289             rotate_info.x1=event.xmotion.x;
12290             rotate_info.y1=event.xmotion.y;
12291           }
12292         }
12293         rotate_info.x2=rotate_info.x1;
12294         rotate_info.y2=rotate_info.y1;
12295         if (direction == HorizontalRotateCommand)
12296           rotate_info.x2+=32;
12297         else
12298           rotate_info.y2-=32;
12299       } while ((state & ExitState) == 0);
12300       (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12301       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12302       if ((state & EscapeState) != 0)
12303         return(MagickTrue);
12304       /*
12305         Draw line as pointer moves until the mouse button is released.
12306       */
12307       distance=0;
12308       (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12309       state=DefaultState;
12310       do
12311       {
12312         if (distance > 9)
12313           {
12314             /*
12315               Display info and draw rotation line.
12316             */
12317             if( IfMagickFalse(windows->info.mapped) )
12318               (void) XMapWindow(display,windows->info.id);
12319             (void) FormatLocaleString(text,MaxTextExtent," %g",
12320               direction == VerticalRotateCommand ? degrees-90.0 : degrees);
12321             XInfoWidget(display,windows,text);
12322             XHighlightLine(display,windows->image.id,
12323               windows->image.highlight_context,&rotate_info);
12324           }
12325         else
12326           if( IfMagickTrue(windows->info.mapped) )
12327             (void) XWithdrawWindow(display,windows->info.id,
12328               windows->info.screen);
12329         /*
12330           Wait for next event.
12331         */
12332         XScreenEvent(display,windows,&event,exception);
12333         if (distance > 9)
12334           XHighlightLine(display,windows->image.id,
12335             windows->image.highlight_context,&rotate_info);
12336         switch (event.type)
12337         {
12338           case ButtonPress:
12339             break;
12340           case ButtonRelease:
12341           {
12342             /*
12343               User has committed to rotation line.
12344             */
12345             rotate_info.x2=event.xbutton.x;
12346             rotate_info.y2=event.xbutton.y;
12347             state|=ExitState;
12348             break;
12349           }
12350           case Expose:
12351             break;
12352           case MotionNotify:
12353           {
12354             rotate_info.x2=event.xmotion.x;
12355             rotate_info.y2=event.xmotion.y;
12356           }
12357           default:
12358             break;
12359         }
12360         /*
12361           Check boundary conditions.
12362         */
12363         if (rotate_info.x2 < 0)
12364           rotate_info.x2=0;
12365         else
12366           if (rotate_info.x2 > (int) windows->image.width)
12367             rotate_info.x2=(short) windows->image.width;
12368         if (rotate_info.y2 < 0)
12369           rotate_info.y2=0;
12370         else
12371           if (rotate_info.y2 > (int) windows->image.height)
12372             rotate_info.y2=(short) windows->image.height;
12373         /*
12374           Compute rotation angle from the slope of the line.
12375         */
12376         degrees=0.0;
12377         distance=(unsigned int)
12378           ((rotate_info.x2-rotate_info.x1+1)*(rotate_info.x2-rotate_info.x1+1))+
12379           ((rotate_info.y2-rotate_info.y1+1)*(rotate_info.y2-rotate_info.y1+1));
12380         if (distance > 9)
12381           degrees=RadiansToDegrees(-atan2((double) (rotate_info.y2-
12382             rotate_info.y1),(double) (rotate_info.x2-rotate_info.x1)));
12383       } while ((state & ExitState) == 0);
12384       (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12385       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12386       if (distance <= 9)
12387         return(MagickTrue);
12388     }
12389   if (direction == VerticalRotateCommand)
12390     degrees-=90.0;
12391   if (degrees == 0.0)
12392     return(MagickTrue);
12393   /*
12394     Rotate image.
12395   */
12396   normalized_degrees=degrees;
12397   while (normalized_degrees < -45.0)
12398     normalized_degrees+=360.0;
12399   for (rotations=0; normalized_degrees > 45.0; rotations++)
12400     normalized_degrees-=90.0;
12401   if (normalized_degrees != 0.0)
12402     (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
12403       exception);
12404   XSetCursorState(display,windows,MagickTrue);
12405   XCheckRefreshWindows(display,windows);
12406   (*image)->background_color.red=(double) ScaleShortToQuantum(
12407     windows->pixel_info->pen_colors[pen_id].red);
12408   (*image)->background_color.green=(double) ScaleShortToQuantum(
12409     windows->pixel_info->pen_colors[pen_id].green);
12410   (*image)->background_color.blue=(double) ScaleShortToQuantum(
12411     windows->pixel_info->pen_colors[pen_id].blue);
12412   rotate_image=RotateImage(*image,degrees,exception);
12413   XSetCursorState(display,windows,MagickFalse);
12414   if (rotate_image == (Image *) NULL)
12415     return(MagickFalse);
12416   *image=DestroyImage(*image);
12417   *image=rotate_image;
12418   if (windows->image.crop_geometry != (char *) NULL)
12419     {
12420       /*
12421         Rotate crop geometry.
12422       */
12423       width=(unsigned int) (*image)->columns;
12424       height=(unsigned int) (*image)->rows;
12425       (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12426       switch (rotations % 4)
12427       {
12428         default:
12429         case 0:
12430           break;
12431         case 1:
12432         {
12433           /*
12434             Rotate 90 degrees.
12435           */
12436           (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12437             "%ux%u%+d%+d",height,width,(int) (*image)->columns-
12438             (int) height-y,x);
12439           break;
12440         }
12441         case 2:
12442         {
12443           /*
12444             Rotate 180 degrees.
12445           */
12446           (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12447             "%ux%u%+d%+d",width,height,(int) width-x,(int) height-y);
12448           break;
12449         }
12450         case 3:
12451         {
12452           /*
12453             Rotate 270 degrees.
12454           */
12455           (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12456             "%ux%u%+d%+d",height,width,y,(int) (*image)->rows-(int) width-x);
12457           break;
12458         }
12459       }
12460     }
12461   if( IfMagickTrue(windows->image.orphan) )
12462     return(MagickTrue);
12463   if (normalized_degrees != 0.0)
12464     {
12465       /*
12466         Update image colormap.
12467       */
12468       windows->image.window_changes.width=(int) (*image)->columns;
12469       windows->image.window_changes.height=(int) (*image)->rows;
12470       if (windows->image.crop_geometry != (char *) NULL)
12471         {
12472           /*
12473             Obtain dimensions of image from crop geometry.
12474           */
12475           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
12476             &width,&height);
12477           windows->image.window_changes.width=(int) width;
12478           windows->image.window_changes.height=(int) height;
12479         }
12480       XConfigureImageColormap(display,resource_info,windows,*image,exception);
12481     }
12482   else
12483     if (((rotations % 4) == 1) || ((rotations % 4) == 3))
12484       {
12485         windows->image.window_changes.width=windows->image.ximage->height;
12486         windows->image.window_changes.height=windows->image.ximage->width;
12487       }
12488   /*
12489     Update image configuration.
12490   */
12491   (void) XConfigureImage(display,resource_info,windows,*image,exception);
12492   return(MagickTrue);
12493 }
12494 \f
12495 /*
12496 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12497 %                                                                             %
12498 %                                                                             %
12499 %                                                                             %
12500 +   X S a v e I m a g e                                                       %
12501 %                                                                             %
12502 %                                                                             %
12503 %                                                                             %
12504 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12505 %
12506 %  XSaveImage() saves an image to a file.
12507 %
12508 %  The format of the XSaveImage method is:
12509 %
12510 %      MagickBooleanType XSaveImage(Display *display,
12511 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
12512 %        ExceptionInfo *exception)
12513 %
12514 %  A description of each parameter follows:
12515 %
12516 %    o display: Specifies a connection to an X server; returned from
12517 %      XOpenDisplay.
12518 %
12519 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12520 %
12521 %    o windows: Specifies a pointer to a XWindows structure.
12522 %
12523 %    o image: the image.
12524 %
12525 %    o exception: return any errors or warnings in this structure.
12526 %
12527 */
12528 static MagickBooleanType XSaveImage(Display *display,
12529   XResourceInfo *resource_info,XWindows *windows,Image *image,
12530   ExceptionInfo *exception)
12531 {
12532   char
12533     filename[MaxTextExtent],
12534     geometry[MaxTextExtent];
12535
12536   Image
12537     *save_image;
12538
12539   ImageInfo
12540     *image_info;
12541
12542   MagickStatusType
12543     status;
12544
12545   /*
12546     Request file name from user.
12547   */
12548   if (resource_info->write_filename != (char *) NULL)
12549     (void) CopyMagickString(filename,resource_info->write_filename,
12550       MaxTextExtent);
12551   else
12552     {
12553       char
12554         path[MaxTextExtent];
12555
12556       int
12557         status;
12558
12559       GetPathComponent(image->filename,HeadPath,path);
12560       GetPathComponent(image->filename,TailPath,filename);
12561       if (*path != '\0')
12562         {
12563           status=chdir(path);
12564           if (status == -1)
12565             (void) ThrowMagickException(exception,GetMagickModule(),
12566               FileOpenError,"UnableToOpenFile","%s",path);
12567         }
12568     }
12569   XFileBrowserWidget(display,windows,"Save",filename);
12570   if (*filename == '\0')
12571     return(MagickTrue);
12572   if( IfMagickTrue(IsPathAccessible(filename)) )
12573     {
12574       int
12575         status;
12576
12577       /*
12578         File exists-- seek user's permission before overwriting.
12579       */
12580       status=XConfirmWidget(display,windows,"Overwrite",filename);
12581       if (status <= 0)
12582         return(MagickTrue);
12583     }
12584   image_info=CloneImageInfo(resource_info->image_info);
12585   (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
12586   (void) SetImageInfo(image_info,1,exception);
12587   if ((LocaleCompare(image_info->magick,"JPEG") == 0) ||
12588       (LocaleCompare(image_info->magick,"JPG") == 0))
12589     {
12590       char
12591         quality[MaxTextExtent];
12592
12593       int
12594         status;
12595
12596       /*
12597         Request JPEG quality from user.
12598       */
12599       (void) FormatLocaleString(quality,MaxTextExtent,"%.20g",(double)
12600         image->quality);
12601       status=XDialogWidget(display,windows,"Save","Enter JPEG quality:",
12602         quality);
12603       if (*quality == '\0')
12604         return(MagickTrue);
12605       image->quality=StringToUnsignedLong(quality);
12606       image_info->interlace=status != 0 ? NoInterlace : PlaneInterlace;
12607     }
12608   if ((LocaleCompare(image_info->magick,"EPS") == 0) ||
12609       (LocaleCompare(image_info->magick,"PDF") == 0) ||
12610       (LocaleCompare(image_info->magick,"PS") == 0) ||
12611       (LocaleCompare(image_info->magick,"PS2") == 0))
12612     {
12613       char
12614         geometry[MaxTextExtent];
12615
12616       /*
12617         Request page geometry from user.
12618       */
12619       (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
12620       if (LocaleCompare(image_info->magick,"PDF") == 0)
12621         (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
12622       if (image_info->page != (char *) NULL)
12623         (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
12624       XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
12625         "Select page geometry:",geometry);
12626       if (*geometry != '\0')
12627         image_info->page=GetPageGeometry(geometry);
12628     }
12629   /*
12630     Apply image transforms.
12631   */
12632   XSetCursorState(display,windows,MagickTrue);
12633   XCheckRefreshWindows(display,windows);
12634   save_image=CloneImage(image,0,0,MagickTrue,exception);
12635   if (save_image == (Image *) NULL)
12636     return(MagickFalse);
12637   (void) FormatLocaleString(geometry,MaxTextExtent,"%dx%d!",
12638     windows->image.ximage->width,windows->image.ximage->height);
12639   (void) TransformImage(&save_image,windows->image.crop_geometry,geometry,
12640     exception);
12641   /*
12642     Write image.
12643   */
12644   (void) CopyMagickString(save_image->filename,filename,MaxTextExtent);
12645   status=WriteImage(image_info,save_image,exception);
12646   if( IfMagickTrue(status) )
12647     image->taint=MagickFalse;
12648   save_image=DestroyImage(save_image);
12649   image_info=DestroyImageInfo(image_info);
12650   XSetCursorState(display,windows,MagickFalse);
12651   return(IsMagickTrue(status));
12652 }
12653 \f
12654 /*
12655 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12656 %                                                                             %
12657 %                                                                             %
12658 %                                                                             %
12659 +   X S c r e e n E v e n t                                                   %
12660 %                                                                             %
12661 %                                                                             %
12662 %                                                                             %
12663 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12664 %
12665 %  XScreenEvent() handles global events associated with the Pan and Magnify
12666 %  windows.
12667 %
12668 %  The format of the XScreenEvent function is:
12669 %
12670 %      void XScreenEvent(Display *display,XWindows *windows,XEvent *event,
12671 %        ExceptionInfo *exception)
12672 %
12673 %  A description of each parameter follows:
12674 %
12675 %    o display: Specifies a pointer to the Display structure;  returned from
12676 %      XOpenDisplay.
12677 %
12678 %    o windows: Specifies a pointer to a XWindows structure.
12679 %
12680 %    o event: Specifies a pointer to a X11 XEvent structure.
12681 %
12682 %    o exception: return any errors or warnings in this structure.
12683 %
12684 */
12685
12686 #if defined(__cplusplus) || defined(c_plusplus)
12687 extern "C" {
12688 #endif
12689
12690 static int XPredicate(Display *magick_unused(display),XEvent *event,char *data)
12691 {
12692   register XWindows
12693     *windows;
12694
12695   windows=(XWindows *) data;
12696   if ((event->type == ClientMessage) &&
12697       (event->xclient.window == windows->image.id))
12698     return(MagickFalse);
12699   return(MagickTrue);
12700 }
12701
12702 #if defined(__cplusplus) || defined(c_plusplus)
12703 }
12704 #endif
12705
12706 static void XScreenEvent(Display *display,XWindows *windows,XEvent *event,
12707   ExceptionInfo *exception)
12708 {
12709   register int
12710     x,
12711     y;
12712
12713   (void) XIfEvent(display,event,XPredicate,(char *) windows);
12714   if (event->xany.window == windows->command.id)
12715     return;
12716   switch (event->type)
12717   {
12718     case ButtonPress:
12719     case ButtonRelease:
12720     {
12721       if ((event->xbutton.button == Button3) &&
12722           (event->xbutton.state & Mod1Mask))
12723         {
12724           /*
12725             Convert Alt-Button3 to Button2.
12726           */
12727           event->xbutton.button=Button2;
12728           event->xbutton.state&=(~Mod1Mask);
12729         }
12730       if (event->xbutton.window == windows->backdrop.id)
12731         {
12732           (void) XSetInputFocus(display,event->xbutton.window,RevertToParent,
12733             event->xbutton.time);
12734           break;
12735         }
12736       if (event->xbutton.window == windows->pan.id)
12737         {
12738           XPanImage(display,windows,event,exception);
12739           break;
12740         }
12741       if (event->xbutton.window == windows->image.id)
12742         if (event->xbutton.button == Button2)
12743           {
12744             /*
12745               Update magnified image.
12746             */
12747             x=event->xbutton.x;
12748             y=event->xbutton.y;
12749             if (x < 0)
12750               x=0;
12751             else
12752               if (x >= (int) windows->image.width)
12753                 x=(int) (windows->image.width-1);
12754             windows->magnify.x=(int) windows->image.x+x;
12755             if (y < 0)
12756               y=0;
12757             else
12758              if (y >= (int) windows->image.height)
12759                y=(int) (windows->image.height-1);
12760             windows->magnify.y=windows->image.y+y;
12761             if( IfMagickFalse(windows->magnify.mapped) )
12762               (void) XMapRaised(display,windows->magnify.id);
12763             XMakeMagnifyImage(display,windows,exception);
12764             if (event->type == ButtonRelease)
12765               (void) XWithdrawWindow(display,windows->info.id,
12766                 windows->info.screen);
12767             break;
12768           }
12769       break;
12770     }
12771     case ClientMessage:
12772     {
12773       /*
12774         If client window delete message, exit.
12775       */
12776       if (event->xclient.message_type != windows->wm_protocols)
12777         break;
12778       if (*event->xclient.data.l != (long) windows->wm_delete_window)
12779         break;
12780       if (event->xclient.window == windows->magnify.id)
12781         {
12782           (void) XWithdrawWindow(display,windows->magnify.id,
12783             windows->magnify.screen);
12784           break;
12785         }
12786       break;
12787     }
12788     case ConfigureNotify:
12789     {
12790       if (event->xconfigure.window == windows->magnify.id)
12791         {
12792           unsigned int
12793             magnify;
12794
12795           /*
12796             Magnify window has a new configuration.
12797           */
12798           windows->magnify.width=(unsigned int) event->xconfigure.width;
12799           windows->magnify.height=(unsigned int) event->xconfigure.height;
12800           if( IfMagickFalse(windows->magnify.mapped) )
12801             break;
12802           magnify=1;
12803           while ((int) magnify <= event->xconfigure.width)
12804             magnify<<=1;
12805           while ((int) magnify <= event->xconfigure.height)
12806             magnify<<=1;
12807           magnify>>=1;
12808           if (((int) magnify != event->xconfigure.width) ||
12809               ((int) magnify != event->xconfigure.height))
12810             {
12811               XWindowChanges
12812                 window_changes;
12813
12814               window_changes.width=(int) magnify;
12815               window_changes.height=(int) magnify;
12816               (void) XReconfigureWMWindow(display,windows->magnify.id,
12817                 windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
12818                 &window_changes);
12819               break;
12820             }
12821           XMakeMagnifyImage(display,windows,exception);
12822           break;
12823         }
12824       break;
12825     }
12826     case Expose:
12827     {
12828       if (event->xexpose.window == windows->image.id)
12829         {
12830           XRefreshWindow(display,&windows->image,event);
12831           break;
12832         }
12833       if (event->xexpose.window == windows->pan.id)
12834         if (event->xexpose.count == 0)
12835           {
12836             XDrawPanRectangle(display,windows);
12837             break;
12838           }
12839       if (event->xexpose.window == windows->magnify.id)
12840         if (event->xexpose.count == 0)
12841           {
12842             XMakeMagnifyImage(display,windows,exception);
12843             break;
12844           }
12845       break;
12846     }
12847     case KeyPress:
12848     {
12849       char
12850         command[MaxTextExtent];
12851
12852       KeySym
12853         key_symbol;
12854
12855       if (event->xkey.window != windows->magnify.id)
12856         break;
12857       /*
12858         Respond to a user key press.
12859       */
12860       (void) XLookupString((XKeyEvent *) &event->xkey,command,(int)
12861         sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12862       XMagnifyWindowCommand(display,windows,event->xkey.state,key_symbol,
12863         exception);
12864       break;
12865     }
12866     case MapNotify:
12867     {
12868       if (event->xmap.window == windows->magnify.id)
12869         {
12870           windows->magnify.mapped=MagickTrue;
12871           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12872           break;
12873         }
12874       if (event->xmap.window == windows->info.id)
12875         {
12876           windows->info.mapped=MagickTrue;
12877           break;
12878         }
12879       break;
12880     }
12881     case MotionNotify:
12882     {
12883       while (XCheckMaskEvent(display,ButtonMotionMask,event)) ;
12884       if (event->xmotion.window == windows->image.id)
12885         if( IfMagickTrue(windows->magnify.mapped) )
12886           {
12887             /*
12888               Update magnified image.
12889             */
12890             x=event->xmotion.x;
12891             y=event->xmotion.y;
12892             if (x < 0)
12893               x=0;
12894             else
12895               if (x >= (int) windows->image.width)
12896                 x=(int) (windows->image.width-1);
12897             windows->magnify.x=(int) windows->image.x+x;
12898             if (y < 0)
12899               y=0;
12900             else
12901              if (y >= (int) windows->image.height)
12902                y=(int) (windows->image.height-1);
12903             windows->magnify.y=windows->image.y+y;
12904             XMakeMagnifyImage(display,windows,exception);
12905           }
12906       break;
12907     }
12908     case UnmapNotify:
12909     {
12910       if (event->xunmap.window == windows->magnify.id)
12911         {
12912           windows->magnify.mapped=MagickFalse;
12913           break;
12914         }
12915       if (event->xunmap.window == windows->info.id)
12916         {
12917           windows->info.mapped=MagickFalse;
12918           break;
12919         }
12920       break;
12921     }
12922     default:
12923       break;
12924   }
12925 }
12926 \f
12927 /*
12928 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12929 %                                                                             %
12930 %                                                                             %
12931 %                                                                             %
12932 +   X S e t C r o p G e o m e t r y                                           %
12933 %                                                                             %
12934 %                                                                             %
12935 %                                                                             %
12936 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12937 %
12938 %  XSetCropGeometry() accepts a cropping geometry relative to the Image window
12939 %  and translates it to a cropping geometry relative to the image.
12940 %
12941 %  The format of the XSetCropGeometry method is:
12942 %
12943 %      void XSetCropGeometry(Display *display,XWindows *windows,
12944 %        RectangleInfo *crop_info,Image *image)
12945 %
12946 %  A description of each parameter follows:
12947 %
12948 %    o display: Specifies a connection to an X server; returned from
12949 %      XOpenDisplay.
12950 %
12951 %    o windows: Specifies a pointer to a XWindows structure.
12952 %
12953 %    o crop_info:  A pointer to a RectangleInfo that defines a region of the
12954 %      Image window to crop.
12955 %
12956 %    o image: the image.
12957 %
12958 */
12959 static void XSetCropGeometry(Display *display,XWindows *windows,
12960   RectangleInfo *crop_info,Image *image)
12961 {
12962   char
12963     text[MaxTextExtent];
12964
12965   int
12966     x,
12967     y;
12968
12969   double
12970     scale_factor;
12971
12972   unsigned int
12973     height,
12974     width;
12975
12976   if( IfMagickTrue(windows->info.mapped) )
12977     {
12978       /*
12979         Display info on cropping rectangle.
12980       */
12981       (void) FormatLocaleString(text,MaxTextExtent," %.20gx%.20g%+.20g%+.20g",
12982         (double) crop_info->width,(double) crop_info->height,(double)
12983         crop_info->x,(double) crop_info->y);
12984       XInfoWidget(display,windows,text);
12985     }
12986   /*
12987     Cropping geometry is relative to any previous crop geometry.
12988   */
12989   x=0;
12990   y=0;
12991   width=(unsigned int) image->columns;
12992   height=(unsigned int) image->rows;
12993   if (windows->image.crop_geometry != (char *) NULL)
12994     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12995   else
12996     windows->image.crop_geometry=AcquireString((char *) NULL);
12997   /*
12998     Define the crop geometry string from the cropping rectangle.
12999   */
13000   scale_factor=(double) width/windows->image.ximage->width;
13001   if (crop_info->x > 0)
13002     x+=(int) (scale_factor*crop_info->x+0.5);
13003   width=(unsigned int) (scale_factor*crop_info->width+0.5);
13004   if (width == 0)
13005     width=1;
13006   scale_factor=(double) height/windows->image.ximage->height;
13007   if (crop_info->y > 0)
13008     y+=(int) (scale_factor*crop_info->y+0.5);
13009   height=(unsigned int) (scale_factor*crop_info->height+0.5);
13010   if (height == 0)
13011     height=1;
13012   (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
13013     "%ux%u%+d%+d",width,height,x,y);
13014 }
13015 \f
13016 /*
13017 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13018 %                                                                             %
13019 %                                                                             %
13020 %                                                                             %
13021 +   X T i l e I m a g e                                                       %
13022 %                                                                             %
13023 %                                                                             %
13024 %                                                                             %
13025 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13026 %
13027 %  XTileImage() loads or deletes a selected tile from a visual image directory.
13028 %  The load or delete command is chosen from a menu.
13029 %
13030 %  The format of the XTileImage method is:
13031 %
13032 %      Image *XTileImage(Display *display,XResourceInfo *resource_info,
13033 %        XWindows *windows,Image *image,XEvent *event,ExceptionInfo *exception)
13034 %
13035 %  A description of each parameter follows:
13036 %
13037 %    o tile_image:  XTileImage reads or deletes the tile image
13038 %      and returns it.  A null image is returned if an error occurs.
13039 %
13040 %    o display: Specifies a connection to an X server;  returned from
13041 %      XOpenDisplay.
13042 %
13043 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13044 %
13045 %    o windows: Specifies a pointer to a XWindows structure.
13046 %
13047 %    o image: the image; returned from ReadImage.
13048 %
13049 %    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
13050 %      the entire image is refreshed.
13051 %
13052 %    o exception: return any errors or warnings in this structure.
13053 %
13054 */
13055 static Image *XTileImage(Display *display,XResourceInfo *resource_info,
13056   XWindows *windows,Image *image,XEvent *event,ExceptionInfo *exception)
13057 {
13058   static const char
13059     *VerbMenu[] =
13060     {
13061       "Load",
13062       "Next",
13063       "Former",
13064       "Delete",
13065       "Update",
13066       (char *) NULL,
13067     };
13068
13069   static const ModeType
13070     TileCommands[] =
13071     {
13072       TileLoadCommand,
13073       TileNextCommand,
13074       TileFormerCommand,
13075       TileDeleteCommand,
13076       TileUpdateCommand
13077     };
13078
13079   char
13080     command[MaxTextExtent],
13081     filename[MaxTextExtent];
13082
13083   Image
13084     *tile_image;
13085
13086   int
13087     id,
13088     status,
13089     tile,
13090     x,
13091     y;
13092
13093   double
13094     scale_factor;
13095
13096   register char
13097     *p,
13098     *q;
13099
13100   register int
13101     i;
13102
13103   unsigned int
13104     height,
13105     width;
13106
13107   /*
13108     Tile image is relative to montage image configuration.
13109   */
13110   x=0;
13111   y=0;
13112   width=(unsigned int) image->columns;
13113   height=(unsigned int) image->rows;
13114   if (windows->image.crop_geometry != (char *) NULL)
13115     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
13116   scale_factor=(double) width/windows->image.ximage->width;
13117   event->xbutton.x+=windows->image.x;
13118   event->xbutton.x=(int) (scale_factor*event->xbutton.x+x+0.5);
13119   scale_factor=(double) height/windows->image.ximage->height;
13120   event->xbutton.y+=windows->image.y;
13121   event->xbutton.y=(int) (scale_factor*event->xbutton.y+y+0.5);
13122   /*
13123     Determine size and location of each tile in the visual image directory.
13124   */
13125   width=(unsigned int) image->columns;
13126   height=(unsigned int) image->rows;
13127   x=0;
13128   y=0;
13129   (void) XParseGeometry(image->montage,&x,&y,&width,&height);
13130   tile=((event->xbutton.y-y)/height)*(((int) image->columns-x)/width)+
13131     (event->xbutton.x-x)/width;
13132   if (tile < 0)
13133     {
13134       /*
13135         Button press is outside any tile.
13136       */
13137       (void) XBell(display,0);
13138       return((Image *) NULL);
13139     }
13140   /*
13141     Determine file name from the tile directory.
13142   */
13143   p=image->directory;
13144   for (i=tile; (i != 0) && (*p != '\0'); )
13145   {
13146     if (*p == '\n')
13147       i--;
13148     p++;
13149   }
13150   if (*p == '\0')
13151     {
13152       /*
13153         Button press is outside any tile.
13154       */
13155       (void) XBell(display,0);
13156       return((Image *) NULL);
13157     }
13158   /*
13159     Select a command from the pop-up menu.
13160   */
13161   id=XMenuWidget(display,windows,"Tile Verb",VerbMenu,command);
13162   if (id < 0)
13163     return((Image *) NULL);
13164   q=p;
13165   while ((*q != '\n') && (*q != '\0'))
13166     q++;
13167   (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13168   /*
13169     Perform command for the selected tile.
13170   */
13171   XSetCursorState(display,windows,MagickTrue);
13172   XCheckRefreshWindows(display,windows);
13173   tile_image=NewImageList();
13174   switch (TileCommands[id])
13175   {
13176     case TileLoadCommand:
13177     {
13178       /*
13179         Load tile image.
13180       */
13181       XCheckRefreshWindows(display,windows);
13182       (void) CopyMagickString(resource_info->image_info->magick,"MIFF",
13183         MaxTextExtent);
13184       (void) CopyMagickString(resource_info->image_info->filename,filename,
13185         MaxTextExtent);
13186       tile_image=ReadImage(resource_info->image_info,exception);
13187       CatchException(exception);
13188       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13189       break;
13190     }
13191     case TileNextCommand:
13192     {
13193       /*
13194         Display next image.
13195       */
13196       XClientMessage(display,windows->image.id,windows->im_protocols,
13197         windows->im_next_image,CurrentTime);
13198       break;
13199     }
13200     case TileFormerCommand:
13201     {
13202       /*
13203         Display former image.
13204       */
13205       XClientMessage(display,windows->image.id,windows->im_protocols,
13206         windows->im_former_image,CurrentTime);
13207       break;
13208     }
13209     case TileDeleteCommand:
13210     {
13211       /*
13212         Delete tile image.
13213       */
13214       if( IfMagickFalse(IsPathAccessible(filename)) )
13215         {
13216           XNoticeWidget(display,windows,"Image file does not exist:",filename);
13217           break;
13218         }
13219       status=XConfirmWidget(display,windows,"Really delete tile",filename);
13220       if (status <= 0)
13221         break;
13222       status=IsMagickTrue(remove_utf8(filename));
13223       if( IfMagickTrue(status) )
13224         {
13225           XNoticeWidget(display,windows,"Unable to delete image file:",
13226             filename);
13227           break;
13228         }
13229     }
13230     case TileUpdateCommand:
13231     {
13232       int
13233         x_offset,
13234         y_offset;
13235
13236       PixelInfo
13237         pixel;
13238
13239       register int
13240         j;
13241
13242       register Quantum
13243         *s;
13244
13245       /*
13246         Ensure all the images exist.
13247       */
13248       tile=0;
13249       GetPixelInfo(image,&pixel);
13250       for (p=image->directory; *p != '\0'; p++)
13251       {
13252         CacheView
13253           *image_view;
13254
13255         q=p;
13256         while ((*q != '\n') && (*q != '\0'))
13257           q++;
13258         (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13259         p=q;
13260         if( IfMagickTrue(IsPathAccessible(filename)) )
13261           {
13262             tile++;
13263             continue;
13264           }
13265         /*
13266           Overwrite tile with background color.
13267         */
13268         x_offset=(int) (width*(tile % (((int) image->columns-x)/width))+x);
13269         y_offset=(int) (height*(tile/(((int) image->columns-x)/width))+y);
13270         image_view=AcquireAuthenticCacheView(image,exception);
13271         (void) GetOneCacheViewVirtualPixelInfo(image_view,0,0,&pixel,exception);
13272         for (i=0; i < (int) height; i++)
13273         {
13274           s=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,(ssize_t)
13275             y_offset+i,width,1,exception);
13276           if (s == (Quantum *) NULL)
13277             break;
13278           for (j=0; j < (int) width; j++)
13279           {
13280             SetPixelInfoPixel(image,&pixel,s);
13281             s+=GetPixelChannels(image);
13282           }
13283           if( IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
13284             break;
13285         }
13286         image_view=DestroyCacheView(image_view);
13287         tile++;
13288       }
13289       windows->image.window_changes.width=(int) image->columns;
13290       windows->image.window_changes.height=(int) image->rows;
13291       XConfigureImageColormap(display,resource_info,windows,image,exception);
13292       (void) XConfigureImage(display,resource_info,windows,image,exception);
13293       break;
13294     }
13295     default:
13296       break;
13297   }
13298   XSetCursorState(display,windows,MagickFalse);
13299   return(tile_image);
13300 }
13301 \f
13302 /*
13303 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13304 %                                                                             %
13305 %                                                                             %
13306 %                                                                             %
13307 +   X T r a n s l a t e I m a g e                                             %
13308 %                                                                             %
13309 %                                                                             %
13310 %                                                                             %
13311 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13312 %
13313 %  XTranslateImage() translates the image within an Image window by one pixel
13314 %  as specified by the key symbol.  If the image has a montage string the
13315 %  translation is respect to the width and height contained within the string.
13316 %
13317 %  The format of the XTranslateImage method is:
13318 %
13319 %      void XTranslateImage(Display *display,XWindows *windows,
13320 %        Image *image,const KeySym key_symbol)
13321 %
13322 %  A description of each parameter follows:
13323 %
13324 %    o display: Specifies a connection to an X server; returned from
13325 %      XOpenDisplay.
13326 %
13327 %    o windows: Specifies a pointer to a XWindows structure.
13328 %
13329 %    o image: the image.
13330 %
13331 %    o key_symbol: Specifies a KeySym which indicates which side of the image
13332 %      to trim.
13333 %
13334 */
13335 static void XTranslateImage(Display *display,XWindows *windows,
13336   Image *image,const KeySym key_symbol)
13337 {
13338   char
13339     text[MaxTextExtent];
13340
13341   int
13342     x,
13343     y;
13344
13345   unsigned int
13346     x_offset,
13347     y_offset;
13348
13349   /*
13350     User specified a pan position offset.
13351   */
13352   x_offset=windows->image.width;
13353   y_offset=windows->image.height;
13354   if (image->montage != (char *) NULL)
13355     (void) XParseGeometry(image->montage,&x,&y,&x_offset,&y_offset);
13356   switch ((int) key_symbol)
13357   {
13358     case XK_Home:
13359     case XK_KP_Home:
13360     {
13361       windows->image.x=(int) windows->image.width/2;
13362       windows->image.y=(int) windows->image.height/2;
13363       break;
13364     }
13365     case XK_Left:
13366     case XK_KP_Left:
13367     {
13368       windows->image.x-=x_offset;
13369       break;
13370     }
13371     case XK_Next:
13372     case XK_Up:
13373     case XK_KP_Up:
13374     {
13375       windows->image.y-=y_offset;
13376       break;
13377     }
13378     case XK_Right:
13379     case XK_KP_Right:
13380     {
13381       windows->image.x+=x_offset;
13382       break;
13383     }
13384     case XK_Prior:
13385     case XK_Down:
13386     case XK_KP_Down:
13387     {
13388       windows->image.y+=y_offset;
13389       break;
13390     }
13391     default:
13392       return;
13393   }
13394   /*
13395     Check boundary conditions.
13396   */
13397   if (windows->image.x < 0)
13398     windows->image.x=0;
13399   else
13400     if ((int) (windows->image.x+windows->image.width) >
13401         windows->image.ximage->width)
13402       windows->image.x=(int) windows->image.ximage->width-windows->image.width;
13403   if (windows->image.y < 0)
13404     windows->image.y=0;
13405   else
13406     if ((int) (windows->image.y+windows->image.height) >
13407         windows->image.ximage->height)
13408       windows->image.y=(int) windows->image.ximage->height-windows->image.height;
13409   /*
13410     Refresh Image window.
13411   */
13412   (void) FormatLocaleString(text,MaxTextExtent," %ux%u%+d%+d ",
13413     windows->image.width,windows->image.height,windows->image.x,
13414     windows->image.y);
13415   XInfoWidget(display,windows,text);
13416   XCheckRefreshWindows(display,windows);
13417   XDrawPanRectangle(display,windows);
13418   XRefreshWindow(display,&windows->image,(XEvent *) NULL);
13419   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13420 }
13421 \f
13422 /*
13423 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13424 %                                                                             %
13425 %                                                                             %
13426 %                                                                             %
13427 +   X T r i m I m a g e                                                       %
13428 %                                                                             %
13429 %                                                                             %
13430 %                                                                             %
13431 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13432 %
13433 %  XTrimImage() trims the edges from the Image window.
13434 %
13435 %  The format of the XTrimImage method is:
13436 %
13437 %      MagickBooleanType XTrimImage(Display *display,
13438 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
13439 %        ExceptionInfo *exception)
13440 %
13441 %  A description of each parameter follows:
13442 %
13443 %    o display: Specifies a connection to an X server; returned from
13444 %      XOpenDisplay.
13445 %
13446 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13447 %
13448 %    o windows: Specifies a pointer to a XWindows structure.
13449 %
13450 %    o image: the image.
13451 %
13452 %    o exception: return any errors or warnings in this structure.
13453 %
13454 */
13455 static MagickBooleanType XTrimImage(Display *display,
13456   XResourceInfo *resource_info,XWindows *windows,Image *image,
13457   ExceptionInfo *exception)
13458 {
13459   RectangleInfo
13460     trim_info;
13461
13462   register int
13463     x,
13464     y;
13465
13466   size_t
13467     background,
13468     pixel;
13469
13470   /*
13471     Trim edges from image.
13472   */
13473   XSetCursorState(display,windows,MagickTrue);
13474   XCheckRefreshWindows(display,windows);
13475   /*
13476     Crop the left edge.
13477   */
13478   background=XGetPixel(windows->image.ximage,0,0);
13479   trim_info.width=(size_t) windows->image.ximage->width;
13480   for (x=0; x < windows->image.ximage->width; x++)
13481   {
13482     for (y=0; y < windows->image.ximage->height; y++)
13483     {
13484       pixel=XGetPixel(windows->image.ximage,x,y);
13485       if (pixel != background)
13486         break;
13487     }
13488     if (y < windows->image.ximage->height)
13489       break;
13490   }
13491   trim_info.x=(ssize_t) x;
13492   if (trim_info.x == (ssize_t) windows->image.ximage->width)
13493     {
13494       XSetCursorState(display,windows,MagickFalse);
13495       return(MagickFalse);
13496     }
13497   /*
13498     Crop the right edge.
13499   */
13500   background=XGetPixel(windows->image.ximage,windows->image.ximage->width-1,0);
13501   for (x=windows->image.ximage->width-1; x != 0; x--)
13502   {
13503     for (y=0; y < windows->image.ximage->height; y++)
13504     {
13505       pixel=XGetPixel(windows->image.ximage,x,y);
13506       if (pixel != background)
13507         break;
13508     }
13509     if (y < windows->image.ximage->height)
13510       break;
13511   }
13512   trim_info.width=(size_t) (x-trim_info.x+1);
13513   /*
13514     Crop the top edge.
13515   */
13516   background=XGetPixel(windows->image.ximage,0,0);
13517   trim_info.height=(size_t) windows->image.ximage->height;
13518   for (y=0; y < windows->image.ximage->height; y++)
13519   {
13520     for (x=0; x < windows->image.ximage->width; x++)
13521     {
13522       pixel=XGetPixel(windows->image.ximage,x,y);
13523       if (pixel != background)
13524         break;
13525     }
13526     if (x < windows->image.ximage->width)
13527       break;
13528   }
13529   trim_info.y=(ssize_t) y;
13530   /*
13531     Crop the bottom edge.
13532   */
13533   background=XGetPixel(windows->image.ximage,0,windows->image.ximage->height-1);
13534   for (y=windows->image.ximage->height-1; y != 0; y--)
13535   {
13536     for (x=0; x < windows->image.ximage->width; x++)
13537     {
13538       pixel=XGetPixel(windows->image.ximage,x,y);
13539       if (pixel != background)
13540         break;
13541     }
13542     if (x < windows->image.ximage->width)
13543       break;
13544   }
13545   trim_info.height=(size_t) y-trim_info.y+1;
13546   if (((unsigned int) trim_info.width != windows->image.width) ||
13547       ((unsigned int) trim_info.height != windows->image.height))
13548     {
13549       /*
13550         Reconfigure Image window as defined by the trimming rectangle.
13551       */
13552       XSetCropGeometry(display,windows,&trim_info,image);
13553       windows->image.window_changes.width=(int) trim_info.width;
13554       windows->image.window_changes.height=(int) trim_info.height;
13555       (void) XConfigureImage(display,resource_info,windows,image,exception);
13556     }
13557   XSetCursorState(display,windows,MagickFalse);
13558   return(MagickTrue);
13559 }
13560 \f
13561 /*
13562 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13563 %                                                                             %
13564 %                                                                             %
13565 %                                                                             %
13566 +   X V i s u a l D i r e c t o r y I m a g e                                 %
13567 %                                                                             %
13568 %                                                                             %
13569 %                                                                             %
13570 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13571 %
13572 %  XVisualDirectoryImage() creates a Visual Image Directory.
13573 %
13574 %  The format of the XVisualDirectoryImage method is:
13575 %
13576 %      Image *XVisualDirectoryImage(Display *display,
13577 %        XResourceInfo *resource_info,XWindows *windows,
13578 %        ExceptionInfo *exception)
13579 %
13580 %  A description of each parameter follows:
13581 %
13582 %    o display: Specifies a connection to an X server; returned from
13583 %      XOpenDisplay.
13584 %
13585 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13586 %
13587 %    o windows: Specifies a pointer to a XWindows structure.
13588 %
13589 %    o exception: return any errors or warnings in this structure.
13590 %
13591 */
13592 static Image *XVisualDirectoryImage(Display *display,
13593   XResourceInfo *resource_info,XWindows *windows,ExceptionInfo *exception)
13594 {
13595 #define TileImageTag  "Scale/Image"
13596 #define XClientName  "montage"
13597
13598   char
13599     **filelist;
13600
13601   Image
13602     *images,
13603     *montage_image,
13604     *next_image,
13605     *thumbnail_image;
13606
13607   ImageInfo
13608     *read_info;
13609
13610   int
13611     number_files;
13612
13613   MagickBooleanType
13614     backdrop;
13615
13616   MagickStatusType
13617     status;
13618
13619   MontageInfo
13620     *montage_info;
13621
13622   RectangleInfo
13623     geometry;
13624
13625   register int
13626     i;
13627
13628   static char
13629     filename[MaxTextExtent] = "\0",
13630     filenames[MaxTextExtent] = "*";
13631
13632   XResourceInfo
13633     background_resources;
13634
13635   /*
13636     Request file name from user.
13637   */
13638   XFileBrowserWidget(display,windows,"Directory",filenames);
13639   if (*filenames == '\0')
13640     return((Image *) NULL);
13641   /*
13642     Expand the filenames.
13643   */
13644   filelist=(char **) AcquireMagickMemory(sizeof(*filelist));
13645   if (filelist == (char **) NULL)
13646     {
13647       ThrowXWindowFatalException(ResourceLimitError,"MemoryAllocationFailed",
13648         filenames);
13649       return((Image *) NULL);
13650     }
13651   number_files=1;
13652   filelist[0]=filenames;
13653   status=ExpandFilenames(&number_files,&filelist);
13654   if( IfMagickFalse(status) || (number_files == 0))
13655     {
13656       if (number_files == 0)
13657         ThrowXWindowFatalException(ImageError,"NoImagesWereFound",filenames)
13658       else
13659         ThrowXWindowFatalException(ResourceLimitError,"MemoryAllocationFailed",
13660           filenames);
13661       return((Image *) NULL);
13662     }
13663   /*
13664     Set image background resources.
13665   */
13666   background_resources=(*resource_info);
13667   background_resources.window_id=AcquireString("");
13668   (void) FormatLocaleString(background_resources.window_id,MaxTextExtent,
13669     "0x%lx",windows->image.id);
13670   background_resources.backdrop=MagickTrue;
13671   /*
13672     Read each image and convert them to a tile.
13673   */
13674   backdrop=IsMagickTrue( (windows->visual_info->klass == TrueColor) ||
13675     (windows->visual_info->klass == DirectColor) );
13676   read_info=CloneImageInfo(resource_info->image_info);
13677   (void) SetImageOption(read_info,"jpeg:size","120x120");
13678   (void) CloneString(&read_info->size,DefaultTileGeometry);
13679   (void) SetImageInfoProgressMonitor(read_info,(MagickProgressMonitor) NULL,
13680     (void *) NULL);
13681   images=NewImageList();
13682   XSetCursorState(display,windows,MagickTrue);
13683   XCheckRefreshWindows(display,windows);
13684   for (i=0; i < (int) number_files; i++)
13685   {
13686     (void) CopyMagickString(read_info->filename,filelist[i],MaxTextExtent);
13687     filelist[i]=DestroyString(filelist[i]);
13688     *read_info->magick='\0';
13689     next_image=ReadImage(read_info,exception);
13690     CatchException(exception);
13691     if (next_image != (Image *) NULL)
13692       {
13693         (void) DeleteImageProperty(next_image,"label");
13694         (void) SetImageProperty(next_image,"label",InterpretImageProperties(
13695           read_info,next_image,DefaultTileLabel,exception),exception);
13696         (void) ParseRegionGeometry(next_image,read_info->size,&geometry,
13697           exception);
13698         thumbnail_image=ThumbnailImage(next_image,geometry.width,
13699           geometry.height,exception);
13700         if (thumbnail_image != (Image *) NULL)
13701           {
13702             next_image=DestroyImage(next_image);
13703             next_image=thumbnail_image;
13704           }
13705         if (backdrop)
13706           {
13707             (void) XDisplayBackgroundImage(display,&background_resources,
13708               next_image,exception);
13709             XSetCursorState(display,windows,MagickTrue);
13710           }
13711         AppendImageToList(&images,next_image);
13712         if (images->progress_monitor != (MagickProgressMonitor) NULL)
13713           {
13714             MagickBooleanType
13715               proceed;
13716
13717             proceed=SetImageProgress(images,LoadImageTag,(MagickOffsetType) i,
13718               (MagickSizeType) number_files);
13719             if( IfMagickFalse(proceed) )
13720               break;
13721           }
13722       }
13723   }
13724   filelist=(char **) RelinquishMagickMemory(filelist);
13725   if (images == (Image *) NULL)
13726     {
13727       read_info=DestroyImageInfo(read_info);
13728       XSetCursorState(display,windows,MagickFalse);
13729       ThrowXWindowFatalException(ImageError,"NoImagesWereLoaded",filenames);
13730       return((Image *) NULL);
13731     }
13732   /*
13733     Create the Visual Image Directory.
13734   */
13735   montage_info=CloneMontageInfo(read_info,(MontageInfo *) NULL);
13736   montage_info->pointsize=10;
13737   if (resource_info->font != (char *) NULL)
13738     (void) CloneString(&montage_info->font,resource_info->font);
13739   (void) CopyMagickString(montage_info->filename,filename,MaxTextExtent);
13740   montage_image=MontageImageList(read_info,montage_info,GetFirstImageInList(
13741     images),exception);
13742   images=DestroyImageList(images);
13743   montage_info=DestroyMontageInfo(montage_info);
13744   read_info=DestroyImageInfo(read_info);
13745   XSetCursorState(display,windows,MagickFalse);
13746   if (montage_image == (Image *) NULL)
13747     return(montage_image);
13748   XClientMessage(display,windows->image.id,windows->im_protocols,
13749     windows->im_next_image,CurrentTime);
13750   return(montage_image);
13751 }
13752 \f
13753 /*
13754 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13755 %                                                                             %
13756 %                                                                             %
13757 %                                                                             %
13758 %   X D i s p l a y B a c k g r o u n d I m a g e                             %
13759 %                                                                             %
13760 %                                                                             %
13761 %                                                                             %
13762 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13763 %
13764 %  XDisplayBackgroundImage() displays an image in the background of a window.
13765 %
13766 %  The format of the XDisplayBackgroundImage method is:
13767 %
13768 %      MagickBooleanType XDisplayBackgroundImage(Display *display,
13769 %        XResourceInfo *resource_info,Image *image,ExceptionInfo *exception)
13770 %
13771 %  A description of each parameter follows:
13772 %
13773 %    o display: Specifies a connection to an X server;  returned from
13774 %      XOpenDisplay.
13775 %
13776 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13777 %
13778 %    o image: the image.
13779 %
13780 %    o exception: return any errors or warnings in this structure.
13781 %
13782 */
13783 MagickExport MagickBooleanType XDisplayBackgroundImage(Display *display,
13784   XResourceInfo *resource_info,Image *image,ExceptionInfo *exception)
13785 {
13786   char
13787     geometry[MaxTextExtent],
13788     visual_type[MaxTextExtent];
13789
13790   int
13791     height,
13792     status,
13793     width;
13794
13795   RectangleInfo
13796     geometry_info;
13797
13798   static XPixelInfo
13799     pixel;
13800
13801   static XStandardColormap
13802     *map_info;
13803
13804   static XVisualInfo
13805     *visual_info = (XVisualInfo *) NULL;
13806
13807   static XWindowInfo
13808     window_info;
13809
13810   size_t
13811     delay;
13812
13813   Window
13814     root_window;
13815
13816   XGCValues
13817     context_values;
13818
13819   XResourceInfo
13820     resources;
13821
13822   XWindowAttributes
13823     window_attributes;
13824
13825   /*
13826     Determine target window.
13827   */
13828   assert(image != (Image *) NULL);
13829   assert(image->signature == MagickSignature);
13830   if( IfMagickTrue(image->debug) )
13831     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
13832   resources=(*resource_info);
13833   window_info.id=(Window) NULL;
13834   root_window=XRootWindow(display,XDefaultScreen(display));
13835   if (LocaleCompare(resources.window_id,"root") == 0)
13836     window_info.id=root_window;
13837   else
13838     {
13839       if (isdigit((unsigned char) *resources.window_id) != 0)
13840         window_info.id=XWindowByID(display,root_window,
13841           (Window) strtol((char *) resources.window_id,(char **) NULL,0));
13842       if (window_info.id == (Window) NULL)
13843         window_info.id=XWindowByName(display,root_window,resources.window_id);
13844     }
13845   if (window_info.id == (Window) NULL)
13846     {
13847       ThrowXWindowFatalException(XServerError,"NoWindowWithSpecifiedIDExists",
13848         resources.window_id);
13849       return(MagickFalse);
13850     }
13851   /*
13852     Determine window visual id.
13853   */
13854   window_attributes.width=XDisplayWidth(display,XDefaultScreen(display));
13855   window_attributes.height=XDisplayHeight(display,XDefaultScreen(display));
13856   (void) CopyMagickString(visual_type,"default",MaxTextExtent);
13857   status=XGetWindowAttributes(display,window_info.id,&window_attributes);
13858   if (status != 0)
13859     (void) FormatLocaleString(visual_type,MaxTextExtent,"0x%lx",
13860       XVisualIDFromVisual(window_attributes.visual));
13861   if (visual_info == (XVisualInfo *) NULL)
13862     {
13863       /*
13864         Allocate standard colormap.
13865       */
13866       map_info=XAllocStandardColormap();
13867       if (map_info == (XStandardColormap *) NULL)
13868         ThrowXWindowFatalException(XServerFatalError,"MemoryAllocationFailed",
13869           image->filename);
13870       map_info->colormap=(Colormap) NULL;
13871       pixel.pixels=(unsigned long *) NULL;
13872       /*
13873         Initialize visual info.
13874       */
13875       resources.map_type=(char *) NULL;
13876       resources.visual_type=visual_type;
13877       visual_info=XBestVisualInfo(display,map_info,&resources);
13878       if (visual_info == (XVisualInfo *) NULL)
13879         ThrowXWindowFatalException(XServerFatalError,"UnableToGetVisual",
13880           resources.visual_type);
13881       /*
13882         Initialize window info.
13883       */
13884       window_info.ximage=(XImage *) NULL;
13885       window_info.matte_image=(XImage *) NULL;
13886       window_info.pixmap=(Pixmap) NULL;
13887       window_info.matte_pixmap=(Pixmap) NULL;
13888     }
13889   /*
13890     Free previous root colors.
13891   */
13892   if (window_info.id == root_window)
13893     (void) XDestroyWindowColors(display,root_window);
13894   /*
13895     Initialize Standard Colormap.
13896   */
13897   resources.colormap=SharedColormap;
13898   XMakeStandardColormap(display,visual_info,&resources,image,map_info,&pixel,
13899     exception);
13900   /*
13901     Graphic context superclass.
13902   */
13903   context_values.background=pixel.background_color.pixel;
13904   context_values.foreground=pixel.foreground_color.pixel;
13905   pixel.annotate_context=XCreateGC(display,window_info.id,
13906     (size_t) (GCBackground | GCForeground),&context_values);
13907   if (pixel.annotate_context == (GC) NULL)
13908     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
13909       image->filename);
13910   /*
13911     Initialize Image window attributes.
13912   */
13913   window_info.name=AcquireString("\0");
13914   window_info.icon_name=AcquireString("\0");
13915   XGetWindowInfo(display,visual_info,map_info,&pixel,(XFontStruct *) NULL,
13916     &resources,&window_info);
13917   /*
13918     Create the X image.
13919   */
13920   window_info.width=(unsigned int) image->columns;
13921   window_info.height=(unsigned int) image->rows;
13922   if ((image->columns != window_info.width) ||
13923       (image->rows != window_info.height))
13924     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13925       image->filename);
13926   (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>",
13927     window_attributes.width,window_attributes.height);
13928   geometry_info.width=window_info.width;
13929   geometry_info.height=window_info.height;
13930   geometry_info.x=(ssize_t) window_info.x;
13931   geometry_info.y=(ssize_t) window_info.y;
13932   (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
13933     &geometry_info.width,&geometry_info.height);
13934   window_info.width=(unsigned int) geometry_info.width;
13935   window_info.height=(unsigned int) geometry_info.height;
13936   window_info.x=(int) geometry_info.x;
13937   window_info.y=(int) geometry_info.y;
13938   status=XMakeImage(display,&resources,&window_info,image,window_info.width,
13939     window_info.height,exception);
13940   if( IfMagickFalse(status) )
13941     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13942       image->filename);
13943   window_info.x=0;
13944   window_info.y=0;
13945   if( IfMagickTrue(image->debug) )
13946     {
13947       (void) LogMagickEvent(X11Event,GetMagickModule(),
13948         "Image: %s[%.20g] %.20gx%.20g ",image->filename,(double) image->scene,
13949         (double) image->columns,(double) image->rows);
13950       if (image->colors != 0)
13951         (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
13952           image->colors);
13953       (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",image->magick);
13954     }
13955   /*
13956     Adjust image dimensions as specified by backdrop or geometry options.
13957   */
13958   width=(int) window_info.width;
13959   height=(int) window_info.height;
13960   if( IfMagickTrue(resources.backdrop) )
13961     {
13962       /*
13963         Center image on window.
13964       */
13965       window_info.x=(window_attributes.width/2)-
13966         (window_info.ximage->width/2);
13967       window_info.y=(window_attributes.height/2)-
13968         (window_info.ximage->height/2);
13969       width=window_attributes.width;
13970       height=window_attributes.height;
13971     }
13972   if ((resources.image_geometry != (char *) NULL) &&
13973       (*resources.image_geometry != '\0'))
13974     {
13975       char
13976         default_geometry[MaxTextExtent];
13977
13978       int
13979         flags,
13980         gravity;
13981
13982       XSizeHints
13983         *size_hints;
13984
13985       /*
13986         User specified geometry.
13987       */
13988       size_hints=XAllocSizeHints();
13989       if (size_hints == (XSizeHints *) NULL)
13990         ThrowXWindowFatalException(ResourceLimitFatalError,
13991           "MemoryAllocationFailed",image->filename);
13992       size_hints->flags=0L;
13993       (void) FormatLocaleString(default_geometry,MaxTextExtent,"%dx%d",
13994         width,height);
13995       flags=XWMGeometry(display,visual_info->screen,resources.image_geometry,
13996         default_geometry,window_info.border_width,size_hints,&window_info.x,
13997         &window_info.y,&width,&height,&gravity);
13998       if (flags & (XValue | YValue))
13999         {
14000           width=window_attributes.width;
14001           height=window_attributes.height;
14002         }
14003       (void) XFree((void *) size_hints);
14004     }
14005   /*
14006     Create the X pixmap.
14007   */
14008   window_info.pixmap=XCreatePixmap(display,window_info.id,(unsigned int) width,
14009     (unsigned int) height,window_info.depth);
14010   if (window_info.pixmap == (Pixmap) NULL)
14011     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXPixmap",
14012       image->filename);
14013   /*
14014     Display pixmap on the window.
14015   */
14016   if (((unsigned int) width > window_info.width) ||
14017       ((unsigned int) height > window_info.height))
14018     (void) XFillRectangle(display,window_info.pixmap,
14019       window_info.annotate_context,0,0,(unsigned int) width,
14020       (unsigned int) height);
14021   (void) XPutImage(display,window_info.pixmap,window_info.annotate_context,
14022     window_info.ximage,0,0,window_info.x,window_info.y,(unsigned int)
14023     window_info.width,(unsigned int) window_info.height);
14024   (void) XSetWindowBackgroundPixmap(display,window_info.id,window_info.pixmap);
14025   (void) XClearWindow(display,window_info.id);
14026   delay=1000*image->delay/MagickMax(image->ticks_per_second,1L);
14027   XDelay(display,delay == 0UL ? 10UL : delay);
14028   (void) XSync(display,MagickFalse);
14029   return(IsMagickTrue(window_info.id == root_window));
14030 }
14031 \f
14032 /*
14033 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
14034 %                                                                             %
14035 %                                                                             %
14036 %                                                                             %
14037 +   X D i s p l a y I m a g e                                                 %
14038 %                                                                             %
14039 %                                                                             %
14040 %                                                                             %
14041 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
14042 %
14043 %  XDisplayImage() displays an image via X11.  A new image is created and
14044 %  returned if the user interactively transforms the displayed image.
14045 %
14046 %  The format of the XDisplayImage method is:
14047 %
14048 %      Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
14049 %        char **argv,int argc,Image **image,size_t *state,
14050 %        ExceptionInfo *exception)
14051 %
14052 %  A description of each parameter follows:
14053 %
14054 %    o nexus:  Method XDisplayImage returns an image when the
14055 %      user chooses 'Open Image' from the command menu or picks a tile
14056 %      from the image directory.  Otherwise a null image is returned.
14057 %
14058 %    o display: Specifies a connection to an X server;  returned from
14059 %      XOpenDisplay.
14060 %
14061 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
14062 %
14063 %    o argv: Specifies the application's argument list.
14064 %
14065 %    o argc: Specifies the number of arguments.
14066 %
14067 %    o image: Specifies an address to an address of an Image structure;
14068 %
14069 %    o exception: return any errors or warnings in this structure.
14070 %
14071 */
14072 MagickExport Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
14073   char **argv,int argc,Image **image,size_t *state,ExceptionInfo *exception)
14074 {
14075 #define MagnifySize  256  /* must be a power of 2 */
14076 #define MagickMenus  10
14077 #define MagickTitle  "Commands"
14078
14079   static const char
14080     *CommandMenu[] =
14081     {
14082       "File",
14083       "Edit",
14084       "View",
14085       "Transform",
14086       "Enhance",
14087       "Effects",
14088       "F/X",
14089       "Image Edit",
14090       "Miscellany",
14091       "Help",
14092       (char *) NULL
14093     },
14094     *FileMenu[] =
14095     {
14096       "Open...",
14097       "Next",
14098       "Former",
14099       "Select...",
14100       "Save...",
14101       "Print...",
14102       "Delete...",
14103       "New...",
14104       "Visual Directory...",
14105       "Quit",
14106       (char *) NULL
14107     },
14108     *EditMenu[] =
14109     {
14110       "Undo",
14111       "Redo",
14112       "Cut",
14113       "Copy",
14114       "Paste",
14115       (char *) NULL
14116     },
14117     *ViewMenu[] =
14118     {
14119       "Half Size",
14120       "Original Size",
14121       "Double Size",
14122       "Resize...",
14123       "Apply",
14124       "Refresh",
14125       "Restore",
14126       (char *) NULL
14127     },
14128     *TransformMenu[] =
14129     {
14130       "Crop",
14131       "Chop",
14132       "Flop",
14133       "Flip",
14134       "Rotate Right",
14135       "Rotate Left",
14136       "Rotate...",
14137       "Shear...",
14138       "Roll...",
14139       "Trim Edges",
14140       (char *) NULL
14141     },
14142     *EnhanceMenu[] =
14143     {
14144       "Hue...",
14145       "Saturation...",
14146       "Brightness...",
14147       "Gamma...",
14148       "Spiff",
14149       "Dull",
14150       "Contrast Stretch...",
14151       "Sigmoidal Contrast...",
14152       "Normalize",
14153       "Equalize",
14154       "Negate",
14155       "Grayscale",
14156       "Map...",
14157       "Quantize...",
14158       (char *) NULL
14159     },
14160     *EffectsMenu[] =
14161     {
14162       "Despeckle",
14163       "Emboss",
14164       "Reduce Noise",
14165       "Add Noise...",
14166       "Sharpen...",
14167       "Blur...",
14168       "Threshold...",
14169       "Edge Detect...",
14170       "Spread...",
14171       "Shade...",
14172       "Raise...",
14173       "Segment...",
14174       (char *) NULL
14175     },
14176     *FXMenu[] =
14177     {
14178       "Solarize...",
14179       "Sepia Tone...",
14180       "Swirl...",
14181       "Implode...",
14182       "Vignette...",
14183       "Wave...",
14184       "Oil Paint...",
14185       "Charcoal Draw...",
14186       (char *) NULL
14187     },
14188     *ImageEditMenu[] =
14189     {
14190       "Annotate...",
14191       "Draw...",
14192       "Color...",
14193       "Matte...",
14194       "Composite...",
14195       "Add Border...",
14196       "Add Frame...",
14197       "Comment...",
14198       "Launch...",
14199       "Region of Interest...",
14200       (char *) NULL
14201     },
14202     *MiscellanyMenu[] =
14203     {
14204       "Image Info",
14205       "Zoom Image",
14206       "Show Preview...",
14207       "Show Histogram",
14208       "Show Matte",
14209       "Background...",
14210       "Slide Show...",
14211       "Preferences...",
14212       (char *) NULL
14213     },
14214     *HelpMenu[] =
14215     {
14216       "Overview",
14217       "Browse Documentation",
14218       "About Display",
14219       (char *) NULL
14220     },
14221     *ShortCutsMenu[] =
14222     {
14223       "Next",
14224       "Former",
14225       "Open...",
14226       "Save...",
14227       "Print...",
14228       "Undo",
14229       "Restore",
14230       "Image Info",
14231       "Quit",
14232       (char *) NULL
14233     },
14234     *VirtualMenu[] =
14235     {
14236       "Image Info",
14237       "Print",
14238       "Next",
14239       "Quit",
14240       (char *) NULL
14241     };
14242
14243   static const char
14244     **Menus[MagickMenus] =
14245     {
14246       FileMenu,
14247       EditMenu,
14248       ViewMenu,
14249       TransformMenu,
14250       EnhanceMenu,
14251       EffectsMenu,
14252       FXMenu,
14253       ImageEditMenu,
14254       MiscellanyMenu,
14255       HelpMenu
14256     };
14257
14258   static CommandType
14259     CommandMenus[] =
14260     {
14261       NullCommand,
14262       NullCommand,
14263       NullCommand,
14264       NullCommand,
14265       NullCommand,
14266       NullCommand,
14267       NullCommand,
14268       NullCommand,
14269       NullCommand,
14270       NullCommand,
14271     },
14272     FileCommands[] =
14273     {
14274       OpenCommand,
14275       NextCommand,
14276       FormerCommand,
14277       SelectCommand,
14278       SaveCommand,
14279       PrintCommand,
14280       DeleteCommand,
14281       NewCommand,
14282       VisualDirectoryCommand,
14283       QuitCommand
14284     },
14285     EditCommands[] =
14286     {
14287       UndoCommand,
14288       RedoCommand,
14289       CutCommand,
14290       CopyCommand,
14291       PasteCommand
14292     },
14293     ViewCommands[] =
14294     {
14295       HalfSizeCommand,
14296       OriginalSizeCommand,
14297       DoubleSizeCommand,
14298       ResizeCommand,
14299       ApplyCommand,
14300       RefreshCommand,
14301       RestoreCommand
14302     },
14303     TransformCommands[] =
14304     {
14305       CropCommand,
14306       ChopCommand,
14307       FlopCommand,
14308       FlipCommand,
14309       RotateRightCommand,
14310       RotateLeftCommand,
14311       RotateCommand,
14312       ShearCommand,
14313       RollCommand,
14314       TrimCommand
14315     },
14316     EnhanceCommands[] =
14317     {
14318       HueCommand,
14319       SaturationCommand,
14320       BrightnessCommand,
14321       GammaCommand,
14322       SpiffCommand,
14323       DullCommand,
14324       ContrastStretchCommand,
14325       SigmoidalContrastCommand,
14326       NormalizeCommand,
14327       EqualizeCommand,
14328       NegateCommand,
14329       GrayscaleCommand,
14330       MapCommand,
14331       QuantizeCommand
14332     },
14333     EffectsCommands[] =
14334     {
14335       DespeckleCommand,
14336       EmbossCommand,
14337       ReduceNoiseCommand,
14338       AddNoiseCommand,
14339       SharpenCommand,
14340       BlurCommand,
14341       ThresholdCommand,
14342       EdgeDetectCommand,
14343       SpreadCommand,
14344       ShadeCommand,
14345       RaiseCommand,
14346       SegmentCommand
14347     },
14348     FXCommands[] =
14349     {
14350       SolarizeCommand,
14351       SepiaToneCommand,
14352       SwirlCommand,
14353       ImplodeCommand,
14354       VignetteCommand,
14355       WaveCommand,
14356       OilPaintCommand,
14357       CharcoalDrawCommand
14358     },
14359     ImageEditCommands[] =
14360     {
14361       AnnotateCommand,
14362       DrawCommand,
14363       ColorCommand,
14364       MatteCommand,
14365       CompositeCommand,
14366       AddBorderCommand,
14367       AddFrameCommand,
14368       CommentCommand,
14369       LaunchCommand,
14370       RegionofInterestCommand
14371     },
14372     MiscellanyCommands[] =
14373     {
14374       InfoCommand,
14375       ZoomCommand,
14376       ShowPreviewCommand,
14377       ShowHistogramCommand,
14378       ShowMatteCommand,
14379       BackgroundCommand,
14380       SlideShowCommand,
14381       PreferencesCommand
14382     },
14383     HelpCommands[] =
14384     {
14385       HelpCommand,
14386       BrowseDocumentationCommand,
14387       VersionCommand
14388     },
14389     ShortCutsCommands[] =
14390     {
14391       NextCommand,
14392       FormerCommand,
14393       OpenCommand,
14394       SaveCommand,
14395       PrintCommand,
14396       UndoCommand,
14397       RestoreCommand,
14398       InfoCommand,
14399       QuitCommand
14400     },
14401     VirtualCommands[] =
14402     {
14403       InfoCommand,
14404       PrintCommand,
14405       NextCommand,
14406       QuitCommand
14407     };
14408
14409   static CommandType
14410     *Commands[MagickMenus] =
14411     {
14412       FileCommands,
14413       EditCommands,
14414       ViewCommands,
14415       TransformCommands,
14416       EnhanceCommands,
14417       EffectsCommands,
14418       FXCommands,
14419       ImageEditCommands,
14420       MiscellanyCommands,
14421       HelpCommands
14422     };
14423
14424   char
14425     command[MaxTextExtent],
14426     *directory,
14427     geometry[MaxTextExtent],
14428     resource_name[MaxTextExtent];
14429
14430   CommandType
14431     command_type;
14432
14433   Image
14434     *display_image,
14435     *nexus;
14436
14437   int
14438     entry,
14439     id;
14440
14441   KeySym
14442     key_symbol;
14443
14444   MagickStatusType
14445     context_mask,
14446     status;
14447
14448   RectangleInfo
14449     geometry_info;
14450
14451   register int
14452     i;
14453
14454   static char
14455     working_directory[MaxTextExtent];
14456
14457   static XPoint
14458     vid_info;
14459
14460   static XWindowInfo
14461     *magick_windows[MaxXWindows];
14462
14463   static unsigned int
14464     number_windows;
14465
14466   struct stat
14467     attributes;
14468
14469   time_t
14470     timer,
14471     timestamp,
14472     update_time;
14473
14474   unsigned int
14475     height,
14476     width;
14477
14478   size_t
14479     delay;
14480
14481   WarningHandler
14482     warning_handler;
14483
14484   Window
14485     root_window;
14486
14487   XClassHint
14488     *class_hints;
14489
14490   XEvent
14491     event;
14492
14493   XFontStruct
14494     *font_info;
14495
14496   XGCValues
14497     context_values;
14498
14499   XPixelInfo
14500     *icon_pixel,
14501     *pixel;
14502
14503   XResourceInfo
14504     *icon_resources;
14505
14506   XStandardColormap
14507     *icon_map,
14508     *map_info;
14509
14510   XVisualInfo
14511     *icon_visual,
14512     *visual_info;
14513
14514   XWindowChanges
14515     window_changes;
14516
14517   XWindows
14518     *windows;
14519
14520   XWMHints
14521     *manager_hints;
14522
14523   assert(image != (Image **) NULL);
14524   assert((*image)->signature == MagickSignature);
14525   if( IfMagickTrue((*image)->debug) )
14526     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*image)->filename);
14527   display_image=(*image);
14528   warning_handler=(WarningHandler) NULL;
14529   windows=XSetWindows((XWindows *) ~0);
14530   if (windows != (XWindows *) NULL)
14531     {
14532       int
14533         status;
14534
14535       status=chdir(working_directory);
14536       if (status == -1)
14537         (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
14538           "UnableToOpenFile","%s",working_directory);
14539       warning_handler=resource_info->display_warnings ?
14540         SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
14541       warning_handler=resource_info->display_warnings ?
14542         SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
14543     }
14544   else
14545     {
14546       /*
14547         Allocate windows structure.
14548       */
14549       resource_info->colors=display_image->colors;
14550       windows=XSetWindows(XInitializeWindows(display,resource_info));
14551       if (windows == (XWindows *) NULL)
14552         ThrowXWindowFatalException(XServerFatalError,"UnableToCreateWindow",
14553           (*image)->filename);
14554       /*
14555         Initialize window id's.
14556       */
14557       number_windows=0;
14558       magick_windows[number_windows++]=(&windows->icon);
14559       magick_windows[number_windows++]=(&windows->backdrop);
14560       magick_windows[number_windows++]=(&windows->image);
14561       magick_windows[number_windows++]=(&windows->info);
14562       magick_windows[number_windows++]=(&windows->command);
14563       magick_windows[number_windows++]=(&windows->widget);
14564       magick_windows[number_windows++]=(&windows->popup);
14565       magick_windows[number_windows++]=(&windows->magnify);
14566       magick_windows[number_windows++]=(&windows->pan);
14567       for (i=0; i < (int) number_windows; i++)
14568         magick_windows[i]->id=(Window) NULL;
14569       vid_info.x=0;
14570       vid_info.y=0;
14571     }
14572   /*
14573     Initialize font info.
14574   */
14575   if (windows->font_info != (XFontStruct *) NULL)
14576     (void) XFreeFont(display,windows->font_info);
14577   windows->font_info=XBestFont(display,resource_info,MagickFalse);
14578   if (windows->font_info == (XFontStruct *) NULL)
14579     ThrowXWindowFatalException(XServerFatalError,"UnableToLoadFont",
14580       resource_info->font);
14581   /*
14582     Initialize Standard Colormap.
14583   */
14584   map_info=windows->map_info;
14585   icon_map=windows->icon_map;
14586   visual_info=windows->visual_info;
14587   icon_visual=windows->icon_visual;
14588   pixel=windows->pixel_info;
14589   icon_pixel=windows->icon_pixel;
14590   font_info=windows->font_info;
14591   icon_resources=windows->icon_resources;
14592   class_hints=windows->class_hints;
14593   manager_hints=windows->manager_hints;
14594   root_window=XRootWindow(display,visual_info->screen);
14595   nexus=NewImageList();
14596   if( IfMagickTrue(display_image->debug) )
14597     {
14598       (void) LogMagickEvent(X11Event,GetMagickModule(),
14599         "Image: %s[%.20g] %.20gx%.20g ",display_image->filename,
14600         (double) display_image->scene,(double) display_image->columns,
14601         (double) display_image->rows);
14602       if (display_image->colors != 0)
14603         (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
14604           display_image->colors);
14605       (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",
14606         display_image->magick);
14607     }
14608   XMakeStandardColormap(display,visual_info,resource_info,display_image,
14609     map_info,pixel,exception);
14610   display_image->taint=MagickFalse;
14611   /*
14612     Initialize graphic context.
14613   */
14614   windows->context.id=(Window) NULL;
14615   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14616     resource_info,&windows->context);
14617   (void) CloneString(&class_hints->res_name,resource_info->client_name);
14618   (void) CloneString(&class_hints->res_class,resource_info->client_name);
14619   class_hints->res_class[0]=(char) toupper((int) class_hints->res_class[0]);
14620   manager_hints->flags=InputHint | StateHint;
14621   manager_hints->input=MagickFalse;
14622   manager_hints->initial_state=WithdrawnState;
14623   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14624     &windows->context);
14625   if( IfMagickTrue(display_image->debug) )
14626     (void) LogMagickEvent(X11Event,GetMagickModule(),
14627       "Window id: 0x%lx (context)",windows->context.id);
14628   context_values.background=pixel->background_color.pixel;
14629   context_values.font=font_info->fid;
14630   context_values.foreground=pixel->foreground_color.pixel;
14631   context_values.graphics_exposures=MagickFalse;
14632   context_mask=(MagickStatusType)
14633     (GCBackground | GCFont | GCForeground | GCGraphicsExposures);
14634   if (pixel->annotate_context != (GC) NULL)
14635     (void) XFreeGC(display,pixel->annotate_context);
14636   pixel->annotate_context=XCreateGC(display,windows->context.id,
14637     context_mask,&context_values);
14638   if (pixel->annotate_context == (GC) NULL)
14639     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14640       display_image->filename);
14641   context_values.background=pixel->depth_color.pixel;
14642   if (pixel->widget_context != (GC) NULL)
14643     (void) XFreeGC(display,pixel->widget_context);
14644   pixel->widget_context=XCreateGC(display,windows->context.id,context_mask,
14645     &context_values);
14646   if (pixel->widget_context == (GC) NULL)
14647     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14648       display_image->filename);
14649   context_values.background=pixel->foreground_color.pixel;
14650   context_values.foreground=pixel->background_color.pixel;
14651   context_values.plane_mask=context_values.background ^
14652     context_values.foreground;
14653   if (pixel->highlight_context != (GC) NULL)
14654     (void) XFreeGC(display,pixel->highlight_context);
14655   pixel->highlight_context=XCreateGC(display,windows->context.id,
14656     (size_t) (context_mask | GCPlaneMask),&context_values);
14657   if (pixel->highlight_context == (GC) NULL)
14658     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14659       display_image->filename);
14660   (void) XDestroyWindow(display,windows->context.id);
14661   /*
14662     Initialize icon window.
14663   */
14664   XGetWindowInfo(display,icon_visual,icon_map,icon_pixel,(XFontStruct *) NULL,
14665     icon_resources,&windows->icon);
14666   windows->icon.geometry=resource_info->icon_geometry;
14667   XBestIconSize(display,&windows->icon,display_image);
14668   windows->icon.attributes.colormap=XDefaultColormap(display,
14669     icon_visual->screen);
14670   windows->icon.attributes.event_mask=ExposureMask | StructureNotifyMask;
14671   manager_hints->flags=InputHint | StateHint;
14672   manager_hints->input=MagickFalse;
14673   manager_hints->initial_state=IconicState;
14674   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14675     &windows->icon);
14676   if( IfMagickTrue(display_image->debug) )
14677     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (icon)",
14678       windows->icon.id);
14679   /*
14680     Initialize graphic context for icon window.
14681   */
14682   if (icon_pixel->annotate_context != (GC) NULL)
14683     (void) XFreeGC(display,icon_pixel->annotate_context);
14684   context_values.background=icon_pixel->background_color.pixel;
14685   context_values.foreground=icon_pixel->foreground_color.pixel;
14686   icon_pixel->annotate_context=XCreateGC(display,windows->icon.id,
14687     (size_t) (GCBackground | GCForeground),&context_values);
14688   if (icon_pixel->annotate_context == (GC) NULL)
14689     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14690       display_image->filename);
14691   windows->icon.annotate_context=icon_pixel->annotate_context;
14692   /*
14693     Initialize Image window.
14694   */
14695   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14696     &windows->image);
14697   windows->image.shape=MagickTrue;  /* non-rectangular shape hint */
14698   if( IfMagickFalse(resource_info->use_shared_memory) )
14699     windows->image.shared_memory=MagickFalse;
14700   if ((resource_info->title != (char *) NULL) && !(*state & MontageImageState))
14701     {
14702       char
14703         *title;
14704
14705       title=InterpretImageProperties(resource_info->image_info,display_image,
14706         resource_info->title,exception);
14707       (void) CopyMagickString(windows->image.name,title,MaxTextExtent);
14708       (void) CopyMagickString(windows->image.icon_name,title,MaxTextExtent);
14709       title=DestroyString(title);
14710     }
14711   else
14712     {
14713       char
14714         filename[MaxTextExtent];
14715
14716       /*
14717         Window name is the base of the filename.
14718       */
14719       GetPathComponent(display_image->magick_filename,TailPath,filename);
14720       if (display_image->scene == 0)
14721         (void) FormatLocaleString(windows->image.name,MaxTextExtent,
14722           "%s: %s",MagickPackageName,filename);
14723       else
14724         (void) FormatLocaleString(windows->image.name,MaxTextExtent,
14725           "%s: %s[scene: %.20g frames: %.20g]",MagickPackageName,filename,
14726           (double) display_image->scene,(double) GetImageListLength(
14727           display_image));
14728       (void) CopyMagickString(windows->image.icon_name,filename,MaxTextExtent);
14729     }
14730   if (resource_info->immutable)
14731     windows->image.immutable=MagickTrue;
14732   windows->image.use_pixmap=resource_info->use_pixmap;
14733   windows->image.geometry=resource_info->image_geometry;
14734   (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>!",
14735     XDisplayWidth(display,visual_info->screen),
14736     XDisplayHeight(display,visual_info->screen));
14737   geometry_info.width=display_image->columns;
14738   geometry_info.height=display_image->rows;
14739   geometry_info.x=0;
14740   geometry_info.y=0;
14741   (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
14742     &geometry_info.width,&geometry_info.height);
14743   windows->image.width=(unsigned int) geometry_info.width;
14744   windows->image.height=(unsigned int) geometry_info.height;
14745   windows->image.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14746     ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14747     KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14748     PropertyChangeMask | StructureNotifyMask | SubstructureNotifyMask;
14749   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14750     resource_info,&windows->backdrop);
14751   if ((resource_info->backdrop) || (windows->backdrop.id != (Window) NULL))
14752     {
14753       /*
14754         Initialize backdrop window.
14755       */
14756       windows->backdrop.x=0;
14757       windows->backdrop.y=0;
14758       (void) CloneString(&windows->backdrop.name,"Backdrop");
14759       windows->backdrop.flags=(size_t) (USSize | USPosition);
14760       windows->backdrop.width=(unsigned int)
14761         XDisplayWidth(display,visual_info->screen);
14762       windows->backdrop.height=(unsigned int)
14763         XDisplayHeight(display,visual_info->screen);
14764       windows->backdrop.border_width=0;
14765       windows->backdrop.immutable=MagickTrue;
14766       windows->backdrop.attributes.do_not_propagate_mask=ButtonPressMask |
14767         ButtonReleaseMask;
14768       windows->backdrop.attributes.event_mask=ButtonPressMask | KeyPressMask |
14769         StructureNotifyMask;
14770       manager_hints->flags=IconWindowHint | InputHint | StateHint;
14771       manager_hints->icon_window=windows->icon.id;
14772       manager_hints->input=MagickTrue;
14773       manager_hints->initial_state=resource_info->iconic ? IconicState :
14774         NormalState;
14775       XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14776         &windows->backdrop);
14777       if( IfMagickTrue(display_image->debug) )
14778         (void) LogMagickEvent(X11Event,GetMagickModule(),
14779           "Window id: 0x%lx (backdrop)",windows->backdrop.id);
14780       (void) XMapWindow(display,windows->backdrop.id);
14781       (void) XClearWindow(display,windows->backdrop.id);
14782       if (windows->image.id != (Window) NULL)
14783         {
14784           (void) XDestroyWindow(display,windows->image.id);
14785           windows->image.id=(Window) NULL;
14786         }
14787       /*
14788         Position image in the center the backdrop.
14789       */
14790       windows->image.flags|=USPosition;
14791       windows->image.x=(XDisplayWidth(display,visual_info->screen)/2)-
14792         (windows->image.width/2);
14793       windows->image.y=(XDisplayHeight(display,visual_info->screen)/2)-
14794         (windows->image.height/2);
14795     }
14796   manager_hints->flags=IconWindowHint | InputHint | StateHint;
14797   manager_hints->icon_window=windows->icon.id;
14798   manager_hints->input=MagickTrue;
14799   manager_hints->initial_state=resource_info->iconic ? IconicState :
14800     NormalState;
14801   if (windows->group_leader.id != (Window) NULL)
14802     {
14803       /*
14804         Follow the leader.
14805       */
14806       manager_hints->flags|=WindowGroupHint;
14807       manager_hints->window_group=windows->group_leader.id;
14808       (void) XSelectInput(display,windows->group_leader.id,StructureNotifyMask);
14809       if( IfMagickTrue(display_image->debug) )
14810         (void) LogMagickEvent(X11Event,GetMagickModule(),
14811           "Window id: 0x%lx (group leader)",windows->group_leader.id);
14812     }
14813   XMakeWindow(display,
14814     (Window) (resource_info->backdrop ? windows->backdrop.id : root_window),
14815     argv,argc,class_hints,manager_hints,&windows->image);
14816   (void) XChangeProperty(display,windows->image.id,windows->im_protocols,
14817     XA_STRING,8,PropModeReplace,(unsigned char *) NULL,0);
14818   if (windows->group_leader.id != (Window) NULL)
14819     (void) XSetTransientForHint(display,windows->image.id,
14820       windows->group_leader.id);
14821   if( IfMagickTrue(display_image->debug) )
14822     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (image)",
14823       windows->image.id);
14824   /*
14825     Initialize Info widget.
14826   */
14827   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14828     &windows->info);
14829   (void) CloneString(&windows->info.name,"Info");
14830   (void) CloneString(&windows->info.icon_name,"Info");
14831   windows->info.border_width=1;
14832   windows->info.x=2;
14833   windows->info.y=2;
14834   windows->info.flags|=PPosition;
14835   windows->info.attributes.win_gravity=UnmapGravity;
14836   windows->info.attributes.event_mask=ButtonPressMask | ExposureMask |
14837     StructureNotifyMask;
14838   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14839   manager_hints->input=MagickFalse;
14840   manager_hints->initial_state=NormalState;
14841   manager_hints->window_group=windows->image.id;
14842   XMakeWindow(display,windows->image.id,argv,argc,class_hints,manager_hints,
14843     &windows->info);
14844   windows->info.highlight_stipple=XCreateBitmapFromData(display,
14845     windows->info.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14846   windows->info.shadow_stipple=XCreateBitmapFromData(display,
14847     windows->info.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14848   (void) XSetTransientForHint(display,windows->info.id,windows->image.id);
14849   if( IfMagickTrue(windows->image.mapped) )
14850     (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14851   if( IfMagickTrue(display_image->debug) )
14852     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (info)",
14853       windows->info.id);
14854   /*
14855     Initialize Command widget.
14856   */
14857   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14858     resource_info,&windows->command);
14859   windows->command.data=MagickMenus;
14860   (void) XCommandWidget(display,windows,CommandMenu,(XEvent *) NULL);
14861   (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.command",
14862     resource_info->client_name);
14863   windows->command.geometry=XGetResourceClass(resource_info->resource_database,
14864     resource_name,"geometry",(char *) NULL);
14865   (void) CloneString(&windows->command.name,MagickTitle);
14866   windows->command.border_width=0;
14867   windows->command.flags|=PPosition;
14868   windows->command.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14869     ButtonReleaseMask | EnterWindowMask | ExposureMask | LeaveWindowMask |
14870     OwnerGrabButtonMask | StructureNotifyMask;
14871   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14872   manager_hints->input=MagickTrue;
14873   manager_hints->initial_state=NormalState;
14874   manager_hints->window_group=windows->image.id;
14875   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14876     &windows->command);
14877   windows->command.highlight_stipple=XCreateBitmapFromData(display,
14878     windows->command.id,(char *) HighlightBitmap,HighlightWidth,
14879     HighlightHeight);
14880   windows->command.shadow_stipple=XCreateBitmapFromData(display,
14881     windows->command.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14882   (void) XSetTransientForHint(display,windows->command.id,windows->image.id);
14883   if( IfMagickTrue(windows->command.mapped) )
14884     (void) XMapRaised(display,windows->command.id);
14885   if( IfMagickTrue(display_image->debug) )
14886     (void) LogMagickEvent(X11Event,GetMagickModule(),
14887       "Window id: 0x%lx (command)",windows->command.id);
14888   /*
14889     Initialize Widget window.
14890   */
14891   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14892     resource_info,&windows->widget);
14893   (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.widget",
14894     resource_info->client_name);
14895   windows->widget.geometry=XGetResourceClass(resource_info->resource_database,
14896     resource_name,"geometry",(char *) NULL);
14897   windows->widget.border_width=0;
14898   windows->widget.flags|=PPosition;
14899   windows->widget.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14900     ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14901     KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14902     StructureNotifyMask;
14903   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14904   manager_hints->input=MagickTrue;
14905   manager_hints->initial_state=NormalState;
14906   manager_hints->window_group=windows->image.id;
14907   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14908     &windows->widget);
14909   windows->widget.highlight_stipple=XCreateBitmapFromData(display,
14910     windows->widget.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14911   windows->widget.shadow_stipple=XCreateBitmapFromData(display,
14912     windows->widget.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14913   (void) XSetTransientForHint(display,windows->widget.id,windows->image.id);
14914   if( IfMagickTrue(display_image->debug) )
14915     (void) LogMagickEvent(X11Event,GetMagickModule(),
14916       "Window id: 0x%lx (widget)",windows->widget.id);
14917   /*
14918     Initialize popup window.
14919   */
14920   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14921     resource_info,&windows->popup);
14922   windows->popup.border_width=0;
14923   windows->popup.flags|=PPosition;
14924   windows->popup.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14925     ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14926     KeyReleaseMask | LeaveWindowMask | StructureNotifyMask;
14927   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14928   manager_hints->input=MagickTrue;
14929   manager_hints->initial_state=NormalState;
14930   manager_hints->window_group=windows->image.id;
14931   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14932     &windows->popup);
14933   windows->popup.highlight_stipple=XCreateBitmapFromData(display,
14934     windows->popup.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14935   windows->popup.shadow_stipple=XCreateBitmapFromData(display,
14936     windows->popup.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14937   (void) XSetTransientForHint(display,windows->popup.id,windows->image.id);
14938   if( IfMagickTrue(display_image->debug) )
14939     (void) LogMagickEvent(X11Event,GetMagickModule(),
14940       "Window id: 0x%lx (pop up)",windows->popup.id);
14941   /*
14942     Initialize Magnify window and cursor.
14943   */
14944   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14945     resource_info,&windows->magnify);
14946   if( IfMagickFalse(resource_info->use_shared_memory) )
14947     windows->magnify.shared_memory=MagickFalse;
14948   (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.magnify",
14949     resource_info->client_name);
14950   windows->magnify.geometry=XGetResourceClass(resource_info->resource_database,
14951     resource_name,"geometry",(char *) NULL);
14952   (void) FormatLocaleString(windows->magnify.name,MaxTextExtent,"Magnify %uX",
14953     resource_info->magnify);
14954   if (windows->magnify.cursor != (Cursor) NULL)
14955     (void) XFreeCursor(display,windows->magnify.cursor);
14956   windows->magnify.cursor=XMakeCursor(display,windows->image.id,
14957     map_info->colormap,resource_info->background_color,
14958     resource_info->foreground_color);
14959   if (windows->magnify.cursor == (Cursor) NULL)
14960     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateCursor",
14961       display_image->filename);
14962   windows->magnify.width=MagnifySize;
14963   windows->magnify.height=MagnifySize;
14964   windows->magnify.flags|=PPosition;
14965   windows->magnify.min_width=MagnifySize;
14966   windows->magnify.min_height=MagnifySize;
14967   windows->magnify.width_inc=MagnifySize;
14968   windows->magnify.height_inc=MagnifySize;
14969   windows->magnify.data=resource_info->magnify;
14970   windows->magnify.attributes.cursor=windows->magnify.cursor;
14971   windows->magnify.attributes.event_mask=ButtonPressMask | ButtonReleaseMask |
14972     ExposureMask | KeyPressMask | KeyReleaseMask | OwnerGrabButtonMask |
14973     StructureNotifyMask;
14974   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14975   manager_hints->input=MagickTrue;
14976   manager_hints->initial_state=NormalState;
14977   manager_hints->window_group=windows->image.id;
14978   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14979     &windows->magnify);
14980   if( IfMagickTrue(display_image->debug) )
14981     (void) LogMagickEvent(X11Event,GetMagickModule(),
14982       "Window id: 0x%lx (magnify)",windows->magnify.id);
14983   (void) XSetTransientForHint(display,windows->magnify.id,windows->image.id);
14984   /*
14985     Initialize panning window.
14986   */
14987   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14988     resource_info,&windows->pan);
14989   (void) CloneString(&windows->pan.name,"Pan Icon");
14990   windows->pan.width=windows->icon.width;
14991   windows->pan.height=windows->icon.height;
14992   (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.pan",
14993     resource_info->client_name);
14994   windows->pan.geometry=XGetResourceClass(resource_info->resource_database,
14995     resource_name,"geometry",(char *) NULL);
14996   (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
14997     &windows->pan.width,&windows->pan.height);
14998   windows->pan.flags|=PPosition;
14999   windows->pan.immutable=MagickTrue;
15000   windows->pan.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
15001     ButtonReleaseMask | ExposureMask | KeyPressMask | KeyReleaseMask |
15002     StructureNotifyMask;
15003   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
15004   manager_hints->input=MagickFalse;
15005   manager_hints->initial_state=NormalState;
15006   manager_hints->window_group=windows->image.id;
15007   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
15008     &windows->pan);
15009   if( IfMagickTrue(display_image->debug) )
15010     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (pan)",
15011       windows->pan.id);
15012   (void) XSetTransientForHint(display,windows->pan.id,windows->image.id);
15013   if( IfMagickTrue(windows->info.mapped) )
15014     (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15015   if( IfMagickFalse(windows->image.mapped) ||
15016       (windows->backdrop.id != (Window) NULL))
15017     (void) XMapWindow(display,windows->image.id);
15018   /*
15019     Set our progress monitor and warning handlers.
15020   */
15021   if (warning_handler == (WarningHandler) NULL)
15022     {
15023       warning_handler=resource_info->display_warnings ?
15024         SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
15025       warning_handler=resource_info->display_warnings ?
15026         SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
15027     }
15028   /*
15029     Initialize Image and Magnify X images.
15030   */
15031   windows->image.x=0;
15032   windows->image.y=0;
15033   windows->magnify.shape=MagickFalse;
15034   width=(unsigned int) display_image->columns;
15035   height=(unsigned int) display_image->rows;
15036   if ((display_image->columns != width) || (display_image->rows != height))
15037     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15038       display_image->filename);
15039   status=XMakeImage(display,resource_info,&windows->image,display_image,
15040     width,height,exception);
15041   if( IfMagickFalse(status) )
15042     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15043       display_image->filename);
15044   status=XMakeImage(display,resource_info,&windows->magnify,(Image *) NULL,
15045     windows->magnify.width,windows->magnify.height,exception);
15046   if( IfMagickFalse(status) )
15047     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15048       display_image->filename);
15049   if( IfMagickTrue(windows->magnify.mapped) )
15050     (void) XMapRaised(display,windows->magnify.id);
15051   if( IfMagickTrue(windows->pan.mapped) )
15052     (void) XMapRaised(display,windows->pan.id);
15053   windows->image.window_changes.width=(int) display_image->columns;
15054   windows->image.window_changes.height=(int) display_image->rows;
15055   (void) XConfigureImage(display,resource_info,windows,display_image,exception);
15056   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15057   (void) XSync(display,MagickFalse);
15058   /*
15059     Respond to events.
15060   */
15061   delay=display_image->delay/MagickMax(display_image->ticks_per_second,1L);
15062   timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15063   update_time=0;
15064   if( IfMagickTrue(resource_info->update) )
15065     {
15066       MagickBooleanType
15067         status;
15068
15069       /*
15070         Determine when file data was last modified.
15071       */
15072       status=GetPathAttributes(display_image->filename,&attributes);
15073       if( IfMagickTrue(status) )
15074         update_time=attributes.st_mtime;
15075     }
15076   *state&=(~FormerImageState);
15077   *state&=(~MontageImageState);
15078   *state&=(~NextImageState);
15079   do
15080   {
15081     /*
15082       Handle a window event.
15083     */
15084     if( IfMagickTrue(windows->image.mapped) )
15085       if ((display_image->delay != 0) || (resource_info->update != 0))
15086         {
15087           if (timer < time((time_t *) NULL))
15088             {
15089               if( IfMagickFalse(resource_info->update) )
15090                 *state|=NextImageState | ExitState;
15091               else
15092                 {
15093                   MagickBooleanType
15094                     status;
15095
15096                   /*
15097                     Determine if image file was modified.
15098                   */
15099                   status=GetPathAttributes(display_image->filename,&attributes);
15100                   if( IfMagickTrue(status) )
15101                     if (update_time != attributes.st_mtime)
15102                       {
15103                         /*
15104                           Redisplay image.
15105                         */
15106                         (void) FormatLocaleString(
15107                           resource_info->image_info->filename,MaxTextExtent,
15108                           "%s:%s",display_image->magick,
15109                           display_image->filename);
15110                         nexus=ReadImage(resource_info->image_info,exception);
15111                         if (nexus != (Image *) NULL)
15112                           {
15113                             nexus=DestroyImage(nexus);
15114                             *state|=NextImageState | ExitState;
15115                           }
15116                       }
15117                   delay=display_image->delay/MagickMax(
15118                     display_image->ticks_per_second,1L);
15119                   timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15120                 }
15121             }
15122           if (XEventsQueued(display,QueuedAfterFlush) == 0)
15123             {
15124               /*
15125                 Do not block if delay > 0.
15126               */
15127               XDelay(display,SuspendTime << 2);
15128               continue;
15129             }
15130         }
15131     timestamp=time((time_t *) NULL);
15132     (void) XNextEvent(display,&event);
15133     if( IfMagickFalse(windows->image.stasis) )
15134       windows->image.stasis=IsMagickTrue((time((time_t *) NULL)-timestamp) > 0);
15135     if( IfMagickFalse(windows->magnify.stasis) )
15136       windows->magnify.stasis=IsMagickTrue((time((time_t *) NULL)-timestamp) > 0);
15137     if (event.xany.window == windows->command.id)
15138       {
15139         /*
15140           Select a command from the Command widget.
15141         */
15142         id=XCommandWidget(display,windows,CommandMenu,&event);
15143         if (id < 0)
15144           continue;
15145         (void) CopyMagickString(command,CommandMenu[id],MaxTextExtent);
15146         command_type=CommandMenus[id];
15147         if (id < MagickMenus)
15148           {
15149             /*
15150               Select a command from a pop-up menu.
15151             */
15152             entry=XMenuWidget(display,windows,CommandMenu[id],Menus[id],
15153               command);
15154             if (entry < 0)
15155               continue;
15156             (void) CopyMagickString(command,Menus[id][entry],MaxTextExtent);
15157             command_type=Commands[id][entry];
15158           }
15159         if (command_type != NullCommand)
15160           nexus=XMagickCommand(display,resource_info,windows,command_type,
15161             &display_image,exception);
15162         continue;
15163       }
15164     switch (event.type)
15165     {
15166       case ButtonPress:
15167       {
15168         if( IfMagickTrue(display_image->debug) )
15169           (void) LogMagickEvent(X11Event,GetMagickModule(),
15170             "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
15171             event.xbutton.button,event.xbutton.x,event.xbutton.y);
15172         if ((event.xbutton.button == Button3) &&
15173             (event.xbutton.state & Mod1Mask))
15174           {
15175             /*
15176               Convert Alt-Button3 to Button2.
15177             */
15178             event.xbutton.button=Button2;
15179             event.xbutton.state&=(~Mod1Mask);
15180           }
15181         if (event.xbutton.window == windows->backdrop.id)
15182           {
15183             (void) XSetInputFocus(display,event.xbutton.window,RevertToParent,
15184               event.xbutton.time);
15185             break;
15186           }
15187         if (event.xbutton.window == windows->image.id)
15188           {
15189             switch (event.xbutton.button)
15190             {
15191               case Button1:
15192               {
15193                 if (resource_info->immutable)
15194                   {
15195                     /*
15196                       Select a command from the Virtual menu.
15197                     */
15198                     entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15199                       command);
15200                     if (entry >= 0)
15201                       nexus=XMagickCommand(display,resource_info,windows,
15202                         VirtualCommands[entry],&display_image,exception);
15203                     break;
15204                   }
15205                 /*
15206                   Map/unmap Command widget.
15207                 */
15208                 if( IfMagickTrue(windows->command.mapped) )
15209                   (void) XWithdrawWindow(display,windows->command.id,
15210                     windows->command.screen);
15211                 else
15212                   {
15213                     (void) XCommandWidget(display,windows,CommandMenu,
15214                       (XEvent *) NULL);
15215                     (void) XMapRaised(display,windows->command.id);
15216                   }
15217                 break;
15218               }
15219               case Button2:
15220               {
15221                 /*
15222                   User pressed the image magnify button.
15223                 */
15224                 (void) XMagickCommand(display,resource_info,windows,ZoomCommand,
15225                   &display_image,exception);
15226                 XMagnifyImage(display,windows,&event,exception);
15227                 break;
15228               }
15229               case Button3:
15230               {
15231                 if (resource_info->immutable)
15232                   {
15233                     /*
15234                       Select a command from the Virtual menu.
15235                     */
15236                     entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15237                       command);
15238                     if (entry >= 0)
15239                       nexus=XMagickCommand(display,resource_info,windows,
15240                         VirtualCommands[entry],&display_image,exception);
15241                     break;
15242                   }
15243                 if (display_image->montage != (char *) NULL)
15244                   {
15245                     /*
15246                       Open or delete a tile from a visual image directory.
15247                     */
15248                     nexus=XTileImage(display,resource_info,windows,
15249                       display_image,&event,exception);
15250                     if (nexus != (Image *) NULL)
15251                       *state|=MontageImageState | NextImageState | ExitState;
15252                     vid_info.x=(short int) windows->image.x;
15253                     vid_info.y=(short int) windows->image.y;
15254                     break;
15255                   }
15256                 /*
15257                   Select a command from the Short Cuts menu.
15258                 */
15259                 entry=XMenuWidget(display,windows,"Short Cuts",ShortCutsMenu,
15260                   command);
15261                 if (entry >= 0)
15262                   nexus=XMagickCommand(display,resource_info,windows,
15263                     ShortCutsCommands[entry],&display_image,exception);
15264                 break;
15265               }
15266               case Button4:
15267               {
15268                 /*
15269                   Wheel up.
15270                 */
15271                 XTranslateImage(display,windows,*image,XK_Up);
15272                 break;
15273               }
15274               case Button5:
15275               {
15276                 /*
15277                   Wheel down.
15278                 */
15279                 XTranslateImage(display,windows,*image,XK_Down);
15280                 break;
15281               }
15282               default:
15283                 break;
15284             }
15285             break;
15286           }
15287         if (event.xbutton.window == windows->magnify.id)
15288           {
15289             int
15290               factor;
15291
15292             static const char
15293               *MagnifyMenu[] =
15294               {
15295                 "2",
15296                 "4",
15297                 "5",
15298                 "6",
15299                 "7",
15300                 "8",
15301                 "9",
15302                 "3",
15303                 (char *) NULL,
15304               };
15305
15306             static KeySym
15307               MagnifyCommands[] =
15308               {
15309                 XK_2,
15310                 XK_4,
15311                 XK_5,
15312                 XK_6,
15313                 XK_7,
15314                 XK_8,
15315                 XK_9,
15316                 XK_3
15317               };
15318
15319             /*
15320               Select a magnify factor from the pop-up menu.
15321             */
15322             factor=XMenuWidget(display,windows,"Magnify",MagnifyMenu,command);
15323             if (factor >= 0)
15324               XMagnifyWindowCommand(display,windows,0,MagnifyCommands[factor],
15325                 exception);
15326             break;
15327           }
15328         if (event.xbutton.window == windows->pan.id)
15329           {
15330             switch (event.xbutton.button)
15331             {
15332               case Button4:
15333               {
15334                 /*
15335                   Wheel up.
15336                 */
15337                 XTranslateImage(display,windows,*image,XK_Up);
15338                 break;
15339               }
15340               case Button5:
15341               {
15342                 /*
15343                   Wheel down.
15344                 */
15345                 XTranslateImage(display,windows,*image,XK_Down);
15346                 break;
15347               }
15348               default:
15349               {
15350                 XPanImage(display,windows,&event,exception);
15351                 break;
15352               }
15353             }
15354             break;
15355           }
15356         delay=display_image->delay/MagickMax(display_image->ticks_per_second,
15357           1L);
15358         timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15359         break;
15360       }
15361       case ButtonRelease:
15362       {
15363         if( IfMagickTrue(display_image->debug) )
15364           (void) LogMagickEvent(X11Event,GetMagickModule(),
15365             "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
15366             event.xbutton.button,event.xbutton.x,event.xbutton.y);
15367         break;
15368       }
15369       case ClientMessage:
15370       {
15371         if( IfMagickTrue(display_image->debug) )
15372           (void) LogMagickEvent(X11Event,GetMagickModule(),
15373             "Client Message: 0x%lx 0x%lx %d 0x%lx",event.xclient.window,
15374             event.xclient.message_type,event.xclient.format,(unsigned long)
15375             event.xclient.data.l[0]);
15376         if (event.xclient.message_type == windows->im_protocols)
15377           {
15378             if (*event.xclient.data.l == (long) windows->im_update_widget)
15379               {
15380                 (void) CloneString(&windows->command.name,MagickTitle);
15381                 windows->command.data=MagickMenus;
15382                 (void) XCommandWidget(display,windows,CommandMenu,
15383                   (XEvent *) NULL);
15384                 break;
15385               }
15386             if (*event.xclient.data.l == (long) windows->im_update_colormap)
15387               {
15388                 /*
15389                   Update graphic context and window colormap.
15390                 */
15391                 for (i=0; i < (int) number_windows; i++)
15392                 {
15393                   if (magick_windows[i]->id == windows->icon.id)
15394                     continue;
15395                   context_values.background=pixel->background_color.pixel;
15396                   context_values.foreground=pixel->foreground_color.pixel;
15397                   (void) XChangeGC(display,magick_windows[i]->annotate_context,
15398                     context_mask,&context_values);
15399                   (void) XChangeGC(display,magick_windows[i]->widget_context,
15400                     context_mask,&context_values);
15401                   context_values.background=pixel->foreground_color.pixel;
15402                   context_values.foreground=pixel->background_color.pixel;
15403                   context_values.plane_mask=context_values.background ^
15404                     context_values.foreground;
15405                   (void) XChangeGC(display,magick_windows[i]->highlight_context,
15406                     (size_t) (context_mask | GCPlaneMask),
15407                     &context_values);
15408                   magick_windows[i]->attributes.background_pixel=
15409                     pixel->background_color.pixel;
15410                   magick_windows[i]->attributes.border_pixel=
15411                     pixel->border_color.pixel;
15412                   magick_windows[i]->attributes.colormap=map_info->colormap;
15413                   (void) XChangeWindowAttributes(display,magick_windows[i]->id,
15414                     (unsigned long) magick_windows[i]->mask,
15415                     &magick_windows[i]->attributes);
15416                 }
15417                 if( IfMagickTrue(windows->pan.mapped) )
15418                   {
15419                     (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
15420                       windows->pan.pixmap);
15421                     (void) XClearWindow(display,windows->pan.id);
15422                     XDrawPanRectangle(display,windows);
15423                   }
15424                 if (windows->backdrop.id != (Window) NULL)
15425                   (void) XInstallColormap(display,map_info->colormap);
15426                 break;
15427               }
15428             if (*event.xclient.data.l == (long) windows->im_former_image)
15429               {
15430                 *state|=FormerImageState | ExitState;
15431                 break;
15432               }
15433             if (*event.xclient.data.l == (long) windows->im_next_image)
15434               {
15435                 *state|=NextImageState | ExitState;
15436                 break;
15437               }
15438             if (*event.xclient.data.l == (long) windows->im_retain_colors)
15439               {
15440                 *state|=RetainColorsState;
15441                 break;
15442               }
15443             if (*event.xclient.data.l == (long) windows->im_exit)
15444               {
15445                 *state|=ExitState;
15446                 break;
15447               }
15448             break;
15449           }
15450         if (event.xclient.message_type == windows->dnd_protocols)
15451           {
15452             Atom
15453               selection,
15454               type;
15455
15456             int
15457               format,
15458               status;
15459
15460             unsigned char
15461               *data;
15462
15463             unsigned long
15464               after,
15465               length;
15466
15467             /*
15468               Display image named by the Drag-and-Drop selection.
15469             */
15470             if ((*event.xclient.data.l != 2) && (*event.xclient.data.l != 128))
15471               break;
15472             selection=XInternAtom(display,"DndSelection",MagickFalse);
15473             status=XGetWindowProperty(display,root_window,selection,0L,(long)
15474               MaxTextExtent,MagickFalse,(Atom) AnyPropertyType,&type,&format,
15475               &length,&after,&data);
15476             if ((status != Success) || (length == 0))
15477               break;
15478             if (*event.xclient.data.l == 2)
15479               {
15480                 /*
15481                   Offix DND.
15482                 */
15483                 (void) CopyMagickString(resource_info->image_info->filename,
15484                   (char *) data,MaxTextExtent);
15485               }
15486             else
15487               {
15488                 /*
15489                   XDND.
15490                 */
15491                 if (strncmp((char *) data, "file:", 5) != 0)
15492                   {
15493                     (void) XFree((void *) data);
15494                     break;
15495                   }
15496                 (void) CopyMagickString(resource_info->image_info->filename,
15497                   ((char *) data)+5,MaxTextExtent);
15498               }
15499             nexus=ReadImage(resource_info->image_info,exception);
15500             CatchException(exception);
15501             if (nexus != (Image *) NULL)
15502               *state|=NextImageState | ExitState;
15503             (void) XFree((void *) data);
15504             break;
15505           }
15506         /*
15507           If client window delete message, exit.
15508         */
15509         if (event.xclient.message_type != windows->wm_protocols)
15510           break;
15511         if (*event.xclient.data.l != (long) windows->wm_delete_window)
15512           break;
15513         (void) XWithdrawWindow(display,event.xclient.window,
15514           visual_info->screen);
15515         if (event.xclient.window == windows->image.id)
15516           {
15517             *state|=ExitState;
15518             break;
15519           }
15520         if (event.xclient.window == windows->pan.id)
15521           {
15522             /*
15523               Restore original image size when pan window is deleted.
15524             */
15525             windows->image.window_changes.width=windows->image.ximage->width;
15526             windows->image.window_changes.height=windows->image.ximage->height;
15527             (void) XConfigureImage(display,resource_info,windows,
15528               display_image,exception);
15529           }
15530         break;
15531       }
15532       case ConfigureNotify:
15533       {
15534         if( IfMagickTrue(display_image->debug) )
15535           (void) LogMagickEvent(X11Event,GetMagickModule(),
15536             "Configure Notify: 0x%lx %dx%d+%d+%d %d",event.xconfigure.window,
15537             event.xconfigure.width,event.xconfigure.height,event.xconfigure.x,
15538             event.xconfigure.y,event.xconfigure.send_event);
15539         if (event.xconfigure.window == windows->image.id)
15540           {
15541             /*
15542               Image window has a new configuration.
15543             */
15544             if (event.xconfigure.send_event != 0)
15545               {
15546                 XWindowChanges
15547                   window_changes;
15548
15549                 /*
15550                   Position the transient windows relative of the Image window.
15551                 */
15552                 if (windows->command.geometry == (char *) NULL)
15553                   if( IfMagickFalse(windows->command.mapped) )
15554                     {
15555                       windows->command.x=event.xconfigure.x-
15556                         windows->command.width-25;
15557                       windows->command.y=event.xconfigure.y;
15558                       XConstrainWindowPosition(display,&windows->command);
15559                       window_changes.x=windows->command.x;
15560                       window_changes.y=windows->command.y;
15561                       (void) XReconfigureWMWindow(display,windows->command.id,
15562                         windows->command.screen,(unsigned int) (CWX | CWY),
15563                         &window_changes);
15564                     }
15565                 if (windows->widget.geometry == (char *) NULL)
15566                   if( IfMagickFalse(windows->widget.mapped) )
15567                     {
15568                       windows->widget.x=event.xconfigure.x+
15569                         event.xconfigure.width/10;
15570                       windows->widget.y=event.xconfigure.y+
15571                         event.xconfigure.height/10;
15572                       XConstrainWindowPosition(display,&windows->widget);
15573                       window_changes.x=windows->widget.x;
15574                       window_changes.y=windows->widget.y;
15575                       (void) XReconfigureWMWindow(display,windows->widget.id,
15576                         windows->widget.screen,(unsigned int) (CWX | CWY),
15577                         &window_changes);
15578                     }
15579                 if (windows->magnify.geometry == (char *) NULL)
15580                   if( IfMagickFalse(windows->magnify.mapped) )
15581                     {
15582                       windows->magnify.x=event.xconfigure.x+
15583                         event.xconfigure.width+25;
15584                       windows->magnify.y=event.xconfigure.y;
15585                       XConstrainWindowPosition(display,&windows->magnify);
15586                       window_changes.x=windows->magnify.x;
15587                       window_changes.y=windows->magnify.y;
15588                       (void) XReconfigureWMWindow(display,windows->magnify.id,
15589                         windows->magnify.screen,(unsigned int) (CWX | CWY),
15590                         &window_changes);
15591                     }
15592                 if (windows->pan.geometry == (char *) NULL)
15593                   if( IfMagickFalse(windows->pan.mapped) )
15594                     {
15595                       windows->pan.x=event.xconfigure.x+
15596                         event.xconfigure.width+25;
15597                       windows->pan.y=event.xconfigure.y+
15598                         windows->magnify.height+50;
15599                       XConstrainWindowPosition(display,&windows->pan);
15600                       window_changes.x=windows->pan.x;
15601                       window_changes.y=windows->pan.y;
15602                       (void) XReconfigureWMWindow(display,windows->pan.id,
15603                         windows->pan.screen,(unsigned int) (CWX | CWY),
15604                         &window_changes);
15605                     }
15606               }
15607             if ((event.xconfigure.width == (int) windows->image.width) &&
15608                 (event.xconfigure.height == (int) windows->image.height))
15609               break;
15610             windows->image.width=(unsigned int) event.xconfigure.width;
15611             windows->image.height=(unsigned int) event.xconfigure.height;
15612             windows->image.x=0;
15613             windows->image.y=0;
15614             if (display_image->montage != (char *) NULL)
15615               {
15616                 windows->image.x=vid_info.x;
15617                 windows->image.y=vid_info.y;
15618               }
15619             if( IfMagickTrue(windows->image.mapped) &&
15620                 IfMagickTrue(windows->image.stasis) )
15621               {
15622                 /*
15623                   Update image window configuration.
15624                 */
15625                 windows->image.window_changes.width=event.xconfigure.width;
15626                 windows->image.window_changes.height=event.xconfigure.height;
15627                 (void) XConfigureImage(display,resource_info,windows,
15628                   display_image,exception);
15629               }
15630             /*
15631               Update pan window configuration.
15632             */
15633             if ((event.xconfigure.width < windows->image.ximage->width) ||
15634                 (event.xconfigure.height < windows->image.ximage->height))
15635               {
15636                 (void) XMapRaised(display,windows->pan.id);
15637                 XDrawPanRectangle(display,windows);
15638               }
15639             else
15640               if( IfMagickTrue(windows->pan.mapped) )
15641                 (void) XWithdrawWindow(display,windows->pan.id,
15642                   windows->pan.screen);
15643             break;
15644           }
15645         if (event.xconfigure.window == windows->magnify.id)
15646           {
15647             unsigned int
15648               magnify;
15649
15650             /*
15651               Magnify window has a new configuration.
15652             */
15653             windows->magnify.width=(unsigned int) event.xconfigure.width;
15654             windows->magnify.height=(unsigned int) event.xconfigure.height;
15655             if( IfMagickFalse(windows->magnify.mapped) )
15656               break;
15657             magnify=1;
15658             while ((int) magnify <= event.xconfigure.width)
15659               magnify<<=1;
15660             while ((int) magnify <= event.xconfigure.height)
15661               magnify<<=1;
15662             magnify>>=1;
15663             if (((int) magnify != event.xconfigure.width) ||
15664                 ((int) magnify != event.xconfigure.height))
15665               {
15666                 window_changes.width=(int) magnify;
15667                 window_changes.height=(int) magnify;
15668                 (void) XReconfigureWMWindow(display,windows->magnify.id,
15669                   windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
15670                   &window_changes);
15671                 break;
15672               }
15673             if( IfMagickTrue(windows->magnify.mapped) &&
15674                 IfMagickTrue(windows->magnify.stasis) )
15675               {
15676                 status=XMakeImage(display,resource_info,&windows->magnify,
15677                   display_image,windows->magnify.width,windows->magnify.height,
15678                   exception);
15679                 XMakeMagnifyImage(display,windows,exception);
15680               }
15681             break;
15682           }
15683         if( IfMagickTrue(windows->magnify.mapped) &&
15684             (event.xconfigure.window == windows->pan.id))
15685           {
15686             /*
15687               Pan icon window has a new configuration.
15688             */
15689             if (event.xconfigure.send_event != 0)
15690               {
15691                 windows->pan.x=event.xconfigure.x;
15692                 windows->pan.y=event.xconfigure.y;
15693               }
15694             windows->pan.width=(unsigned int) event.xconfigure.width;
15695             windows->pan.height=(unsigned int) event.xconfigure.height;
15696             break;
15697           }
15698         if (event.xconfigure.window == windows->icon.id)
15699           {
15700             /*
15701               Icon window has a new configuration.
15702             */
15703             windows->icon.width=(unsigned int) event.xconfigure.width;
15704             windows->icon.height=(unsigned int) event.xconfigure.height;
15705             break;
15706           }
15707         break;
15708       }
15709       case DestroyNotify:
15710       {
15711         /*
15712           Group leader has exited.
15713         */
15714         if( IfMagickTrue(display_image->debug) )
15715           (void) LogMagickEvent(X11Event,GetMagickModule(),
15716             "Destroy Notify: 0x%lx",event.xdestroywindow.window);
15717         if (event.xdestroywindow.window == windows->group_leader.id)
15718           {
15719             *state|=ExitState;
15720             break;
15721           }
15722         break;
15723       }
15724       case EnterNotify:
15725       {
15726         /*
15727           Selectively install colormap.
15728         */
15729         if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15730           if (event.xcrossing.mode != NotifyUngrab)
15731             XInstallColormap(display,map_info->colormap);
15732         break;
15733       }
15734       case Expose:
15735       {
15736         if( IfMagickTrue(display_image->debug) )
15737           (void) LogMagickEvent(X11Event,GetMagickModule(),
15738             "Expose: 0x%lx %dx%d+%d+%d",event.xexpose.window,
15739             event.xexpose.width,event.xexpose.height,event.xexpose.x,
15740             event.xexpose.y);
15741         /*
15742           Refresh windows that are now exposed.
15743         */
15744         if ((event.xexpose.window == windows->image.id) &&
15745             IfMagickTrue(windows->image.mapped) )
15746           {
15747             XRefreshWindow(display,&windows->image,&event);
15748             delay=display_image->delay/MagickMax(
15749               display_image->ticks_per_second,1L);
15750             timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15751             break;
15752           }
15753         if ((event.xexpose.window == windows->magnify.id) &&
15754             IfMagickTrue(windows->magnify.mapped))
15755           {
15756             XMakeMagnifyImage(display,windows,exception);
15757             break;
15758           }
15759         if (event.xexpose.window == windows->pan.id)
15760           {
15761             XDrawPanRectangle(display,windows);
15762             break;
15763           }
15764         if (event.xexpose.window == windows->icon.id)
15765           {
15766             XRefreshWindow(display,&windows->icon,&event);
15767             break;
15768           }
15769         break;
15770       }
15771       case KeyPress:
15772       {
15773         int
15774           length;
15775
15776         /*
15777           Respond to a user key press.
15778         */
15779         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
15780           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15781         *(command+length)='\0';
15782         if( IfMagickTrue(display_image->debug) )
15783           (void) LogMagickEvent(X11Event,GetMagickModule(),
15784             "Key press: %d 0x%lx (%s)",event.xkey.state,(unsigned long)
15785             key_symbol,command);
15786         if (event.xkey.window == windows->image.id)
15787           {
15788             command_type=XImageWindowCommand(display,resource_info,windows,
15789               event.xkey.state,key_symbol,&display_image,exception);
15790             if (command_type != NullCommand)
15791               nexus=XMagickCommand(display,resource_info,windows,command_type,
15792                 &display_image,exception);
15793           }
15794         if (event.xkey.window == windows->magnify.id)
15795           XMagnifyWindowCommand(display,windows,event.xkey.state,key_symbol,
15796             exception);
15797         if (event.xkey.window == windows->pan.id)
15798           {
15799             if ((key_symbol == XK_q) || (key_symbol == XK_Escape))
15800               (void) XWithdrawWindow(display,windows->pan.id,
15801                 windows->pan.screen);
15802             else
15803               if ((key_symbol == XK_F1) || (key_symbol == XK_Help))
15804                 XTextViewWidget(display,resource_info,windows,MagickFalse,
15805                   "Help Viewer - Image Pan",ImagePanHelp);
15806               else
15807                 XTranslateImage(display,windows,*image,key_symbol);
15808           }
15809         delay=display_image->delay/MagickMax(
15810           display_image->ticks_per_second,1L);
15811         timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15812         break;
15813       }
15814       case KeyRelease:
15815       {
15816         /*
15817           Respond to a user key release.
15818         */
15819         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
15820           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15821         if( IfMagickTrue(display_image->debug) )
15822           (void) LogMagickEvent(X11Event,GetMagickModule(),
15823             "Key release: 0x%lx (%c)",(unsigned long) key_symbol,*command);
15824         break;
15825       }
15826       case LeaveNotify:
15827       {
15828         /*
15829           Selectively uninstall colormap.
15830         */
15831         if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15832           if (event.xcrossing.mode != NotifyUngrab)
15833             XUninstallColormap(display,map_info->colormap);
15834         break;
15835       }
15836       case MapNotify:
15837       {
15838         if( IfMagickTrue(display_image->debug) )
15839           (void) LogMagickEvent(X11Event,GetMagickModule(),"Map Notify: 0x%lx",
15840             event.xmap.window);
15841         if (event.xmap.window == windows->backdrop.id)
15842           {
15843             (void) XSetInputFocus(display,event.xmap.window,RevertToParent,
15844               CurrentTime);
15845             windows->backdrop.mapped=MagickTrue;
15846             break;
15847           }
15848         if (event.xmap.window == windows->image.id)
15849           {
15850             if (windows->backdrop.id != (Window) NULL)
15851               (void) XInstallColormap(display,map_info->colormap);
15852             if (LocaleCompare(display_image->magick,"LOGO") == 0)
15853               {
15854                 if (LocaleCompare(display_image->filename,"LOGO") == 0)
15855                   nexus=XOpenImage(display,resource_info,windows,MagickFalse);
15856               }
15857             if (((int) windows->image.width < windows->image.ximage->width) ||
15858                 ((int) windows->image.height < windows->image.ximage->height))
15859               (void) XMapRaised(display,windows->pan.id);
15860             windows->image.mapped=MagickTrue;
15861             break;
15862           }
15863         if (event.xmap.window == windows->magnify.id)
15864           {
15865             XMakeMagnifyImage(display,windows,exception);
15866             windows->magnify.mapped=MagickTrue;
15867             (void) XWithdrawWindow(display,windows->info.id,
15868               windows->info.screen);
15869             break;
15870           }
15871         if (event.xmap.window == windows->pan.id)
15872           {
15873             XMakePanImage(display,resource_info,windows,display_image,
15874               exception);
15875             windows->pan.mapped=MagickTrue;
15876             break;
15877           }
15878         if (event.xmap.window == windows->info.id)
15879           {
15880             windows->info.mapped=MagickTrue;
15881             break;
15882           }
15883         if (event.xmap.window == windows->icon.id)
15884           {
15885             MagickBooleanType
15886               taint;
15887
15888             /*
15889               Create an icon image.
15890             */
15891             taint=display_image->taint;
15892             XMakeStandardColormap(display,icon_visual,icon_resources,
15893               display_image,icon_map,icon_pixel,exception);
15894             (void) XMakeImage(display,icon_resources,&windows->icon,
15895               display_image,windows->icon.width,windows->icon.height,
15896               exception);
15897             display_image->taint=taint;
15898             (void) XSetWindowBackgroundPixmap(display,windows->icon.id,
15899               windows->icon.pixmap);
15900             (void) XClearWindow(display,windows->icon.id);
15901             (void) XWithdrawWindow(display,windows->info.id,
15902               windows->info.screen);
15903             windows->icon.mapped=MagickTrue;
15904             break;
15905           }
15906         if (event.xmap.window == windows->command.id)
15907           {
15908             windows->command.mapped=MagickTrue;
15909             break;
15910           }
15911         if (event.xmap.window == windows->popup.id)
15912           {
15913             windows->popup.mapped=MagickTrue;
15914             break;
15915           }
15916         if (event.xmap.window == windows->widget.id)
15917           {
15918             windows->widget.mapped=MagickTrue;
15919             break;
15920           }
15921         break;
15922       }
15923       case MappingNotify:
15924       {
15925         (void) XRefreshKeyboardMapping(&event.xmapping);
15926         break;
15927       }
15928       case NoExpose:
15929         break;
15930       case PropertyNotify:
15931       {
15932         Atom
15933           type;
15934
15935         int
15936           format,
15937           status;
15938
15939         unsigned char
15940           *data;
15941
15942         unsigned long
15943           after,
15944           length;
15945
15946         if( IfMagickTrue(display_image->debug) )
15947           (void) LogMagickEvent(X11Event,GetMagickModule(),
15948             "Property Notify: 0x%lx 0x%lx %d",event.xproperty.window,
15949             event.xproperty.atom,event.xproperty.state);
15950         if (event.xproperty.atom != windows->im_remote_command)
15951           break;
15952         /*
15953           Display image named by the remote command protocol.
15954         */
15955         status=XGetWindowProperty(display,event.xproperty.window,
15956           event.xproperty.atom,0L,(long) MaxTextExtent,MagickFalse,(Atom)
15957           AnyPropertyType,&type,&format,&length,&after,&data);
15958         if ((status != Success) || (length == 0))
15959           break;
15960         if (LocaleCompare((char *) data,"-quit") == 0)
15961           {
15962             XClientMessage(display,windows->image.id,windows->im_protocols,
15963               windows->im_exit,CurrentTime);
15964             (void) XFree((void *) data);
15965             break;
15966           }
15967         (void) CopyMagickString(resource_info->image_info->filename,
15968           (char *) data,MaxTextExtent);
15969         (void) XFree((void *) data);
15970         nexus=ReadImage(resource_info->image_info,exception);
15971         CatchException(exception);
15972         if (nexus != (Image *) NULL)
15973           *state|=NextImageState | ExitState;
15974         break;
15975       }
15976       case ReparentNotify:
15977       {
15978         if( IfMagickTrue(display_image->debug) )
15979           (void) LogMagickEvent(X11Event,GetMagickModule(),
15980             "Reparent Notify: 0x%lx=>0x%lx",event.xreparent.parent,
15981             event.xreparent.window);
15982         break;
15983       }
15984       case UnmapNotify:
15985       {
15986         if( IfMagickTrue(display_image->debug) )
15987           (void) LogMagickEvent(X11Event,GetMagickModule(),
15988             "Unmap Notify: 0x%lx",event.xunmap.window);
15989         if (event.xunmap.window == windows->backdrop.id)
15990           {
15991             windows->backdrop.mapped=MagickFalse;
15992             break;
15993           }
15994         if (event.xunmap.window == windows->image.id)
15995           {
15996             windows->image.mapped=MagickFalse;
15997             break;
15998           }
15999         if (event.xunmap.window == windows->magnify.id)
16000           {
16001             windows->magnify.mapped=MagickFalse;
16002             break;
16003           }
16004         if (event.xunmap.window == windows->pan.id)
16005           {
16006             windows->pan.mapped=MagickFalse;
16007             break;
16008           }
16009         if (event.xunmap.window == windows->info.id)
16010           {
16011             windows->info.mapped=MagickFalse;
16012             break;
16013           }
16014         if (event.xunmap.window == windows->icon.id)
16015           {
16016             if (map_info->colormap == icon_map->colormap)
16017               XConfigureImageColormap(display,resource_info,windows,
16018                 display_image,exception);
16019             (void) XFreeStandardColormap(display,icon_visual,icon_map,
16020               icon_pixel);
16021             windows->icon.mapped=MagickFalse;
16022             break;
16023           }
16024         if (event.xunmap.window == windows->command.id)
16025           {
16026             windows->command.mapped=MagickFalse;
16027             break;
16028           }
16029         if (event.xunmap.window == windows->popup.id)
16030           {
16031             if (windows->backdrop.id != (Window) NULL)
16032               (void) XSetInputFocus(display,windows->image.id,RevertToParent,
16033                 CurrentTime);
16034             windows->popup.mapped=MagickFalse;
16035             break;
16036           }
16037         if (event.xunmap.window == windows->widget.id)
16038           {
16039             if (windows->backdrop.id != (Window) NULL)
16040               (void) XSetInputFocus(display,windows->image.id,RevertToParent,
16041                 CurrentTime);
16042             windows->widget.mapped=MagickFalse;
16043             break;
16044           }
16045         break;
16046       }
16047       default:
16048       {
16049         if( IfMagickTrue(display_image->debug) )
16050           (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
16051             event.type);
16052         break;
16053       }
16054     }
16055   } while (!(*state & ExitState));
16056   if ((*state & ExitState) == 0)
16057     (void) XMagickCommand(display,resource_info,windows,FreeBuffersCommand,
16058       &display_image,exception);
16059   else
16060     if( IfMagickTrue(resource_info->confirm_edit) )
16061       {
16062         /*
16063           Query user if image has changed.
16064         */
16065         if( IfMagickFalse(resource_info->immutable) &&
16066             IfMagickTrue(display_image->taint))
16067           {
16068             int
16069               status;
16070
16071             status=XConfirmWidget(display,windows,"Your image changed.",
16072               "Do you want to save it");
16073             if (status == 0)
16074               *state&=(~ExitState);
16075             else
16076               if (status > 0)
16077                 (void) XMagickCommand(display,resource_info,windows,SaveCommand,
16078                   &display_image,exception);
16079           }
16080       }
16081   if ((windows->visual_info->klass == GrayScale) ||
16082       (windows->visual_info->klass == PseudoColor) ||
16083       (windows->visual_info->klass == DirectColor))
16084     {
16085       /*
16086         Withdraw pan and Magnify window.
16087       */
16088       if( IfMagickTrue(windows->info.mapped) )
16089         (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
16090       if( IfMagickTrue(windows->magnify.mapped) )
16091         (void) XWithdrawWindow(display,windows->magnify.id,
16092           windows->magnify.screen);
16093       if( IfMagickTrue(windows->command.mapped) )
16094         (void) XWithdrawWindow(display,windows->command.id,
16095           windows->command.screen);
16096     }
16097   if( IfMagickTrue(windows->pan.mapped) )
16098     (void) XWithdrawWindow(display,windows->pan.id,windows->pan.screen);
16099   if( IfMagickFalse(resource_info->backdrop) )
16100     if (windows->backdrop.mapped)
16101       {
16102         (void) XWithdrawWindow(display,windows->backdrop.id,
16103           windows->backdrop.screen);
16104         (void) XDestroyWindow(display,windows->backdrop.id);
16105         windows->backdrop.id=(Window) NULL;
16106         (void) XWithdrawWindow(display,windows->image.id,
16107           windows->image.screen);
16108         (void) XDestroyWindow(display,windows->image.id);
16109         windows->image.id=(Window) NULL;
16110       }
16111   XSetCursorState(display,windows,MagickTrue);
16112   XCheckRefreshWindows(display,windows);
16113   if (((*state & FormerImageState) != 0) || ((*state & NextImageState) != 0))
16114     *state&=(~ExitState);
16115   if (*state & ExitState)
16116     {
16117       /*
16118         Free Standard Colormap.
16119       */
16120       (void) XFreeStandardColormap(display,icon_visual,icon_map,icon_pixel);
16121       if (resource_info->map_type == (char *) NULL)
16122         (void) XFreeStandardColormap(display,visual_info,map_info,pixel);
16123       /*
16124         Free X resources.
16125       */
16126       if (resource_info->copy_image != (Image *) NULL)
16127         {
16128           resource_info->copy_image=DestroyImage(resource_info->copy_image);
16129           resource_info->copy_image=NewImageList();
16130         }
16131       DestroyXResources();
16132     }
16133   (void) XSync(display,MagickFalse);
16134   /*
16135     Restore our progress monitor and warning handlers.
16136   */
16137   (void) SetErrorHandler(warning_handler);
16138   (void) SetWarningHandler(warning_handler);
16139   /*
16140     Change to home directory.
16141   */
16142   directory=getcwd(working_directory,MaxTextExtent);
16143   (void) directory;
16144   {
16145     int
16146       status;
16147
16148     status=chdir(resource_info->home_directory);
16149     if (status == -1)
16150       (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
16151         "UnableToOpenFile","%s",resource_info->home_directory);
16152   }
16153   *image=display_image;
16154   return(nexus);
16155 }
16156 #else
16157 \f
16158 /*
16159 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16160 %                                                                             %
16161 %                                                                             %
16162 %                                                                             %
16163 +   D i s p l a y I m a g e s                                                 %
16164 %                                                                             %
16165 %                                                                             %
16166 %                                                                             %
16167 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16168 %
16169 %  DisplayImages() displays an image sequence to any X window screen.  It
16170 %  returns a value other than 0 if successful.  Check the exception member
16171 %  of image to determine the reason for any failure.
16172 %
16173 %  The format of the DisplayImages method is:
16174 %
16175 %      MagickBooleanType DisplayImages(const ImageInfo *image_info,
16176 %        Image *images,ExceptionInfo *exception)
16177 %
16178 %  A description of each parameter follows:
16179 %
16180 %    o image_info: the image info.
16181 %
16182 %    o image: the image.
16183 %
16184 %    o exception: return any errors or warnings in this structure.
16185 %
16186 */
16187 MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
16188   Image *image,ExceptionInfo *exception)
16189 {
16190   assert(image_info != (const ImageInfo *) NULL);
16191   assert(image_info->signature == MagickSignature);
16192   assert(image != (Image *) NULL);
16193   assert(image->signature == MagickSignature);
16194   if( IfMagickTrue(image->debug) )
16195     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
16196   (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16197     "DelegateLibrarySupportNotBuiltIn","'%s' (X11)",image->filename);
16198   return(MagickFalse);
16199 }
16200 \f
16201 /*
16202 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16203 %                                                                             %
16204 %                                                                             %
16205 %                                                                             %
16206 +   R e m o t e D i s p l a y C o m m a n d                                   %
16207 %                                                                             %
16208 %                                                                             %
16209 %                                                                             %
16210 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16211 %
16212 %  RemoteDisplayCommand() encourages a remote display program to display the
16213 %  specified image filename.
16214 %
16215 %  The format of the RemoteDisplayCommand method is:
16216 %
16217 %      MagickBooleanType RemoteDisplayCommand(const ImageInfo *image,
16218 %        const char *window,const char *filename,ExceptionInfo *exception)
16219 %
16220 %  A description of each parameter follows:
16221 %
16222 %    o image_info: the image info.
16223 %
16224 %    o window: Specifies the name or id of an X window.
16225 %
16226 %    o filename: the name of the image filename to display.
16227 %
16228 %    o exception: return any errors or warnings in this structure.
16229 %
16230 */
16231 MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
16232   const char *window,const char *filename,ExceptionInfo *exception)
16233 {
16234   assert(image_info != (const ImageInfo *) NULL);
16235   assert(image_info->signature == MagickSignature);
16236   assert(filename != (char *) NULL);
16237   (void) window;
16238   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
16239   (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16240     "DelegateLibrarySupportNotBuiltIn","'%s' (X11)",image_info->filename);
16241   return(MagickFalse);
16242 }
16243 #endif