display.c revision 63240888c3975789a09c2494a4654b523931df96
1/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3%                                                                             %
4%                                                                             %
5%                                                                             %
6%               DDDD   IIIII  SSSSS  PPPP   L       AAA   Y   Y               %
7%               D   D    I    SS     P   P  L      A   A   Y Y                %
8%               D   D    I     SSS   PPPP   L      AAAAA    Y                 %
9%               D   D    I       SS  P      L      A   A    Y                 %
10%               DDDD   IIIII  SSSSS  P      LLLLL  A   A    Y                 %
11%                                                                             %
12%                                                                             %
13%        MagickCore Methods to Interactively Display and Edit an Image        %
14%                                                                             %
15%                             Software Design                                 %
16%                               John Cristy                                   %
17%                                July 1992                                    %
18%                                                                             %
19%                                                                             %
20%  Copyright 1999-2011 ImageMagick Studio LLC, a non-profit organization      %
21%  dedicated to making software imaging solutions freely available.           %
22%                                                                             %
23%  You may not use this file except in compliance with the License.  You may  %
24%  obtain a copy of the License at                                            %
25%                                                                             %
26%    http://www.imagemagick.org/script/license.php                            %
27%                                                                             %
28%  Unless required by applicable law or agreed to in writing, software        %
29%  distributed under the License is distributed on an "AS IS" BASIS,          %
30%  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
31%  See the License for the specific language governing permissions and        %
32%  limitations under the License.                                             %
33%                                                                             %
34%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35%
36%
37*/
38
39/*
40  Include declarations.
41*/
42#include "MagickCore/studio.h"
43#include "MagickCore/artifact.h"
44#include "MagickCore/blob.h"
45#include "MagickCore/cache.h"
46#include "MagickCore/client.h"
47#include "MagickCore/color.h"
48#include "MagickCore/colorspace.h"
49#include "MagickCore/composite.h"
50#include "MagickCore/constitute.h"
51#include "MagickCore/decorate.h"
52#include "MagickCore/delegate.h"
53#include "MagickCore/display.h"
54#include "MagickCore/display-private.h"
55#include "MagickCore/draw.h"
56#include "MagickCore/effect.h"
57#include "MagickCore/enhance.h"
58#include "MagickCore/exception.h"
59#include "MagickCore/exception-private.h"
60#include "MagickCore/fx.h"
61#include "MagickCore/geometry.h"
62#include "MagickCore/image.h"
63#include "MagickCore/image-private.h"
64#include "MagickCore/list.h"
65#include "MagickCore/log.h"
66#include "MagickCore/magick.h"
67#include "MagickCore/memory_.h"
68#include "MagickCore/monitor.h"
69#include "MagickCore/monitor-private.h"
70#include "MagickCore/montage.h"
71#include "MagickCore/option.h"
72#include "MagickCore/paint.h"
73#include "MagickCore/pixel.h"
74#include "MagickCore/pixel-accessor.h"
75#include "MagickCore/PreRvIcccm.h"
76#include "MagickCore/property.h"
77#include "MagickCore/quantum.h"
78#include "MagickCore/quantum-private.h"
79#include "MagickCore/resize.h"
80#include "MagickCore/resource_.h"
81#include "MagickCore/shear.h"
82#include "MagickCore/segment.h"
83#include "MagickCore/string_.h"
84#include "MagickCore/string-private.h"
85#include "MagickCore/transform.h"
86#include "MagickCore/threshold.h"
87#include "MagickCore/utility.h"
88#include "MagickCore/version.h"
89#include "MagickCore/widget.h"
90#include "MagickCore/xwindow-private.h"
91
92#if defined(MAGICKCORE_X11_DELEGATE)
93/*
94  Define declarations.
95*/
96#define MaxColors  MagickMin((ssize_t) windows->visual_info->colormap_size,256L)
97
98/*
99  Constant declarations.
100*/
101static const unsigned char
102  HighlightBitmap[8] =
103  {
104    0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55
105  },
106  OpaqueBitmap[8] =
107  {
108    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
109  },
110  ShadowBitmap[8] =
111  {
112    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
113  };
114
115static const char
116  *PageSizes[] =
117  {
118    "Letter",
119    "Tabloid",
120    "Ledger",
121    "Legal",
122    "Statement",
123    "Executive",
124    "A3",
125    "A4",
126    "A5",
127    "B4",
128    "B5",
129    "Folio",
130    "Quarto",
131    "10x14",
132    (char *) NULL
133  };
134
135/*
136  Help widget declarations.
137*/
138static const char
139  *ImageAnnotateHelp[] =
140  {
141    "In annotate mode, the Command widget has these options:",
142    "",
143    "    Font Name",
144    "      fixed",
145    "      variable",
146    "      5x8",
147    "      6x10",
148    "      7x13bold",
149    "      8x13bold",
150    "      9x15bold",
151    "      10x20",
152    "      12x24",
153    "      Browser...",
154    "    Font Color",
155    "      black",
156    "      blue",
157    "      cyan",
158    "      green",
159    "      gray",
160    "      red",
161    "      magenta",
162    "      yellow",
163    "      white",
164    "      transparent",
165    "      Browser...",
166    "    Font Color",
167    "      black",
168    "      blue",
169    "      cyan",
170    "      green",
171    "      gray",
172    "      red",
173    "      magenta",
174    "      yellow",
175    "      white",
176    "      transparent",
177    "      Browser...",
178    "    Rotate Text",
179    "      -90",
180    "      -45",
181    "      -30",
182    "      0",
183    "      30",
184    "      45",
185    "      90",
186    "      180",
187    "      Dialog...",
188    "    Help",
189    "    Dismiss",
190    "",
191    "Choose a font name from the Font Name sub-menu.  Additional",
192    "font names can be specified with the font browser.  You can",
193    "change the menu names by setting the X resources font1",
194    "through font9.",
195    "",
196    "Choose a font color from the Font Color sub-menu.",
197    "Additional font colors can be specified with the color",
198    "browser.  You can change the menu colors by setting the X",
199    "resources pen1 through pen9.",
200    "",
201    "If you select the color browser and press Grab, you can",
202    "choose the font color by moving the pointer to the desired",
203    "color on the screen and press any button.",
204    "",
205    "If you choose to rotate the text, choose Rotate Text from the",
206    "menu and select an angle.  Typically you will only want to",
207    "rotate one line of text at a time.  Depending on the angle you",
208    "choose, subsequent lines may end up overwriting each other.",
209    "",
210    "Choosing a font and its color is optional.  The default font",
211    "is fixed and the default color is black.  However, you must",
212    "choose a location to begin entering text and press button 1.",
213    "An underscore character will appear at the location of the",
214    "pointer.  The cursor changes to a pencil to indicate you are",
215    "in text mode.  To exit immediately, press Dismiss.",
216    "",
217    "In text mode, any key presses will display the character at",
218    "the location of the underscore and advance the underscore",
219    "cursor.  Enter your text and once completed press Apply to",
220    "finish your image annotation.  To correct errors press BACK",
221    "SPACE.  To delete an entire line of text, press DELETE.  Any",
222    "text that exceeds the boundaries of the image window is",
223    "automagically continued onto the next line.",
224    "",
225    "The actual color you request for the font is saved in the",
226    "image.  However, the color that appears in your image window",
227    "may be different.  For example, on a monochrome screen the",
228    "text will appear black or white even if you choose the color",
229    "red as the font color.  However, the image saved to a file",
230    "with -write is written with red lettering.  To assure the",
231    "correct color text in the final image, any PseudoClass image",
232    "is promoted to DirectClass (see miff(5)).  To force a",
233    "PseudoClass image to remain PseudoClass, use -colors.",
234    (char *) NULL,
235  },
236  *ImageChopHelp[] =
237  {
238    "In chop mode, the Command widget has these options:",
239    "",
240    "    Direction",
241    "      horizontal",
242    "      vertical",
243    "    Help",
244    "    Dismiss",
245    "",
246    "If the you choose the horizontal direction (this the",
247    "default), the area of the image between the two horizontal",
248    "endpoints of the chop line is removed.  Otherwise, the area",
249    "of the image between the two vertical endpoints of the chop",
250    "line is removed.",
251    "",
252    "Select a location within the image window to begin your chop,",
253    "press and hold any button.  Next, move the pointer to",
254    "another location in the image.  As you move a line will",
255    "connect the initial location and the pointer.  When you",
256    "release the button, the area within the image to chop is",
257    "determined by which direction you choose from the Command",
258    "widget.",
259    "",
260    "To cancel the image chopping, move the pointer back to the",
261    "starting point of the line and release the button.",
262    (char *) NULL,
263  },
264  *ImageColorEditHelp[] =
265  {
266    "In color edit mode, the Command widget has these options:",
267    "",
268    "    Method",
269    "      point",
270    "      replace",
271    "      floodfill",
272    "      filltoborder",
273    "      reset",
274    "    Pixel Color",
275    "      black",
276    "      blue",
277    "      cyan",
278    "      green",
279    "      gray",
280    "      red",
281    "      magenta",
282    "      yellow",
283    "      white",
284    "      Browser...",
285    "    Border Color",
286    "      black",
287    "      blue",
288    "      cyan",
289    "      green",
290    "      gray",
291    "      red",
292    "      magenta",
293    "      yellow",
294    "      white",
295    "      Browser...",
296    "    Fuzz",
297    "      0%",
298    "      2%",
299    "      5%",
300    "      10%",
301    "      15%",
302    "      Dialog...",
303    "    Undo",
304    "    Help",
305    "    Dismiss",
306    "",
307    "Choose a color editing method from the Method sub-menu",
308    "of the Command widget.  The point method recolors any pixel",
309    "selected with the pointer until the button is released.  The",
310    "replace method recolors any pixel that matches the color of",
311    "the pixel you select with a button press.  Floodfill recolors",
312    "any pixel that matches the color of the pixel you select with",
313    "a button press and is a neighbor.  Whereas filltoborder recolors",
314    "any neighbor pixel that is not the border color.  Finally reset",
315    "changes the entire image to the designated color.",
316    "",
317    "Next, choose a pixel color from the Pixel Color sub-menu.",
318    "Additional pixel colors can be specified with the color",
319    "browser.  You can change the menu colors by setting the X",
320    "resources pen1 through pen9.",
321    "",
322    "Now press button 1 to select a pixel within the image window",
323    "to change its color.  Additional pixels may be recolored as",
324    "prescribed by the method you choose.",
325    "",
326    "If the Magnify widget is mapped, it can be helpful in positioning",
327    "your pointer within the image (refer to button 2).",
328    "",
329    "The actual color you request for the pixels is saved in the",
330    "image.  However, the color that appears in your image window",
331    "may be different.  For example, on a monochrome screen the",
332    "pixel will appear black or white even if you choose the",
333    "color red as the pixel color.  However, the image saved to a",
334    "file with -write is written with red pixels.  To assure the",
335    "correct color text in the final image, any PseudoClass image",
336    "is promoted to DirectClass (see miff(5)).  To force a",
337    "PseudoClass image to remain PseudoClass, use -colors.",
338    (char *) NULL,
339  },
340  *ImageCompositeHelp[] =
341  {
342    "First a widget window is displayed requesting you to enter an",
343    "image name. Press Composite, Grab or type a file name.",
344    "Press Cancel if you choose not to create a composite image.",
345    "When you choose Grab, move the pointer to the desired window",
346    "and press any button.",
347    "",
348    "If the Composite image does not have any matte information,",
349    "you are informed and the file browser is displayed again.",
350    "Enter the name of a mask image.  The image is typically",
351    "grayscale and the same size as the composite image.  If the",
352    "image is not grayscale, it is converted to grayscale and the",
353    "resulting intensities are used as matte information.",
354    "",
355    "A small window appears showing the location of the cursor in",
356    "the image window. You are now in composite mode.  To exit",
357    "immediately, press Dismiss.  In composite mode, the Command",
358    "widget has these options:",
359    "",
360    "    Operators",
361    "      Over",
362    "      In",
363    "      Out",
364    "      Atop",
365    "      Xor",
366    "      Plus",
367    "      Minus",
368    "      Add",
369    "      Subtract",
370    "      Difference",
371    "      Multiply",
372    "      Bumpmap",
373    "      Copy",
374    "      CopyRed",
375    "      CopyGreen",
376    "      CopyBlue",
377    "      CopyOpacity",
378    "      Clear",
379    "    Dissolve",
380    "    Displace",
381    "    Help",
382    "    Dismiss",
383    "",
384    "Choose a composite operation from the Operators sub-menu of",
385    "the Command widget.  How each operator behaves is described",
386    "below.  Image window is the image currently displayed on",
387    "your X server and image is the image obtained with the File",
388    "Browser widget.",
389    "",
390    "Over     The result is the union of the two image shapes,",
391    "         with image obscuring image window in the region of",
392    "         overlap.",
393    "",
394    "In       The result is simply image cut by the shape of",
395    "         image window.  None of the image data of image",
396    "         window is in the result.",
397    "",
398    "Out      The resulting image is image with the shape of",
399    "         image window cut out.",
400    "",
401    "Atop     The result is the same shape as image image window,",
402    "         with image obscuring image window where the image",
403    "         shapes overlap.  Note this differs from over",
404    "         because the portion of image outside image window's",
405    "         shape does not appear in the result.",
406    "",
407    "Xor      The result is the image data from both image and",
408    "         image window that is outside the overlap region.",
409    "         The overlap region is blank.",
410    "",
411    "Plus     The result is just the sum of the image data.",
412    "         Output values are cropped to QuantumRange (no overflow).",
413    "",
414    "Minus    The result of image - image window, with underflow",
415    "         cropped to zero.",
416    "",
417    "Add      The result of image + image window, with overflow",
418    "         wrapping around (mod 256).",
419    "",
420    "Subtract The result of image - image window, with underflow",
421    "         wrapping around (mod 256).  The add and subtract",
422    "         operators can be used to perform reversible",
423    "         transformations.",
424    "",
425    "Difference",
426    "         The result of abs(image - image window).  This",
427    "         useful for comparing two very similar images.",
428    "",
429    "Multiply",
430    "         The result of image * image window.  This",
431    "         useful for the creation of drop-shadows.",
432    "",
433    "Bumpmap  The result of surface normals from image * image",
434    "         window.",
435    "",
436    "Copy     The resulting image is image window replaced with",
437    "         image.  Here the matte information is ignored.",
438    "",
439    "CopyRed  The red layer of the image window is replace with",
440    "         the red layer of the image.  The other layers are",
441    "         untouched.",
442    "",
443    "CopyGreen",
444    "         The green layer of the image window is replace with",
445    "         the green layer of the image.  The other layers are",
446    "         untouched.",
447    "",
448    "CopyBlue The blue layer of the image window is replace with",
449    "         the blue layer of the image.  The other layers are",
450    "         untouched.",
451    "",
452    "CopyOpacity",
453    "         The matte layer of the image window is replace with",
454    "         the matte layer of the image.  The other layers are",
455    "         untouched.",
456    "",
457    "The image compositor requires a matte, or alpha channel in",
458    "the image for some operations.  This extra channel usually",
459    "defines a mask which represents a sort of a cookie-cutter",
460    "for the image.  This the case when matte is opaque (full",
461    "coverage) for pixels inside the shape, zero outside, and",
462    "between 0 and QuantumRange on the boundary.  If image does not",
463    "have a matte channel, it is initialized with 0 for any pixel",
464    "matching in color to pixel location (0,0), otherwise QuantumRange.",
465    "",
466    "If you choose Dissolve, the composite operator becomes Over.  The",
467    "image matte channel percent transparency is initialized to factor.",
468    "The image window is initialized to (100-factor). Where factor is the",
469    "value you specify in the Dialog widget.",
470    "",
471    "Displace shifts the image pixels as defined by a displacement",
472    "map.  With this option, image is used as a displacement map.",
473    "Black, within the displacement map, is a maximum positive",
474    "displacement.  White is a maximum negative displacement and",
475    "middle gray is neutral.  The displacement is scaled to determine",
476    "the pixel shift.  By default, the displacement applies in both the",
477    "horizontal and vertical directions.  However, if you specify a mask,",
478    "image is the horizontal X displacement and mask the vertical Y",
479    "displacement.",
480    "",
481    "Note that matte information for image window is not retained",
482    "for colormapped X server visuals (e.g. StaticColor,",
483    "StaticColor, GrayScale, PseudoColor).  Correct compositing",
484    "behavior may require a TrueColor or DirectColor visual or a",
485    "Standard Colormap.",
486    "",
487    "Choosing a composite operator is optional.  The default",
488    "operator is replace.  However, you must choose a location to",
489    "composite your image and press button 1.  Press and hold the",
490    "button before releasing and an outline of the image will",
491    "appear to help you identify your location.",
492    "",
493    "The actual colors of the composite image is saved.  However,",
494    "the color that appears in image window may be different.",
495    "For example, on a monochrome screen image window will appear",
496    "black or white even though your composited image may have",
497    "many colors.  If the image is saved to a file it is written",
498    "with the correct colors.  To assure the correct colors are",
499    "saved in the final image, any PseudoClass image is promoted",
500    "to DirectClass (see miff(5)).  To force a PseudoClass image",
501    "to remain PseudoClass, use -colors.",
502    (char *) NULL,
503  },
504  *ImageCutHelp[] =
505  {
506    "In cut mode, the Command widget has these options:",
507    "",
508    "    Help",
509    "    Dismiss",
510    "",
511    "To define a cut region, press button 1 and drag.  The",
512    "cut region is defined by a highlighted rectangle that",
513    "expands or contracts as it follows the pointer.  Once you",
514    "are satisfied with the cut region, release the button.",
515    "You are now in rectify mode.  In rectify mode, the Command",
516    "widget has these options:",
517    "",
518    "    Cut",
519    "    Help",
520    "    Dismiss",
521    "",
522    "You can make adjustments by moving the pointer to one of the",
523    "cut rectangle corners, pressing a button, and dragging.",
524    "Finally, press Cut to commit your copy region.  To",
525    "exit without cutting the image, press Dismiss.",
526    (char *) NULL,
527  },
528  *ImageCopyHelp[] =
529  {
530    "In copy mode, the Command widget has these options:",
531    "",
532    "    Help",
533    "    Dismiss",
534    "",
535    "To define a copy region, press button 1 and drag.  The",
536    "copy region is defined by a highlighted rectangle that",
537    "expands or contracts as it follows the pointer.  Once you",
538    "are satisfied with the copy region, release the button.",
539    "You are now in rectify mode.  In rectify mode, the Command",
540    "widget has these options:",
541    "",
542    "    Copy",
543    "    Help",
544    "    Dismiss",
545    "",
546    "You can make adjustments by moving the pointer to one of the",
547    "copy rectangle corners, pressing a button, and dragging.",
548    "Finally, press Copy to commit your copy region.  To",
549    "exit without copying the image, press Dismiss.",
550    (char *) NULL,
551  },
552  *ImageCropHelp[] =
553  {
554    "In crop mode, the Command widget has these options:",
555    "",
556    "    Help",
557    "    Dismiss",
558    "",
559    "To define a cropping region, press button 1 and drag.  The",
560    "cropping region is defined by a highlighted rectangle that",
561    "expands or contracts as it follows the pointer.  Once you",
562    "are satisfied with the cropping region, release the button.",
563    "You are now in rectify mode.  In rectify mode, the Command",
564    "widget has these options:",
565    "",
566    "    Crop",
567    "    Help",
568    "    Dismiss",
569    "",
570    "You can make adjustments by moving the pointer to one of the",
571    "cropping rectangle corners, pressing a button, and dragging.",
572    "Finally, press Crop to commit your cropping region.  To",
573    "exit without cropping the image, press Dismiss.",
574    (char *) NULL,
575  },
576  *ImageDrawHelp[] =
577  {
578    "The cursor changes to a crosshair to indicate you are in",
579    "draw mode.  To exit immediately, press Dismiss.  In draw mode,",
580    "the Command widget has these options:",
581    "",
582    "    Element",
583    "      point",
584    "      line",
585    "      rectangle",
586    "      fill rectangle",
587    "      circle",
588    "      fill circle",
589    "      ellipse",
590    "      fill ellipse",
591    "      polygon",
592    "      fill polygon",
593    "    Color",
594    "      black",
595    "      blue",
596    "      cyan",
597    "      green",
598    "      gray",
599    "      red",
600    "      magenta",
601    "      yellow",
602    "      white",
603    "      transparent",
604    "      Browser...",
605    "    Stipple",
606    "      Brick",
607    "      Diagonal",
608    "      Scales",
609    "      Vertical",
610    "      Wavy",
611    "      Translucent",
612    "      Opaque",
613    "      Open...",
614    "    Width",
615    "      1",
616    "      2",
617    "      4",
618    "      8",
619    "      16",
620    "      Dialog...",
621    "    Undo",
622    "    Help",
623    "    Dismiss",
624    "",
625    "Choose a drawing primitive from the Element sub-menu.",
626    "",
627    "Choose a color from the Color sub-menu.  Additional",
628    "colors can be specified with the color browser.",
629    "",
630    "If you choose the color browser and press Grab, you can",
631    "select the color by moving the pointer to the desired",
632    "color on the screen and press any button.  The transparent",
633    "color updates the image matte channel and is useful for",
634    "image compositing.",
635    "",
636    "Choose a stipple, if appropriate, from the Stipple sub-menu.",
637    "Additional stipples can be specified with the file browser.",
638    "Stipples obtained from the file browser must be on disk in the",
639    "X11 bitmap format.",
640    "",
641    "Choose a width, if appropriate, from the Width sub-menu.  To",
642    "choose a specific width select the Dialog widget.",
643    "",
644    "Choose a point in the Image window and press button 1 and",
645    "hold.  Next, move the pointer to another location in the",
646    "image.  As you move, a line connects the initial location and",
647    "the pointer.  When you release the button, the image is",
648    "updated with the primitive you just drew.  For polygons, the",
649    "image is updated when you press and release the button without",
650    "moving the pointer.",
651    "",
652    "To cancel image drawing, move the pointer back to the",
653    "starting point of the line and release the button.",
654    (char *) NULL,
655  },
656  *DisplayHelp[] =
657  {
658    "BUTTONS",
659    "  The effects of each button press is described below.  Three",
660    "  buttons are required.  If you have a two button mouse,",
661    "  button 1 and 3 are returned.  Press ALT and button 3 to",
662    "  simulate button 2.",
663    "",
664    "  1    Press this button to map or unmap the Command widget.",
665    "",
666    "  2    Press and drag to define a region of the image to",
667    "       magnify.",
668    "",
669    "  3    Press and drag to choose from a select set of commands.",
670    "       This button behaves differently if the image being",
671    "       displayed is a visual image directory.  Here, choose a",
672    "       particular tile of the directory and press this button and",
673    "       drag to select a command from a pop-up menu.  Choose from",
674    "       these menu items:",
675    "",
676    "           Open",
677    "           Next",
678    "           Former",
679    "           Delete",
680    "           Update",
681    "",
682    "       If you choose Open, the image represented by the tile is",
683    "       displayed.  To return to the visual image directory, choose",
684    "       Next from the Command widget.  Next and Former moves to the",
685    "       next or former image respectively.  Choose Delete to delete",
686    "       a particular image tile.  Finally, choose Update to",
687    "       synchronize all the image tiles with their respective",
688    "       images.",
689    "",
690    "COMMAND WIDGET",
691    "  The Command widget lists a number of sub-menus and commands.",
692    "  They are",
693    "",
694    "      File",
695    "        Open...",
696    "        Next",
697    "        Former",
698    "        Select...",
699    "        Save...",
700    "        Print...",
701    "        Delete...",
702    "        New...",
703    "        Visual Directory...",
704    "        Quit",
705    "      Edit",
706    "        Undo",
707    "        Redo",
708    "        Cut",
709    "        Copy",
710    "        Paste",
711    "      View",
712    "        Half Size",
713    "        Original Size",
714    "        Double Size",
715    "        Resize...",
716    "        Apply",
717    "        Refresh",
718    "        Restore",
719    "      Transform",
720    "        Crop",
721    "        Chop",
722    "        Flop",
723    "        Flip",
724    "        Rotate Right",
725    "        Rotate Left",
726    "        Rotate...",
727    "        Shear...",
728    "        Roll...",
729    "        Trim Edges",
730    "      Enhance",
731    "        Brightness...",
732    "        Saturation...",
733    "        Hue...",
734    "        Gamma...",
735    "        Sharpen...",
736    "        Dull",
737    "        Contrast Stretch...",
738    "        Sigmoidal Contrast...",
739    "        Normalize",
740    "        Equalize",
741    "        Negate",
742    "        Grayscale",
743    "        Map...",
744    "        Quantize...",
745    "      Effects",
746    "        Despeckle",
747    "        Emboss",
748    "        Reduce Noise",
749    "        Add Noise",
750    "        Sharpen...",
751    "        Blur...",
752    "        Threshold...",
753    "        Edge Detect...",
754    "        Spread...",
755    "        Shade...",
756    "        Painting...",
757    "        Segment...",
758    "      F/X",
759    "        Solarize...",
760    "        Sepia Tone...",
761    "        Swirl...",
762    "        Implode...",
763    "        Vignette...",
764    "        Wave...",
765    "        Oil Painting...",
766    "        Charcoal Drawing...",
767    "      Image Edit",
768    "        Annotate...",
769    "        Draw...",
770    "        Color...",
771    "        Matte...",
772    "        Composite...",
773    "        Add Border...",
774    "        Add Frame...",
775    "        Comment...",
776    "        Launch...",
777    "        Region of Interest...",
778    "      Miscellany",
779    "        Image Info",
780    "        Zoom Image",
781    "        Show Preview...",
782    "        Show Histogram",
783    "        Show Matte",
784    "        Background...",
785    "        Slide Show",
786    "        Preferences...",
787    "      Help",
788    "        Overview",
789    "        Browse Documentation",
790    "        About Display",
791    "",
792    "  Menu items with a indented triangle have a sub-menu.  They",
793    "  are represented above as the indented items.  To access a",
794    "  sub-menu item, move the pointer to the appropriate menu and",
795    "  press a button and drag.  When you find the desired sub-menu",
796    "  item, release the button and the command is executed.  Move",
797    "  the pointer away from the sub-menu if you decide not to",
798    "  execute a particular command.",
799    "",
800    "KEYBOARD ACCELERATORS",
801    "  Accelerators are one or two key presses that effect a",
802    "  particular command.  The keyboard accelerators that",
803    "  display(1) understands is:",
804    "",
805    "  Ctl+O     Press to open an image from a file.",
806    "",
807    "  space     Press to display the next image.",
808    "",
809    "            If the image is a multi-paged document such as a Postscript",
810    "            document, you can skip ahead several pages by preceding",
811    "            this command with a number.  For example to display the",
812    "            third page beyond the current page, press 3<space>.",
813    "",
814    "  backspace Press to display the former image.",
815    "",
816    "            If the image is a multi-paged document such as a Postscript",
817    "            document, you can skip behind several pages by preceding",
818    "            this command with a number.  For example to display the",
819    "            third page preceding the current page, press 3<backspace>.",
820    "",
821    "  Ctl+S     Press to write the image to a file.",
822    "",
823    "  Ctl+P     Press to print the image to a Postscript printer.",
824    "",
825    "  Ctl+D     Press to delete an image file.",
826    "",
827    "  Ctl+N     Press to create a blank canvas.",
828    "",
829    "  Ctl+Q     Press to discard all images and exit program.",
830    "",
831    "  Ctl+Z     Press to undo last image transformation.",
832    "",
833    "  Ctl+R     Press to redo last image transformation.",
834    "",
835    "  Ctl+X     Press to cut a region of the image.",
836    "",
837    "  Ctl+C     Press to copy a region of the image.",
838    "",
839    "  Ctl+V     Press to paste a region to the image.",
840    "",
841    "  <         Press to half the image size.",
842    "",
843    "  -         Press to return to the original image size.",
844    "",
845    "  >         Press to double the image size.",
846    "",
847    "  %         Press to resize the image to a width and height you",
848    "            specify.",
849    "",
850    "Cmd-A       Press to make any image transformations permanent."
851    "",
852    "            By default, any image size transformations are applied",
853    "            to the original image to create the image displayed on",
854    "            the X server.  However, the transformations are not",
855    "            permanent (i.e. the original image does not change",
856    "            size only the X image does).  For example, if you",
857    "            press > the X image will appear to double in size,",
858    "            but the original image will in fact remain the same size.",
859    "            To force the original image to double in size, press >",
860    "            followed by Cmd-A.",
861    "",
862    "  @         Press to refresh the image window.",
863    "",
864    "  C         Press to cut out a rectangular region of the image.",
865    "",
866    "  [         Press to chop the image.",
867    "",
868    "  H         Press to flop image in the horizontal direction.",
869    "",
870    "  V         Press to flip image in the vertical direction.",
871    "",
872    "  /         Press to rotate the image 90 degrees clockwise.",
873    "",
874    " \\         Press to rotate the image 90 degrees counter-clockwise.",
875    "",
876    "  *         Press to rotate the image the number of degrees you",
877    "            specify.",
878    "",
879    "  S         Press to shear the image the number of degrees you",
880    "            specify.",
881    "",
882    "  R         Press to roll the image.",
883    "",
884    "  T         Press to trim the image edges.",
885    "",
886    "  Shft-H    Press to vary the image hue.",
887    "",
888    "  Shft-S    Press to vary the color saturation.",
889    "",
890    "  Shft-L    Press to vary the color brightness.",
891    "",
892    "  Shft-G    Press to gamma correct the image.",
893    "",
894    "  Shft-C    Press to sharpen the image contrast.",
895    "",
896    "  Shft-Z    Press to dull the image contrast.",
897    "",
898    "  =         Press to perform histogram equalization on the image.",
899    "",
900    "  Shft-N    Press to perform histogram normalization on the image.",
901    "",
902    "  Shft-~    Press to negate the colors of the image.",
903    "",
904    "  .         Press to convert the image colors to gray.",
905    "",
906    "  Shft-#    Press to set the maximum number of unique colors in the",
907    "            image.",
908    "",
909    "  F2        Press to reduce the speckles in an image.",
910    "",
911    "  F3        Press to eliminate peak noise from an image.",
912    "",
913    "  F4        Press to add noise to an image.",
914    "",
915    "  F5        Press to sharpen an image.",
916    "",
917    "  F6        Press to delete an image file.",
918    "",
919    "  F7        Press to threshold the image.",
920    "",
921    "  F8        Press to detect edges within an image.",
922    "",
923    "  F9        Press to emboss an image.",
924    "",
925    "  F10       Press to displace pixels by a random amount.",
926    "",
927    "  F11       Press to negate all pixels above the threshold level.",
928    "",
929    "  F12       Press to shade the image using a distant light source.",
930    "",
931    "  F13       Press to lighten or darken image edges to create a 3-D effect.",
932    "",
933    "  F14       Press to segment the image by color.",
934    "",
935    "  Meta-S    Press to swirl image pixels about the center.",
936    "",
937    "  Meta-I    Press to implode image pixels about the center.",
938    "",
939    "  Meta-W    Press to alter an image along a sine wave.",
940    "",
941    "  Meta-P    Press to simulate an oil painting.",
942    "",
943    "  Meta-C    Press to simulate a charcoal drawing.",
944    "",
945    "  Alt-A     Press to annotate the image with text.",
946    "",
947    "  Alt-D     Press to draw on an image.",
948    "",
949    "  Alt-P     Press to edit an image pixel color.",
950    "",
951    "  Alt-M     Press to edit the image matte information.",
952    "",
953    "  Alt-V     Press to composite the image with another.",
954    "",
955    "  Alt-B     Press to add a border to the image.",
956    "",
957    "  Alt-F     Press to add an ornamental border to the image.",
958    "",
959    "  Alt-Shft-!",
960    "            Press to add an image comment.",
961    "",
962    "  Ctl-A     Press to apply image processing techniques to a region",
963    "            of interest.",
964    "",
965    "  Shft-?    Press to display information about the image.",
966    "",
967    "  Shft-+    Press to map the zoom image window.",
968    "",
969    "  Shft-P    Press to preview an image enhancement, effect, or f/x.",
970    "",
971    "  F1        Press to display helpful information about display(1).",
972    "",
973    "  Find      Press to browse documentation about ImageMagick.",
974    "",
975    "  1-9       Press to change the level of magnification.",
976    "",
977    "  Use the arrow keys to move the image one pixel up, down,",
978    "  left, or right within the magnify window.  Be sure to first",
979    "  map the magnify window by pressing button 2.",
980    "",
981    "  Press ALT and one of the arrow keys to trim off one pixel",
982    "  from any side of the image.",
983    (char *) NULL,
984  },
985  *ImageMatteEditHelp[] =
986  {
987    "Matte information within an image is useful for some",
988    "operations such as image compositing (See IMAGE",
989    "COMPOSITING).  This extra channel usually defines a mask",
990    "which represents a sort of a cookie-cutter for the image.",
991    "This the case when matte is opaque (full coverage) for",
992    "pixels inside the shape, zero outside, and between 0 and",
993    "QuantumRange on the boundary.",
994    "",
995    "A small window appears showing the location of the cursor in",
996    "the image window. You are now in matte edit mode.  To exit",
997    "immediately, press Dismiss.  In matte edit mode, the Command",
998    "widget has these options:",
999    "",
1000    "    Method",
1001    "      point",
1002    "      replace",
1003    "      floodfill",
1004    "      filltoborder",
1005    "      reset",
1006    "    Border Color",
1007    "      black",
1008    "      blue",
1009    "      cyan",
1010    "      green",
1011    "      gray",
1012    "      red",
1013    "      magenta",
1014    "      yellow",
1015    "      white",
1016    "      Browser...",
1017    "    Fuzz",
1018    "      0%",
1019    "      2%",
1020    "      5%",
1021    "      10%",
1022    "      15%",
1023    "      Dialog...",
1024    "    Matte",
1025    "      Opaque",
1026    "      Transparent",
1027    "      Dialog...",
1028    "    Undo",
1029    "    Help",
1030    "    Dismiss",
1031    "",
1032    "Choose a matte editing method from the Method sub-menu of",
1033    "the Command widget.  The point method changes the matte value",
1034    "of any pixel selected with the pointer until the button is",
1035    "is released.  The replace method changes the matte value of",
1036    "any pixel that matches the color of the pixel you select with",
1037    "a button press.  Floodfill changes the matte value of any pixel",
1038    "that matches the color of the pixel you select with a button",
1039    "press and is a neighbor.  Whereas filltoborder changes the matte",
1040    "value any neighbor pixel that is not the border color.  Finally",
1041    "reset changes the entire image to the designated matte value.",
1042    "",
1043    "Choose Matte Value and pick Opaque or Transarent.  For other values",
1044    "select the Dialog entry.  Here a dialog appears requesting a matte",
1045    "value.  The value you select is assigned as the opacity value of the",
1046    "selected pixel or pixels.",
1047    "",
1048    "Now, press any button to select a pixel within the image",
1049    "window to change its matte value.",
1050    "",
1051    "If the Magnify widget is mapped, it can be helpful in positioning",
1052    "your pointer within the image (refer to button 2).",
1053    "",
1054    "Matte information is only valid in a DirectClass image.",
1055    "Therefore, any PseudoClass image is promoted to DirectClass",
1056    "(see miff(5)).  Note that matte information for PseudoClass",
1057    "is not retained for colormapped X server visuals (e.g.",
1058    "StaticColor, StaticColor, GrayScale, PseudoColor) unless you",
1059    "immediately save your image to a file (refer to Write).",
1060    "Correct matte editing behavior may require a TrueColor or",
1061    "DirectColor visual or a Standard Colormap.",
1062    (char *) NULL,
1063  },
1064  *ImagePanHelp[] =
1065  {
1066    "When an image exceeds the width or height of the X server",
1067    "screen, display maps a small panning icon.  The rectangle",
1068    "within the panning icon shows the area that is currently",
1069    "displayed in the image window.  To pan about the image,",
1070    "press any button and drag the pointer within the panning",
1071    "icon.  The pan rectangle moves with the pointer and the",
1072    "image window is updated to reflect the location of the",
1073    "rectangle within the panning icon.  When you have selected",
1074    "the area of the image you wish to view, release the button.",
1075    "",
1076    "Use the arrow keys to pan the image one pixel up, down,",
1077    "left, or right within the image window.",
1078    "",
1079    "The panning icon is withdrawn if the image becomes smaller",
1080    "than the dimensions of the X server screen.",
1081    (char *) NULL,
1082  },
1083  *ImagePasteHelp[] =
1084  {
1085    "A small window appears showing the location of the cursor in",
1086    "the image window. You are now in paste mode.  To exit",
1087    "immediately, press Dismiss.  In paste mode, the Command",
1088    "widget has these options:",
1089    "",
1090    "    Operators",
1091    "      over",
1092    "      in",
1093    "      out",
1094    "      atop",
1095    "      xor",
1096    "      plus",
1097    "      minus",
1098    "      add",
1099    "      subtract",
1100    "      difference",
1101    "      replace",
1102    "    Help",
1103    "    Dismiss",
1104    "",
1105    "Choose a composite operation from the Operators sub-menu of",
1106    "the Command widget.  How each operator behaves is described",
1107    "below.  Image window is the image currently displayed on",
1108    "your X server and image is the image obtained with the File",
1109    "Browser widget.",
1110    "",
1111    "Over     The result is the union of the two image shapes,",
1112    "         with image obscuring image window in the region of",
1113    "         overlap.",
1114    "",
1115    "In       The result is simply image cut by the shape of",
1116    "         image window.  None of the image data of image",
1117    "         window is in the result.",
1118    "",
1119    "Out      The resulting image is image with the shape of",
1120    "         image window cut out.",
1121    "",
1122    "Atop     The result is the same shape as image image window,",
1123    "         with image obscuring image window where the image",
1124    "         shapes overlap.  Note this differs from over",
1125    "         because the portion of image outside image window's",
1126    "         shape does not appear in the result.",
1127    "",
1128    "Xor      The result is the image data from both image and",
1129    "         image window that is outside the overlap region.",
1130    "         The overlap region is blank.",
1131    "",
1132    "Plus     The result is just the sum of the image data.",
1133    "         Output values are cropped to QuantumRange (no overflow).",
1134    "         This operation is independent of the matte",
1135    "         channels.",
1136    "",
1137    "Minus    The result of image - image window, with underflow",
1138    "         cropped to zero.",
1139    "",
1140    "Add      The result of image + image window, with overflow",
1141    "         wrapping around (mod 256).",
1142    "",
1143    "Subtract The result of image - image window, with underflow",
1144    "         wrapping around (mod 256).  The add and subtract",
1145    "         operators can be used to perform reversible",
1146    "         transformations.",
1147    "",
1148    "Difference",
1149    "         The result of abs(image - image window).  This",
1150    "         useful for comparing two very similar images.",
1151    "",
1152    "Copy     The resulting image is image window replaced with",
1153    "         image.  Here the matte information is ignored.",
1154    "",
1155    "CopyRed  The red layer of the image window is replace with",
1156    "         the red layer of the image.  The other layers are",
1157    "         untouched.",
1158    "",
1159    "CopyGreen",
1160    "         The green layer of the image window is replace with",
1161    "         the green layer of the image.  The other layers are",
1162    "         untouched.",
1163    "",
1164    "CopyBlue The blue layer of the image window is replace with",
1165    "         the blue layer of the image.  The other layers are",
1166    "         untouched.",
1167    "",
1168    "CopyOpacity",
1169    "         The matte layer of the image window is replace with",
1170    "         the matte layer of the image.  The other layers are",
1171    "         untouched.",
1172    "",
1173    "The image compositor requires a matte, or alpha channel in",
1174    "the image for some operations.  This extra channel usually",
1175    "defines a mask which represents a sort of a cookie-cutter",
1176    "for the image.  This the case when matte is opaque (full",
1177    "coverage) for pixels inside the shape, zero outside, and",
1178    "between 0 and QuantumRange on the boundary.  If image does not",
1179    "have a matte channel, it is initialized with 0 for any pixel",
1180    "matching in color to pixel location (0,0), otherwise QuantumRange.",
1181    "",
1182    "Note that matte information for image window is not retained",
1183    "for colormapped X server visuals (e.g. StaticColor,",
1184    "StaticColor, GrayScale, PseudoColor).  Correct compositing",
1185    "behavior may require a TrueColor or DirectColor visual or a",
1186    "Standard Colormap.",
1187    "",
1188    "Choosing a composite operator is optional.  The default",
1189    "operator is replace.  However, you must choose a location to",
1190    "paste your image and press button 1.  Press and hold the",
1191    "button before releasing and an outline of the image will",
1192    "appear to help you identify your location.",
1193    "",
1194    "The actual colors of the pasted image is saved.  However,",
1195    "the color that appears in image window may be different.",
1196    "For example, on a monochrome screen image window will appear",
1197    "black or white even though your pasted image may have",
1198    "many colors.  If the image is saved to a file it is written",
1199    "with the correct colors.  To assure the correct colors are",
1200    "saved in the final image, any PseudoClass image is promoted",
1201    "to DirectClass (see miff(5)).  To force a PseudoClass image",
1202    "to remain PseudoClass, use -colors.",
1203    (char *) NULL,
1204  },
1205  *ImageROIHelp[] =
1206  {
1207    "In region of interest mode, the Command widget has these",
1208    "options:",
1209    "",
1210    "    Help",
1211    "    Dismiss",
1212    "",
1213    "To define a region of interest, press button 1 and drag.",
1214    "The region of interest is defined by a highlighted rectangle",
1215    "that expands or contracts as it follows the pointer.  Once",
1216    "you are satisfied with the region of interest, release the",
1217    "button.  You are now in apply mode.  In apply mode the",
1218    "Command widget has these options:",
1219    "",
1220    "      File",
1221    "        Save...",
1222    "        Print...",
1223    "      Edit",
1224    "        Undo",
1225    "        Redo",
1226    "      Transform",
1227    "        Flop",
1228    "        Flip",
1229    "        Rotate Right",
1230    "        Rotate Left",
1231    "      Enhance",
1232    "        Hue...",
1233    "        Saturation...",
1234    "        Brightness...",
1235    "        Gamma...",
1236    "        Spiff",
1237    "        Dull",
1238    "        Contrast Stretch",
1239    "        Sigmoidal Contrast...",
1240    "        Normalize",
1241    "        Equalize",
1242    "        Negate",
1243    "        Grayscale",
1244    "        Map...",
1245    "        Quantize...",
1246    "      Effects",
1247    "        Despeckle",
1248    "        Emboss",
1249    "        Reduce Noise",
1250    "        Sharpen...",
1251    "        Blur...",
1252    "        Threshold...",
1253    "        Edge Detect...",
1254    "        Spread...",
1255    "        Shade...",
1256    "        Raise...",
1257    "        Segment...",
1258    "      F/X",
1259    "        Solarize...",
1260    "        Sepia Tone...",
1261    "        Swirl...",
1262    "        Implode...",
1263    "        Vignette...",
1264    "        Wave...",
1265    "        Oil Painting...",
1266    "        Charcoal Drawing...",
1267    "      Miscellany",
1268    "        Image Info",
1269    "        Zoom Image",
1270    "        Show Preview...",
1271    "        Show Histogram",
1272    "        Show Matte",
1273    "      Help",
1274    "      Dismiss",
1275    "",
1276    "You can make adjustments to the region of interest by moving",
1277    "the pointer to one of the rectangle corners, pressing a",
1278    "button, and dragging.  Finally, choose an image processing",
1279    "technique from the Command widget.  You can choose more than",
1280    "one image processing technique to apply to an area.",
1281    "Alternatively, you can move the region of interest before",
1282    "applying another image processing technique.  To exit, press",
1283    "Dismiss.",
1284    (char *) NULL,
1285  },
1286  *ImageRotateHelp[] =
1287  {
1288    "In rotate mode, the Command widget has these options:",
1289    "",
1290    "    Pixel Color",
1291    "      black",
1292    "      blue",
1293    "      cyan",
1294    "      green",
1295    "      gray",
1296    "      red",
1297    "      magenta",
1298    "      yellow",
1299    "      white",
1300    "      Browser...",
1301    "    Direction",
1302    "      horizontal",
1303    "      vertical",
1304    "    Help",
1305    "    Dismiss",
1306    "",
1307    "Choose a background color from the Pixel Color sub-menu.",
1308    "Additional background colors can be specified with the color",
1309    "browser.  You can change the menu colors by setting the X",
1310    "resources pen1 through pen9.",
1311    "",
1312    "If you choose the color browser and press Grab, you can",
1313    "select the background color by moving the pointer to the",
1314    "desired color on the screen and press any button.",
1315    "",
1316    "Choose a point in the image window and press this button and",
1317    "hold.  Next, move the pointer to another location in the",
1318    "image.  As you move a line connects the initial location and",
1319    "the pointer.  When you release the button, the degree of",
1320    "image rotation is determined by the slope of the line you",
1321    "just drew.  The slope is relative to the direction you",
1322    "choose from the Direction sub-menu of the Command widget.",
1323    "",
1324    "To cancel the image rotation, move the pointer back to the",
1325    "starting point of the line and release the button.",
1326    (char *) NULL,
1327  };
1328
1329/*
1330  Enumeration declarations.
1331*/
1332typedef enum
1333{
1334  CopyMode,
1335  CropMode,
1336  CutMode
1337} ClipboardMode;
1338
1339typedef enum
1340{
1341  OpenCommand,
1342  NextCommand,
1343  FormerCommand,
1344  SelectCommand,
1345  SaveCommand,
1346  PrintCommand,
1347  DeleteCommand,
1348  NewCommand,
1349  VisualDirectoryCommand,
1350  QuitCommand,
1351  UndoCommand,
1352  RedoCommand,
1353  CutCommand,
1354  CopyCommand,
1355  PasteCommand,
1356  HalfSizeCommand,
1357  OriginalSizeCommand,
1358  DoubleSizeCommand,
1359  ResizeCommand,
1360  ApplyCommand,
1361  RefreshCommand,
1362  RestoreCommand,
1363  CropCommand,
1364  ChopCommand,
1365  FlopCommand,
1366  FlipCommand,
1367  RotateRightCommand,
1368  RotateLeftCommand,
1369  RotateCommand,
1370  ShearCommand,
1371  RollCommand,
1372  TrimCommand,
1373  HueCommand,
1374  SaturationCommand,
1375  BrightnessCommand,
1376  GammaCommand,
1377  SpiffCommand,
1378  DullCommand,
1379  ContrastStretchCommand,
1380  SigmoidalContrastCommand,
1381  NormalizeCommand,
1382  EqualizeCommand,
1383  NegateCommand,
1384  GrayscaleCommand,
1385  MapCommand,
1386  QuantizeCommand,
1387  DespeckleCommand,
1388  EmbossCommand,
1389  ReduceNoiseCommand,
1390  AddNoiseCommand,
1391  SharpenCommand,
1392  BlurCommand,
1393  ThresholdCommand,
1394  EdgeDetectCommand,
1395  SpreadCommand,
1396  ShadeCommand,
1397  RaiseCommand,
1398  SegmentCommand,
1399  SolarizeCommand,
1400  SepiaToneCommand,
1401  SwirlCommand,
1402  ImplodeCommand,
1403  VignetteCommand,
1404  WaveCommand,
1405  OilPaintCommand,
1406  CharcoalDrawCommand,
1407  AnnotateCommand,
1408  DrawCommand,
1409  ColorCommand,
1410  MatteCommand,
1411  CompositeCommand,
1412  AddBorderCommand,
1413  AddFrameCommand,
1414  CommentCommand,
1415  LaunchCommand,
1416  RegionofInterestCommand,
1417  ROIHelpCommand,
1418  ROIDismissCommand,
1419  InfoCommand,
1420  ZoomCommand,
1421  ShowPreviewCommand,
1422  ShowHistogramCommand,
1423  ShowMatteCommand,
1424  BackgroundCommand,
1425  SlideShowCommand,
1426  PreferencesCommand,
1427  HelpCommand,
1428  BrowseDocumentationCommand,
1429  VersionCommand,
1430  SaveToUndoBufferCommand,
1431  FreeBuffersCommand,
1432  NullCommand
1433} CommandType;
1434
1435typedef enum
1436{
1437  AnnotateNameCommand,
1438  AnnotateFontColorCommand,
1439  AnnotateBackgroundColorCommand,
1440  AnnotateRotateCommand,
1441  AnnotateHelpCommand,
1442  AnnotateDismissCommand,
1443  TextHelpCommand,
1444  TextApplyCommand,
1445  ChopDirectionCommand,
1446  ChopHelpCommand,
1447  ChopDismissCommand,
1448  HorizontalChopCommand,
1449  VerticalChopCommand,
1450  ColorEditMethodCommand,
1451  ColorEditColorCommand,
1452  ColorEditBorderCommand,
1453  ColorEditFuzzCommand,
1454  ColorEditUndoCommand,
1455  ColorEditHelpCommand,
1456  ColorEditDismissCommand,
1457  CompositeOperatorsCommand,
1458  CompositeDissolveCommand,
1459  CompositeDisplaceCommand,
1460  CompositeHelpCommand,
1461  CompositeDismissCommand,
1462  CropHelpCommand,
1463  CropDismissCommand,
1464  RectifyCopyCommand,
1465  RectifyHelpCommand,
1466  RectifyDismissCommand,
1467  DrawElementCommand,
1468  DrawColorCommand,
1469  DrawStippleCommand,
1470  DrawWidthCommand,
1471  DrawUndoCommand,
1472  DrawHelpCommand,
1473  DrawDismissCommand,
1474  MatteEditMethod,
1475  MatteEditBorderCommand,
1476  MatteEditFuzzCommand,
1477  MatteEditValueCommand,
1478  MatteEditUndoCommand,
1479  MatteEditHelpCommand,
1480  MatteEditDismissCommand,
1481  PasteOperatorsCommand,
1482  PasteHelpCommand,
1483  PasteDismissCommand,
1484  RotateColorCommand,
1485  RotateDirectionCommand,
1486  RotateCropCommand,
1487  RotateSharpenCommand,
1488  RotateHelpCommand,
1489  RotateDismissCommand,
1490  HorizontalRotateCommand,
1491  VerticalRotateCommand,
1492  TileLoadCommand,
1493  TileNextCommand,
1494  TileFormerCommand,
1495  TileDeleteCommand,
1496  TileUpdateCommand
1497} ModeType;
1498
1499/*
1500  Stipples.
1501*/
1502#define BricksWidth  20
1503#define BricksHeight  20
1504#define DiagonalWidth  16
1505#define DiagonalHeight  16
1506#define HighlightWidth  8
1507#define HighlightHeight  8
1508#define OpaqueWidth  8
1509#define OpaqueHeight  8
1510#define ScalesWidth  16
1511#define ScalesHeight  16
1512#define ShadowWidth  8
1513#define ShadowHeight  8
1514#define VerticalWidth  16
1515#define VerticalHeight  16
1516#define WavyWidth  16
1517#define WavyHeight  16
1518
1519/*
1520  Constant declaration.
1521*/
1522static const int
1523  RoiDelta = 8;
1524
1525static const unsigned char
1526  BricksBitmap[] =
1527  {
1528    0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00,
1529    0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01,
1530    0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00,
1531    0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f,
1532    0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01
1533  },
1534  DiagonalBitmap[] =
1535  {
1536    0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88,
1537    0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22,
1538    0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22
1539  },
1540  ScalesBitmap[] =
1541  {
1542    0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3, 0x80, 0x80, 0x80, 0x80,
1543    0x41, 0x41, 0x3e, 0x3e, 0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3,
1544    0x80, 0x80, 0x80, 0x80, 0x41, 0x41, 0x3e, 0x3e
1545  },
1546  VerticalBitmap[] =
1547  {
1548    0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1549    0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1550    0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11
1551  },
1552  WavyBitmap[] =
1553  {
1554    0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfb, 0xff,
1555    0xe7, 0xff, 0x1f, 0xff, 0xff, 0xf8, 0xff, 0xe7, 0xff, 0xdf, 0xff, 0xbf,
1556    0xff, 0xbf, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f
1557  };
1558
1559/*
1560  Function prototypes.
1561*/
1562static CommandType
1563  XImageWindowCommand(Display *,XResourceInfo *,XWindows *,
1564    const MagickStatusType,KeySym,Image **);
1565
1566static Image
1567  *XMagickCommand(Display *,XResourceInfo *,XWindows *,const CommandType,
1568    Image **),
1569  *XOpenImage(Display *,XResourceInfo *,XWindows *,const MagickBooleanType),
1570  *XTileImage(Display *,XResourceInfo *,XWindows *,Image *,XEvent *),
1571  *XVisualDirectoryImage(Display *,XResourceInfo *,XWindows *);
1572
1573static MagickBooleanType
1574  XAnnotateEditImage(Display *,XResourceInfo *,XWindows *,Image *),
1575  XDrawEditImage(Display *,XResourceInfo *,XWindows *,Image **),
1576  XChopImage(Display *,XResourceInfo *,XWindows *,Image **),
1577  XCropImage(Display *,XResourceInfo *,XWindows *,Image *,const ClipboardMode),
1578  XBackgroundImage(Display *,XResourceInfo *,XWindows *,Image **),
1579  XColorEditImage(Display *,XResourceInfo *,XWindows *,Image **),
1580  XCompositeImage(Display *,XResourceInfo *,XWindows *,Image *),
1581  XConfigureImage(Display *,XResourceInfo *,XWindows *,Image *),
1582  XMatteEditImage(Display *,XResourceInfo *,XWindows *,Image **),
1583  XPasteImage(Display *,XResourceInfo *,XWindows *,Image *),
1584  XPrintImage(Display *,XResourceInfo *,XWindows *,Image *),
1585  XRotateImage(Display *,XResourceInfo *,XWindows *,double,Image **),
1586  XROIImage(Display *,XResourceInfo *,XWindows *,Image **),
1587  XSaveImage(Display *,XResourceInfo *,XWindows *,Image *),
1588  XTrimImage(Display *,XResourceInfo *,XWindows *,Image *);
1589
1590static void
1591  XDrawPanRectangle(Display *,XWindows *),
1592  XImageCache(Display *,XResourceInfo *,XWindows *,const CommandType,Image **),
1593  XMagnifyImage(Display *,XWindows *,XEvent *),
1594  XMakePanImage(Display *,XResourceInfo *,XWindows *,Image *),
1595  XPanImage(Display *,XWindows *,XEvent *),
1596  XMagnifyWindowCommand(Display *,XWindows *,const MagickStatusType,
1597    const KeySym),
1598  XSetCropGeometry(Display *,XWindows *,RectangleInfo *,Image *),
1599  XScreenEvent(Display *,XWindows *,XEvent *),
1600  XTranslateImage(Display *,XWindows *,Image *,const KeySym);
1601
1602/*
1603%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1604%                                                                             %
1605%                                                                             %
1606%                                                                             %
1607%   D i s p l a y I m a g e s                                                 %
1608%                                                                             %
1609%                                                                             %
1610%                                                                             %
1611%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1612%
1613%  DisplayImages() displays an image sequence to any X window screen.  It
1614%  returns a value other than 0 if successful.  Check the exception member
1615%  of image to determine the reason for any failure.
1616%
1617%  The format of the DisplayImages method is:
1618%
1619%      MagickBooleanType DisplayImages(const ImageInfo *image_info,
1620%        Image *images)
1621%
1622%  A description of each parameter follows:
1623%
1624%    o image_info: the image info.
1625%
1626%    o image: the image.
1627%
1628*/
1629MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
1630  Image *images)
1631{
1632  char
1633    *argv[1];
1634
1635  Display
1636    *display;
1637
1638  Image
1639    *image;
1640
1641  register ssize_t
1642    i;
1643
1644  size_t
1645    state;
1646
1647  XrmDatabase
1648    resource_database;
1649
1650  XResourceInfo
1651    resource_info;
1652
1653  assert(image_info != (const ImageInfo *) NULL);
1654  assert(image_info->signature == MagickSignature);
1655  assert(images != (Image *) NULL);
1656  assert(images->signature == MagickSignature);
1657  if (images->debug != MagickFalse)
1658    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
1659  display=XOpenDisplay(image_info->server_name);
1660  if (display == (Display *) NULL)
1661    {
1662      (void) ThrowMagickException(&images->exception,GetMagickModule(),
1663        XServerError,"UnableToOpenXServer","`%s'",XDisplayName(
1664        image_info->server_name));
1665      return(MagickFalse);
1666    }
1667  if (images->exception.severity != UndefinedException)
1668    CatchException(&images->exception);
1669  (void) XSetErrorHandler(XError);
1670  resource_database=XGetResourceDatabase(display,GetClientName());
1671  (void) ResetMagickMemory(&resource_info,0,sizeof(resource_info));
1672  XGetResourceInfo(image_info,resource_database,GetClientName(),&resource_info);
1673  if (image_info->page != (char *) NULL)
1674    resource_info.image_geometry=AcquireString(image_info->page);
1675  resource_info.immutable=MagickTrue;
1676  argv[0]=AcquireString(GetClientName());
1677  state=DefaultState;
1678  for (i=0; (state & ExitState) == 0; i++)
1679  {
1680    if ((images->iterations != 0) && (i >= (ssize_t) images->iterations))
1681      break;
1682    image=GetImageFromList(images,i % GetImageListLength(images));
1683    (void) XDisplayImage(display,&resource_info,argv,1,&image,&state);
1684  }
1685  argv[0]=DestroyString(argv[0]);
1686  (void) XCloseDisplay(display);
1687  XDestroyResourceInfo(&resource_info);
1688  if (images->exception.severity != UndefinedException)
1689    return(MagickFalse);
1690  return(MagickTrue);
1691}
1692
1693/*
1694%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1695%                                                                             %
1696%                                                                             %
1697%                                                                             %
1698%   R e m o t e D i s p l a y C o m m a n d                                   %
1699%                                                                             %
1700%                                                                             %
1701%                                                                             %
1702%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1703%
1704%  RemoteDisplayCommand() encourages a remote display program to display the
1705%  specified image filename.
1706%
1707%  The format of the RemoteDisplayCommand method is:
1708%
1709%      MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1710%        const char *window,const char *filename,ExceptionInfo *exception)
1711%
1712%  A description of each parameter follows:
1713%
1714%    o image_info: the image info.
1715%
1716%    o window: Specifies the name or id of an X window.
1717%
1718%    o filename: the name of the image filename to display.
1719%
1720%    o exception: return any errors or warnings in this structure.
1721%
1722*/
1723MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1724  const char *window,const char *filename,ExceptionInfo *exception)
1725{
1726  Display
1727    *display;
1728
1729  MagickStatusType
1730    status;
1731
1732  assert(image_info != (const ImageInfo *) NULL);
1733  assert(image_info->signature == MagickSignature);
1734  assert(filename != (char *) NULL);
1735  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
1736  display=XOpenDisplay(image_info->server_name);
1737  if (display == (Display *) NULL)
1738    {
1739      (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
1740        "UnableToOpenXServer","`%s'",XDisplayName(image_info->server_name));
1741      return(MagickFalse);
1742    }
1743  (void) XSetErrorHandler(XError);
1744  status=XRemoteCommand(display,window,filename);
1745  (void) XCloseDisplay(display);
1746  return(status != 0 ? MagickTrue : MagickFalse);
1747}
1748
1749/*
1750%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1751%                                                                             %
1752%                                                                             %
1753%                                                                             %
1754+   X A n n o t a t e E d i t I m a g e                                       %
1755%                                                                             %
1756%                                                                             %
1757%                                                                             %
1758%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1759%
1760%  XAnnotateEditImage() annotates the image with text.
1761%
1762%  The format of the XAnnotateEditImage method is:
1763%
1764%      MagickBooleanType XAnnotateEditImage(Display *display,
1765%        XResourceInfo *resource_info,XWindows *windows,Image *image)
1766%
1767%  A description of each parameter follows:
1768%
1769%    o display: Specifies a connection to an X server;  returned from
1770%      XOpenDisplay.
1771%
1772%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
1773%
1774%    o windows: Specifies a pointer to a XWindows structure.
1775%
1776%    o image: the image; returned from ReadImage.
1777%
1778*/
1779
1780static inline ssize_t MagickMax(const ssize_t x,const ssize_t y)
1781{
1782  if (x > y)
1783    return(x);
1784  return(y);
1785}
1786
1787static inline ssize_t MagickMin(const ssize_t x,const ssize_t y)
1788{
1789  if (x < y)
1790    return(x);
1791  return(y);
1792}
1793
1794static MagickBooleanType XAnnotateEditImage(Display *display,
1795  XResourceInfo *resource_info,XWindows *windows,Image *image)
1796{
1797  static const char
1798    *AnnotateMenu[] =
1799    {
1800      "Font Name",
1801      "Font Color",
1802      "Box Color",
1803      "Rotate Text",
1804      "Help",
1805      "Dismiss",
1806      (char *) NULL
1807    },
1808    *TextMenu[] =
1809    {
1810      "Help",
1811      "Apply",
1812      (char *) NULL
1813    };
1814
1815  static const ModeType
1816    AnnotateCommands[] =
1817    {
1818      AnnotateNameCommand,
1819      AnnotateFontColorCommand,
1820      AnnotateBackgroundColorCommand,
1821      AnnotateRotateCommand,
1822      AnnotateHelpCommand,
1823      AnnotateDismissCommand
1824    },
1825    TextCommands[] =
1826    {
1827      TextHelpCommand,
1828      TextApplyCommand
1829    };
1830
1831  static MagickBooleanType
1832    transparent_box = MagickTrue,
1833    transparent_pen = MagickFalse;
1834
1835  static MagickRealType
1836    degrees = 0.0;
1837
1838  static unsigned int
1839    box_id = MaxNumberPens-2,
1840    font_id = 0,
1841    pen_id = 0;
1842
1843  char
1844    command[MaxTextExtent],
1845    text[MaxTextExtent];
1846
1847  const char
1848    *ColorMenu[MaxNumberPens+1];
1849
1850  Cursor
1851    cursor;
1852
1853  GC
1854    annotate_context;
1855
1856  int
1857    id,
1858    pen_number,
1859    status,
1860    x,
1861    y;
1862
1863  KeySym
1864    key_symbol;
1865
1866  register char
1867    *p;
1868
1869  register ssize_t
1870    i;
1871
1872  unsigned int
1873    height,
1874    width;
1875
1876  size_t
1877    state;
1878
1879  XAnnotateInfo
1880    *annotate_info,
1881    *previous_info;
1882
1883  XColor
1884    color;
1885
1886  XFontStruct
1887    *font_info;
1888
1889  XEvent
1890    event,
1891    text_event;
1892
1893  /*
1894    Map Command widget.
1895  */
1896  (void) CloneString(&windows->command.name,"Annotate");
1897  windows->command.data=4;
1898  (void) XCommandWidget(display,windows,AnnotateMenu,(XEvent *) NULL);
1899  (void) XMapRaised(display,windows->command.id);
1900  XClientMessage(display,windows->image.id,windows->im_protocols,
1901    windows->im_update_widget,CurrentTime);
1902  /*
1903    Track pointer until button 1 is pressed.
1904  */
1905  XQueryPosition(display,windows->image.id,&x,&y);
1906  (void) XSelectInput(display,windows->image.id,
1907    windows->image.attributes.event_mask | PointerMotionMask);
1908  cursor=XCreateFontCursor(display,XC_left_side);
1909  (void) XCheckDefineCursor(display,windows->image.id,cursor);
1910  state=DefaultState;
1911  do
1912  {
1913    if (windows->info.mapped != MagickFalse)
1914      {
1915        /*
1916          Display pointer position.
1917        */
1918        (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
1919          x+windows->image.x,y+windows->image.y);
1920        XInfoWidget(display,windows,text);
1921      }
1922    /*
1923      Wait for next event.
1924    */
1925    XScreenEvent(display,windows,&event);
1926    if (event.xany.window == windows->command.id)
1927      {
1928        /*
1929          Select a command from the Command widget.
1930        */
1931        id=XCommandWidget(display,windows,AnnotateMenu,&event);
1932        (void) XCheckDefineCursor(display,windows->image.id,cursor);
1933        if (id < 0)
1934          continue;
1935        switch (AnnotateCommands[id])
1936        {
1937          case AnnotateNameCommand:
1938          {
1939            const char
1940              *FontMenu[MaxNumberFonts];
1941
1942            int
1943              font_number;
1944
1945            /*
1946              Initialize menu selections.
1947            */
1948            for (i=0; i < MaxNumberFonts; i++)
1949              FontMenu[i]=resource_info->font_name[i];
1950            FontMenu[MaxNumberFonts-2]="Browser...";
1951            FontMenu[MaxNumberFonts-1]=(const char *) NULL;
1952            /*
1953              Select a font name from the pop-up menu.
1954            */
1955            font_number=XMenuWidget(display,windows,AnnotateMenu[id],
1956              (const char **) FontMenu,command);
1957            if (font_number < 0)
1958              break;
1959            if (font_number == (MaxNumberFonts-2))
1960              {
1961                static char
1962                  font_name[MaxTextExtent] = "fixed";
1963
1964                /*
1965                  Select a font name from a browser.
1966                */
1967                resource_info->font_name[font_number]=font_name;
1968                XFontBrowserWidget(display,windows,"Select",font_name);
1969                if (*font_name == '\0')
1970                  break;
1971              }
1972            /*
1973              Initialize font info.
1974            */
1975            font_info=XLoadQueryFont(display,resource_info->font_name[
1976              font_number]);
1977            if (font_info == (XFontStruct *) NULL)
1978              {
1979                XNoticeWidget(display,windows,"Unable to load font:",
1980                  resource_info->font_name[font_number]);
1981                break;
1982              }
1983            font_id=(unsigned int) font_number;
1984            (void) XFreeFont(display,font_info);
1985            break;
1986          }
1987          case AnnotateFontColorCommand:
1988          {
1989            /*
1990              Initialize menu selections.
1991            */
1992            for (i=0; i < (int) (MaxNumberPens-2); i++)
1993              ColorMenu[i]=resource_info->pen_colors[i];
1994            ColorMenu[MaxNumberPens-2]="transparent";
1995            ColorMenu[MaxNumberPens-1]="Browser...";
1996            ColorMenu[MaxNumberPens]=(const char *) NULL;
1997            /*
1998              Select a pen color from the pop-up menu.
1999            */
2000            pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
2001              (const char **) ColorMenu,command);
2002            if (pen_number < 0)
2003              break;
2004            transparent_pen=pen_number == (MaxNumberPens-2) ? MagickTrue :
2005              MagickFalse;
2006            if (transparent_pen != MagickFalse)
2007              break;
2008            if (pen_number == (MaxNumberPens-1))
2009              {
2010                static char
2011                  color_name[MaxTextExtent] = "gray";
2012
2013                /*
2014                  Select a pen color from a dialog.
2015                */
2016                resource_info->pen_colors[pen_number]=color_name;
2017                XColorBrowserWidget(display,windows,"Select",color_name);
2018                if (*color_name == '\0')
2019                  break;
2020              }
2021            /*
2022              Set pen color.
2023            */
2024            (void) XParseColor(display,windows->map_info->colormap,
2025              resource_info->pen_colors[pen_number],&color);
2026            XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
2027              (unsigned int) MaxColors,&color);
2028            windows->pixel_info->pen_colors[pen_number]=color;
2029            pen_id=(unsigned int) pen_number;
2030            break;
2031          }
2032          case AnnotateBackgroundColorCommand:
2033          {
2034            /*
2035              Initialize menu selections.
2036            */
2037            for (i=0; i < (int) (MaxNumberPens-2); i++)
2038              ColorMenu[i]=resource_info->pen_colors[i];
2039            ColorMenu[MaxNumberPens-2]="transparent";
2040            ColorMenu[MaxNumberPens-1]="Browser...";
2041            ColorMenu[MaxNumberPens]=(const char *) NULL;
2042            /*
2043              Select a pen color from the pop-up menu.
2044            */
2045            pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
2046              (const char **) ColorMenu,command);
2047            if (pen_number < 0)
2048              break;
2049            transparent_box=pen_number == (MaxNumberPens-2) ? MagickTrue :
2050              MagickFalse;
2051            if (transparent_box != MagickFalse)
2052              break;
2053            if (pen_number == (MaxNumberPens-1))
2054              {
2055                static char
2056                  color_name[MaxTextExtent] = "gray";
2057
2058                /*
2059                  Select a pen color from a dialog.
2060                */
2061                resource_info->pen_colors[pen_number]=color_name;
2062                XColorBrowserWidget(display,windows,"Select",color_name);
2063                if (*color_name == '\0')
2064                  break;
2065              }
2066            /*
2067              Set pen color.
2068            */
2069            (void) XParseColor(display,windows->map_info->colormap,
2070              resource_info->pen_colors[pen_number],&color);
2071            XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
2072              (unsigned int) MaxColors,&color);
2073            windows->pixel_info->pen_colors[pen_number]=color;
2074            box_id=(unsigned int) pen_number;
2075            break;
2076          }
2077          case AnnotateRotateCommand:
2078          {
2079            int
2080              entry;
2081
2082            static char
2083              angle[MaxTextExtent] = "30.0";
2084
2085            static const char
2086              *RotateMenu[] =
2087              {
2088                "-90",
2089                "-45",
2090                "-30",
2091                "0",
2092                "30",
2093                "45",
2094                "90",
2095                "180",
2096                "Dialog...",
2097                (char *) NULL,
2098              };
2099
2100            /*
2101              Select a command from the pop-up menu.
2102            */
2103            entry=XMenuWidget(display,windows,AnnotateMenu[id],RotateMenu,
2104              command);
2105            if (entry < 0)
2106              break;
2107            if (entry != 8)
2108              {
2109                degrees=InterpretLocaleValue(RotateMenu[entry],(char **) NULL);
2110                break;
2111              }
2112            (void) XDialogWidget(display,windows,"OK","Enter rotation angle:",
2113              angle);
2114            if (*angle == '\0')
2115              break;
2116            degrees=InterpretLocaleValue(angle,(char **) NULL);
2117            break;
2118          }
2119          case AnnotateHelpCommand:
2120          {
2121            XTextViewWidget(display,resource_info,windows,MagickFalse,
2122              "Help Viewer - Image Annotation",ImageAnnotateHelp);
2123            break;
2124          }
2125          case AnnotateDismissCommand:
2126          {
2127            /*
2128              Prematurely exit.
2129            */
2130            state|=EscapeState;
2131            state|=ExitState;
2132            break;
2133          }
2134          default:
2135            break;
2136        }
2137        continue;
2138      }
2139    switch (event.type)
2140    {
2141      case ButtonPress:
2142      {
2143        if (event.xbutton.button != Button1)
2144          break;
2145        if (event.xbutton.window != windows->image.id)
2146          break;
2147        /*
2148          Change to text entering mode.
2149        */
2150        x=event.xbutton.x;
2151        y=event.xbutton.y;
2152        state|=ExitState;
2153        break;
2154      }
2155      case ButtonRelease:
2156        break;
2157      case Expose:
2158        break;
2159      case KeyPress:
2160      {
2161        if (event.xkey.window != windows->image.id)
2162          break;
2163        /*
2164          Respond to a user key press.
2165        */
2166        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2167          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2168        switch ((int) key_symbol)
2169        {
2170          case XK_Escape:
2171          case XK_F20:
2172          {
2173            /*
2174              Prematurely exit.
2175            */
2176            state|=EscapeState;
2177            state|=ExitState;
2178            break;
2179          }
2180          case XK_F1:
2181          case XK_Help:
2182          {
2183            XTextViewWidget(display,resource_info,windows,MagickFalse,
2184              "Help Viewer - Image Annotation",ImageAnnotateHelp);
2185            break;
2186          }
2187          default:
2188          {
2189            (void) XBell(display,0);
2190            break;
2191          }
2192        }
2193        break;
2194      }
2195      case MotionNotify:
2196      {
2197        /*
2198          Map and unmap Info widget as cursor crosses its boundaries.
2199        */
2200        x=event.xmotion.x;
2201        y=event.xmotion.y;
2202        if (windows->info.mapped != MagickFalse)
2203          {
2204            if ((x < (int) (windows->info.x+windows->info.width)) &&
2205                (y < (int) (windows->info.y+windows->info.height)))
2206              (void) XWithdrawWindow(display,windows->info.id,
2207                windows->info.screen);
2208          }
2209        else
2210          if ((x > (int) (windows->info.x+windows->info.width)) ||
2211              (y > (int) (windows->info.y+windows->info.height)))
2212            (void) XMapWindow(display,windows->info.id);
2213        break;
2214      }
2215      default:
2216        break;
2217    }
2218  } while ((state & ExitState) == 0);
2219  (void) XSelectInput(display,windows->image.id,
2220    windows->image.attributes.event_mask);
2221  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
2222  if ((state & EscapeState) != 0)
2223    return(MagickTrue);
2224  /*
2225    Set font info and check boundary conditions.
2226  */
2227  font_info=XLoadQueryFont(display,resource_info->font_name[font_id]);
2228  if (font_info == (XFontStruct *) NULL)
2229    {
2230      XNoticeWidget(display,windows,"Unable to load font:",
2231        resource_info->font_name[font_id]);
2232      font_info=windows->font_info;
2233    }
2234  if ((x+font_info->max_bounds.width) >= (int) windows->image.width)
2235    x=(int) windows->image.width-font_info->max_bounds.width;
2236  if (y < (int) (font_info->ascent+font_info->descent))
2237    y=(int) font_info->ascent+font_info->descent;
2238  if (((int) font_info->max_bounds.width > (int) windows->image.width) ||
2239      ((font_info->ascent+font_info->descent) >= (int) windows->image.height))
2240    return(MagickFalse);
2241  /*
2242    Initialize annotate structure.
2243  */
2244  annotate_info=(XAnnotateInfo *) AcquireMagickMemory(sizeof(*annotate_info));
2245  if (annotate_info == (XAnnotateInfo *) NULL)
2246    return(MagickFalse);
2247  XGetAnnotateInfo(annotate_info);
2248  annotate_info->x=x;
2249  annotate_info->y=y;
2250  if ((transparent_box == MagickFalse) && (transparent_pen == MagickFalse))
2251    annotate_info->stencil=OpaqueStencil;
2252  else
2253    if (transparent_box == MagickFalse)
2254      annotate_info->stencil=BackgroundStencil;
2255    else
2256      annotate_info->stencil=ForegroundStencil;
2257  annotate_info->height=(unsigned int) font_info->ascent+font_info->descent;
2258  annotate_info->degrees=degrees;
2259  annotate_info->font_info=font_info;
2260  annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2261    windows->image.width/MagickMax((ssize_t) font_info->min_bounds.width,1)+2UL,
2262    sizeof(*annotate_info->text));
2263  if (annotate_info->text == (char *) NULL)
2264    return(MagickFalse);
2265  /*
2266    Create cursor and set graphic context.
2267  */
2268  cursor=XCreateFontCursor(display,XC_pencil);
2269  (void) XCheckDefineCursor(display,windows->image.id,cursor);
2270  annotate_context=windows->image.annotate_context;
2271  (void) XSetFont(display,annotate_context,font_info->fid);
2272  (void) XSetBackground(display,annotate_context,
2273    windows->pixel_info->pen_colors[box_id].pixel);
2274  (void) XSetForeground(display,annotate_context,
2275    windows->pixel_info->pen_colors[pen_id].pixel);
2276  /*
2277    Begin annotating the image with text.
2278  */
2279  (void) CloneString(&windows->command.name,"Text");
2280  windows->command.data=0;
2281  (void) XCommandWidget(display,windows,TextMenu,(XEvent *) NULL);
2282  state=DefaultState;
2283  (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2284  text_event.xexpose.width=(int) font_info->max_bounds.width;
2285  text_event.xexpose.height=font_info->max_bounds.ascent+
2286    font_info->max_bounds.descent;
2287  p=annotate_info->text;
2288  do
2289  {
2290    /*
2291      Display text cursor.
2292    */
2293    *p='\0';
2294    (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2295    /*
2296      Wait for next event.
2297    */
2298    XScreenEvent(display,windows,&event);
2299    if (event.xany.window == windows->command.id)
2300      {
2301        /*
2302          Select a command from the Command widget.
2303        */
2304        (void) XSetBackground(display,annotate_context,
2305          windows->pixel_info->background_color.pixel);
2306        (void) XSetForeground(display,annotate_context,
2307          windows->pixel_info->foreground_color.pixel);
2308        id=XCommandWidget(display,windows,AnnotateMenu,&event);
2309        (void) XSetBackground(display,annotate_context,
2310          windows->pixel_info->pen_colors[box_id].pixel);
2311        (void) XSetForeground(display,annotate_context,
2312          windows->pixel_info->pen_colors[pen_id].pixel);
2313        if (id < 0)
2314          continue;
2315        switch (TextCommands[id])
2316        {
2317          case TextHelpCommand:
2318          {
2319            XTextViewWidget(display,resource_info,windows,MagickFalse,
2320              "Help Viewer - Image Annotation",ImageAnnotateHelp);
2321            (void) XCheckDefineCursor(display,windows->image.id,cursor);
2322            break;
2323          }
2324          case TextApplyCommand:
2325          {
2326            /*
2327              Finished annotating.
2328            */
2329            annotate_info->width=(unsigned int) XTextWidth(font_info,
2330              annotate_info->text,(int) strlen(annotate_info->text));
2331            XRefreshWindow(display,&windows->image,&text_event);
2332            state|=ExitState;
2333            break;
2334          }
2335          default:
2336            break;
2337        }
2338        continue;
2339      }
2340    /*
2341      Erase text cursor.
2342    */
2343    text_event.xexpose.x=x;
2344    text_event.xexpose.y=y-font_info->max_bounds.ascent;
2345    (void) XClearArea(display,windows->image.id,x,text_event.xexpose.y,
2346      (unsigned int) text_event.xexpose.width,(unsigned int)
2347      text_event.xexpose.height,MagickFalse);
2348    XRefreshWindow(display,&windows->image,&text_event);
2349    switch (event.type)
2350    {
2351      case ButtonPress:
2352      {
2353        if (event.xbutton.window != windows->image.id)
2354          break;
2355        if (event.xbutton.button == Button2)
2356          {
2357            /*
2358              Request primary selection.
2359            */
2360            (void) XConvertSelection(display,XA_PRIMARY,XA_STRING,XA_STRING,
2361              windows->image.id,CurrentTime);
2362            break;
2363          }
2364        break;
2365      }
2366      case Expose:
2367      {
2368        if (event.xexpose.count == 0)
2369          {
2370            XAnnotateInfo
2371              *text_info;
2372
2373            /*
2374              Refresh Image window.
2375            */
2376            XRefreshWindow(display,&windows->image,(XEvent *) NULL);
2377            text_info=annotate_info;
2378            while (text_info != (XAnnotateInfo *) NULL)
2379            {
2380              if (annotate_info->stencil == ForegroundStencil)
2381                (void) XDrawString(display,windows->image.id,annotate_context,
2382                  text_info->x,text_info->y,text_info->text,
2383                  (int) strlen(text_info->text));
2384              else
2385                (void) XDrawImageString(display,windows->image.id,
2386                  annotate_context,text_info->x,text_info->y,text_info->text,
2387                  (int) strlen(text_info->text));
2388              text_info=text_info->previous;
2389            }
2390            (void) XDrawString(display,windows->image.id,annotate_context,
2391              x,y,"_",1);
2392          }
2393        break;
2394      }
2395      case KeyPress:
2396      {
2397        int
2398          length;
2399
2400        if (event.xkey.window != windows->image.id)
2401          break;
2402        /*
2403          Respond to a user key press.
2404        */
2405        length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
2406          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2407        *(command+length)='\0';
2408        if (((event.xkey.state & ControlMask) != 0) ||
2409            ((event.xkey.state & Mod1Mask) != 0))
2410          state|=ModifierState;
2411        if ((state & ModifierState) != 0)
2412          switch ((int) key_symbol)
2413          {
2414            case XK_u:
2415            case XK_U:
2416            {
2417              key_symbol=DeleteCommand;
2418              break;
2419            }
2420            default:
2421              break;
2422          }
2423        switch ((int) key_symbol)
2424        {
2425          case XK_BackSpace:
2426          {
2427            /*
2428              Erase one character.
2429            */
2430            if (p == annotate_info->text)
2431              {
2432                if (annotate_info->previous == (XAnnotateInfo *) NULL)
2433                  break;
2434                else
2435                  {
2436                    /*
2437                      Go to end of the previous line of text.
2438                    */
2439                    annotate_info=annotate_info->previous;
2440                    p=annotate_info->text;
2441                    x=annotate_info->x+annotate_info->width;
2442                    y=annotate_info->y;
2443                    if (annotate_info->width != 0)
2444                      p+=strlen(annotate_info->text);
2445                    break;
2446                  }
2447              }
2448            p--;
2449            x-=XTextWidth(font_info,p,1);
2450            text_event.xexpose.x=x;
2451            text_event.xexpose.y=y-font_info->max_bounds.ascent;
2452            XRefreshWindow(display,&windows->image,&text_event);
2453            break;
2454          }
2455          case XK_bracketleft:
2456          {
2457            key_symbol=XK_Escape;
2458            break;
2459          }
2460          case DeleteCommand:
2461          {
2462            /*
2463              Erase the entire line of text.
2464            */
2465            while (p != annotate_info->text)
2466            {
2467              p--;
2468              x-=XTextWidth(font_info,p,1);
2469              text_event.xexpose.x=x;
2470              XRefreshWindow(display,&windows->image,&text_event);
2471            }
2472            break;
2473          }
2474          case XK_Escape:
2475          case XK_F20:
2476          {
2477            /*
2478              Finished annotating.
2479            */
2480            annotate_info->width=(unsigned int) XTextWidth(font_info,
2481              annotate_info->text,(int) strlen(annotate_info->text));
2482            XRefreshWindow(display,&windows->image,&text_event);
2483            state|=ExitState;
2484            break;
2485          }
2486          default:
2487          {
2488            /*
2489              Draw a single character on the Image window.
2490            */
2491            if ((state & ModifierState) != 0)
2492              break;
2493            if (*command == '\0')
2494              break;
2495            *p=(*command);
2496            if (annotate_info->stencil == ForegroundStencil)
2497              (void) XDrawString(display,windows->image.id,annotate_context,
2498                x,y,p,1);
2499            else
2500              (void) XDrawImageString(display,windows->image.id,
2501                annotate_context,x,y,p,1);
2502            x+=XTextWidth(font_info,p,1);
2503            p++;
2504            if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2505              break;
2506          }
2507          case XK_Return:
2508          case XK_KP_Enter:
2509          {
2510            /*
2511              Advance to the next line of text.
2512            */
2513            *p='\0';
2514            annotate_info->width=(unsigned int) XTextWidth(font_info,
2515              annotate_info->text,(int) strlen(annotate_info->text));
2516            if (annotate_info->next != (XAnnotateInfo *) NULL)
2517              {
2518                /*
2519                  Line of text already exists.
2520                */
2521                annotate_info=annotate_info->next;
2522                x=annotate_info->x;
2523                y=annotate_info->y;
2524                p=annotate_info->text;
2525                break;
2526              }
2527            annotate_info->next=(XAnnotateInfo *) AcquireMagickMemory(
2528              sizeof(*annotate_info->next));
2529            if (annotate_info->next == (XAnnotateInfo *) NULL)
2530              return(MagickFalse);
2531            *annotate_info->next=(*annotate_info);
2532            annotate_info->next->previous=annotate_info;
2533            annotate_info=annotate_info->next;
2534            annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2535              windows->image.width/MagickMax((ssize_t)
2536              font_info->min_bounds.width,1)+2UL,sizeof(*annotate_info->text));
2537            if (annotate_info->text == (char *) NULL)
2538              return(MagickFalse);
2539            annotate_info->y+=annotate_info->height;
2540            if (annotate_info->y > (int) windows->image.height)
2541              annotate_info->y=(int) annotate_info->height;
2542            annotate_info->next=(XAnnotateInfo *) NULL;
2543            x=annotate_info->x;
2544            y=annotate_info->y;
2545            p=annotate_info->text;
2546            break;
2547          }
2548        }
2549        break;
2550      }
2551      case KeyRelease:
2552      {
2553        /*
2554          Respond to a user key release.
2555        */
2556        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2557          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2558        state&=(~ModifierState);
2559        break;
2560      }
2561      case SelectionNotify:
2562      {
2563        Atom
2564          type;
2565
2566        int
2567          format;
2568
2569        unsigned char
2570          *data;
2571
2572        unsigned long
2573          after,
2574          length;
2575
2576        /*
2577          Obtain response from primary selection.
2578        */
2579        if (event.xselection.property == (Atom) None)
2580          break;
2581        status=XGetWindowProperty(display,event.xselection.requestor,
2582          event.xselection.property,0L,(long) MaxTextExtent,True,XA_STRING,
2583          &type,&format,&length,&after,&data);
2584        if ((status != Success) || (type != XA_STRING) || (format == 32) ||
2585            (length == 0))
2586          break;
2587        /*
2588          Annotate Image window with primary selection.
2589        */
2590        for (i=0; i < (ssize_t) length; i++)
2591        {
2592          if ((char) data[i] != '\n')
2593            {
2594              /*
2595                Draw a single character on the Image window.
2596              */
2597              *p=(char) data[i];
2598              (void) XDrawString(display,windows->image.id,annotate_context,
2599                x,y,p,1);
2600              x+=XTextWidth(font_info,p,1);
2601              p++;
2602              if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2603                continue;
2604            }
2605          /*
2606            Advance to the next line of text.
2607          */
2608          *p='\0';
2609          annotate_info->width=(unsigned int) XTextWidth(font_info,
2610            annotate_info->text,(int) strlen(annotate_info->text));
2611          if (annotate_info->next != (XAnnotateInfo *) NULL)
2612            {
2613              /*
2614                Line of text already exists.
2615              */
2616              annotate_info=annotate_info->next;
2617              x=annotate_info->x;
2618              y=annotate_info->y;
2619              p=annotate_info->text;
2620              continue;
2621            }
2622          annotate_info->next=(XAnnotateInfo *) AcquireMagickMemory(
2623            sizeof(*annotate_info->next));
2624          if (annotate_info->next == (XAnnotateInfo *) NULL)
2625            return(MagickFalse);
2626          *annotate_info->next=(*annotate_info);
2627          annotate_info->next->previous=annotate_info;
2628          annotate_info=annotate_info->next;
2629          annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2630            windows->image.width/MagickMax((ssize_t)
2631            font_info->min_bounds.width,1)+2UL,sizeof(*annotate_info->text));
2632          if (annotate_info->text == (char *) NULL)
2633            return(MagickFalse);
2634          annotate_info->y+=annotate_info->height;
2635          if (annotate_info->y > (int) windows->image.height)
2636            annotate_info->y=(int) annotate_info->height;
2637          annotate_info->next=(XAnnotateInfo *) NULL;
2638          x=annotate_info->x;
2639          y=annotate_info->y;
2640          p=annotate_info->text;
2641        }
2642        (void) XFree((void *) data);
2643        break;
2644      }
2645      default:
2646        break;
2647    }
2648  } while ((state & ExitState) == 0);
2649  (void) XFreeCursor(display,cursor);
2650  /*
2651    Annotation is relative to image configuration.
2652  */
2653  width=(unsigned int) image->columns;
2654  height=(unsigned int) image->rows;
2655  x=0;
2656  y=0;
2657  if (windows->image.crop_geometry != (char *) NULL)
2658    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
2659  /*
2660    Initialize annotated image.
2661  */
2662  XSetCursorState(display,windows,MagickTrue);
2663  XCheckRefreshWindows(display,windows);
2664  while (annotate_info != (XAnnotateInfo *) NULL)
2665  {
2666    if (annotate_info->width == 0)
2667      {
2668        /*
2669          No text on this line--  go to the next line of text.
2670        */
2671        previous_info=annotate_info->previous;
2672        annotate_info->text=(char *)
2673          RelinquishMagickMemory(annotate_info->text);
2674        annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2675        annotate_info=previous_info;
2676        continue;
2677      }
2678    /*
2679      Determine pixel index for box and pen color.
2680    */
2681    windows->pixel_info->box_color=windows->pixel_info->pen_colors[box_id];
2682    if (windows->pixel_info->colors != 0)
2683      for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
2684        if (windows->pixel_info->pixels[i] ==
2685            windows->pixel_info->pen_colors[box_id].pixel)
2686          {
2687            windows->pixel_info->box_index=(unsigned short) i;
2688            break;
2689          }
2690    windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
2691    if (windows->pixel_info->colors != 0)
2692      for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
2693        if (windows->pixel_info->pixels[i] ==
2694            windows->pixel_info->pen_colors[pen_id].pixel)
2695          {
2696            windows->pixel_info->pen_index=(unsigned short) i;
2697            break;
2698          }
2699    /*
2700      Define the annotate geometry string.
2701    */
2702    annotate_info->x=(int)
2703      width*(annotate_info->x+windows->image.x)/windows->image.ximage->width;
2704    annotate_info->y=(int) height*(annotate_info->y-font_info->ascent+
2705      windows->image.y)/windows->image.ximage->height;
2706    (void) FormatLocaleString(annotate_info->geometry,MaxTextExtent,
2707      "%ux%u%+d%+d",width*annotate_info->width/windows->image.ximage->width,
2708      height*annotate_info->height/windows->image.ximage->height,
2709      annotate_info->x+x,annotate_info->y+y);
2710    /*
2711      Annotate image with text.
2712    */
2713    status=XAnnotateImage(display,windows->pixel_info,annotate_info,image);
2714    if (status == 0)
2715      return(MagickFalse);
2716    /*
2717      Free up memory.
2718    */
2719    previous_info=annotate_info->previous;
2720    annotate_info->text=DestroyString(annotate_info->text);
2721    annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2722    annotate_info=previous_info;
2723  }
2724  (void) XSetForeground(display,annotate_context,
2725    windows->pixel_info->foreground_color.pixel);
2726  (void) XSetBackground(display,annotate_context,
2727    windows->pixel_info->background_color.pixel);
2728  (void) XSetFont(display,annotate_context,windows->font_info->fid);
2729  XSetCursorState(display,windows,MagickFalse);
2730  (void) XFreeFont(display,font_info);
2731  /*
2732    Update image configuration.
2733  */
2734  XConfigureImageColormap(display,resource_info,windows,image);
2735  (void) XConfigureImage(display,resource_info,windows,image);
2736  return(MagickTrue);
2737}
2738
2739/*
2740%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2741%                                                                             %
2742%                                                                             %
2743%                                                                             %
2744+   X B a c k g r o u n d I m a g e                                           %
2745%                                                                             %
2746%                                                                             %
2747%                                                                             %
2748%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2749%
2750%  XBackgroundImage() displays the image in the background of a window.
2751%
2752%  The format of the XBackgroundImage method is:
2753%
2754%      MagickBooleanType XBackgroundImage(Display *display,
2755%        XResourceInfo *resource_info,XWindows *windows,Image **image)
2756%
2757%  A description of each parameter follows:
2758%
2759%    o display: Specifies a connection to an X server; returned from
2760%      XOpenDisplay.
2761%
2762%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2763%
2764%    o windows: Specifies a pointer to a XWindows structure.
2765%
2766%    o image: the image.
2767%
2768*/
2769static MagickBooleanType XBackgroundImage(Display *display,
2770  XResourceInfo *resource_info,XWindows *windows,Image **image)
2771{
2772#define BackgroundImageTag  "Background/Image"
2773
2774  int
2775    status;
2776
2777  static char
2778    window_id[MaxTextExtent] = "root";
2779
2780  XResourceInfo
2781    background_resources;
2782
2783  /*
2784    Put image in background.
2785  */
2786  status=XDialogWidget(display,windows,"Background",
2787    "Enter window id (id 0x00 selects window with pointer):",window_id);
2788  if (*window_id == '\0')
2789    return(MagickFalse);
2790  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
2791  XInfoWidget(display,windows,BackgroundImageTag);
2792  XSetCursorState(display,windows,MagickTrue);
2793  XCheckRefreshWindows(display,windows);
2794  background_resources=(*resource_info);
2795  background_resources.window_id=window_id;
2796  background_resources.backdrop=status != 0 ? MagickTrue : MagickFalse;
2797  status=XDisplayBackgroundImage(display,&background_resources,*image);
2798  if (status != MagickFalse)
2799    XClientMessage(display,windows->image.id,windows->im_protocols,
2800      windows->im_retain_colors,CurrentTime);
2801  XSetCursorState(display,windows,MagickFalse);
2802  (void) XMagickCommand(display,resource_info,windows,UndoCommand,image);
2803  return(MagickTrue);
2804}
2805
2806/*
2807%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2808%                                                                             %
2809%                                                                             %
2810%                                                                             %
2811+   X C h o p I m a g e                                                       %
2812%                                                                             %
2813%                                                                             %
2814%                                                                             %
2815%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2816%
2817%  XChopImage() chops the X image.
2818%
2819%  The format of the XChopImage method is:
2820%
2821%    MagickBooleanType XChopImage(Display *display,XResourceInfo *resource_info,
2822%      XWindows *windows,Image **image)
2823%
2824%  A description of each parameter follows:
2825%
2826%    o display: Specifies a connection to an X server; returned from
2827%      XOpenDisplay.
2828%
2829%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2830%
2831%    o windows: Specifies a pointer to a XWindows structure.
2832%
2833%    o image: the image.
2834%
2835*/
2836static MagickBooleanType XChopImage(Display *display,
2837  XResourceInfo *resource_info,XWindows *windows,Image **image)
2838{
2839  static const char
2840    *ChopMenu[] =
2841    {
2842      "Direction",
2843      "Help",
2844      "Dismiss",
2845      (char *) NULL
2846    };
2847
2848  static ModeType
2849    direction = HorizontalChopCommand;
2850
2851  static const ModeType
2852    ChopCommands[] =
2853    {
2854      ChopDirectionCommand,
2855      ChopHelpCommand,
2856      ChopDismissCommand
2857    },
2858    DirectionCommands[] =
2859    {
2860      HorizontalChopCommand,
2861      VerticalChopCommand
2862    };
2863
2864  char
2865    text[MaxTextExtent];
2866
2867  Image
2868    *chop_image;
2869
2870  int
2871    id,
2872    x,
2873    y;
2874
2875  MagickRealType
2876    scale_factor;
2877
2878  RectangleInfo
2879    chop_info;
2880
2881  unsigned int
2882    distance,
2883    height,
2884    width;
2885
2886  size_t
2887    state;
2888
2889  XEvent
2890    event;
2891
2892  XSegment
2893    segment_info;
2894
2895  /*
2896    Map Command widget.
2897  */
2898  (void) CloneString(&windows->command.name,"Chop");
2899  windows->command.data=1;
2900  (void) XCommandWidget(display,windows,ChopMenu,(XEvent *) NULL);
2901  (void) XMapRaised(display,windows->command.id);
2902  XClientMessage(display,windows->image.id,windows->im_protocols,
2903    windows->im_update_widget,CurrentTime);
2904  /*
2905    Track pointer until button 1 is pressed.
2906  */
2907  XQueryPosition(display,windows->image.id,&x,&y);
2908  (void) XSelectInput(display,windows->image.id,
2909    windows->image.attributes.event_mask | PointerMotionMask);
2910  state=DefaultState;
2911  do
2912  {
2913    if (windows->info.mapped != MagickFalse)
2914      {
2915        /*
2916          Display pointer position.
2917        */
2918        (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
2919          x+windows->image.x,y+windows->image.y);
2920        XInfoWidget(display,windows,text);
2921      }
2922    /*
2923      Wait for next event.
2924    */
2925    XScreenEvent(display,windows,&event);
2926    if (event.xany.window == windows->command.id)
2927      {
2928        /*
2929          Select a command from the Command widget.
2930        */
2931        id=XCommandWidget(display,windows,ChopMenu,&event);
2932        if (id < 0)
2933          continue;
2934        switch (ChopCommands[id])
2935        {
2936          case ChopDirectionCommand:
2937          {
2938            char
2939              command[MaxTextExtent];
2940
2941            static const char
2942              *Directions[] =
2943              {
2944                "horizontal",
2945                "vertical",
2946                (char *) NULL,
2947              };
2948
2949            /*
2950              Select a command from the pop-up menu.
2951            */
2952            id=XMenuWidget(display,windows,ChopMenu[id],Directions,command);
2953            if (id >= 0)
2954              direction=DirectionCommands[id];
2955            break;
2956          }
2957          case ChopHelpCommand:
2958          {
2959            XTextViewWidget(display,resource_info,windows,MagickFalse,
2960              "Help Viewer - Image Chop",ImageChopHelp);
2961            break;
2962          }
2963          case ChopDismissCommand:
2964          {
2965            /*
2966              Prematurely exit.
2967            */
2968            state|=EscapeState;
2969            state|=ExitState;
2970            break;
2971          }
2972          default:
2973            break;
2974        }
2975        continue;
2976      }
2977    switch (event.type)
2978    {
2979      case ButtonPress:
2980      {
2981        if (event.xbutton.button != Button1)
2982          break;
2983        if (event.xbutton.window != windows->image.id)
2984          break;
2985        /*
2986          User has committed to start point of chopping line.
2987        */
2988        segment_info.x1=(short int) event.xbutton.x;
2989        segment_info.x2=(short int) event.xbutton.x;
2990        segment_info.y1=(short int) event.xbutton.y;
2991        segment_info.y2=(short int) event.xbutton.y;
2992        state|=ExitState;
2993        break;
2994      }
2995      case ButtonRelease:
2996        break;
2997      case Expose:
2998        break;
2999      case KeyPress:
3000      {
3001        char
3002          command[MaxTextExtent];
3003
3004        KeySym
3005          key_symbol;
3006
3007        if (event.xkey.window != windows->image.id)
3008          break;
3009        /*
3010          Respond to a user key press.
3011        */
3012        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3013          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3014        switch ((int) key_symbol)
3015        {
3016          case XK_Escape:
3017          case XK_F20:
3018          {
3019            /*
3020              Prematurely exit.
3021            */
3022            state|=EscapeState;
3023            state|=ExitState;
3024            break;
3025          }
3026          case XK_F1:
3027          case XK_Help:
3028          {
3029            (void) XSetFunction(display,windows->image.highlight_context,
3030              GXcopy);
3031            XTextViewWidget(display,resource_info,windows,MagickFalse,
3032              "Help Viewer - Image Chop",ImageChopHelp);
3033            (void) XSetFunction(display,windows->image.highlight_context,
3034              GXinvert);
3035            break;
3036          }
3037          default:
3038          {
3039            (void) XBell(display,0);
3040            break;
3041          }
3042        }
3043        break;
3044      }
3045      case MotionNotify:
3046      {
3047        /*
3048          Map and unmap Info widget as text cursor crosses its boundaries.
3049        */
3050        x=event.xmotion.x;
3051        y=event.xmotion.y;
3052        if (windows->info.mapped != MagickFalse)
3053          {
3054            if ((x < (int) (windows->info.x+windows->info.width)) &&
3055                (y < (int) (windows->info.y+windows->info.height)))
3056              (void) XWithdrawWindow(display,windows->info.id,
3057                windows->info.screen);
3058          }
3059        else
3060          if ((x > (int) (windows->info.x+windows->info.width)) ||
3061              (y > (int) (windows->info.y+windows->info.height)))
3062            (void) XMapWindow(display,windows->info.id);
3063      }
3064    }
3065  } while ((state & ExitState) == 0);
3066  (void) XSelectInput(display,windows->image.id,
3067    windows->image.attributes.event_mask);
3068  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3069  if ((state & EscapeState) != 0)
3070    return(MagickTrue);
3071  /*
3072    Draw line as pointer moves until the mouse button is released.
3073  */
3074  chop_info.width=0;
3075  chop_info.height=0;
3076  chop_info.x=0;
3077  chop_info.y=0;
3078  distance=0;
3079  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
3080  state=DefaultState;
3081  do
3082  {
3083    if (distance > 9)
3084      {
3085        /*
3086          Display info and draw chopping line.
3087        */
3088        if (windows->info.mapped == MagickFalse)
3089          (void) XMapWindow(display,windows->info.id);
3090        (void) FormatLocaleString(text,MaxTextExtent,
3091          " %.20gx%.20g%+.20g%+.20g",(double) chop_info.width,(double)
3092          chop_info.height,(double) chop_info.x,(double) chop_info.y);
3093        XInfoWidget(display,windows,text);
3094        XHighlightLine(display,windows->image.id,
3095          windows->image.highlight_context,&segment_info);
3096      }
3097    else
3098      if (windows->info.mapped != MagickFalse)
3099        (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3100    /*
3101      Wait for next event.
3102    */
3103    XScreenEvent(display,windows,&event);
3104    if (distance > 9)
3105      XHighlightLine(display,windows->image.id,
3106        windows->image.highlight_context,&segment_info);
3107    switch (event.type)
3108    {
3109      case ButtonPress:
3110      {
3111        segment_info.x2=(short int) event.xmotion.x;
3112        segment_info.y2=(short int) event.xmotion.y;
3113        break;
3114      }
3115      case ButtonRelease:
3116      {
3117        /*
3118          User has committed to chopping line.
3119        */
3120        segment_info.x2=(short int) event.xbutton.x;
3121        segment_info.y2=(short int) event.xbutton.y;
3122        state|=ExitState;
3123        break;
3124      }
3125      case Expose:
3126        break;
3127      case MotionNotify:
3128      {
3129        segment_info.x2=(short int) event.xmotion.x;
3130        segment_info.y2=(short int) event.xmotion.y;
3131      }
3132      default:
3133        break;
3134    }
3135    /*
3136      Check boundary conditions.
3137    */
3138    if (segment_info.x2 < 0)
3139      segment_info.x2=0;
3140    else
3141      if (segment_info.x2 > windows->image.ximage->width)
3142        segment_info.x2=windows->image.ximage->width;
3143    if (segment_info.y2 < 0)
3144      segment_info.y2=0;
3145    else
3146      if (segment_info.y2 > windows->image.ximage->height)
3147        segment_info.y2=windows->image.ximage->height;
3148    distance=(unsigned int)
3149      (((segment_info.x2-segment_info.x1)*(segment_info.x2-segment_info.x1))+
3150       ((segment_info.y2-segment_info.y1)*(segment_info.y2-segment_info.y1)));
3151    /*
3152      Compute chopping geometry.
3153    */
3154    if (direction == HorizontalChopCommand)
3155      {
3156        chop_info.width=(size_t) (segment_info.x2-segment_info.x1+1);
3157        chop_info.x=(ssize_t) windows->image.x+segment_info.x1;
3158        chop_info.height=0;
3159        chop_info.y=0;
3160        if (segment_info.x1 > (int) segment_info.x2)
3161          {
3162            chop_info.width=(size_t) (segment_info.x1-segment_info.x2+1);
3163            chop_info.x=(ssize_t) windows->image.x+segment_info.x2;
3164          }
3165      }
3166    else
3167      {
3168        chop_info.width=0;
3169        chop_info.height=(size_t) (segment_info.y2-segment_info.y1+1);
3170        chop_info.x=0;
3171        chop_info.y=(ssize_t) windows->image.y+segment_info.y1;
3172        if (segment_info.y1 > segment_info.y2)
3173          {
3174            chop_info.height=(size_t) (segment_info.y1-segment_info.y2+1);
3175            chop_info.y=(ssize_t) windows->image.y+segment_info.y2;
3176          }
3177      }
3178  } while ((state & ExitState) == 0);
3179  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
3180  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3181  if (distance <= 9)
3182    return(MagickTrue);
3183  /*
3184    Image chopping is relative to image configuration.
3185  */
3186  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
3187  XSetCursorState(display,windows,MagickTrue);
3188  XCheckRefreshWindows(display,windows);
3189  windows->image.window_changes.width=windows->image.ximage->width-
3190    (unsigned int) chop_info.width;
3191  windows->image.window_changes.height=windows->image.ximage->height-
3192    (unsigned int) chop_info.height;
3193  width=(unsigned int) (*image)->columns;
3194  height=(unsigned int) (*image)->rows;
3195  x=0;
3196  y=0;
3197  if (windows->image.crop_geometry != (char *) NULL)
3198    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
3199  scale_factor=(MagickRealType) width/windows->image.ximage->width;
3200  chop_info.x+=x;
3201  chop_info.x=(ssize_t) (scale_factor*chop_info.x+0.5);
3202  chop_info.width=(unsigned int) (scale_factor*chop_info.width+0.5);
3203  scale_factor=(MagickRealType) height/windows->image.ximage->height;
3204  chop_info.y+=y;
3205  chop_info.y=(ssize_t) (scale_factor*chop_info.y+0.5);
3206  chop_info.height=(unsigned int) (scale_factor*chop_info.height+0.5);
3207  /*
3208    Chop image.
3209  */
3210  chop_image=ChopImage(*image,&chop_info,&(*image)->exception);
3211  XSetCursorState(display,windows,MagickFalse);
3212  if (chop_image == (Image *) NULL)
3213    return(MagickFalse);
3214  *image=DestroyImage(*image);
3215  *image=chop_image;
3216  /*
3217    Update image configuration.
3218  */
3219  XConfigureImageColormap(display,resource_info,windows,*image);
3220  (void) XConfigureImage(display,resource_info,windows,*image);
3221  return(MagickTrue);
3222}
3223
3224/*
3225%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3226%                                                                             %
3227%                                                                             %
3228%                                                                             %
3229+   X C o l o r E d i t I m a g e                                             %
3230%                                                                             %
3231%                                                                             %
3232%                                                                             %
3233%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3234%
3235%  XColorEditImage() allows the user to interactively change the color of one
3236%  pixel for a DirectColor image or one colormap entry for a PseudoClass image.
3237%
3238%  The format of the XColorEditImage method is:
3239%
3240%      MagickBooleanType XColorEditImage(Display *display,
3241%        XResourceInfo *resource_info,XWindows *windows,Image **image)
3242%
3243%  A description of each parameter follows:
3244%
3245%    o display: Specifies a connection to an X server;  returned from
3246%      XOpenDisplay.
3247%
3248%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3249%
3250%    o windows: Specifies a pointer to a XWindows structure.
3251%
3252%    o image: the image; returned from ReadImage.
3253%
3254*/
3255
3256
3257static MagickBooleanType XColorEditImage(Display *display,
3258  XResourceInfo *resource_info,XWindows *windows,Image **image)
3259{
3260  static const char
3261    *ColorEditMenu[] =
3262    {
3263      "Method",
3264      "Pixel Color",
3265      "Border Color",
3266      "Fuzz",
3267      "Undo",
3268      "Help",
3269      "Dismiss",
3270      (char *) NULL
3271    };
3272
3273  static const ModeType
3274    ColorEditCommands[] =
3275    {
3276      ColorEditMethodCommand,
3277      ColorEditColorCommand,
3278      ColorEditBorderCommand,
3279      ColorEditFuzzCommand,
3280      ColorEditUndoCommand,
3281      ColorEditHelpCommand,
3282      ColorEditDismissCommand
3283    };
3284
3285  static PaintMethod
3286    method = PointMethod;
3287
3288  static unsigned int
3289    pen_id = 0;
3290
3291  static XColor
3292    border_color = { 0, 0, 0, 0, 0, 0 };
3293
3294  char
3295    command[MaxTextExtent],
3296    text[MaxTextExtent];
3297
3298  Cursor
3299    cursor;
3300
3301  ExceptionInfo
3302    *exception;
3303
3304  int
3305    entry,
3306    id,
3307    x,
3308    x_offset,
3309    y,
3310    y_offset;
3311
3312  register Quantum
3313    *q;
3314
3315  register ssize_t
3316    i;
3317
3318  unsigned int
3319    height,
3320    width;
3321
3322  size_t
3323    state;
3324
3325  XColor
3326    color;
3327
3328  XEvent
3329    event;
3330
3331  /*
3332    Map Command widget.
3333  */
3334  (void) CloneString(&windows->command.name,"Color Edit");
3335  windows->command.data=4;
3336  (void) XCommandWidget(display,windows,ColorEditMenu,(XEvent *) NULL);
3337  (void) XMapRaised(display,windows->command.id);
3338  XClientMessage(display,windows->image.id,windows->im_protocols,
3339    windows->im_update_widget,CurrentTime);
3340  /*
3341    Make cursor.
3342  */
3343  cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
3344    resource_info->background_color,resource_info->foreground_color);
3345  (void) XCheckDefineCursor(display,windows->image.id,cursor);
3346  /*
3347    Track pointer until button 1 is pressed.
3348  */
3349  XQueryPosition(display,windows->image.id,&x,&y);
3350  (void) XSelectInput(display,windows->image.id,
3351    windows->image.attributes.event_mask | PointerMotionMask);
3352  state=DefaultState;
3353  do
3354  {
3355    if (windows->info.mapped != MagickFalse)
3356      {
3357        /*
3358          Display pointer position.
3359        */
3360        (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
3361          x+windows->image.x,y+windows->image.y);
3362        XInfoWidget(display,windows,text);
3363      }
3364    /*
3365      Wait for next event.
3366    */
3367    XScreenEvent(display,windows,&event);
3368    if (event.xany.window == windows->command.id)
3369      {
3370        /*
3371          Select a command from the Command widget.
3372        */
3373        id=XCommandWidget(display,windows,ColorEditMenu,&event);
3374        if (id < 0)
3375          {
3376            (void) XCheckDefineCursor(display,windows->image.id,cursor);
3377            continue;
3378          }
3379        switch (ColorEditCommands[id])
3380        {
3381          case ColorEditMethodCommand:
3382          {
3383            char
3384              **methods;
3385
3386            /*
3387              Select a method from the pop-up menu.
3388            */
3389            methods=(char **) GetCommandOptions(MagickMethodOptions);
3390            if (methods == (char **) NULL)
3391              break;
3392            entry=XMenuWidget(display,windows,ColorEditMenu[id],
3393              (const char **) methods,command);
3394            if (entry >= 0)
3395              method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
3396                MagickFalse,methods[entry]);
3397            methods=DestroyStringList(methods);
3398            break;
3399          }
3400          case ColorEditColorCommand:
3401          {
3402            const char
3403              *ColorMenu[MaxNumberPens];
3404
3405            int
3406              pen_number;
3407
3408            /*
3409              Initialize menu selections.
3410            */
3411            for (i=0; i < (int) (MaxNumberPens-2); i++)
3412              ColorMenu[i]=resource_info->pen_colors[i];
3413            ColorMenu[MaxNumberPens-2]="Browser...";
3414            ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3415            /*
3416              Select a pen color from the pop-up menu.
3417            */
3418            pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3419              (const char **) ColorMenu,command);
3420            if (pen_number < 0)
3421              break;
3422            if (pen_number == (MaxNumberPens-2))
3423              {
3424                static char
3425                  color_name[MaxTextExtent] = "gray";
3426
3427                /*
3428                  Select a pen color from a dialog.
3429                */
3430                resource_info->pen_colors[pen_number]=color_name;
3431                XColorBrowserWidget(display,windows,"Select",color_name);
3432                if (*color_name == '\0')
3433                  break;
3434              }
3435            /*
3436              Set pen color.
3437            */
3438            (void) XParseColor(display,windows->map_info->colormap,
3439              resource_info->pen_colors[pen_number],&color);
3440            XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
3441              (unsigned int) MaxColors,&color);
3442            windows->pixel_info->pen_colors[pen_number]=color;
3443            pen_id=(unsigned int) pen_number;
3444            break;
3445          }
3446          case ColorEditBorderCommand:
3447          {
3448            const char
3449              *ColorMenu[MaxNumberPens];
3450
3451            int
3452              pen_number;
3453
3454            /*
3455              Initialize menu selections.
3456            */
3457            for (i=0; i < (int) (MaxNumberPens-2); i++)
3458              ColorMenu[i]=resource_info->pen_colors[i];
3459            ColorMenu[MaxNumberPens-2]="Browser...";
3460            ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3461            /*
3462              Select a pen color from the pop-up menu.
3463            */
3464            pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3465              (const char **) ColorMenu,command);
3466            if (pen_number < 0)
3467              break;
3468            if (pen_number == (MaxNumberPens-2))
3469              {
3470                static char
3471                  color_name[MaxTextExtent] = "gray";
3472
3473                /*
3474                  Select a pen color from a dialog.
3475                */
3476                resource_info->pen_colors[pen_number]=color_name;
3477                XColorBrowserWidget(display,windows,"Select",color_name);
3478                if (*color_name == '\0')
3479                  break;
3480              }
3481            /*
3482              Set border color.
3483            */
3484            (void) XParseColor(display,windows->map_info->colormap,
3485              resource_info->pen_colors[pen_number],&border_color);
3486            break;
3487          }
3488          case ColorEditFuzzCommand:
3489          {
3490            static char
3491              fuzz[MaxTextExtent];
3492
3493            static const char
3494              *FuzzMenu[] =
3495              {
3496                "0%",
3497                "2%",
3498                "5%",
3499                "10%",
3500                "15%",
3501                "Dialog...",
3502                (char *) NULL,
3503              };
3504
3505            /*
3506              Select a command from the pop-up menu.
3507            */
3508            entry=XMenuWidget(display,windows,ColorEditMenu[id],FuzzMenu,
3509              command);
3510            if (entry < 0)
3511              break;
3512            if (entry != 5)
3513              {
3514                (*image)->fuzz=SiPrefixToDouble(FuzzMenu[entry],1.0*
3515                  QuantumRange+1.0);
3516                break;
3517              }
3518            (void) (void) CopyMagickString(fuzz,"20%",MaxTextExtent);
3519            (void) XDialogWidget(display,windows,"Ok",
3520              "Enter fuzz factor (0.0 - 99.9%):",fuzz);
3521            if (*fuzz == '\0')
3522              break;
3523            (void) ConcatenateMagickString(fuzz,"%",MaxTextExtent);
3524            (*image)->fuzz=SiPrefixToDouble(fuzz,1.0*QuantumRange+1.0);
3525            break;
3526          }
3527          case ColorEditUndoCommand:
3528          {
3529            (void) XMagickCommand(display,resource_info,windows,UndoCommand,
3530              image);
3531            break;
3532          }
3533          case ColorEditHelpCommand:
3534          default:
3535          {
3536            XTextViewWidget(display,resource_info,windows,MagickFalse,
3537              "Help Viewer - Image Annotation",ImageColorEditHelp);
3538            break;
3539          }
3540          case ColorEditDismissCommand:
3541          {
3542            /*
3543              Prematurely exit.
3544            */
3545            state|=EscapeState;
3546            state|=ExitState;
3547            break;
3548          }
3549        }
3550        (void) XCheckDefineCursor(display,windows->image.id,cursor);
3551        continue;
3552      }
3553    switch (event.type)
3554    {
3555      case ButtonPress:
3556      {
3557        if (event.xbutton.button != Button1)
3558          break;
3559        if ((event.xbutton.window != windows->image.id) &&
3560            (event.xbutton.window != windows->magnify.id))
3561          break;
3562        /*
3563          exit loop.
3564        */
3565        x=event.xbutton.x;
3566        y=event.xbutton.y;
3567        (void) XMagickCommand(display,resource_info,windows,
3568          SaveToUndoBufferCommand,image);
3569        state|=UpdateConfigurationState;
3570        break;
3571      }
3572      case ButtonRelease:
3573      {
3574        if (event.xbutton.button != Button1)
3575          break;
3576        if ((event.xbutton.window != windows->image.id) &&
3577            (event.xbutton.window != windows->magnify.id))
3578          break;
3579        /*
3580          Update colormap information.
3581        */
3582        x=event.xbutton.x;
3583        y=event.xbutton.y;
3584        XConfigureImageColormap(display,resource_info,windows,*image);
3585        (void) XConfigureImage(display,resource_info,windows,*image);
3586        XInfoWidget(display,windows,text);
3587        (void) XCheckDefineCursor(display,windows->image.id,cursor);
3588        state&=(~UpdateConfigurationState);
3589        break;
3590      }
3591      case Expose:
3592        break;
3593      case KeyPress:
3594      {
3595        KeySym
3596          key_symbol;
3597
3598        if (event.xkey.window == windows->magnify.id)
3599          {
3600            Window
3601              window;
3602
3603            window=windows->magnify.id;
3604            while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
3605          }
3606        if (event.xkey.window != windows->image.id)
3607          break;
3608        /*
3609          Respond to a user key press.
3610        */
3611        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3612          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3613        switch ((int) key_symbol)
3614        {
3615          case XK_Escape:
3616          case XK_F20:
3617          {
3618            /*
3619              Prematurely exit.
3620            */
3621            state|=ExitState;
3622            break;
3623          }
3624          case XK_F1:
3625          case XK_Help:
3626          {
3627            XTextViewWidget(display,resource_info,windows,MagickFalse,
3628              "Help Viewer - Image Annotation",ImageColorEditHelp);
3629            break;
3630          }
3631          default:
3632          {
3633            (void) XBell(display,0);
3634            break;
3635          }
3636        }
3637        break;
3638      }
3639      case MotionNotify:
3640      {
3641        /*
3642          Map and unmap Info widget as cursor crosses its boundaries.
3643        */
3644        x=event.xmotion.x;
3645        y=event.xmotion.y;
3646        if (windows->info.mapped != MagickFalse)
3647          {
3648            if ((x < (int) (windows->info.x+windows->info.width)) &&
3649                (y < (int) (windows->info.y+windows->info.height)))
3650              (void) XWithdrawWindow(display,windows->info.id,
3651                windows->info.screen);
3652          }
3653        else
3654          if ((x > (int) (windows->info.x+windows->info.width)) ||
3655              (y > (int) (windows->info.y+windows->info.height)))
3656            (void) XMapWindow(display,windows->info.id);
3657        break;
3658      }
3659      default:
3660        break;
3661    }
3662    if (event.xany.window == windows->magnify.id)
3663      {
3664        x=windows->magnify.x-windows->image.x;
3665        y=windows->magnify.y-windows->image.y;
3666      }
3667    x_offset=x;
3668    y_offset=y;
3669    if ((state & UpdateConfigurationState) != 0)
3670      {
3671        CacheView
3672          *image_view;
3673
3674        int
3675          x,
3676          y;
3677
3678        /*
3679          Pixel edit is relative to image configuration.
3680        */
3681        (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
3682          MagickTrue);
3683        color=windows->pixel_info->pen_colors[pen_id];
3684        XPutPixel(windows->image.ximage,x_offset,y_offset,color.pixel);
3685        width=(unsigned int) (*image)->columns;
3686        height=(unsigned int) (*image)->rows;
3687        x=0;
3688        y=0;
3689        if (windows->image.crop_geometry != (char *) NULL)
3690          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
3691            &width,&height);
3692        x_offset=(int)
3693          (width*(windows->image.x+x_offset)/windows->image.ximage->width+x);
3694        y_offset=(int)
3695          (height*(windows->image.y+y_offset)/windows->image.ximage->height+y);
3696        if ((x_offset < 0) || (y_offset < 0))
3697          continue;
3698        if ((x_offset >= (int) (*image)->columns) ||
3699            (y_offset >= (int) (*image)->rows))
3700          continue;
3701        exception=(&(*image)->exception);
3702        image_view=AcquireCacheView(*image);
3703        switch (method)
3704        {
3705          case PointMethod:
3706          default:
3707          {
3708            /*
3709              Update color information using point algorithm.
3710            */
3711            if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
3712              return(MagickFalse);
3713            q=GetCacheViewAuthenticPixels(image_view,(ssize_t)x_offset,
3714              (ssize_t) y_offset,1,1,exception);
3715            if (q == (Quantum *) NULL)
3716              break;
3717            SetPixelRed(*image,ScaleShortToQuantum(color.red),q);
3718            SetPixelGreen(*image,ScaleShortToQuantum(color.green),q);
3719            SetPixelBlue(*image,ScaleShortToQuantum(color.blue),q);
3720            (void) SyncCacheViewAuthenticPixels(image_view,
3721              &(*image)->exception);
3722            break;
3723          }
3724          case ReplaceMethod:
3725          {
3726            PixelPacket
3727              pixel,
3728              target;
3729
3730            /*
3731              Update color information using replace algorithm.
3732            */
3733            (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t) x_offset,
3734              (ssize_t) y_offset,&target,&(*image)->exception);
3735            if ((*image)->storage_class == DirectClass)
3736              {
3737                for (y=0; y < (int) (*image)->rows; y++)
3738                {
3739                  q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3740                    (*image)->columns,1,exception);
3741                  if (q == (Quantum *) NULL)
3742                    break;
3743                  for (x=0; x < (int) (*image)->columns; x++)
3744                  {
3745                    GetPixelPacket(*image,q,&pixel);
3746                    if (IsFuzzyEquivalencePixelPacket(*image,&pixel,&target))
3747                      {
3748                        SetPixelRed(*image,ScaleShortToQuantum(
3749                          color.red),q);
3750                        SetPixelGreen(*image,ScaleShortToQuantum(
3751                          color.green),q);
3752                        SetPixelBlue(*image,ScaleShortToQuantum(
3753                          color.blue),q);
3754                      }
3755                    q+=GetPixelChannels(*image);
3756                  }
3757                  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3758                    break;
3759                }
3760              }
3761            else
3762              {
3763                for (i=0; i < (ssize_t) (*image)->colors; i++)
3764                  if (IsFuzzyEquivalencePixelPacket(*image,(*image)->colormap+i,&target))
3765                    {
3766                      (*image)->colormap[i].red=ScaleShortToQuantum(
3767                        color.red);
3768                      (*image)->colormap[i].green=ScaleShortToQuantum(
3769                        color.green);
3770                      (*image)->colormap[i].blue=ScaleShortToQuantum(
3771                        color.blue);
3772                    }
3773                (void) SyncImage(*image);
3774              }
3775            break;
3776          }
3777          case FloodfillMethod:
3778          case FillToBorderMethod:
3779          {
3780            DrawInfo
3781              *draw_info;
3782
3783            PixelInfo
3784              target;
3785
3786            /*
3787              Update color information using floodfill algorithm.
3788            */
3789            (void) GetOneVirtualMagickPixel(*image,(ssize_t) x_offset,
3790              (ssize_t) y_offset,&target,exception);
3791            if (method == FillToBorderMethod)
3792              {
3793                target.red=(MagickRealType)
3794                  ScaleShortToQuantum(border_color.red);
3795                target.green=(MagickRealType)
3796                  ScaleShortToQuantum(border_color.green);
3797                target.blue=(MagickRealType)
3798                  ScaleShortToQuantum(border_color.blue);
3799              }
3800            draw_info=CloneDrawInfo(resource_info->image_info,
3801              (DrawInfo *) NULL);
3802            (void) QueryColorDatabase(resource_info->pen_colors[pen_id],
3803              &draw_info->fill,exception);
3804            (void) FloodfillPaintImage(*image,draw_info,&target,(ssize_t)
3805              x_offset,(ssize_t) y_offset,method == FloodfillMethod ?
3806              MagickFalse : MagickTrue);
3807            draw_info=DestroyDrawInfo(draw_info);
3808            break;
3809          }
3810          case ResetMethod:
3811          {
3812            /*
3813              Update color information using reset algorithm.
3814            */
3815            if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
3816              return(MagickFalse);
3817            for (y=0; y < (int) (*image)->rows; y++)
3818            {
3819              q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3820                (*image)->columns,1,exception);
3821              if (q == (Quantum *) NULL)
3822                break;
3823              for (x=0; x < (int) (*image)->columns; x++)
3824              {
3825                SetPixelRed(*image,ScaleShortToQuantum(color.red),q);
3826                SetPixelGreen(*image,ScaleShortToQuantum(color.green),q);
3827                SetPixelBlue(*image,ScaleShortToQuantum(color.blue),q);
3828                q+=GetPixelChannels(*image);
3829              }
3830              if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3831                break;
3832            }
3833            break;
3834          }
3835        }
3836        image_view=DestroyCacheView(image_view);
3837        state&=(~UpdateConfigurationState);
3838      }
3839  } while ((state & ExitState) == 0);
3840  (void) XSelectInput(display,windows->image.id,
3841    windows->image.attributes.event_mask);
3842  XSetCursorState(display,windows,MagickFalse);
3843  (void) XFreeCursor(display,cursor);
3844  return(MagickTrue);
3845}
3846
3847/*
3848%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3849%                                                                             %
3850%                                                                             %
3851%                                                                             %
3852+   X C o m p o s i t e I m a g e                                             %
3853%                                                                             %
3854%                                                                             %
3855%                                                                             %
3856%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3857%
3858%  XCompositeImage() requests an image name from the user, reads the image and
3859%  composites it with the X window image at a location the user chooses with
3860%  the pointer.
3861%
3862%  The format of the XCompositeImage method is:
3863%
3864%      MagickBooleanType XCompositeImage(Display *display,
3865%        XResourceInfo *resource_info,XWindows *windows,Image *image)
3866%
3867%  A description of each parameter follows:
3868%
3869%    o display: Specifies a connection to an X server;  returned from
3870%      XOpenDisplay.
3871%
3872%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3873%
3874%    o windows: Specifies a pointer to a XWindows structure.
3875%
3876%    o image: the image; returned from ReadImage.
3877%
3878*/
3879static MagickBooleanType XCompositeImage(Display *display,
3880  XResourceInfo *resource_info,XWindows *windows,Image *image)
3881{
3882  static char
3883    displacement_geometry[MaxTextExtent] = "30x30",
3884    filename[MaxTextExtent] = "\0";
3885
3886  static const char
3887    *CompositeMenu[] =
3888    {
3889      "Operators",
3890      "Dissolve",
3891      "Displace",
3892      "Help",
3893      "Dismiss",
3894      (char *) NULL
3895    };
3896
3897  static CompositeOperator
3898    compose = CopyCompositeOp;
3899
3900  static const ModeType
3901    CompositeCommands[] =
3902    {
3903      CompositeOperatorsCommand,
3904      CompositeDissolveCommand,
3905      CompositeDisplaceCommand,
3906      CompositeHelpCommand,
3907      CompositeDismissCommand
3908    };
3909
3910  char
3911    text[MaxTextExtent];
3912
3913  Cursor
3914    cursor;
3915
3916  Image
3917    *composite_image;
3918
3919  int
3920    entry,
3921    id,
3922    x,
3923    y;
3924
3925  MagickRealType
3926    blend,
3927    scale_factor;
3928
3929  RectangleInfo
3930    highlight_info,
3931    composite_info;
3932
3933  unsigned int
3934    height,
3935    width;
3936
3937  size_t
3938    state;
3939
3940  XEvent
3941    event;
3942
3943  /*
3944    Request image file name from user.
3945  */
3946  XFileBrowserWidget(display,windows,"Composite",filename);
3947  if (*filename == '\0')
3948    return(MagickTrue);
3949  /*
3950    Read image.
3951  */
3952  XSetCursorState(display,windows,MagickTrue);
3953  XCheckRefreshWindows(display,windows);
3954  (void) CopyMagickString(resource_info->image_info->filename,filename,
3955    MaxTextExtent);
3956  composite_image=ReadImage(resource_info->image_info,&image->exception);
3957  CatchException(&image->exception);
3958  XSetCursorState(display,windows,MagickFalse);
3959  if (composite_image == (Image *) NULL)
3960    return(MagickFalse);
3961  /*
3962    Map Command widget.
3963  */
3964  (void) CloneString(&windows->command.name,"Composite");
3965  windows->command.data=1;
3966  (void) XCommandWidget(display,windows,CompositeMenu,(XEvent *) NULL);
3967  (void) XMapRaised(display,windows->command.id);
3968  XClientMessage(display,windows->image.id,windows->im_protocols,
3969    windows->im_update_widget,CurrentTime);
3970  /*
3971    Track pointer until button 1 is pressed.
3972  */
3973  XQueryPosition(display,windows->image.id,&x,&y);
3974  (void) XSelectInput(display,windows->image.id,
3975    windows->image.attributes.event_mask | PointerMotionMask);
3976  composite_info.x=(ssize_t) windows->image.x+x;
3977  composite_info.y=(ssize_t) windows->image.y+y;
3978  composite_info.width=0;
3979  composite_info.height=0;
3980  cursor=XCreateFontCursor(display,XC_ul_angle);
3981  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
3982  blend=0.0;
3983  state=DefaultState;
3984  do
3985  {
3986    if (windows->info.mapped != MagickFalse)
3987      {
3988        /*
3989          Display pointer position.
3990        */
3991        (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
3992          (long) composite_info.x,(long) composite_info.y);
3993        XInfoWidget(display,windows,text);
3994      }
3995    highlight_info=composite_info;
3996    highlight_info.x=composite_info.x-windows->image.x;
3997    highlight_info.y=composite_info.y-windows->image.y;
3998    XHighlightRectangle(display,windows->image.id,
3999      windows->image.highlight_context,&highlight_info);
4000    /*
4001      Wait for next event.
4002    */
4003    XScreenEvent(display,windows,&event);
4004    XHighlightRectangle(display,windows->image.id,
4005      windows->image.highlight_context,&highlight_info);
4006    if (event.xany.window == windows->command.id)
4007      {
4008        /*
4009          Select a command from the Command widget.
4010        */
4011        id=XCommandWidget(display,windows,CompositeMenu,&event);
4012        if (id < 0)
4013          continue;
4014        switch (CompositeCommands[id])
4015        {
4016          case CompositeOperatorsCommand:
4017          {
4018            char
4019              command[MaxTextExtent],
4020              **operators;
4021
4022            /*
4023              Select a command from the pop-up menu.
4024            */
4025            operators=GetCommandOptions(MagickComposeOptions);
4026            if (operators == (char **) NULL)
4027              break;
4028            entry=XMenuWidget(display,windows,CompositeMenu[id],
4029              (const char **) operators,command);
4030            if (entry >= 0)
4031              compose=(CompositeOperator) ParseCommandOption(
4032                MagickComposeOptions,MagickFalse,operators[entry]);
4033            operators=DestroyStringList(operators);
4034            break;
4035          }
4036          case CompositeDissolveCommand:
4037          {
4038            static char
4039              factor[MaxTextExtent] = "20.0";
4040
4041            /*
4042              Dissolve the two images a given percent.
4043            */
4044            (void) XSetFunction(display,windows->image.highlight_context,
4045              GXcopy);
4046            (void) XDialogWidget(display,windows,"Dissolve",
4047              "Enter the blend factor (0.0 - 99.9%):",factor);
4048            (void) XSetFunction(display,windows->image.highlight_context,
4049              GXinvert);
4050            if (*factor == '\0')
4051              break;
4052            blend=InterpretLocaleValue(factor,(char **) NULL);
4053            compose=DissolveCompositeOp;
4054            break;
4055          }
4056          case CompositeDisplaceCommand:
4057          {
4058            /*
4059              Get horizontal and vertical scale displacement geometry.
4060            */
4061            (void) XSetFunction(display,windows->image.highlight_context,
4062              GXcopy);
4063            (void) XDialogWidget(display,windows,"Displace",
4064              "Enter the horizontal and vertical scale:",displacement_geometry);
4065            (void) XSetFunction(display,windows->image.highlight_context,
4066              GXinvert);
4067            if (*displacement_geometry == '\0')
4068              break;
4069            compose=DisplaceCompositeOp;
4070            break;
4071          }
4072          case CompositeHelpCommand:
4073          {
4074            (void) XSetFunction(display,windows->image.highlight_context,
4075              GXcopy);
4076            XTextViewWidget(display,resource_info,windows,MagickFalse,
4077              "Help Viewer - Image Composite",ImageCompositeHelp);
4078            (void) XSetFunction(display,windows->image.highlight_context,
4079              GXinvert);
4080            break;
4081          }
4082          case CompositeDismissCommand:
4083          {
4084            /*
4085              Prematurely exit.
4086            */
4087            state|=EscapeState;
4088            state|=ExitState;
4089            break;
4090          }
4091          default:
4092            break;
4093        }
4094        continue;
4095      }
4096    switch (event.type)
4097    {
4098      case ButtonPress:
4099      {
4100        if (image->debug != MagickFalse)
4101          (void) LogMagickEvent(X11Event,GetMagickModule(),
4102            "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
4103            event.xbutton.button,event.xbutton.x,event.xbutton.y);
4104        if (event.xbutton.button != Button1)
4105          break;
4106        if (event.xbutton.window != windows->image.id)
4107          break;
4108        /*
4109          Change cursor.
4110        */
4111        composite_info.width=composite_image->columns;
4112        composite_info.height=composite_image->rows;
4113        (void) XCheckDefineCursor(display,windows->image.id,cursor);
4114        composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4115        composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4116        break;
4117      }
4118      case ButtonRelease:
4119      {
4120        if (image->debug != MagickFalse)
4121          (void) LogMagickEvent(X11Event,GetMagickModule(),
4122            "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
4123            event.xbutton.button,event.xbutton.x,event.xbutton.y);
4124        if (event.xbutton.button != Button1)
4125          break;
4126        if (event.xbutton.window != windows->image.id)
4127          break;
4128        if ((composite_info.width != 0) && (composite_info.height != 0))
4129          {
4130            /*
4131              User has selected the location of the composite image.
4132            */
4133            composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4134            composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4135            state|=ExitState;
4136          }
4137        break;
4138      }
4139      case Expose:
4140        break;
4141      case KeyPress:
4142      {
4143        char
4144          command[MaxTextExtent];
4145
4146        KeySym
4147          key_symbol;
4148
4149        int
4150          length;
4151
4152        if (event.xkey.window != windows->image.id)
4153          break;
4154        /*
4155          Respond to a user key press.
4156        */
4157        length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
4158          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4159        *(command+length)='\0';
4160        if (image->debug != MagickFalse)
4161          (void) LogMagickEvent(X11Event,GetMagickModule(),
4162            "Key press: 0x%lx (%s)",(unsigned long) key_symbol,command);
4163        switch ((int) key_symbol)
4164        {
4165          case XK_Escape:
4166          case XK_F20:
4167          {
4168            /*
4169              Prematurely exit.
4170            */
4171            composite_image=DestroyImage(composite_image);
4172            state|=EscapeState;
4173            state|=ExitState;
4174            break;
4175          }
4176          case XK_F1:
4177          case XK_Help:
4178          {
4179            (void) XSetFunction(display,windows->image.highlight_context,
4180              GXcopy);
4181            XTextViewWidget(display,resource_info,windows,MagickFalse,
4182              "Help Viewer - Image Composite",ImageCompositeHelp);
4183            (void) XSetFunction(display,windows->image.highlight_context,
4184              GXinvert);
4185            break;
4186          }
4187          default:
4188          {
4189            (void) XBell(display,0);
4190            break;
4191          }
4192        }
4193        break;
4194      }
4195      case MotionNotify:
4196      {
4197        /*
4198          Map and unmap Info widget as text cursor crosses its boundaries.
4199        */
4200        x=event.xmotion.x;
4201        y=event.xmotion.y;
4202        if (windows->info.mapped != MagickFalse)
4203          {
4204            if ((x < (int) (windows->info.x+windows->info.width)) &&
4205                (y < (int) (windows->info.y+windows->info.height)))
4206              (void) XWithdrawWindow(display,windows->info.id,
4207                windows->info.screen);
4208          }
4209        else
4210          if ((x > (int) (windows->info.x+windows->info.width)) ||
4211              (y > (int) (windows->info.y+windows->info.height)))
4212            (void) XMapWindow(display,windows->info.id);
4213        composite_info.x=(ssize_t) windows->image.x+x;
4214        composite_info.y=(ssize_t) windows->image.y+y;
4215        break;
4216      }
4217      default:
4218      {
4219        if (image->debug != MagickFalse)
4220          (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
4221            event.type);
4222        break;
4223      }
4224    }
4225  } while ((state & ExitState) == 0);
4226  (void) XSelectInput(display,windows->image.id,
4227    windows->image.attributes.event_mask);
4228  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
4229  XSetCursorState(display,windows,MagickFalse);
4230  (void) XFreeCursor(display,cursor);
4231  if ((state & EscapeState) != 0)
4232    return(MagickTrue);
4233  /*
4234    Image compositing is relative to image configuration.
4235  */
4236  XSetCursorState(display,windows,MagickTrue);
4237  XCheckRefreshWindows(display,windows);
4238  width=(unsigned int) image->columns;
4239  height=(unsigned int) image->rows;
4240  x=0;
4241  y=0;
4242  if (windows->image.crop_geometry != (char *) NULL)
4243    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
4244  scale_factor=(MagickRealType) width/windows->image.ximage->width;
4245  composite_info.x+=x;
4246  composite_info.x=(ssize_t) (scale_factor*composite_info.x+0.5);
4247  composite_info.width=(unsigned int) (scale_factor*composite_info.width+0.5);
4248  scale_factor=(MagickRealType) height/windows->image.ximage->height;
4249  composite_info.y+=y;
4250  composite_info.y=(ssize_t) (scale_factor*composite_info.y+0.5);
4251  composite_info.height=(unsigned int) (scale_factor*composite_info.height+0.5);
4252  if ((composite_info.width != composite_image->columns) ||
4253      (composite_info.height != composite_image->rows))
4254    {
4255      Image
4256        *resize_image;
4257
4258      /*
4259        Scale composite image.
4260      */
4261      resize_image=ResizeImage(composite_image,composite_info.width,
4262        composite_info.height,composite_image->filter,composite_image->blur,
4263        &image->exception);
4264      composite_image=DestroyImage(composite_image);
4265      if (resize_image == (Image *) NULL)
4266        {
4267          XSetCursorState(display,windows,MagickFalse);
4268          return(MagickFalse);
4269        }
4270      composite_image=resize_image;
4271    }
4272  if (compose == DisplaceCompositeOp)
4273    (void) SetImageArtifact(composite_image,"compose:args",
4274      displacement_geometry);
4275  if (blend != 0.0)
4276    {
4277      CacheView
4278        *image_view;
4279
4280      ExceptionInfo
4281        *exception;
4282
4283      int
4284        y;
4285
4286      Quantum
4287        opacity;
4288
4289      register int
4290        x;
4291
4292      register Quantum
4293        *q;
4294
4295      /*
4296        Create mattes for blending.
4297      */
4298      exception=(&image->exception);
4299      (void) SetImageAlphaChannel(composite_image,OpaqueAlphaChannel,exception);
4300      opacity=(Quantum) (ScaleQuantumToChar((Quantum) QuantumRange)-
4301        ((ssize_t) ScaleQuantumToChar((Quantum) QuantumRange)*blend)/100);
4302      if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
4303        return(MagickFalse);
4304      image->matte=MagickTrue;
4305      image_view=AcquireCacheView(image);
4306      for (y=0; y < (int) image->rows; y++)
4307      {
4308        q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,image->columns,1,
4309          exception);
4310        if (q == (Quantum *) NULL)
4311          break;
4312        for (x=0; x < (int) image->columns; x++)
4313        {
4314          SetPixelAlpha(image,opacity,q);
4315          q+=GetPixelChannels(image);
4316        }
4317        if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4318          break;
4319      }
4320      image_view=DestroyCacheView(image_view);
4321    }
4322  /*
4323    Composite image with X Image window.
4324  */
4325  (void) CompositeImage(image,compose,composite_image,composite_info.x,
4326    composite_info.y);
4327  composite_image=DestroyImage(composite_image);
4328  XSetCursorState(display,windows,MagickFalse);
4329  /*
4330    Update image configuration.
4331  */
4332  XConfigureImageColormap(display,resource_info,windows,image);
4333  (void) XConfigureImage(display,resource_info,windows,image);
4334  return(MagickTrue);
4335}
4336
4337/*
4338%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4339%                                                                             %
4340%                                                                             %
4341%                                                                             %
4342+   X C o n f i g u r e I m a g e                                             %
4343%                                                                             %
4344%                                                                             %
4345%                                                                             %
4346%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4347%
4348%  XConfigureImage() creates a new X image.  It also notifies the window
4349%  manager of the new image size and configures the transient widows.
4350%
4351%  The format of the XConfigureImage method is:
4352%
4353%      MagickBooleanType XConfigureImage(Display *display,
4354%        XResourceInfo *resource_info,XWindows *windows,Image *image)
4355%
4356%  A description of each parameter follows:
4357%
4358%    o display: Specifies a connection to an X server; returned from
4359%      XOpenDisplay.
4360%
4361%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4362%
4363%    o windows: Specifies a pointer to a XWindows structure.
4364%
4365%    o image: the image.
4366%
4367%
4368*/
4369static MagickBooleanType XConfigureImage(Display *display,
4370  XResourceInfo *resource_info,XWindows *windows,Image *image)
4371{
4372  char
4373    geometry[MaxTextExtent];
4374
4375  MagickStatusType
4376    status;
4377
4378  size_t
4379    mask,
4380    height,
4381    width;
4382
4383  ssize_t
4384    x,
4385    y;
4386
4387  XSizeHints
4388    *size_hints;
4389
4390  XWindowChanges
4391    window_changes;
4392
4393  /*
4394    Dismiss if window dimensions are zero.
4395  */
4396  width=(unsigned int) windows->image.window_changes.width;
4397  height=(unsigned int) windows->image.window_changes.height;
4398  if (image->debug != MagickFalse)
4399    (void) LogMagickEvent(X11Event,GetMagickModule(),
4400      "Configure Image: %dx%d=>%.20gx%.20g",windows->image.ximage->width,
4401      windows->image.ximage->height,(double) width,(double) height);
4402  if ((width*height) == 0)
4403    return(MagickTrue);
4404  x=0;
4405  y=0;
4406  /*
4407    Resize image to fit Image window dimensions.
4408  */
4409  XSetCursorState(display,windows,MagickTrue);
4410  (void) XFlush(display);
4411  if (((int) width != windows->image.ximage->width) ||
4412      ((int) height != windows->image.ximage->height))
4413    image->taint=MagickTrue;
4414  windows->magnify.x=(int)
4415    width*windows->magnify.x/windows->image.ximage->width;
4416  windows->magnify.y=(int)
4417    height*windows->magnify.y/windows->image.ximage->height;
4418  windows->image.x=(int) (width*windows->image.x/windows->image.ximage->width);
4419  windows->image.y=(int)
4420    (height*windows->image.y/windows->image.ximage->height);
4421  status=XMakeImage(display,resource_info,&windows->image,image,
4422    (unsigned int) width,(unsigned int) height);
4423  if (status == MagickFalse)
4424    XNoticeWidget(display,windows,"Unable to configure X image:",
4425      windows->image.name);
4426  /*
4427    Notify window manager of the new configuration.
4428  */
4429  if (resource_info->image_geometry != (char *) NULL)
4430    (void) FormatLocaleString(geometry,MaxTextExtent,"%s>!",
4431      resource_info->image_geometry);
4432  else
4433    (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>!",
4434      XDisplayWidth(display,windows->image.screen),
4435      XDisplayHeight(display,windows->image.screen));
4436  (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
4437  window_changes.width=(int) width;
4438  if (window_changes.width > XDisplayWidth(display,windows->image.screen))
4439    window_changes.width=XDisplayWidth(display,windows->image.screen);
4440  window_changes.height=(int) height;
4441  if (window_changes.height > XDisplayHeight(display,windows->image.screen))
4442    window_changes.height=XDisplayHeight(display,windows->image.screen);
4443  mask=(size_t) (CWWidth | CWHeight);
4444  if (resource_info->backdrop)
4445    {
4446      mask|=CWX | CWY;
4447      window_changes.x=(int)
4448        ((XDisplayWidth(display,windows->image.screen)/2)-(width/2));
4449      window_changes.y=(int)
4450        ((XDisplayHeight(display,windows->image.screen)/2)-(height/2));
4451    }
4452  (void) XReconfigureWMWindow(display,windows->image.id,windows->image.screen,
4453    (unsigned int) mask,&window_changes);
4454  (void) XClearWindow(display,windows->image.id);
4455  XRefreshWindow(display,&windows->image,(XEvent *) NULL);
4456  /*
4457    Update Magnify window configuration.
4458  */
4459  if (windows->magnify.mapped != MagickFalse)
4460    XMakeMagnifyImage(display,windows);
4461  windows->pan.crop_geometry=windows->image.crop_geometry;
4462  XBestIconSize(display,&windows->pan,image);
4463  while (((windows->pan.width << 1) < MaxIconSize) &&
4464         ((windows->pan.height << 1) < MaxIconSize))
4465  {
4466    windows->pan.width<<=1;
4467    windows->pan.height<<=1;
4468  }
4469  if (windows->pan.geometry != (char *) NULL)
4470    (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
4471      &windows->pan.width,&windows->pan.height);
4472  window_changes.width=(int) windows->pan.width;
4473  window_changes.height=(int) windows->pan.height;
4474  size_hints=XAllocSizeHints();
4475  if (size_hints != (XSizeHints *) NULL)
4476    {
4477      /*
4478        Set new size hints.
4479      */
4480      size_hints->flags=PSize | PMinSize | PMaxSize;
4481      size_hints->width=window_changes.width;
4482      size_hints->height=window_changes.height;
4483      size_hints->min_width=size_hints->width;
4484      size_hints->min_height=size_hints->height;
4485      size_hints->max_width=size_hints->width;
4486      size_hints->max_height=size_hints->height;
4487      (void) XSetNormalHints(display,windows->pan.id,size_hints);
4488      (void) XFree((void *) size_hints);
4489    }
4490  (void) XReconfigureWMWindow(display,windows->pan.id,windows->pan.screen,
4491    (unsigned int) (CWWidth | CWHeight),&window_changes);
4492  /*
4493    Update icon window configuration.
4494  */
4495  windows->icon.crop_geometry=windows->image.crop_geometry;
4496  XBestIconSize(display,&windows->icon,image);
4497  window_changes.width=(int) windows->icon.width;
4498  window_changes.height=(int) windows->icon.height;
4499  (void) XReconfigureWMWindow(display,windows->icon.id,windows->icon.screen,
4500    (unsigned int) (CWWidth | CWHeight),&window_changes);
4501  XSetCursorState(display,windows,MagickFalse);
4502  return(status != 0 ? MagickTrue : MagickFalse);
4503}
4504
4505/*
4506%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4507%                                                                             %
4508%                                                                             %
4509%                                                                             %
4510+   X C r o p I m a g e                                                       %
4511%                                                                             %
4512%                                                                             %
4513%                                                                             %
4514%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4515%
4516%  XCropImage() allows the user to select a region of the image and crop, copy,
4517%  or cut it.  For copy or cut, the image can subsequently be composited onto
4518%  the image with XPasteImage.
4519%
4520%  The format of the XCropImage method is:
4521%
4522%      MagickBooleanType XCropImage(Display *display,
4523%        XResourceInfo *resource_info,XWindows *windows,Image *image,
4524%        const ClipboardMode mode)
4525%
4526%  A description of each parameter follows:
4527%
4528%    o display: Specifies a connection to an X server; returned from
4529%      XOpenDisplay.
4530%
4531%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4532%
4533%    o windows: Specifies a pointer to a XWindows structure.
4534%
4535%    o image: the image; returned from ReadImage.
4536%
4537%    o mode: This unsigned value specified whether the image should be
4538%      cropped, copied, or cut.
4539%
4540*/
4541static MagickBooleanType XCropImage(Display *display,
4542  XResourceInfo *resource_info,XWindows *windows,Image *image,
4543  const ClipboardMode mode)
4544{
4545  static const char
4546    *CropModeMenu[] =
4547    {
4548      "Help",
4549      "Dismiss",
4550      (char *) NULL
4551    },
4552    *RectifyModeMenu[] =
4553    {
4554      "Crop",
4555      "Help",
4556      "Dismiss",
4557      (char *) NULL
4558    };
4559
4560  static const ModeType
4561    CropCommands[] =
4562    {
4563      CropHelpCommand,
4564      CropDismissCommand
4565    },
4566    RectifyCommands[] =
4567    {
4568      RectifyCopyCommand,
4569      RectifyHelpCommand,
4570      RectifyDismissCommand
4571    };
4572
4573  CacheView
4574    *image_view;
4575
4576  char
4577    command[MaxTextExtent],
4578    text[MaxTextExtent];
4579
4580  Cursor
4581    cursor;
4582
4583  ExceptionInfo
4584    *exception;
4585
4586  int
4587    id,
4588    x,
4589    y;
4590
4591  KeySym
4592    key_symbol;
4593
4594  Image
4595    *crop_image;
4596
4597  MagickRealType
4598    scale_factor;
4599
4600  RectangleInfo
4601    crop_info,
4602    highlight_info;
4603
4604  register Quantum
4605    *q;
4606
4607  unsigned int
4608    height,
4609    width;
4610
4611  size_t
4612    state;
4613
4614  XEvent
4615    event;
4616
4617  /*
4618    Map Command widget.
4619  */
4620  switch (mode)
4621  {
4622    case CopyMode:
4623    {
4624      (void) CloneString(&windows->command.name,"Copy");
4625      break;
4626    }
4627    case CropMode:
4628    {
4629      (void) CloneString(&windows->command.name,"Crop");
4630      break;
4631    }
4632    case CutMode:
4633    {
4634      (void) CloneString(&windows->command.name,"Cut");
4635      break;
4636    }
4637  }
4638  RectifyModeMenu[0]=windows->command.name;
4639  windows->command.data=0;
4640  (void) XCommandWidget(display,windows,CropModeMenu,(XEvent *) NULL);
4641  (void) XMapRaised(display,windows->command.id);
4642  XClientMessage(display,windows->image.id,windows->im_protocols,
4643    windows->im_update_widget,CurrentTime);
4644  /*
4645    Track pointer until button 1 is pressed.
4646  */
4647  XQueryPosition(display,windows->image.id,&x,&y);
4648  (void) XSelectInput(display,windows->image.id,
4649    windows->image.attributes.event_mask | PointerMotionMask);
4650  crop_info.x=(ssize_t) windows->image.x+x;
4651  crop_info.y=(ssize_t) windows->image.y+y;
4652  crop_info.width=0;
4653  crop_info.height=0;
4654  cursor=XCreateFontCursor(display,XC_fleur);
4655  state=DefaultState;
4656  do
4657  {
4658    if (windows->info.mapped != MagickFalse)
4659      {
4660        /*
4661          Display pointer position.
4662        */
4663        (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
4664          (long) crop_info.x,(long) crop_info.y);
4665        XInfoWidget(display,windows,text);
4666      }
4667    /*
4668      Wait for next event.
4669    */
4670    XScreenEvent(display,windows,&event);
4671    if (event.xany.window == windows->command.id)
4672      {
4673        /*
4674          Select a command from the Command widget.
4675        */
4676        id=XCommandWidget(display,windows,CropModeMenu,&event);
4677        if (id < 0)
4678          continue;
4679        switch (CropCommands[id])
4680        {
4681          case CropHelpCommand:
4682          {
4683            switch (mode)
4684            {
4685              case CopyMode:
4686              {
4687                XTextViewWidget(display,resource_info,windows,MagickFalse,
4688                  "Help Viewer - Image Copy",ImageCopyHelp);
4689                break;
4690              }
4691              case CropMode:
4692              {
4693                XTextViewWidget(display,resource_info,windows,MagickFalse,
4694                  "Help Viewer - Image Crop",ImageCropHelp);
4695                break;
4696              }
4697              case CutMode:
4698              {
4699                XTextViewWidget(display,resource_info,windows,MagickFalse,
4700                  "Help Viewer - Image Cut",ImageCutHelp);
4701                break;
4702              }
4703            }
4704            break;
4705          }
4706          case CropDismissCommand:
4707          {
4708            /*
4709              Prematurely exit.
4710            */
4711            state|=EscapeState;
4712            state|=ExitState;
4713            break;
4714          }
4715          default:
4716            break;
4717        }
4718        continue;
4719      }
4720    switch (event.type)
4721    {
4722      case ButtonPress:
4723      {
4724        if (event.xbutton.button != Button1)
4725          break;
4726        if (event.xbutton.window != windows->image.id)
4727          break;
4728        /*
4729          Note first corner of cropping rectangle-- exit loop.
4730        */
4731        (void) XCheckDefineCursor(display,windows->image.id,cursor);
4732        crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4733        crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4734        state|=ExitState;
4735        break;
4736      }
4737      case ButtonRelease:
4738        break;
4739      case Expose:
4740        break;
4741      case KeyPress:
4742      {
4743        if (event.xkey.window != windows->image.id)
4744          break;
4745        /*
4746          Respond to a user key press.
4747        */
4748        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
4749          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4750        switch ((int) key_symbol)
4751        {
4752          case XK_Escape:
4753          case XK_F20:
4754          {
4755            /*
4756              Prematurely exit.
4757            */
4758            state|=EscapeState;
4759            state|=ExitState;
4760            break;
4761          }
4762          case XK_F1:
4763          case XK_Help:
4764          {
4765            switch (mode)
4766            {
4767              case CopyMode:
4768              {
4769                XTextViewWidget(display,resource_info,windows,MagickFalse,
4770                  "Help Viewer - Image Copy",ImageCopyHelp);
4771                break;
4772              }
4773              case CropMode:
4774              {
4775                XTextViewWidget(display,resource_info,windows,MagickFalse,
4776                  "Help Viewer - Image Crop",ImageCropHelp);
4777                break;
4778              }
4779              case CutMode:
4780              {
4781                XTextViewWidget(display,resource_info,windows,MagickFalse,
4782                  "Help Viewer - Image Cut",ImageCutHelp);
4783                break;
4784              }
4785            }
4786            break;
4787          }
4788          default:
4789          {
4790            (void) XBell(display,0);
4791            break;
4792          }
4793        }
4794        break;
4795      }
4796      case MotionNotify:
4797      {
4798        if (event.xmotion.window != windows->image.id)
4799          break;
4800        /*
4801          Map and unmap Info widget as text cursor crosses its boundaries.
4802        */
4803        x=event.xmotion.x;
4804        y=event.xmotion.y;
4805        if (windows->info.mapped != MagickFalse)
4806          {
4807            if ((x < (int) (windows->info.x+windows->info.width)) &&
4808                (y < (int) (windows->info.y+windows->info.height)))
4809              (void) XWithdrawWindow(display,windows->info.id,
4810                windows->info.screen);
4811          }
4812        else
4813          if ((x > (int) (windows->info.x+windows->info.width)) ||
4814              (y > (int) (windows->info.y+windows->info.height)))
4815            (void) XMapWindow(display,windows->info.id);
4816        crop_info.x=(ssize_t) windows->image.x+x;
4817        crop_info.y=(ssize_t) windows->image.y+y;
4818        break;
4819      }
4820      default:
4821        break;
4822    }
4823  } while ((state & ExitState) == 0);
4824  (void) XSelectInput(display,windows->image.id,
4825    windows->image.attributes.event_mask);
4826  if ((state & EscapeState) != 0)
4827    {
4828      /*
4829        User want to exit without cropping.
4830      */
4831      (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4832      (void) XFreeCursor(display,cursor);
4833      return(MagickTrue);
4834    }
4835  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
4836  do
4837  {
4838    /*
4839      Size rectangle as pointer moves until the mouse button is released.
4840    */
4841    x=(int) crop_info.x;
4842    y=(int) crop_info.y;
4843    crop_info.width=0;
4844    crop_info.height=0;
4845    state=DefaultState;
4846    do
4847    {
4848      highlight_info=crop_info;
4849      highlight_info.x=crop_info.x-windows->image.x;
4850      highlight_info.y=crop_info.y-windows->image.y;
4851      if ((highlight_info.width > 3) && (highlight_info.height > 3))
4852        {
4853          /*
4854            Display info and draw cropping rectangle.
4855          */
4856          if (windows->info.mapped == MagickFalse)
4857            (void) XMapWindow(display,windows->info.id);
4858          (void) FormatLocaleString(text,MaxTextExtent,
4859            " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
4860            crop_info.height,(double) crop_info.x,(double) crop_info.y);
4861          XInfoWidget(display,windows,text);
4862          XHighlightRectangle(display,windows->image.id,
4863            windows->image.highlight_context,&highlight_info);
4864        }
4865      else
4866        if (windows->info.mapped != MagickFalse)
4867          (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4868      /*
4869        Wait for next event.
4870      */
4871      XScreenEvent(display,windows,&event);
4872      if ((highlight_info.width > 3) && (highlight_info.height > 3))
4873        XHighlightRectangle(display,windows->image.id,
4874          windows->image.highlight_context,&highlight_info);
4875      switch (event.type)
4876      {
4877        case ButtonPress:
4878        {
4879          crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4880          crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4881          break;
4882        }
4883        case ButtonRelease:
4884        {
4885          /*
4886            User has committed to cropping rectangle.
4887          */
4888          crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4889          crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4890          XSetCursorState(display,windows,MagickFalse);
4891          state|=ExitState;
4892          windows->command.data=0;
4893          (void) XCommandWidget(display,windows,RectifyModeMenu,
4894            (XEvent *) NULL);
4895          break;
4896        }
4897        case Expose:
4898          break;
4899        case MotionNotify:
4900        {
4901          crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
4902          crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
4903        }
4904        default:
4905          break;
4906      }
4907      if ((((int) crop_info.x != x) && ((int) crop_info.y != y)) ||
4908          ((state & ExitState) != 0))
4909        {
4910          /*
4911            Check boundary conditions.
4912          */
4913          if (crop_info.x < 0)
4914            crop_info.x=0;
4915          else
4916            if (crop_info.x > (ssize_t) windows->image.ximage->width)
4917              crop_info.x=(ssize_t) windows->image.ximage->width;
4918          if ((int) crop_info.x < x)
4919            crop_info.width=(unsigned int) (x-crop_info.x);
4920          else
4921            {
4922              crop_info.width=(unsigned int) (crop_info.x-x);
4923              crop_info.x=(ssize_t) x;
4924            }
4925          if (crop_info.y < 0)
4926            crop_info.y=0;
4927          else
4928            if (crop_info.y > (ssize_t) windows->image.ximage->height)
4929              crop_info.y=(ssize_t) windows->image.ximage->height;
4930          if ((int) crop_info.y < y)
4931            crop_info.height=(unsigned int) (y-crop_info.y);
4932          else
4933            {
4934              crop_info.height=(unsigned int) (crop_info.y-y);
4935              crop_info.y=(ssize_t) y;
4936            }
4937        }
4938    } while ((state & ExitState) == 0);
4939    /*
4940      Wait for user to grab a corner of the rectangle or press return.
4941    */
4942    state=DefaultState;
4943    (void) XMapWindow(display,windows->info.id);
4944    do
4945    {
4946      if (windows->info.mapped != MagickFalse)
4947        {
4948          /*
4949            Display pointer position.
4950          */
4951          (void) FormatLocaleString(text,MaxTextExtent,
4952            " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
4953            crop_info.height,(double) crop_info.x,(double) crop_info.y);
4954          XInfoWidget(display,windows,text);
4955        }
4956      highlight_info=crop_info;
4957      highlight_info.x=crop_info.x-windows->image.x;
4958      highlight_info.y=crop_info.y-windows->image.y;
4959      if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
4960        {
4961          state|=EscapeState;
4962          state|=ExitState;
4963          break;
4964        }
4965      XHighlightRectangle(display,windows->image.id,
4966        windows->image.highlight_context,&highlight_info);
4967      XScreenEvent(display,windows,&event);
4968      if (event.xany.window == windows->command.id)
4969        {
4970          /*
4971            Select a command from the Command widget.
4972          */
4973          (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
4974          id=XCommandWidget(display,windows,RectifyModeMenu,&event);
4975          (void) XSetFunction(display,windows->image.highlight_context,
4976            GXinvert);
4977          XHighlightRectangle(display,windows->image.id,
4978            windows->image.highlight_context,&highlight_info);
4979          if (id >= 0)
4980            switch (RectifyCommands[id])
4981            {
4982              case RectifyCopyCommand:
4983              {
4984                state|=ExitState;
4985                break;
4986              }
4987              case RectifyHelpCommand:
4988              {
4989                (void) XSetFunction(display,windows->image.highlight_context,
4990                  GXcopy);
4991                switch (mode)
4992                {
4993                  case CopyMode:
4994                  {
4995                    XTextViewWidget(display,resource_info,windows,MagickFalse,
4996                      "Help Viewer - Image Copy",ImageCopyHelp);
4997                    break;
4998                  }
4999                  case CropMode:
5000                  {
5001                    XTextViewWidget(display,resource_info,windows,MagickFalse,
5002                      "Help Viewer - Image Crop",ImageCropHelp);
5003                    break;
5004                  }
5005                  case CutMode:
5006                  {
5007                    XTextViewWidget(display,resource_info,windows,MagickFalse,
5008                      "Help Viewer - Image Cut",ImageCutHelp);
5009                    break;
5010                  }
5011                }
5012                (void) XSetFunction(display,windows->image.highlight_context,
5013                  GXinvert);
5014                break;
5015              }
5016              case RectifyDismissCommand:
5017              {
5018                /*
5019                  Prematurely exit.
5020                */
5021                state|=EscapeState;
5022                state|=ExitState;
5023                break;
5024              }
5025              default:
5026                break;
5027            }
5028          continue;
5029        }
5030      XHighlightRectangle(display,windows->image.id,
5031        windows->image.highlight_context,&highlight_info);
5032      switch (event.type)
5033      {
5034        case ButtonPress:
5035        {
5036          if (event.xbutton.button != Button1)
5037            break;
5038          if (event.xbutton.window != windows->image.id)
5039            break;
5040          x=windows->image.x+event.xbutton.x;
5041          y=windows->image.y+event.xbutton.y;
5042          if ((x < (int) (crop_info.x+RoiDelta)) &&
5043              (x > (int) (crop_info.x-RoiDelta)) &&
5044              (y < (int) (crop_info.y+RoiDelta)) &&
5045              (y > (int) (crop_info.y-RoiDelta)))
5046            {
5047              crop_info.x=(ssize_t) (crop_info.x+crop_info.width);
5048              crop_info.y=(ssize_t) (crop_info.y+crop_info.height);
5049              state|=UpdateConfigurationState;
5050              break;
5051            }
5052          if ((x < (int) (crop_info.x+RoiDelta)) &&
5053              (x > (int) (crop_info.x-RoiDelta)) &&
5054              (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5055              (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5056            {
5057              crop_info.x=(ssize_t) (crop_info.x+crop_info.width);
5058              state|=UpdateConfigurationState;
5059              break;
5060            }
5061          if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5062              (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5063              (y < (int) (crop_info.y+RoiDelta)) &&
5064              (y > (int) (crop_info.y-RoiDelta)))
5065            {
5066              crop_info.y=(ssize_t) (crop_info.y+crop_info.height);
5067              state|=UpdateConfigurationState;
5068              break;
5069            }
5070          if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5071              (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5072              (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5073              (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5074            {
5075              state|=UpdateConfigurationState;
5076              break;
5077            }
5078        }
5079        case ButtonRelease:
5080        {
5081          if (event.xbutton.window == windows->pan.id)
5082            if ((highlight_info.x != crop_info.x-windows->image.x) ||
5083                (highlight_info.y != crop_info.y-windows->image.y))
5084              XHighlightRectangle(display,windows->image.id,
5085                windows->image.highlight_context,&highlight_info);
5086          (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5087            event.xbutton.time);
5088          break;
5089        }
5090        case Expose:
5091        {
5092          if (event.xexpose.window == windows->image.id)
5093            if (event.xexpose.count == 0)
5094              {
5095                event.xexpose.x=(int) highlight_info.x;
5096                event.xexpose.y=(int) highlight_info.y;
5097                event.xexpose.width=(int) highlight_info.width;
5098                event.xexpose.height=(int) highlight_info.height;
5099                XRefreshWindow(display,&windows->image,&event);
5100              }
5101          if (event.xexpose.window == windows->info.id)
5102            if (event.xexpose.count == 0)
5103              XInfoWidget(display,windows,text);
5104          break;
5105        }
5106        case KeyPress:
5107        {
5108          if (event.xkey.window != windows->image.id)
5109            break;
5110          /*
5111            Respond to a user key press.
5112          */
5113          (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5114            sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5115          switch ((int) key_symbol)
5116          {
5117            case XK_Escape:
5118            case XK_F20:
5119              state|=EscapeState;
5120            case XK_Return:
5121            {
5122              state|=ExitState;
5123              break;
5124            }
5125            case XK_Home:
5126            case XK_KP_Home:
5127            {
5128              crop_info.x=(ssize_t) (windows->image.width/2L-
5129                crop_info.width/2L);
5130              crop_info.y=(ssize_t) (windows->image.height/2L-
5131                crop_info.height/2L);
5132              break;
5133            }
5134            case XK_Left:
5135            case XK_KP_Left:
5136            {
5137              crop_info.x--;
5138              break;
5139            }
5140            case XK_Up:
5141            case XK_KP_Up:
5142            case XK_Next:
5143            {
5144              crop_info.y--;
5145              break;
5146            }
5147            case XK_Right:
5148            case XK_KP_Right:
5149            {
5150              crop_info.x++;
5151              break;
5152            }
5153            case XK_Prior:
5154            case XK_Down:
5155            case XK_KP_Down:
5156            {
5157              crop_info.y++;
5158              break;
5159            }
5160            case XK_F1:
5161            case XK_Help:
5162            {
5163              (void) XSetFunction(display,windows->image.highlight_context,
5164                GXcopy);
5165              switch (mode)
5166              {
5167                case CopyMode:
5168                {
5169                  XTextViewWidget(display,resource_info,windows,MagickFalse,
5170                    "Help Viewer - Image Copy",ImageCopyHelp);
5171                  break;
5172                }
5173                case CropMode:
5174                {
5175                  XTextViewWidget(display,resource_info,windows,MagickFalse,
5176                    "Help Viewer - Image Cropg",ImageCropHelp);
5177                  break;
5178                }
5179                case CutMode:
5180                {
5181                  XTextViewWidget(display,resource_info,windows,MagickFalse,
5182                    "Help Viewer - Image Cutg",ImageCutHelp);
5183                  break;
5184                }
5185              }
5186              (void) XSetFunction(display,windows->image.highlight_context,
5187                GXinvert);
5188              break;
5189            }
5190            default:
5191            {
5192              (void) XBell(display,0);
5193              break;
5194            }
5195          }
5196          (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5197            event.xkey.time);
5198          break;
5199        }
5200        case KeyRelease:
5201          break;
5202        case MotionNotify:
5203        {
5204          if (event.xmotion.window != windows->image.id)
5205            break;
5206          /*
5207            Map and unmap Info widget as text cursor crosses its boundaries.
5208          */
5209          x=event.xmotion.x;
5210          y=event.xmotion.y;
5211          if (windows->info.mapped != MagickFalse)
5212            {
5213              if ((x < (int) (windows->info.x+windows->info.width)) &&
5214                  (y < (int) (windows->info.y+windows->info.height)))
5215                (void) XWithdrawWindow(display,windows->info.id,
5216                  windows->info.screen);
5217            }
5218          else
5219            if ((x > (int) (windows->info.x+windows->info.width)) ||
5220                (y > (int) (windows->info.y+windows->info.height)))
5221              (void) XMapWindow(display,windows->info.id);
5222          crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
5223          crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
5224          break;
5225        }
5226        case SelectionRequest:
5227        {
5228          XSelectionEvent
5229            notify;
5230
5231          XSelectionRequestEvent
5232            *request;
5233
5234          /*
5235            Set primary selection.
5236          */
5237          (void) FormatLocaleString(text,MaxTextExtent,
5238            "%.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
5239            crop_info.height,(double) crop_info.x,(double) crop_info.y);
5240          request=(&(event.xselectionrequest));
5241          (void) XChangeProperty(request->display,request->requestor,
5242            request->property,request->target,8,PropModeReplace,
5243            (unsigned char *) text,(int) strlen(text));
5244          notify.type=SelectionNotify;
5245          notify.display=request->display;
5246          notify.requestor=request->requestor;
5247          notify.selection=request->selection;
5248          notify.target=request->target;
5249          notify.time=request->time;
5250          if (request->property == None)
5251            notify.property=request->target;
5252          else
5253            notify.property=request->property;
5254          (void) XSendEvent(request->display,request->requestor,False,0,
5255            (XEvent *) &notify);
5256        }
5257        default:
5258          break;
5259      }
5260      if ((state & UpdateConfigurationState) != 0)
5261        {
5262          (void) XPutBackEvent(display,&event);
5263          (void) XCheckDefineCursor(display,windows->image.id,cursor);
5264          break;
5265        }
5266    } while ((state & ExitState) == 0);
5267  } while ((state & ExitState) == 0);
5268  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
5269  XSetCursorState(display,windows,MagickFalse);
5270  if ((state & EscapeState) != 0)
5271    return(MagickTrue);
5272  if (mode == CropMode)
5273    if (((int) crop_info.width != windows->image.ximage->width) ||
5274        ((int) crop_info.height != windows->image.ximage->height))
5275      {
5276        /*
5277          Reconfigure Image window as defined by cropping rectangle.
5278        */
5279        XSetCropGeometry(display,windows,&crop_info,image);
5280        windows->image.window_changes.width=(int) crop_info.width;
5281        windows->image.window_changes.height=(int) crop_info.height;
5282        (void) XConfigureImage(display,resource_info,windows,image);
5283        return(MagickTrue);
5284      }
5285  /*
5286    Copy image before applying image transforms.
5287  */
5288  XSetCursorState(display,windows,MagickTrue);
5289  XCheckRefreshWindows(display,windows);
5290  width=(unsigned int) image->columns;
5291  height=(unsigned int) image->rows;
5292  x=0;
5293  y=0;
5294  if (windows->image.crop_geometry != (char *) NULL)
5295    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
5296  scale_factor=(MagickRealType) width/windows->image.ximage->width;
5297  crop_info.x+=x;
5298  crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
5299  crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
5300  scale_factor=(MagickRealType) height/windows->image.ximage->height;
5301  crop_info.y+=y;
5302  crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
5303  crop_info.height=(unsigned int) (scale_factor*crop_info.height+0.5);
5304  crop_image=CropImage(image,&crop_info,&image->exception);
5305  XSetCursorState(display,windows,MagickFalse);
5306  if (crop_image == (Image *) NULL)
5307    return(MagickFalse);
5308  if (resource_info->copy_image != (Image *) NULL)
5309    resource_info->copy_image=DestroyImage(resource_info->copy_image);
5310  resource_info->copy_image=crop_image;
5311  if (mode == CopyMode)
5312    {
5313      (void) XConfigureImage(display,resource_info,windows,image);
5314      return(MagickTrue);
5315    }
5316  /*
5317    Cut image.
5318  */
5319  exception=(&image->exception);
5320  if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
5321    return(MagickFalse);
5322  image->matte=MagickTrue;
5323  image_view=AcquireCacheView(image);
5324  for (y=0; y < (int) crop_info.height; y++)
5325  {
5326    q=GetCacheViewAuthenticPixels(image_view,crop_info.x,y+crop_info.y,
5327      crop_info.width,1,exception);
5328    if (q == (Quantum *) NULL)
5329      break;
5330    for (x=0; x < (int) crop_info.width; x++)
5331    {
5332      SetPixelAlpha(image,TransparentAlpha,q);
5333      q+=GetPixelChannels(image);
5334    }
5335    if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
5336      break;
5337  }
5338  image_view=DestroyCacheView(image_view);
5339  /*
5340    Update image configuration.
5341  */
5342  XConfigureImageColormap(display,resource_info,windows,image);
5343  (void) XConfigureImage(display,resource_info,windows,image);
5344  return(MagickTrue);
5345}
5346
5347/*
5348%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5349%                                                                             %
5350%                                                                             %
5351%                                                                             %
5352+   X D r a w I m a g e                                                       %
5353%                                                                             %
5354%                                                                             %
5355%                                                                             %
5356%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5357%
5358%  XDrawEditImage() draws a graphic element (point, line, rectangle, etc.) on
5359%  the image.
5360%
5361%  The format of the XDrawEditImage method is:
5362%
5363%      MagickBooleanType XDrawEditImage(Display *display,
5364%        XResourceInfo *resource_info,XWindows *windows,Image **image)
5365%
5366%  A description of each parameter follows:
5367%
5368%    o display: Specifies a connection to an X server; returned from
5369%      XOpenDisplay.
5370%
5371%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
5372%
5373%    o windows: Specifies a pointer to a XWindows structure.
5374%
5375%    o image: the image.
5376%
5377*/
5378static MagickBooleanType XDrawEditImage(Display *display,
5379  XResourceInfo *resource_info,XWindows *windows,Image **image)
5380{
5381  static const char
5382    *DrawMenu[] =
5383    {
5384      "Element",
5385      "Color",
5386      "Stipple",
5387      "Width",
5388      "Undo",
5389      "Help",
5390      "Dismiss",
5391      (char *) NULL
5392    };
5393
5394  static ElementType
5395    element = PointElement;
5396
5397  static const ModeType
5398    DrawCommands[] =
5399    {
5400      DrawElementCommand,
5401      DrawColorCommand,
5402      DrawStippleCommand,
5403      DrawWidthCommand,
5404      DrawUndoCommand,
5405      DrawHelpCommand,
5406      DrawDismissCommand
5407    };
5408
5409  static Pixmap
5410    stipple = (Pixmap) NULL;
5411
5412  static unsigned int
5413    pen_id = 0,
5414    line_width = 1;
5415
5416  char
5417    command[MaxTextExtent],
5418    text[MaxTextExtent];
5419
5420  Cursor
5421    cursor;
5422
5423  int
5424    entry,
5425    id,
5426    number_coordinates,
5427    x,
5428    y;
5429
5430  MagickRealType
5431    degrees;
5432
5433  MagickStatusType
5434    status;
5435
5436  RectangleInfo
5437    rectangle_info;
5438
5439  register int
5440    i;
5441
5442  unsigned int
5443    distance,
5444    height,
5445    max_coordinates,
5446    width;
5447
5448  size_t
5449    state;
5450
5451  Window
5452    root_window;
5453
5454  XDrawInfo
5455    draw_info;
5456
5457  XEvent
5458    event;
5459
5460  XPoint
5461    *coordinate_info;
5462
5463  XSegment
5464    line_info;
5465
5466  /*
5467    Allocate polygon info.
5468  */
5469  max_coordinates=2048;
5470  coordinate_info=(XPoint *) AcquireQuantumMemory((size_t) max_coordinates,
5471    sizeof(*coordinate_info));
5472  if (coordinate_info == (XPoint *) NULL)
5473    {
5474      (void) ThrowMagickException(&(*image)->exception,GetMagickModule(),
5475        ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
5476      return(MagickFalse);
5477    }
5478  /*
5479    Map Command widget.
5480  */
5481  (void) CloneString(&windows->command.name,"Draw");
5482  windows->command.data=4;
5483  (void) XCommandWidget(display,windows,DrawMenu,(XEvent *) NULL);
5484  (void) XMapRaised(display,windows->command.id);
5485  XClientMessage(display,windows->image.id,windows->im_protocols,
5486    windows->im_update_widget,CurrentTime);
5487  /*
5488    Wait for first button press.
5489  */
5490  root_window=XRootWindow(display,XDefaultScreen(display));
5491  draw_info.stencil=OpaqueStencil;
5492  status=MagickTrue;
5493  cursor=XCreateFontCursor(display,XC_tcross);
5494  for ( ; ; )
5495  {
5496    XQueryPosition(display,windows->image.id,&x,&y);
5497    (void) XSelectInput(display,windows->image.id,
5498      windows->image.attributes.event_mask | PointerMotionMask);
5499    (void) XCheckDefineCursor(display,windows->image.id,cursor);
5500    state=DefaultState;
5501    do
5502    {
5503      if (windows->info.mapped != MagickFalse)
5504        {
5505          /*
5506            Display pointer position.
5507          */
5508          (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
5509            x+windows->image.x,y+windows->image.y);
5510          XInfoWidget(display,windows,text);
5511        }
5512      /*
5513        Wait for next event.
5514      */
5515      XScreenEvent(display,windows,&event);
5516      if (event.xany.window == windows->command.id)
5517        {
5518          /*
5519            Select a command from the Command widget.
5520          */
5521          id=XCommandWidget(display,windows,DrawMenu,&event);
5522          if (id < 0)
5523            continue;
5524          switch (DrawCommands[id])
5525          {
5526            case DrawElementCommand:
5527            {
5528              static const char
5529                *Elements[] =
5530                {
5531                  "point",
5532                  "line",
5533                  "rectangle",
5534                  "fill rectangle",
5535                  "circle",
5536                  "fill circle",
5537                  "ellipse",
5538                  "fill ellipse",
5539                  "polygon",
5540                  "fill polygon",
5541                  (char *) NULL,
5542                };
5543
5544              /*
5545                Select a command from the pop-up menu.
5546              */
5547              element=(ElementType) (XMenuWidget(display,windows,
5548                DrawMenu[id],Elements,command)+1);
5549              break;
5550            }
5551            case DrawColorCommand:
5552            {
5553              const char
5554                *ColorMenu[MaxNumberPens+1];
5555
5556              int
5557                pen_number;
5558
5559              MagickBooleanType
5560                transparent;
5561
5562              XColor
5563                color;
5564
5565              /*
5566                Initialize menu selections.
5567              */
5568              for (i=0; i < (int) (MaxNumberPens-2); i++)
5569                ColorMenu[i]=resource_info->pen_colors[i];
5570              ColorMenu[MaxNumberPens-2]="transparent";
5571              ColorMenu[MaxNumberPens-1]="Browser...";
5572              ColorMenu[MaxNumberPens]=(char *) NULL;
5573              /*
5574                Select a pen color from the pop-up menu.
5575              */
5576              pen_number=XMenuWidget(display,windows,DrawMenu[id],
5577                (const char **) ColorMenu,command);
5578              if (pen_number < 0)
5579                break;
5580              transparent=pen_number == (MaxNumberPens-2) ? MagickTrue :
5581                MagickFalse;
5582              if (transparent != MagickFalse)
5583                {
5584                  draw_info.stencil=TransparentStencil;
5585                  break;
5586                }
5587              if (pen_number == (MaxNumberPens-1))
5588                {
5589                  static char
5590                    color_name[MaxTextExtent] = "gray";
5591
5592                  /*
5593                    Select a pen color from a dialog.
5594                  */
5595                  resource_info->pen_colors[pen_number]=color_name;
5596                  XColorBrowserWidget(display,windows,"Select",color_name);
5597                  if (*color_name == '\0')
5598                    break;
5599                }
5600              /*
5601                Set pen color.
5602              */
5603              (void) XParseColor(display,windows->map_info->colormap,
5604                resource_info->pen_colors[pen_number],&color);
5605              XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
5606                (unsigned int) MaxColors,&color);
5607              windows->pixel_info->pen_colors[pen_number]=color;
5608              pen_id=(unsigned int) pen_number;
5609              draw_info.stencil=OpaqueStencil;
5610              break;
5611            }
5612            case DrawStippleCommand:
5613            {
5614              Image
5615                *stipple_image;
5616
5617              ImageInfo
5618                *image_info;
5619
5620              int
5621                status;
5622
5623              static char
5624                filename[MaxTextExtent] = "\0";
5625
5626              static const char
5627                *StipplesMenu[] =
5628                {
5629                  "Brick",
5630                  "Diagonal",
5631                  "Scales",
5632                  "Vertical",
5633                  "Wavy",
5634                  "Translucent",
5635                  "Opaque",
5636                  (char *) NULL,
5637                  (char *) NULL,
5638                };
5639
5640              /*
5641                Select a command from the pop-up menu.
5642              */
5643              StipplesMenu[7]="Open...";
5644              entry=XMenuWidget(display,windows,DrawMenu[id],StipplesMenu,
5645                command);
5646              if (entry < 0)
5647                break;
5648              if (stipple != (Pixmap) NULL)
5649                (void) XFreePixmap(display,stipple);
5650              stipple=(Pixmap) NULL;
5651              if (entry != 7)
5652                {
5653                  switch (entry)
5654                  {
5655                    case 0:
5656                    {
5657                      stipple=XCreateBitmapFromData(display,root_window,
5658                        (char *) BricksBitmap,BricksWidth,BricksHeight);
5659                      break;
5660                    }
5661                    case 1:
5662                    {
5663                      stipple=XCreateBitmapFromData(display,root_window,
5664                        (char *) DiagonalBitmap,DiagonalWidth,DiagonalHeight);
5665                      break;
5666                    }
5667                    case 2:
5668                    {
5669                      stipple=XCreateBitmapFromData(display,root_window,
5670                        (char *) ScalesBitmap,ScalesWidth,ScalesHeight);
5671                      break;
5672                    }
5673                    case 3:
5674                    {
5675                      stipple=XCreateBitmapFromData(display,root_window,
5676                        (char *) VerticalBitmap,VerticalWidth,VerticalHeight);
5677                      break;
5678                    }
5679                    case 4:
5680                    {
5681                      stipple=XCreateBitmapFromData(display,root_window,
5682                        (char *) WavyBitmap,WavyWidth,WavyHeight);
5683                      break;
5684                    }
5685                    case 5:
5686                    {
5687                      stipple=XCreateBitmapFromData(display,root_window,
5688                        (char *) HighlightBitmap,HighlightWidth,
5689                        HighlightHeight);
5690                      break;
5691                    }
5692                    case 6:
5693                    default:
5694                    {
5695                      stipple=XCreateBitmapFromData(display,root_window,
5696                        (char *) OpaqueBitmap,OpaqueWidth,OpaqueHeight);
5697                      break;
5698                    }
5699                  }
5700                  break;
5701                }
5702              XFileBrowserWidget(display,windows,"Stipple",filename);
5703              if (*filename == '\0')
5704                break;
5705              /*
5706                Read image.
5707              */
5708              XSetCursorState(display,windows,MagickTrue);
5709              XCheckRefreshWindows(display,windows);
5710              image_info=AcquireImageInfo();
5711              (void) CopyMagickString(image_info->filename,filename,
5712                MaxTextExtent);
5713              stipple_image=ReadImage(image_info,&(*image)->exception);
5714              CatchException(&(*image)->exception);
5715              XSetCursorState(display,windows,MagickFalse);
5716              if (stipple_image == (Image *) NULL)
5717                break;
5718              (void) AcquireUniqueFileResource(filename);
5719              (void) FormatLocaleString(stipple_image->filename,MaxTextExtent,
5720                "xbm:%s",filename);
5721              (void) WriteImage(image_info,stipple_image);
5722              stipple_image=DestroyImage(stipple_image);
5723              image_info=DestroyImageInfo(image_info);
5724              status=XReadBitmapFile(display,root_window,filename,&width,
5725                &height,&stipple,&x,&y);
5726              (void) RelinquishUniqueFileResource(filename);
5727              if ((status != BitmapSuccess) != 0)
5728                XNoticeWidget(display,windows,"Unable to read X bitmap image:",
5729                  filename);
5730              break;
5731            }
5732            case DrawWidthCommand:
5733            {
5734              static char
5735                width[MaxTextExtent] = "0";
5736
5737              static const char
5738                *WidthsMenu[] =
5739                {
5740                  "1",
5741                  "2",
5742                  "4",
5743                  "8",
5744                  "16",
5745                  "Dialog...",
5746                  (char *) NULL,
5747                };
5748
5749              /*
5750                Select a command from the pop-up menu.
5751              */
5752              entry=XMenuWidget(display,windows,DrawMenu[id],WidthsMenu,
5753                command);
5754              if (entry < 0)
5755                break;
5756              if (entry != 5)
5757                {
5758                  line_width=(unsigned int) StringToUnsignedLong(
5759                    WidthsMenu[entry]);
5760                  break;
5761                }
5762              (void) XDialogWidget(display,windows,"Ok","Enter line width:",
5763                width);
5764              if (*width == '\0')
5765                break;
5766              line_width=(unsigned int) StringToUnsignedLong(width);
5767              break;
5768            }
5769            case DrawUndoCommand:
5770            {
5771              (void) XMagickCommand(display,resource_info,windows,UndoCommand,
5772                image);
5773              break;
5774            }
5775            case DrawHelpCommand:
5776            {
5777              XTextViewWidget(display,resource_info,windows,MagickFalse,
5778                "Help Viewer - Image Rotation",ImageDrawHelp);
5779              (void) XCheckDefineCursor(display,windows->image.id,cursor);
5780              break;
5781            }
5782            case DrawDismissCommand:
5783            {
5784              /*
5785                Prematurely exit.
5786              */
5787              state|=EscapeState;
5788              state|=ExitState;
5789              break;
5790            }
5791            default:
5792              break;
5793          }
5794          (void) XCheckDefineCursor(display,windows->image.id,cursor);
5795          continue;
5796        }
5797      switch (event.type)
5798      {
5799        case ButtonPress:
5800        {
5801          if (event.xbutton.button != Button1)
5802            break;
5803          if (event.xbutton.window != windows->image.id)
5804            break;
5805          /*
5806            exit loop.
5807          */
5808          x=event.xbutton.x;
5809          y=event.xbutton.y;
5810          state|=ExitState;
5811          break;
5812        }
5813        case ButtonRelease:
5814          break;
5815        case Expose:
5816          break;
5817        case KeyPress:
5818        {
5819          KeySym
5820            key_symbol;
5821
5822          if (event.xkey.window != windows->image.id)
5823            break;
5824          /*
5825            Respond to a user key press.
5826          */
5827          (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5828            sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5829          switch ((int) key_symbol)
5830          {
5831            case XK_Escape:
5832            case XK_F20:
5833            {
5834              /*
5835                Prematurely exit.
5836              */
5837              state|=EscapeState;
5838              state|=ExitState;
5839              break;
5840            }
5841            case XK_F1:
5842            case XK_Help:
5843            {
5844              XTextViewWidget(display,resource_info,windows,MagickFalse,
5845                "Help Viewer - Image Rotation",ImageDrawHelp);
5846              break;
5847            }
5848            default:
5849            {
5850              (void) XBell(display,0);
5851              break;
5852            }
5853          }
5854          break;
5855        }
5856        case MotionNotify:
5857        {
5858          /*
5859            Map and unmap Info widget as text cursor crosses its boundaries.
5860          */
5861          x=event.xmotion.x;
5862          y=event.xmotion.y;
5863          if (windows->info.mapped != MagickFalse)
5864            {
5865              if ((x < (int) (windows->info.x+windows->info.width)) &&
5866                  (y < (int) (windows->info.y+windows->info.height)))
5867                (void) XWithdrawWindow(display,windows->info.id,
5868                  windows->info.screen);
5869            }
5870          else
5871            if ((x > (int) (windows->info.x+windows->info.width)) ||
5872                (y > (int) (windows->info.y+windows->info.height)))
5873              (void) XMapWindow(display,windows->info.id);
5874          break;
5875        }
5876      }
5877    } while ((state & ExitState) == 0);
5878    (void) XSelectInput(display,windows->image.id,
5879      windows->image.attributes.event_mask);
5880    (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
5881    if ((state & EscapeState) != 0)
5882      break;
5883    /*
5884      Draw element as pointer moves until the button is released.
5885    */
5886    distance=0;
5887    degrees=0.0;
5888    line_info.x1=x;
5889    line_info.y1=y;
5890    line_info.x2=x;
5891    line_info.y2=y;
5892    rectangle_info.x=(ssize_t) x;
5893    rectangle_info.y=(ssize_t) y;
5894    rectangle_info.width=0;
5895    rectangle_info.height=0;
5896    number_coordinates=1;
5897    coordinate_info->x=x;
5898    coordinate_info->y=y;
5899    (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
5900    state=DefaultState;
5901    do
5902    {
5903      switch (element)
5904      {
5905        case PointElement:
5906        default:
5907        {
5908          if (number_coordinates > 1)
5909            {
5910              (void) XDrawLines(display,windows->image.id,
5911                windows->image.highlight_context,coordinate_info,
5912                number_coordinates,CoordModeOrigin);
5913              (void) FormatLocaleString(text,MaxTextExtent," %+d%+d",
5914                coordinate_info[number_coordinates-1].x,
5915                coordinate_info[number_coordinates-1].y);
5916              XInfoWidget(display,windows,text);
5917            }
5918          break;
5919        }
5920        case LineElement:
5921        {
5922          if (distance > 9)
5923            {
5924              /*
5925                Display angle of the line.
5926              */
5927              degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
5928                line_info.y1),(double) (line_info.x2-line_info.x1)));
5929              (void) FormatLocaleString(text,MaxTextExtent," %g",
5930                (double) degrees);
5931              XInfoWidget(display,windows,text);
5932              XHighlightLine(display,windows->image.id,
5933                windows->image.highlight_context,&line_info);
5934            }
5935          else
5936            if (windows->info.mapped != MagickFalse)
5937              (void) XWithdrawWindow(display,windows->info.id,
5938                windows->info.screen);
5939          break;
5940        }
5941        case RectangleElement:
5942        case FillRectangleElement:
5943        {
5944          if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
5945            {
5946              /*
5947                Display info and draw drawing rectangle.
5948              */
5949              (void) FormatLocaleString(text,MaxTextExtent,
5950                " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
5951                (double) rectangle_info.height,(double) rectangle_info.x,
5952                (double) rectangle_info.y);
5953              XInfoWidget(display,windows,text);
5954              XHighlightRectangle(display,windows->image.id,
5955                windows->image.highlight_context,&rectangle_info);
5956            }
5957          else
5958            if (windows->info.mapped != MagickFalse)
5959              (void) XWithdrawWindow(display,windows->info.id,
5960                windows->info.screen);
5961          break;
5962        }
5963        case CircleElement:
5964        case FillCircleElement:
5965        case EllipseElement:
5966        case FillEllipseElement:
5967        {
5968          if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
5969            {
5970              /*
5971                Display info and draw drawing rectangle.
5972              */
5973              (void) FormatLocaleString(text,MaxTextExtent,
5974                " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
5975                (double) rectangle_info.height,(double) rectangle_info.x,
5976                (double) rectangle_info.y);
5977              XInfoWidget(display,windows,text);
5978              XHighlightEllipse(display,windows->image.id,
5979                windows->image.highlight_context,&rectangle_info);
5980            }
5981          else
5982            if (windows->info.mapped != MagickFalse)
5983              (void) XWithdrawWindow(display,windows->info.id,
5984                windows->info.screen);
5985          break;
5986        }
5987        case PolygonElement:
5988        case FillPolygonElement:
5989        {
5990          if (number_coordinates > 1)
5991            (void) XDrawLines(display,windows->image.id,
5992              windows->image.highlight_context,coordinate_info,
5993              number_coordinates,CoordModeOrigin);
5994          if (distance > 9)
5995            {
5996              /*
5997                Display angle of the line.
5998              */
5999              degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
6000                line_info.y1),(double) (line_info.x2-line_info.x1)));
6001              (void) FormatLocaleString(text,MaxTextExtent," %g",
6002                (double) degrees);
6003              XInfoWidget(display,windows,text);
6004              XHighlightLine(display,windows->image.id,
6005                windows->image.highlight_context,&line_info);
6006            }
6007          else
6008            if (windows->info.mapped != MagickFalse)
6009              (void) XWithdrawWindow(display,windows->info.id,
6010                windows->info.screen);
6011          break;
6012        }
6013      }
6014      /*
6015        Wait for next event.
6016      */
6017      XScreenEvent(display,windows,&event);
6018      switch (element)
6019      {
6020        case PointElement:
6021        default:
6022        {
6023          if (number_coordinates > 1)
6024            (void) XDrawLines(display,windows->image.id,
6025              windows->image.highlight_context,coordinate_info,
6026              number_coordinates,CoordModeOrigin);
6027          break;
6028        }
6029        case LineElement:
6030        {
6031          if (distance > 9)
6032            XHighlightLine(display,windows->image.id,
6033              windows->image.highlight_context,&line_info);
6034          break;
6035        }
6036        case RectangleElement:
6037        case FillRectangleElement:
6038        {
6039          if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6040            XHighlightRectangle(display,windows->image.id,
6041              windows->image.highlight_context,&rectangle_info);
6042          break;
6043        }
6044        case CircleElement:
6045        case FillCircleElement:
6046        case EllipseElement:
6047        case FillEllipseElement:
6048        {
6049          if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6050            XHighlightEllipse(display,windows->image.id,
6051              windows->image.highlight_context,&rectangle_info);
6052          break;
6053        }
6054        case PolygonElement:
6055        case FillPolygonElement:
6056        {
6057          if (number_coordinates > 1)
6058            (void) XDrawLines(display,windows->image.id,
6059              windows->image.highlight_context,coordinate_info,
6060              number_coordinates,CoordModeOrigin);
6061          if (distance > 9)
6062            XHighlightLine(display,windows->image.id,
6063              windows->image.highlight_context,&line_info);
6064          break;
6065        }
6066      }
6067      switch (event.type)
6068      {
6069        case ButtonPress:
6070          break;
6071        case ButtonRelease:
6072        {
6073          /*
6074            User has committed to element.
6075          */
6076          line_info.x2=event.xbutton.x;
6077          line_info.y2=event.xbutton.y;
6078          rectangle_info.x=(ssize_t) event.xbutton.x;
6079          rectangle_info.y=(ssize_t) event.xbutton.y;
6080          coordinate_info[number_coordinates].x=event.xbutton.x;
6081          coordinate_info[number_coordinates].y=event.xbutton.y;
6082          if (((element != PolygonElement) &&
6083               (element != FillPolygonElement)) || (distance <= 9))
6084            {
6085              state|=ExitState;
6086              break;
6087            }
6088          number_coordinates++;
6089          if (number_coordinates < (int) max_coordinates)
6090            {
6091              line_info.x1=event.xbutton.x;
6092              line_info.y1=event.xbutton.y;
6093              break;
6094            }
6095          max_coordinates<<=1;
6096          coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6097            max_coordinates,sizeof(*coordinate_info));
6098          if (coordinate_info == (XPoint *) NULL)
6099            (void) ThrowMagickException(&(*image)->exception,GetMagickModule(),
6100              ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6101          break;
6102        }
6103        case Expose:
6104          break;
6105        case MotionNotify:
6106        {
6107          if (event.xmotion.window != windows->image.id)
6108            break;
6109          if (element != PointElement)
6110            {
6111              line_info.x2=event.xmotion.x;
6112              line_info.y2=event.xmotion.y;
6113              rectangle_info.x=(ssize_t) event.xmotion.x;
6114              rectangle_info.y=(ssize_t) event.xmotion.y;
6115              break;
6116            }
6117          coordinate_info[number_coordinates].x=event.xbutton.x;
6118          coordinate_info[number_coordinates].y=event.xbutton.y;
6119          number_coordinates++;
6120          if (number_coordinates < (int) max_coordinates)
6121            break;
6122          max_coordinates<<=1;
6123          coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6124            max_coordinates,sizeof(*coordinate_info));
6125          if (coordinate_info == (XPoint *) NULL)
6126            (void) ThrowMagickException(&(*image)->exception,GetMagickModule(),
6127              ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6128          break;
6129        }
6130        default:
6131          break;
6132      }
6133      /*
6134        Check boundary conditions.
6135      */
6136      if (line_info.x2 < 0)
6137        line_info.x2=0;
6138      else
6139        if (line_info.x2 > (int) windows->image.width)
6140          line_info.x2=(short) windows->image.width;
6141      if (line_info.y2 < 0)
6142        line_info.y2=0;
6143      else
6144        if (line_info.y2 > (int) windows->image.height)
6145          line_info.y2=(short) windows->image.height;
6146      distance=(unsigned int)
6147        (((line_info.x2-line_info.x1+1)*(line_info.x2-line_info.x1+1))+
6148         ((line_info.y2-line_info.y1+1)*(line_info.y2-line_info.y1+1)));
6149      if ((((int) rectangle_info.x != x) && ((int) rectangle_info.y != y)) ||
6150          ((state & ExitState) != 0))
6151        {
6152          if (rectangle_info.x < 0)
6153            rectangle_info.x=0;
6154          else
6155            if (rectangle_info.x > (ssize_t) windows->image.width)
6156              rectangle_info.x=(ssize_t) windows->image.width;
6157          if ((int) rectangle_info.x < x)
6158            rectangle_info.width=(unsigned int) (x-rectangle_info.x);
6159          else
6160            {
6161              rectangle_info.width=(unsigned int) (rectangle_info.x-x);
6162              rectangle_info.x=(ssize_t) x;
6163            }
6164          if (rectangle_info.y < 0)
6165            rectangle_info.y=0;
6166          else
6167            if (rectangle_info.y > (ssize_t) windows->image.height)
6168              rectangle_info.y=(ssize_t) windows->image.height;
6169          if ((int) rectangle_info.y < y)
6170            rectangle_info.height=(unsigned int) (y-rectangle_info.y);
6171          else
6172            {
6173              rectangle_info.height=(unsigned int) (rectangle_info.y-y);
6174              rectangle_info.y=(ssize_t) y;
6175            }
6176        }
6177    } while ((state & ExitState) == 0);
6178    (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
6179    if ((element == PointElement) || (element == PolygonElement) ||
6180        (element == FillPolygonElement))
6181      {
6182        /*
6183          Determine polygon bounding box.
6184        */
6185        rectangle_info.x=(ssize_t) coordinate_info->x;
6186        rectangle_info.y=(ssize_t) coordinate_info->y;
6187        x=coordinate_info->x;
6188        y=coordinate_info->y;
6189        for (i=1; i < number_coordinates; i++)
6190        {
6191          if (coordinate_info[i].x > x)
6192            x=coordinate_info[i].x;
6193          if (coordinate_info[i].y > y)
6194            y=coordinate_info[i].y;
6195          if ((ssize_t) coordinate_info[i].x < rectangle_info.x)
6196            rectangle_info.x=MagickMax((ssize_t) coordinate_info[i].x,0);
6197          if ((ssize_t) coordinate_info[i].y < rectangle_info.y)
6198            rectangle_info.y=MagickMax((ssize_t) coordinate_info[i].y,0);
6199        }
6200        rectangle_info.width=(size_t) (x-rectangle_info.x);
6201        rectangle_info.height=(size_t) (y-rectangle_info.y);
6202        for (i=0; i < number_coordinates; i++)
6203        {
6204          coordinate_info[i].x-=rectangle_info.x;
6205          coordinate_info[i].y-=rectangle_info.y;
6206        }
6207      }
6208    else
6209      if (distance <= 9)
6210        continue;
6211      else
6212        if ((element == RectangleElement) ||
6213            (element == CircleElement) || (element == EllipseElement))
6214          {
6215            rectangle_info.width--;
6216            rectangle_info.height--;
6217          }
6218    /*
6219      Drawing is relative to image configuration.
6220    */
6221    draw_info.x=(int) rectangle_info.x;
6222    draw_info.y=(int) rectangle_info.y;
6223    (void) XMagickCommand(display,resource_info,windows,SaveToUndoBufferCommand,
6224      image);
6225    width=(unsigned int) (*image)->columns;
6226    height=(unsigned int) (*image)->rows;
6227    x=0;
6228    y=0;
6229    if (windows->image.crop_geometry != (char *) NULL)
6230      (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
6231    draw_info.x+=windows->image.x-(line_width/2);
6232    if (draw_info.x < 0)
6233      draw_info.x=0;
6234    draw_info.x=(int) (width*draw_info.x/windows->image.ximage->width);
6235    draw_info.y+=windows->image.y-(line_width/2);
6236    if (draw_info.y < 0)
6237      draw_info.y=0;
6238    draw_info.y=(int) height*draw_info.y/windows->image.ximage->height;
6239    draw_info.width=(unsigned int) rectangle_info.width+(line_width << 1);
6240    if (draw_info.width > (unsigned int) (*image)->columns)
6241      draw_info.width=(unsigned int) (*image)->columns;
6242    draw_info.height=(unsigned int) rectangle_info.height+(line_width << 1);
6243    if (draw_info.height > (unsigned int) (*image)->rows)
6244      draw_info.height=(unsigned int) (*image)->rows;
6245    (void) FormatLocaleString(draw_info.geometry,MaxTextExtent,"%ux%u%+d%+d",
6246      width*draw_info.width/windows->image.ximage->width,
6247      height*draw_info.height/windows->image.ximage->height,
6248      draw_info.x+x,draw_info.y+y);
6249    /*
6250      Initialize drawing attributes.
6251    */
6252    draw_info.degrees=0.0;
6253    draw_info.element=element;
6254    draw_info.stipple=stipple;
6255    draw_info.line_width=line_width;
6256    draw_info.line_info=line_info;
6257    if (line_info.x1 > (int) (line_width/2))
6258      draw_info.line_info.x1=(short) line_width/2;
6259    if (line_info.y1 > (int) (line_width/2))
6260      draw_info.line_info.y1=(short) line_width/2;
6261    draw_info.line_info.x2=(short) (line_info.x2-line_info.x1+(line_width/2));
6262    draw_info.line_info.y2=(short) (line_info.y2-line_info.y1+(line_width/2));
6263    if ((draw_info.line_info.x2 < 0) && (draw_info.line_info.y2 < 0))
6264      {
6265        draw_info.line_info.x2=(-draw_info.line_info.x2);
6266        draw_info.line_info.y2=(-draw_info.line_info.y2);
6267      }
6268    if (draw_info.line_info.x2 < 0)
6269      {
6270        draw_info.line_info.x2=(-draw_info.line_info.x2);
6271        Swap(draw_info.line_info.x1,draw_info.line_info.x2);
6272      }
6273    if (draw_info.line_info.y2 < 0)
6274      {
6275        draw_info.line_info.y2=(-draw_info.line_info.y2);
6276        Swap(draw_info.line_info.y1,draw_info.line_info.y2);
6277      }
6278    draw_info.rectangle_info=rectangle_info;
6279    if (draw_info.rectangle_info.x > (ssize_t) (line_width/2))
6280      draw_info.rectangle_info.x=(ssize_t) line_width/2;
6281    if (draw_info.rectangle_info.y > (ssize_t) (line_width/2))
6282      draw_info.rectangle_info.y=(ssize_t) line_width/2;
6283    draw_info.number_coordinates=(unsigned int) number_coordinates;
6284    draw_info.coordinate_info=coordinate_info;
6285    windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
6286    /*
6287      Draw element on image.
6288    */
6289    XSetCursorState(display,windows,MagickTrue);
6290    XCheckRefreshWindows(display,windows);
6291    status=XDrawImage(display,windows->pixel_info,&draw_info,*image);
6292    XSetCursorState(display,windows,MagickFalse);
6293    /*
6294      Update image colormap and return to image drawing.
6295    */
6296    XConfigureImageColormap(display,resource_info,windows,*image);
6297    (void) XConfigureImage(display,resource_info,windows,*image);
6298  }
6299  XSetCursorState(display,windows,MagickFalse);
6300  coordinate_info=(XPoint *) RelinquishMagickMemory(coordinate_info);
6301  return(status != 0 ? MagickTrue : MagickFalse);
6302}
6303
6304/*
6305%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6306%                                                                             %
6307%                                                                             %
6308%                                                                             %
6309+   X D r a w P a n R e c t a n g l e                                         %
6310%                                                                             %
6311%                                                                             %
6312%                                                                             %
6313%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6314%
6315%  XDrawPanRectangle() draws a rectangle in the pan window.  The pan window
6316%  displays a zoom image and the rectangle shows which portion of the image is
6317%  displayed in the Image window.
6318%
6319%  The format of the XDrawPanRectangle method is:
6320%
6321%      XDrawPanRectangle(Display *display,XWindows *windows)
6322%
6323%  A description of each parameter follows:
6324%
6325%    o display: Specifies a connection to an X server;  returned from
6326%      XOpenDisplay.
6327%
6328%    o windows: Specifies a pointer to a XWindows structure.
6329%
6330*/
6331static void XDrawPanRectangle(Display *display,XWindows *windows)
6332{
6333  MagickRealType
6334    scale_factor;
6335
6336  RectangleInfo
6337    highlight_info;
6338
6339  /*
6340    Determine dimensions of the panning rectangle.
6341  */
6342  scale_factor=(MagickRealType) windows->pan.width/windows->image.ximage->width;
6343  highlight_info.x=(ssize_t) (scale_factor*windows->image.x+0.5);
6344  highlight_info.width=(unsigned int) (scale_factor*windows->image.width+0.5);
6345  scale_factor=(MagickRealType)
6346    windows->pan.height/windows->image.ximage->height;
6347  highlight_info.y=(ssize_t) (scale_factor*windows->image.y+0.5);
6348  highlight_info.height=(unsigned int) (scale_factor*windows->image.height+0.5);
6349  /*
6350    Display the panning rectangle.
6351  */
6352  (void) XClearWindow(display,windows->pan.id);
6353  XHighlightRectangle(display,windows->pan.id,windows->pan.annotate_context,
6354    &highlight_info);
6355}
6356
6357/*
6358%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6359%                                                                             %
6360%                                                                             %
6361%                                                                             %
6362+   X I m a g e C a c h e                                                     %
6363%                                                                             %
6364%                                                                             %
6365%                                                                             %
6366%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6367%
6368%  XImageCache() handles the creation, manipulation, and destruction of the
6369%  image cache (undo and redo buffers).
6370%
6371%  The format of the XImageCache method is:
6372%
6373%      void XImageCache(Display *display,XResourceInfo *resource_info,
6374%        XWindows *windows,const CommandType command,Image **image)
6375%
6376%  A description of each parameter follows:
6377%
6378%    o display: Specifies a connection to an X server; returned from
6379%      XOpenDisplay.
6380%
6381%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6382%
6383%    o windows: Specifies a pointer to a XWindows structure.
6384%
6385%    o command: Specifies a command to perform.
6386%
6387%    o image: the image;  XImageCache may transform the image and return a new
6388%      image pointer.
6389%
6390*/
6391static void XImageCache(Display *display,XResourceInfo *resource_info,
6392  XWindows *windows,const CommandType command,Image **image)
6393{
6394  Image
6395    *cache_image;
6396
6397  static Image
6398    *redo_image = (Image *) NULL,
6399    *undo_image = (Image *) NULL;
6400
6401  switch (command)
6402  {
6403    case FreeBuffersCommand:
6404    {
6405      /*
6406        Free memory from the undo and redo cache.
6407      */
6408      while (undo_image != (Image *) NULL)
6409      {
6410        cache_image=undo_image;
6411        undo_image=GetPreviousImageInList(undo_image);
6412        cache_image->list=DestroyImage(cache_image->list);
6413        cache_image=DestroyImage(cache_image);
6414      }
6415      undo_image=NewImageList();
6416      if (redo_image != (Image *) NULL)
6417        redo_image=DestroyImage(redo_image);
6418      redo_image=NewImageList();
6419      return;
6420    }
6421    case UndoCommand:
6422    {
6423      char
6424        image_geometry[MaxTextExtent];
6425
6426      /*
6427        Undo the last image transformation.
6428      */
6429      if (undo_image == (Image *) NULL)
6430        {
6431          (void) XBell(display,0);
6432          return;
6433        }
6434      cache_image=undo_image;
6435      undo_image=GetPreviousImageInList(undo_image);
6436      windows->image.window_changes.width=(int) cache_image->columns;
6437      windows->image.window_changes.height=(int) cache_image->rows;
6438      (void) FormatLocaleString(image_geometry,MaxTextExtent,"%dx%d!",
6439        windows->image.ximage->width,windows->image.ximage->height);
6440      (void) TransformImage(image,windows->image.crop_geometry,image_geometry);
6441      if (windows->image.crop_geometry != (char *) NULL)
6442        windows->image.crop_geometry=(char *)
6443          RelinquishMagickMemory(windows->image.crop_geometry);
6444      windows->image.crop_geometry=cache_image->geometry;
6445      if (redo_image != (Image *) NULL)
6446        redo_image=DestroyImage(redo_image);
6447      redo_image=(*image);
6448      *image=cache_image->list;
6449      cache_image=DestroyImage(cache_image);
6450      if (windows->image.orphan != MagickFalse)
6451        return;
6452      XConfigureImageColormap(display,resource_info,windows,*image);
6453      (void) XConfigureImage(display,resource_info,windows,*image);
6454      return;
6455    }
6456    case CutCommand:
6457    case PasteCommand:
6458    case ApplyCommand:
6459    case HalfSizeCommand:
6460    case OriginalSizeCommand:
6461    case DoubleSizeCommand:
6462    case ResizeCommand:
6463    case TrimCommand:
6464    case CropCommand:
6465    case ChopCommand:
6466    case FlipCommand:
6467    case FlopCommand:
6468    case RotateRightCommand:
6469    case RotateLeftCommand:
6470    case RotateCommand:
6471    case ShearCommand:
6472    case RollCommand:
6473    case NegateCommand:
6474    case ContrastStretchCommand:
6475    case SigmoidalContrastCommand:
6476    case NormalizeCommand:
6477    case EqualizeCommand:
6478    case HueCommand:
6479    case SaturationCommand:
6480    case BrightnessCommand:
6481    case GammaCommand:
6482    case SpiffCommand:
6483    case DullCommand:
6484    case GrayscaleCommand:
6485    case MapCommand:
6486    case QuantizeCommand:
6487    case DespeckleCommand:
6488    case EmbossCommand:
6489    case ReduceNoiseCommand:
6490    case AddNoiseCommand:
6491    case SharpenCommand:
6492    case BlurCommand:
6493    case ThresholdCommand:
6494    case EdgeDetectCommand:
6495    case SpreadCommand:
6496    case ShadeCommand:
6497    case RaiseCommand:
6498    case SegmentCommand:
6499    case SolarizeCommand:
6500    case SepiaToneCommand:
6501    case SwirlCommand:
6502    case ImplodeCommand:
6503    case VignetteCommand:
6504    case WaveCommand:
6505    case OilPaintCommand:
6506    case CharcoalDrawCommand:
6507    case AnnotateCommand:
6508    case AddBorderCommand:
6509    case AddFrameCommand:
6510    case CompositeCommand:
6511    case CommentCommand:
6512    case LaunchCommand:
6513    case RegionofInterestCommand:
6514    case SaveToUndoBufferCommand:
6515    case RedoCommand:
6516    {
6517      Image
6518        *previous_image;
6519
6520      ssize_t
6521        bytes;
6522
6523      bytes=(ssize_t) ((*image)->columns*(*image)->rows*sizeof(PixelPacket));
6524      if (undo_image != (Image *) NULL)
6525        {
6526          /*
6527            Ensure the undo cache has enough memory available.
6528          */
6529          previous_image=undo_image;
6530          while (previous_image != (Image *) NULL)
6531          {
6532            bytes+=previous_image->list->columns*previous_image->list->rows*
6533              sizeof(PixelPacket);
6534            if (bytes <= (ssize_t) (resource_info->undo_cache << 20))
6535              {
6536                previous_image=GetPreviousImageInList(previous_image);
6537                continue;
6538              }
6539            bytes-=previous_image->list->columns*previous_image->list->rows*
6540              sizeof(PixelPacket);
6541            if (previous_image == undo_image)
6542              undo_image=NewImageList();
6543            else
6544              previous_image->next->previous=NewImageList();
6545            break;
6546          }
6547          while (previous_image != (Image *) NULL)
6548          {
6549            /*
6550              Delete any excess memory from undo cache.
6551            */
6552            cache_image=previous_image;
6553            previous_image=GetPreviousImageInList(previous_image);
6554            cache_image->list=DestroyImage(cache_image->list);
6555            cache_image=DestroyImage(cache_image);
6556          }
6557        }
6558      if (bytes > (ssize_t) (resource_info->undo_cache << 20))
6559        break;
6560      /*
6561        Save image before transformations are applied.
6562      */
6563      cache_image=AcquireImage((ImageInfo *) NULL);
6564      if (cache_image == (Image *) NULL)
6565        break;
6566      XSetCursorState(display,windows,MagickTrue);
6567      XCheckRefreshWindows(display,windows);
6568      cache_image->list=CloneImage(*image,0,0,MagickTrue,&(*image)->exception);
6569      XSetCursorState(display,windows,MagickFalse);
6570      if (cache_image->list == (Image *) NULL)
6571        {
6572          cache_image=DestroyImage(cache_image);
6573          break;
6574        }
6575      cache_image->columns=(size_t) windows->image.ximage->width;
6576      cache_image->rows=(size_t) windows->image.ximage->height;
6577      cache_image->geometry=windows->image.crop_geometry;
6578      if (windows->image.crop_geometry != (char *) NULL)
6579        {
6580          cache_image->geometry=AcquireString((char *) NULL);
6581          (void) CopyMagickString(cache_image->geometry,
6582            windows->image.crop_geometry,MaxTextExtent);
6583        }
6584      if (undo_image == (Image *) NULL)
6585        {
6586          undo_image=cache_image;
6587          break;
6588        }
6589      undo_image->next=cache_image;
6590      undo_image->next->previous=undo_image;
6591      undo_image=undo_image->next;
6592      break;
6593    }
6594    default:
6595      break;
6596  }
6597  if (command == RedoCommand)
6598    {
6599      /*
6600        Redo the last image transformation.
6601      */
6602      if (redo_image == (Image *) NULL)
6603        {
6604          (void) XBell(display,0);
6605          return;
6606        }
6607      windows->image.window_changes.width=(int) redo_image->columns;
6608      windows->image.window_changes.height=(int) redo_image->rows;
6609      if (windows->image.crop_geometry != (char *) NULL)
6610        windows->image.crop_geometry=(char *)
6611          RelinquishMagickMemory(windows->image.crop_geometry);
6612      windows->image.crop_geometry=redo_image->geometry;
6613      *image=DestroyImage(*image);
6614      *image=redo_image;
6615      redo_image=NewImageList();
6616      if (windows->image.orphan != MagickFalse)
6617        return;
6618      XConfigureImageColormap(display,resource_info,windows,*image);
6619      (void) XConfigureImage(display,resource_info,windows,*image);
6620      return;
6621    }
6622  if (command != InfoCommand)
6623    return;
6624  /*
6625    Display image info.
6626  */
6627  XSetCursorState(display,windows,MagickTrue);
6628  XCheckRefreshWindows(display,windows);
6629  XDisplayImageInfo(display,resource_info,windows,undo_image,*image);
6630  XSetCursorState(display,windows,MagickFalse);
6631}
6632
6633/*
6634%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6635%                                                                             %
6636%                                                                             %
6637%                                                                             %
6638+   X I m a g e W i n d o w C o m m a n d                                     %
6639%                                                                             %
6640%                                                                             %
6641%                                                                             %
6642%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6643%
6644%  XImageWindowCommand() makes a transform to the image or Image window as
6645%  specified by a user menu button or keyboard command.
6646%
6647%  The format of the XMagickCommand method is:
6648%
6649%      CommandType XImageWindowCommand(Display *display,
6650%        XResourceInfo *resource_info,XWindows *windows,
6651%        const MagickStatusType state,KeySym key_symbol,Image **image)
6652%
6653%  A description of each parameter follows:
6654%
6655%    o nexus:  Method XImageWindowCommand returns an image when the
6656%      user chooses 'Open Image' from the command menu.  Otherwise a null
6657%      image is returned.
6658%
6659%    o display: Specifies a connection to an X server; returned from
6660%      XOpenDisplay.
6661%
6662%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6663%
6664%    o windows: Specifies a pointer to a XWindows structure.
6665%
6666%    o state: key mask.
6667%
6668%    o key_symbol: Specifies a command to perform.
6669%
6670%    o image: the image;  XImageWIndowCommand
6671%      may transform the image and return a new image pointer.
6672%
6673*/
6674static CommandType XImageWindowCommand(Display *display,
6675  XResourceInfo *resource_info,XWindows *windows,const MagickStatusType state,
6676  KeySym key_symbol,Image **image)
6677{
6678  static char
6679    delta[MaxTextExtent] = "";
6680
6681  static const char
6682    Digits[] = "01234567890";
6683
6684  static KeySym
6685    last_symbol = XK_0;
6686
6687  if ((key_symbol >= XK_0) && (key_symbol <= XK_9))
6688    {
6689      if (((last_symbol < XK_0) || (last_symbol > XK_9)))
6690        {
6691          *delta='\0';
6692          resource_info->quantum=1;
6693        }
6694      last_symbol=key_symbol;
6695      delta[strlen(delta)+1]='\0';
6696      delta[strlen(delta)]=Digits[key_symbol-XK_0];
6697      resource_info->quantum=StringToLong(delta);
6698      return(NullCommand);
6699    }
6700  last_symbol=key_symbol;
6701  if (resource_info->immutable)
6702    {
6703      /*
6704        Virtual image window has a restricted command set.
6705      */
6706      switch (key_symbol)
6707      {
6708        case XK_question:
6709          return(InfoCommand);
6710        case XK_p:
6711        case XK_Print:
6712          return(PrintCommand);
6713        case XK_space:
6714          return(NextCommand);
6715        case XK_q:
6716        case XK_Escape:
6717          return(QuitCommand);
6718        default:
6719          break;
6720      }
6721      return(NullCommand);
6722    }
6723  switch ((int) key_symbol)
6724  {
6725    case XK_o:
6726    {
6727      if ((state & ControlMask) == 0)
6728        break;
6729      return(OpenCommand);
6730    }
6731    case XK_space:
6732      return(NextCommand);
6733    case XK_BackSpace:
6734      return(FormerCommand);
6735    case XK_s:
6736    {
6737      if ((state & Mod1Mask) != 0)
6738        return(SwirlCommand);
6739      if ((state & ControlMask) == 0)
6740        return(ShearCommand);
6741      return(SaveCommand);
6742    }
6743    case XK_p:
6744    case XK_Print:
6745    {
6746      if ((state & Mod1Mask) != 0)
6747        return(OilPaintCommand);
6748      if ((state & Mod4Mask) != 0)
6749        return(ColorCommand);
6750      if ((state & ControlMask) == 0)
6751        return(NullCommand);
6752      return(PrintCommand);
6753    }
6754    case XK_d:
6755    {
6756      if ((state & Mod4Mask) != 0)
6757        return(DrawCommand);
6758      if ((state & ControlMask) == 0)
6759        return(NullCommand);
6760      return(DeleteCommand);
6761    }
6762    case XK_Select:
6763    {
6764      if ((state & ControlMask) == 0)
6765        return(NullCommand);
6766      return(SelectCommand);
6767    }
6768    case XK_n:
6769    {
6770      if ((state & ControlMask) == 0)
6771        return(NullCommand);
6772      return(NewCommand);
6773    }
6774    case XK_q:
6775    case XK_Escape:
6776      return(QuitCommand);
6777    case XK_z:
6778    case XK_Undo:
6779    {
6780      if ((state & ControlMask) == 0)
6781        return(NullCommand);
6782      return(UndoCommand);
6783    }
6784    case XK_r:
6785    case XK_Redo:
6786    {
6787      if ((state & ControlMask) == 0)
6788        return(RollCommand);
6789      return(RedoCommand);
6790    }
6791    case XK_x:
6792    {
6793      if ((state & ControlMask) == 0)
6794        return(NullCommand);
6795      return(CutCommand);
6796    }
6797    case XK_c:
6798    {
6799      if ((state & Mod1Mask) != 0)
6800        return(CharcoalDrawCommand);
6801      if ((state & ControlMask) == 0)
6802        return(CropCommand);
6803      return(CopyCommand);
6804    }
6805    case XK_v:
6806    case XK_Insert:
6807    {
6808      if ((state & Mod4Mask) != 0)
6809        return(CompositeCommand);
6810      if ((state & ControlMask) == 0)
6811        return(FlipCommand);
6812      return(PasteCommand);
6813    }
6814    case XK_less:
6815      return(HalfSizeCommand);
6816    case XK_minus:
6817      return(OriginalSizeCommand);
6818    case XK_greater:
6819      return(DoubleSizeCommand);
6820    case XK_percent:
6821      return(ResizeCommand);
6822    case XK_at:
6823      return(RefreshCommand);
6824    case XK_bracketleft:
6825      return(ChopCommand);
6826    case XK_h:
6827      return(FlopCommand);
6828    case XK_slash:
6829      return(RotateRightCommand);
6830    case XK_backslash:
6831      return(RotateLeftCommand);
6832    case XK_asterisk:
6833      return(RotateCommand);
6834    case XK_t:
6835      return(TrimCommand);
6836    case XK_H:
6837      return(HueCommand);
6838    case XK_S:
6839      return(SaturationCommand);
6840    case XK_L:
6841      return(BrightnessCommand);
6842    case XK_G:
6843      return(GammaCommand);
6844    case XK_C:
6845      return(SpiffCommand);
6846    case XK_Z:
6847      return(DullCommand);
6848    case XK_N:
6849      return(NormalizeCommand);
6850    case XK_equal:
6851      return(EqualizeCommand);
6852    case XK_asciitilde:
6853      return(NegateCommand);
6854    case XK_period:
6855      return(GrayscaleCommand);
6856    case XK_numbersign:
6857      return(QuantizeCommand);
6858    case XK_F2:
6859      return(DespeckleCommand);
6860    case XK_F3:
6861      return(EmbossCommand);
6862    case XK_F4:
6863      return(ReduceNoiseCommand);
6864    case XK_F5:
6865      return(AddNoiseCommand);
6866    case XK_F6:
6867      return(SharpenCommand);
6868    case XK_F7:
6869      return(BlurCommand);
6870    case XK_F8:
6871      return(ThresholdCommand);
6872    case XK_F9:
6873      return(EdgeDetectCommand);
6874    case XK_F10:
6875      return(SpreadCommand);
6876    case XK_F11:
6877      return(ShadeCommand);
6878    case XK_F12:
6879      return(RaiseCommand);
6880    case XK_F13:
6881      return(SegmentCommand);
6882    case XK_i:
6883    {
6884      if ((state & Mod1Mask) == 0)
6885        return(NullCommand);
6886      return(ImplodeCommand);
6887    }
6888    case XK_w:
6889    {
6890      if ((state & Mod1Mask) == 0)
6891        return(NullCommand);
6892      return(WaveCommand);
6893    }
6894    case XK_m:
6895    {
6896      if ((state & Mod4Mask) == 0)
6897        return(NullCommand);
6898      return(MatteCommand);
6899    }
6900    case XK_b:
6901    {
6902      if ((state & Mod4Mask) == 0)
6903        return(NullCommand);
6904      return(AddBorderCommand);
6905    }
6906    case XK_f:
6907    {
6908      if ((state & Mod4Mask) == 0)
6909        return(NullCommand);
6910      return(AddFrameCommand);
6911    }
6912    case XK_exclam:
6913    {
6914      if ((state & Mod4Mask) == 0)
6915        return(NullCommand);
6916      return(CommentCommand);
6917    }
6918    case XK_a:
6919    {
6920      if ((state & Mod1Mask) != 0)
6921        return(ApplyCommand);
6922      if ((state & Mod4Mask) != 0)
6923        return(AnnotateCommand);
6924      if ((state & ControlMask) == 0)
6925        return(NullCommand);
6926      return(RegionofInterestCommand);
6927    }
6928    case XK_question:
6929      return(InfoCommand);
6930    case XK_plus:
6931      return(ZoomCommand);
6932    case XK_P:
6933    {
6934      if ((state & ShiftMask) == 0)
6935        return(NullCommand);
6936      return(ShowPreviewCommand);
6937    }
6938    case XK_Execute:
6939      return(LaunchCommand);
6940    case XK_F1:
6941      return(HelpCommand);
6942    case XK_Find:
6943      return(BrowseDocumentationCommand);
6944    case XK_Menu:
6945    {
6946      (void) XMapRaised(display,windows->command.id);
6947      return(NullCommand);
6948    }
6949    case XK_Next:
6950    case XK_Prior:
6951    case XK_Home:
6952    case XK_KP_Home:
6953    {
6954      XTranslateImage(display,windows,*image,key_symbol);
6955      return(NullCommand);
6956    }
6957    case XK_Up:
6958    case XK_KP_Up:
6959    case XK_Down:
6960    case XK_KP_Down:
6961    case XK_Left:
6962    case XK_KP_Left:
6963    case XK_Right:
6964    case XK_KP_Right:
6965    {
6966      if ((state & Mod1Mask) != 0)
6967        {
6968          RectangleInfo
6969            crop_info;
6970
6971          /*
6972            Trim one pixel from edge of image.
6973          */
6974          crop_info.x=0;
6975          crop_info.y=0;
6976          crop_info.width=(size_t) windows->image.ximage->width;
6977          crop_info.height=(size_t) windows->image.ximage->height;
6978          if ((key_symbol == XK_Up) || (key_symbol == XK_KP_Up))
6979            {
6980              if (resource_info->quantum >= (int) crop_info.height)
6981                resource_info->quantum=(int) crop_info.height-1;
6982              crop_info.height-=resource_info->quantum;
6983            }
6984          if ((key_symbol == XK_Down) || (key_symbol == XK_KP_Down))
6985            {
6986              if (resource_info->quantum >= (int) (crop_info.height-crop_info.y))
6987                resource_info->quantum=(int) (crop_info.height-crop_info.y-1);
6988              crop_info.y+=resource_info->quantum;
6989              crop_info.height-=resource_info->quantum;
6990            }
6991          if ((key_symbol == XK_Left) || (key_symbol == XK_KP_Left))
6992            {
6993              if (resource_info->quantum >= (int) crop_info.width)
6994                resource_info->quantum=(int) crop_info.width-1;
6995              crop_info.width-=resource_info->quantum;
6996            }
6997          if ((key_symbol == XK_Right) || (key_symbol == XK_KP_Right))
6998            {
6999              if (resource_info->quantum >= (int) (crop_info.width-crop_info.x))
7000                resource_info->quantum=(int) (crop_info.width-crop_info.x-1);
7001              crop_info.x+=resource_info->quantum;
7002              crop_info.width-=resource_info->quantum;
7003            }
7004          if ((int) (windows->image.x+windows->image.width) >
7005              (int) crop_info.width)
7006            windows->image.x=(int) (crop_info.width-windows->image.width);
7007          if ((int) (windows->image.y+windows->image.height) >
7008              (int) crop_info.height)
7009            windows->image.y=(int) (crop_info.height-windows->image.height);
7010          XSetCropGeometry(display,windows,&crop_info,*image);
7011          windows->image.window_changes.width=(int) crop_info.width;
7012          windows->image.window_changes.height=(int) crop_info.height;
7013          (void) XSetWindowBackgroundPixmap(display,windows->image.id,None);
7014          (void) XConfigureImage(display,resource_info,windows,*image);
7015          return(NullCommand);
7016        }
7017      XTranslateImage(display,windows,*image,key_symbol);
7018      return(NullCommand);
7019    }
7020    default:
7021      return(NullCommand);
7022  }
7023  return(NullCommand);
7024}
7025
7026/*
7027%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7028%                                                                             %
7029%                                                                             %
7030%                                                                             %
7031+   X M a g i c k C o m m a n d                                               %
7032%                                                                             %
7033%                                                                             %
7034%                                                                             %
7035%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7036%
7037%  XMagickCommand() makes a transform to the image or Image window as
7038%  specified by a user menu button or keyboard command.
7039%
7040%  The format of the XMagickCommand method is:
7041%
7042%      Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7043%        XWindows *windows,const CommandType command,Image **image)
7044%
7045%  A description of each parameter follows:
7046%
7047%    o nexus:  Method XMagickCommand returns an image when the
7048%      user chooses 'Load Image' from the command menu.  Otherwise a null
7049%      image is returned.
7050%
7051%    o display: Specifies a connection to an X server; returned from
7052%      XOpenDisplay.
7053%
7054%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
7055%
7056%    o windows: Specifies a pointer to a XWindows structure.
7057%
7058%    o command: Specifies a command to perform.
7059%
7060%    o image: the image;  XMagickCommand
7061%      may transform the image and return a new image pointer.
7062%
7063*/
7064static Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7065  XWindows *windows,const CommandType command,Image **image)
7066{
7067  char
7068    filename[MaxTextExtent],
7069    geometry[MaxTextExtent],
7070    modulate_factors[MaxTextExtent];
7071
7072  GeometryInfo
7073    geometry_info;
7074
7075  Image
7076    *nexus;
7077
7078  ImageInfo
7079    *image_info;
7080
7081  int
7082    x,
7083    y;
7084
7085  MagickStatusType
7086    flags,
7087    status;
7088
7089  QuantizeInfo
7090    quantize_info;
7091
7092  RectangleInfo
7093    page_geometry;
7094
7095  register int
7096    i;
7097
7098  static char
7099    color[MaxTextExtent] = "gray";
7100
7101  unsigned int
7102    height,
7103    width;
7104
7105  /*
7106    Process user command.
7107  */
7108  XCheckRefreshWindows(display,windows);
7109  XImageCache(display,resource_info,windows,command,image);
7110  nexus=NewImageList();
7111  windows->image.window_changes.width=windows->image.ximage->width;
7112  windows->image.window_changes.height=windows->image.ximage->height;
7113  image_info=CloneImageInfo(resource_info->image_info);
7114  SetGeometryInfo(&geometry_info);
7115  GetQuantizeInfo(&quantize_info);
7116  switch (command)
7117  {
7118    case OpenCommand:
7119    {
7120      /*
7121        Load image.
7122      */
7123      nexus=XOpenImage(display,resource_info,windows,MagickFalse);
7124      break;
7125    }
7126    case NextCommand:
7127    {
7128      /*
7129        Display next image.
7130      */
7131      for (i=0; i < resource_info->quantum; i++)
7132        XClientMessage(display,windows->image.id,windows->im_protocols,
7133          windows->im_next_image,CurrentTime);
7134      break;
7135    }
7136    case FormerCommand:
7137    {
7138      /*
7139        Display former image.
7140      */
7141      for (i=0; i < resource_info->quantum; i++)
7142        XClientMessage(display,windows->image.id,windows->im_protocols,
7143          windows->im_former_image,CurrentTime);
7144      break;
7145    }
7146    case SelectCommand:
7147    {
7148      int
7149        status;
7150
7151      /*
7152        Select image.
7153      */
7154      status=chdir(resource_info->home_directory);
7155      if (status == -1)
7156        (void) ThrowMagickException(&(*image)->exception,GetMagickModule(),
7157          FileOpenError,"UnableToOpenFile","%s",resource_info->home_directory);
7158      nexus=XOpenImage(display,resource_info,windows,MagickTrue);
7159      break;
7160    }
7161    case SaveCommand:
7162    {
7163      /*
7164        Save image.
7165      */
7166      status=XSaveImage(display,resource_info,windows,*image);
7167      if (status == MagickFalse)
7168        {
7169          XNoticeWidget(display,windows,"Unable to write X image:",
7170            (*image)->filename);
7171          break;
7172        }
7173      break;
7174    }
7175    case PrintCommand:
7176    {
7177      /*
7178        Print image.
7179      */
7180      status=XPrintImage(display,resource_info,windows,*image);
7181      if (status == MagickFalse)
7182        {
7183          XNoticeWidget(display,windows,"Unable to print X image:",
7184            (*image)->filename);
7185          break;
7186        }
7187      break;
7188    }
7189    case DeleteCommand:
7190    {
7191      static char
7192        filename[MaxTextExtent] = "\0";
7193
7194      /*
7195        Delete image file.
7196      */
7197      XFileBrowserWidget(display,windows,"Delete",filename);
7198      if (*filename == '\0')
7199        break;
7200      status=remove(filename) != 0 ? MagickTrue : MagickFalse;
7201      if (status != MagickFalse)
7202        XNoticeWidget(display,windows,"Unable to delete image file:",filename);
7203      break;
7204    }
7205    case NewCommand:
7206    {
7207      int
7208        status;
7209
7210      static char
7211        color[MaxTextExtent] = "gray",
7212        geometry[MaxTextExtent] = "640x480";
7213
7214      static const char
7215        *format = "gradient";
7216
7217      /*
7218        Query user for canvas geometry.
7219      */
7220      status=XDialogWidget(display,windows,"New","Enter image geometry:",
7221        geometry);
7222      if (*geometry == '\0')
7223        break;
7224      if (status == 0)
7225        format="xc";
7226      XColorBrowserWidget(display,windows,"Select",color);
7227      if (*color == '\0')
7228        break;
7229      /*
7230        Create canvas.
7231      */
7232      (void) FormatLocaleString(image_info->filename,MaxTextExtent,
7233        "%s:%s",format,color);
7234      (void) CloneString(&image_info->size,geometry);
7235      nexus=ReadImage(image_info,&(*image)->exception);
7236      CatchException(&(*image)->exception);
7237      XClientMessage(display,windows->image.id,windows->im_protocols,
7238        windows->im_next_image,CurrentTime);
7239      break;
7240    }
7241    case VisualDirectoryCommand:
7242    {
7243      /*
7244        Visual Image directory.
7245      */
7246      nexus=XVisualDirectoryImage(display,resource_info,windows);
7247      break;
7248    }
7249    case QuitCommand:
7250    {
7251      /*
7252        exit program.
7253      */
7254      if (resource_info->confirm_exit == MagickFalse)
7255        XClientMessage(display,windows->image.id,windows->im_protocols,
7256          windows->im_exit,CurrentTime);
7257      else
7258        {
7259          int
7260            status;
7261
7262          /*
7263            Confirm program exit.
7264          */
7265          status=XConfirmWidget(display,windows,"Do you really want to exit",
7266            resource_info->client_name);
7267          if (status > 0)
7268            XClientMessage(display,windows->image.id,windows->im_protocols,
7269              windows->im_exit,CurrentTime);
7270        }
7271      break;
7272    }
7273    case CutCommand:
7274    {
7275      /*
7276        Cut image.
7277      */
7278      (void) XCropImage(display,resource_info,windows,*image,CutMode);
7279      break;
7280    }
7281    case CopyCommand:
7282    {
7283      /*
7284        Copy image.
7285      */
7286      (void) XCropImage(display,resource_info,windows,*image,CopyMode);
7287      break;
7288    }
7289    case PasteCommand:
7290    {
7291      /*
7292        Paste image.
7293      */
7294      status=XPasteImage(display,resource_info,windows,*image);
7295      if (status == MagickFalse)
7296        {
7297          XNoticeWidget(display,windows,"Unable to paste X image",
7298            (*image)->filename);
7299          break;
7300        }
7301      break;
7302    }
7303    case HalfSizeCommand:
7304    {
7305      /*
7306        Half image size.
7307      */
7308      windows->image.window_changes.width=windows->image.ximage->width/2;
7309      windows->image.window_changes.height=windows->image.ximage->height/2;
7310      (void) XConfigureImage(display,resource_info,windows,*image);
7311      break;
7312    }
7313    case OriginalSizeCommand:
7314    {
7315      /*
7316        Original image size.
7317      */
7318      windows->image.window_changes.width=(int) (*image)->columns;
7319      windows->image.window_changes.height=(int) (*image)->rows;
7320      (void) XConfigureImage(display,resource_info,windows,*image);
7321      break;
7322    }
7323    case DoubleSizeCommand:
7324    {
7325      /*
7326        Double the image size.
7327      */
7328      windows->image.window_changes.width=windows->image.ximage->width << 1;
7329      windows->image.window_changes.height=windows->image.ximage->height << 1;
7330      (void) XConfigureImage(display,resource_info,windows,*image);
7331      break;
7332    }
7333    case ResizeCommand:
7334    {
7335      int
7336        status;
7337
7338      size_t
7339        height,
7340        width;
7341
7342      ssize_t
7343        x,
7344        y;
7345
7346      /*
7347        Resize image.
7348      */
7349      width=(size_t) windows->image.ximage->width;
7350      height=(size_t) windows->image.ximage->height;
7351      x=0;
7352      y=0;
7353      (void) FormatLocaleString(geometry,MaxTextExtent,"%.20gx%.20g+0+0",
7354        (double) width,(double) height);
7355      status=XDialogWidget(display,windows,"Resize",
7356        "Enter resize geometry (e.g. 640x480, 200%):",geometry);
7357      if (*geometry == '\0')
7358        break;
7359      if (status == 0)
7360        (void) ConcatenateMagickString(geometry,"!",MaxTextExtent);
7361      (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
7362      windows->image.window_changes.width=(int) width;
7363      windows->image.window_changes.height=(int) height;
7364      (void) XConfigureImage(display,resource_info,windows,*image);
7365      break;
7366    }
7367    case ApplyCommand:
7368    {
7369      char
7370        image_geometry[MaxTextExtent];
7371
7372      if ((windows->image.crop_geometry == (char *) NULL) &&
7373          ((int) (*image)->columns == windows->image.ximage->width) &&
7374          ((int) (*image)->rows == windows->image.ximage->height))
7375        break;
7376      /*
7377        Apply size transforms to image.
7378      */
7379      XSetCursorState(display,windows,MagickTrue);
7380      XCheckRefreshWindows(display,windows);
7381      /*
7382        Crop and/or scale displayed image.
7383      */
7384      (void) FormatLocaleString(image_geometry,MaxTextExtent,"%dx%d!",
7385        windows->image.ximage->width,windows->image.ximage->height);
7386      (void) TransformImage(image,windows->image.crop_geometry,image_geometry);
7387      if (windows->image.crop_geometry != (char *) NULL)
7388        windows->image.crop_geometry=(char *)
7389          RelinquishMagickMemory(windows->image.crop_geometry);
7390      windows->image.x=0;
7391      windows->image.y=0;
7392      XConfigureImageColormap(display,resource_info,windows,*image);
7393      (void) XConfigureImage(display,resource_info,windows,*image);
7394      break;
7395    }
7396    case RefreshCommand:
7397    {
7398      (void) XConfigureImage(display,resource_info,windows,*image);
7399      break;
7400    }
7401    case RestoreCommand:
7402    {
7403      /*
7404        Restore Image window to its original size.
7405      */
7406      if ((windows->image.width == (unsigned int) (*image)->columns) &&
7407          (windows->image.height == (unsigned int) (*image)->rows) &&
7408          (windows->image.crop_geometry == (char *) NULL))
7409        {
7410          (void) XBell(display,0);
7411          break;
7412        }
7413      windows->image.window_changes.width=(int) (*image)->columns;
7414      windows->image.window_changes.height=(int) (*image)->rows;
7415      if (windows->image.crop_geometry != (char *) NULL)
7416        {
7417          windows->image.crop_geometry=(char *)
7418            RelinquishMagickMemory(windows->image.crop_geometry);
7419          windows->image.crop_geometry=(char *) NULL;
7420          windows->image.x=0;
7421          windows->image.y=0;
7422        }
7423      XConfigureImageColormap(display,resource_info,windows,*image);
7424      (void) XConfigureImage(display,resource_info,windows,*image);
7425      break;
7426    }
7427    case CropCommand:
7428    {
7429      /*
7430        Crop image.
7431      */
7432      (void) XCropImage(display,resource_info,windows,*image,CropMode);
7433      break;
7434    }
7435    case ChopCommand:
7436    {
7437      /*
7438        Chop image.
7439      */
7440      status=XChopImage(display,resource_info,windows,image);
7441      if (status == MagickFalse)
7442        {
7443          XNoticeWidget(display,windows,"Unable to cut X image",
7444            (*image)->filename);
7445          break;
7446        }
7447      break;
7448    }
7449    case FlopCommand:
7450    {
7451      Image
7452        *flop_image;
7453
7454      /*
7455        Flop image scanlines.
7456      */
7457      XSetCursorState(display,windows,MagickTrue);
7458      XCheckRefreshWindows(display,windows);
7459      flop_image=FlopImage(*image,&(*image)->exception);
7460      if (flop_image != (Image *) NULL)
7461        {
7462          *image=DestroyImage(*image);
7463          *image=flop_image;
7464        }
7465      CatchException(&(*image)->exception);
7466      XSetCursorState(display,windows,MagickFalse);
7467      if (windows->image.crop_geometry != (char *) NULL)
7468        {
7469          /*
7470            Flop crop geometry.
7471          */
7472          width=(unsigned int) (*image)->columns;
7473          height=(unsigned int) (*image)->rows;
7474          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7475            &width,&height);
7476          (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
7477            "%ux%u%+d%+d",width,height,(int) (*image)->columns-(int) width-x,y);
7478        }
7479      if (windows->image.orphan != MagickFalse)
7480        break;
7481      (void) XConfigureImage(display,resource_info,windows,*image);
7482      break;
7483    }
7484    case FlipCommand:
7485    {
7486      Image
7487        *flip_image;
7488
7489      /*
7490        Flip image scanlines.
7491      */
7492      XSetCursorState(display,windows,MagickTrue);
7493      XCheckRefreshWindows(display,windows);
7494      flip_image=FlipImage(*image,&(*image)->exception);
7495      if (flip_image != (Image *) NULL)
7496        {
7497          *image=DestroyImage(*image);
7498          *image=flip_image;
7499        }
7500      CatchException(&(*image)->exception);
7501      XSetCursorState(display,windows,MagickFalse);
7502      if (windows->image.crop_geometry != (char *) NULL)
7503        {
7504          /*
7505            Flip crop geometry.
7506          */
7507          width=(unsigned int) (*image)->columns;
7508          height=(unsigned int) (*image)->rows;
7509          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7510            &width,&height);
7511          (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
7512            "%ux%u%+d%+d",width,height,x,(int) (*image)->rows-(int) height-y);
7513        }
7514      if (windows->image.orphan != MagickFalse)
7515        break;
7516      (void) XConfigureImage(display,resource_info,windows,*image);
7517      break;
7518    }
7519    case RotateRightCommand:
7520    {
7521      /*
7522        Rotate image 90 degrees clockwise.
7523      */
7524      status=XRotateImage(display,resource_info,windows,90.0,image);
7525      if (status == MagickFalse)
7526        {
7527          XNoticeWidget(display,windows,"Unable to rotate X image",
7528            (*image)->filename);
7529          break;
7530        }
7531      break;
7532    }
7533    case RotateLeftCommand:
7534    {
7535      /*
7536        Rotate image 90 degrees counter-clockwise.
7537      */
7538      status=XRotateImage(display,resource_info,windows,-90.0,image);
7539      if (status == MagickFalse)
7540        {
7541          XNoticeWidget(display,windows,"Unable to rotate X image",
7542            (*image)->filename);
7543          break;
7544        }
7545      break;
7546    }
7547    case RotateCommand:
7548    {
7549      /*
7550        Rotate image.
7551      */
7552      status=XRotateImage(display,resource_info,windows,0.0,image);
7553      if (status == MagickFalse)
7554        {
7555          XNoticeWidget(display,windows,"Unable to rotate X image",
7556            (*image)->filename);
7557          break;
7558        }
7559      break;
7560    }
7561    case ShearCommand:
7562    {
7563      Image
7564        *shear_image;
7565
7566      static char
7567        geometry[MaxTextExtent] = "45.0x45.0";
7568
7569      /*
7570        Query user for shear color and geometry.
7571      */
7572      XColorBrowserWidget(display,windows,"Select",color);
7573      if (*color == '\0')
7574        break;
7575      (void) XDialogWidget(display,windows,"Shear","Enter shear geometry:",
7576        geometry);
7577      if (*geometry == '\0')
7578        break;
7579      /*
7580        Shear image.
7581      */
7582      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
7583      XSetCursorState(display,windows,MagickTrue);
7584      XCheckRefreshWindows(display,windows);
7585      (void) QueryColorDatabase(color,&(*image)->background_color,
7586        &(*image)->exception);
7587      flags=ParseGeometry(geometry,&geometry_info);
7588      if ((flags & SigmaValue) == 0)
7589        geometry_info.sigma=geometry_info.rho;
7590      shear_image=ShearImage(*image,geometry_info.rho,geometry_info.sigma,
7591        &(*image)->exception);
7592      if (shear_image != (Image *) NULL)
7593        {
7594          *image=DestroyImage(*image);
7595          *image=shear_image;
7596        }
7597      CatchException(&(*image)->exception);
7598      XSetCursorState(display,windows,MagickFalse);
7599      if (windows->image.orphan != MagickFalse)
7600        break;
7601      windows->image.window_changes.width=(int) (*image)->columns;
7602      windows->image.window_changes.height=(int) (*image)->rows;
7603      XConfigureImageColormap(display,resource_info,windows,*image);
7604      (void) XConfigureImage(display,resource_info,windows,*image);
7605      break;
7606    }
7607    case RollCommand:
7608    {
7609      Image
7610        *roll_image;
7611
7612      static char
7613        geometry[MaxTextExtent] = "+2+2";
7614
7615      /*
7616        Query user for the roll geometry.
7617      */
7618      (void) XDialogWidget(display,windows,"Roll","Enter roll geometry:",
7619        geometry);
7620      if (*geometry == '\0')
7621        break;
7622      /*
7623        Roll image.
7624      */
7625      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
7626      XSetCursorState(display,windows,MagickTrue);
7627      XCheckRefreshWindows(display,windows);
7628      (void) ParsePageGeometry(*image,geometry,&page_geometry,
7629        &(*image)->exception);
7630      roll_image=RollImage(*image,page_geometry.x,page_geometry.y,
7631        &(*image)->exception);
7632      if (roll_image != (Image *) NULL)
7633        {
7634          *image=DestroyImage(*image);
7635          *image=roll_image;
7636        }
7637      CatchException(&(*image)->exception);
7638      XSetCursorState(display,windows,MagickFalse);
7639      if (windows->image.orphan != MagickFalse)
7640        break;
7641      windows->image.window_changes.width=(int) (*image)->columns;
7642      windows->image.window_changes.height=(int) (*image)->rows;
7643      XConfigureImageColormap(display,resource_info,windows,*image);
7644      (void) XConfigureImage(display,resource_info,windows,*image);
7645      break;
7646    }
7647    case TrimCommand:
7648    {
7649      static char
7650        fuzz[MaxTextExtent];
7651
7652      /*
7653        Query user for the fuzz factor.
7654      */
7655      (void) FormatLocaleString(fuzz,MaxTextExtent,"%g%%",100.0*
7656        (*image)->fuzz/(QuantumRange+1.0));
7657      (void) XDialogWidget(display,windows,"Trim","Enter fuzz factor:",fuzz);
7658      if (*fuzz == '\0')
7659        break;
7660      (*image)->fuzz=SiPrefixToDouble(fuzz,(double) QuantumRange+1.0);
7661      /*
7662        Trim image.
7663      */
7664      status=XTrimImage(display,resource_info,windows,*image);
7665      if (status == MagickFalse)
7666        {
7667          XNoticeWidget(display,windows,"Unable to trim X image",
7668            (*image)->filename);
7669          break;
7670        }
7671      break;
7672    }
7673    case HueCommand:
7674    {
7675      static char
7676        hue_percent[MaxTextExtent] = "110";
7677
7678      /*
7679        Query user for percent hue change.
7680      */
7681      (void) XDialogWidget(display,windows,"Apply",
7682        "Enter percent change in image hue (0-200):",hue_percent);
7683      if (*hue_percent == '\0')
7684        break;
7685      /*
7686        Vary the image hue.
7687      */
7688      XSetCursorState(display,windows,MagickTrue);
7689      XCheckRefreshWindows(display,windows);
7690      (void) CopyMagickString(modulate_factors,"100.0/100.0/",MaxTextExtent);
7691      (void) ConcatenateMagickString(modulate_factors,hue_percent,
7692        MaxTextExtent);
7693      (void) ModulateImage(*image,modulate_factors);
7694      XSetCursorState(display,windows,MagickFalse);
7695      if (windows->image.orphan != MagickFalse)
7696        break;
7697      XConfigureImageColormap(display,resource_info,windows,*image);
7698      (void) XConfigureImage(display,resource_info,windows,*image);
7699      break;
7700    }
7701    case SaturationCommand:
7702    {
7703      static char
7704        saturation_percent[MaxTextExtent] = "110";
7705
7706      /*
7707        Query user for percent saturation change.
7708      */
7709      (void) XDialogWidget(display,windows,"Apply",
7710        "Enter percent change in color saturation (0-200):",saturation_percent);
7711      if (*saturation_percent == '\0')
7712        break;
7713      /*
7714        Vary color saturation.
7715      */
7716      XSetCursorState(display,windows,MagickTrue);
7717      XCheckRefreshWindows(display,windows);
7718      (void) CopyMagickString(modulate_factors,"100.0/",MaxTextExtent);
7719      (void) ConcatenateMagickString(modulate_factors,saturation_percent,
7720        MaxTextExtent);
7721      (void) ModulateImage(*image,modulate_factors);
7722      XSetCursorState(display,windows,MagickFalse);
7723      if (windows->image.orphan != MagickFalse)
7724        break;
7725      XConfigureImageColormap(display,resource_info,windows,*image);
7726      (void) XConfigureImage(display,resource_info,windows,*image);
7727      break;
7728    }
7729    case BrightnessCommand:
7730    {
7731      static char
7732        brightness_percent[MaxTextExtent] = "110";
7733
7734      /*
7735        Query user for percent brightness change.
7736      */
7737      (void) XDialogWidget(display,windows,"Apply",
7738        "Enter percent change in color brightness (0-200):",brightness_percent);
7739      if (*brightness_percent == '\0')
7740        break;
7741      /*
7742        Vary the color brightness.
7743      */
7744      XSetCursorState(display,windows,MagickTrue);
7745      XCheckRefreshWindows(display,windows);
7746      (void) CopyMagickString(modulate_factors,brightness_percent,
7747        MaxTextExtent);
7748      (void) ModulateImage(*image,modulate_factors);
7749      XSetCursorState(display,windows,MagickFalse);
7750      if (windows->image.orphan != MagickFalse)
7751        break;
7752      XConfigureImageColormap(display,resource_info,windows,*image);
7753      (void) XConfigureImage(display,resource_info,windows,*image);
7754      break;
7755    }
7756    case GammaCommand:
7757    {
7758      static char
7759        factor[MaxTextExtent] = "1.6";
7760
7761      /*
7762        Query user for gamma value.
7763      */
7764      (void) XDialogWidget(display,windows,"Gamma",
7765        "Enter gamma value (e.g. 1.2):",factor);
7766      if (*factor == '\0')
7767        break;
7768      /*
7769        Gamma correct image.
7770      */
7771      XSetCursorState(display,windows,MagickTrue);
7772      XCheckRefreshWindows(display,windows);
7773      (void) GammaImage(*image,atof(factor),&(*image)->exception);
7774      XSetCursorState(display,windows,MagickFalse);
7775      if (windows->image.orphan != MagickFalse)
7776        break;
7777      XConfigureImageColormap(display,resource_info,windows,*image);
7778      (void) XConfigureImage(display,resource_info,windows,*image);
7779      break;
7780    }
7781    case SpiffCommand:
7782    {
7783      /*
7784        Sharpen the image contrast.
7785      */
7786      XSetCursorState(display,windows,MagickTrue);
7787      XCheckRefreshWindows(display,windows);
7788      (void) ContrastImage(*image,MagickTrue);
7789      XSetCursorState(display,windows,MagickFalse);
7790      if (windows->image.orphan != MagickFalse)
7791        break;
7792      XConfigureImageColormap(display,resource_info,windows,*image);
7793      (void) XConfigureImage(display,resource_info,windows,*image);
7794      break;
7795    }
7796    case DullCommand:
7797    {
7798      /*
7799        Dull the image contrast.
7800      */
7801      XSetCursorState(display,windows,MagickTrue);
7802      XCheckRefreshWindows(display,windows);
7803      (void) ContrastImage(*image,MagickFalse);
7804      XSetCursorState(display,windows,MagickFalse);
7805      if (windows->image.orphan != MagickFalse)
7806        break;
7807      XConfigureImageColormap(display,resource_info,windows,*image);
7808      (void) XConfigureImage(display,resource_info,windows,*image);
7809      break;
7810    }
7811    case ContrastStretchCommand:
7812    {
7813      double
7814        black_point,
7815        white_point;
7816
7817      static char
7818        levels[MaxTextExtent] = "1%";
7819
7820      /*
7821        Query user for gamma value.
7822      */
7823      (void) XDialogWidget(display,windows,"Contrast Stretch",
7824        "Enter black and white points:",levels);
7825      if (*levels == '\0')
7826        break;
7827      /*
7828        Contrast stretch image.
7829      */
7830      XSetCursorState(display,windows,MagickTrue);
7831      XCheckRefreshWindows(display,windows);
7832      flags=ParseGeometry(levels,&geometry_info);
7833      black_point=geometry_info.rho;
7834      white_point=(flags & SigmaValue) != 0 ? geometry_info.sigma : black_point;
7835      if ((flags & PercentValue) != 0)
7836        {
7837          black_point*=(double) (*image)->columns*(*image)->rows/100.0;
7838          white_point*=(double) (*image)->columns*(*image)->rows/100.0;
7839        }
7840      white_point=(MagickRealType) (*image)->columns*(*image)->rows-white_point;
7841      (void) ContrastStretchImage(*image,black_point,white_point);
7842      XSetCursorState(display,windows,MagickFalse);
7843      if (windows->image.orphan != MagickFalse)
7844        break;
7845      XConfigureImageColormap(display,resource_info,windows,*image);
7846      (void) XConfigureImage(display,resource_info,windows,*image);
7847      break;
7848    }
7849    case SigmoidalContrastCommand:
7850    {
7851      GeometryInfo
7852        geometry_info;
7853
7854      MagickStatusType
7855        flags;
7856
7857      static char
7858        levels[MaxTextExtent] = "3x50%";
7859
7860      /*
7861        Query user for gamma value.
7862      */
7863      (void) XDialogWidget(display,windows,"Sigmoidal Contrast",
7864        "Enter contrast and midpoint:",levels);
7865      if (*levels == '\0')
7866        break;
7867      /*
7868        Contrast stretch image.
7869      */
7870      XSetCursorState(display,windows,MagickTrue);
7871      XCheckRefreshWindows(display,windows);
7872      flags=ParseGeometry(levels,&geometry_info);
7873      if ((flags & SigmaValue) == 0)
7874        geometry_info.sigma=1.0*QuantumRange/2.0;
7875      if ((flags & PercentValue) != 0)
7876        geometry_info.sigma=1.0*QuantumRange*geometry_info.sigma/100.0;
7877      (void) SigmoidalContrastImage(*image,MagickTrue,geometry_info.rho,
7878        geometry_info.sigma);
7879      XSetCursorState(display,windows,MagickFalse);
7880      if (windows->image.orphan != MagickFalse)
7881        break;
7882      XConfigureImageColormap(display,resource_info,windows,*image);
7883      (void) XConfigureImage(display,resource_info,windows,*image);
7884      break;
7885    }
7886    case NormalizeCommand:
7887    {
7888      /*
7889        Perform histogram normalization on the image.
7890      */
7891      XSetCursorState(display,windows,MagickTrue);
7892      XCheckRefreshWindows(display,windows);
7893      (void) NormalizeImage(*image);
7894      XSetCursorState(display,windows,MagickFalse);
7895      if (windows->image.orphan != MagickFalse)
7896        break;
7897      XConfigureImageColormap(display,resource_info,windows,*image);
7898      (void) XConfigureImage(display,resource_info,windows,*image);
7899      break;
7900    }
7901    case EqualizeCommand:
7902    {
7903      /*
7904        Perform histogram equalization on the image.
7905      */
7906      XSetCursorState(display,windows,MagickTrue);
7907      XCheckRefreshWindows(display,windows);
7908      (void) EqualizeImage(*image);
7909      XSetCursorState(display,windows,MagickFalse);
7910      if (windows->image.orphan != MagickFalse)
7911        break;
7912      XConfigureImageColormap(display,resource_info,windows,*image);
7913      (void) XConfigureImage(display,resource_info,windows,*image);
7914      break;
7915    }
7916    case NegateCommand:
7917    {
7918      /*
7919        Negate colors in image.
7920      */
7921      XSetCursorState(display,windows,MagickTrue);
7922      XCheckRefreshWindows(display,windows);
7923      (void) NegateImage(*image,MagickFalse,&(*image)->exception);
7924      XSetCursorState(display,windows,MagickFalse);
7925      if (windows->image.orphan != MagickFalse)
7926        break;
7927      XConfigureImageColormap(display,resource_info,windows,*image);
7928      (void) XConfigureImage(display,resource_info,windows,*image);
7929      break;
7930    }
7931    case GrayscaleCommand:
7932    {
7933      /*
7934        Convert image to grayscale.
7935      */
7936      XSetCursorState(display,windows,MagickTrue);
7937      XCheckRefreshWindows(display,windows);
7938      (void) SetImageType(*image,(*image)->matte == MagickFalse ?
7939        GrayscaleType : GrayscaleMatteType);
7940      XSetCursorState(display,windows,MagickFalse);
7941      if (windows->image.orphan != MagickFalse)
7942        break;
7943      XConfigureImageColormap(display,resource_info,windows,*image);
7944      (void) XConfigureImage(display,resource_info,windows,*image);
7945      break;
7946    }
7947    case MapCommand:
7948    {
7949      Image
7950        *affinity_image;
7951
7952      static char
7953        filename[MaxTextExtent] = "\0";
7954
7955      /*
7956        Request image file name from user.
7957      */
7958      XFileBrowserWidget(display,windows,"Map",filename);
7959      if (*filename == '\0')
7960        break;
7961      /*
7962        Map image.
7963      */
7964      XSetCursorState(display,windows,MagickTrue);
7965      XCheckRefreshWindows(display,windows);
7966      (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
7967      affinity_image=ReadImage(image_info,&(*image)->exception);
7968      if (affinity_image != (Image *) NULL)
7969        {
7970          (void) RemapImage(&quantize_info,*image,affinity_image);
7971          affinity_image=DestroyImage(affinity_image);
7972        }
7973      CatchException(&(*image)->exception);
7974      XSetCursorState(display,windows,MagickFalse);
7975      if (windows->image.orphan != MagickFalse)
7976        break;
7977      XConfigureImageColormap(display,resource_info,windows,*image);
7978      (void) XConfigureImage(display,resource_info,windows,*image);
7979      break;
7980    }
7981    case QuantizeCommand:
7982    {
7983      int
7984        status;
7985
7986      static char
7987        colors[MaxTextExtent] = "256";
7988
7989      /*
7990        Query user for maximum number of colors.
7991      */
7992      status=XDialogWidget(display,windows,"Quantize",
7993        "Maximum number of colors:",colors);
7994      if (*colors == '\0')
7995        break;
7996      /*
7997        Color reduce the image.
7998      */
7999      XSetCursorState(display,windows,MagickTrue);
8000      XCheckRefreshWindows(display,windows);
8001      quantize_info.number_colors=StringToUnsignedLong(colors);
8002      quantize_info.dither=status != 0 ? MagickTrue : MagickFalse;
8003      (void) QuantizeImage(&quantize_info,*image);
8004      XSetCursorState(display,windows,MagickFalse);
8005      if (windows->image.orphan != MagickFalse)
8006        break;
8007      XConfigureImageColormap(display,resource_info,windows,*image);
8008      (void) XConfigureImage(display,resource_info,windows,*image);
8009      break;
8010    }
8011    case DespeckleCommand:
8012    {
8013      Image
8014        *despeckle_image;
8015
8016      /*
8017        Despeckle image.
8018      */
8019      XSetCursorState(display,windows,MagickTrue);
8020      XCheckRefreshWindows(display,windows);
8021      despeckle_image=DespeckleImage(*image,&(*image)->exception);
8022      if (despeckle_image != (Image *) NULL)
8023        {
8024          *image=DestroyImage(*image);
8025          *image=despeckle_image;
8026        }
8027      CatchException(&(*image)->exception);
8028      XSetCursorState(display,windows,MagickFalse);
8029      if (windows->image.orphan != MagickFalse)
8030        break;
8031      XConfigureImageColormap(display,resource_info,windows,*image);
8032      (void) XConfigureImage(display,resource_info,windows,*image);
8033      break;
8034    }
8035    case EmbossCommand:
8036    {
8037      Image
8038        *emboss_image;
8039
8040      static char
8041        radius[MaxTextExtent] = "0.0x1.0";
8042
8043      /*
8044        Query user for emboss radius.
8045      */
8046      (void) XDialogWidget(display,windows,"Emboss",
8047        "Enter the emboss radius and standard deviation:",radius);
8048      if (*radius == '\0')
8049        break;
8050      /*
8051        Reduce noise in the image.
8052      */
8053      XSetCursorState(display,windows,MagickTrue);
8054      XCheckRefreshWindows(display,windows);
8055      flags=ParseGeometry(radius,&geometry_info);
8056      if ((flags & SigmaValue) == 0)
8057        geometry_info.sigma=1.0;
8058      emboss_image=EmbossImage(*image,geometry_info.rho,geometry_info.sigma,
8059        &(*image)->exception);
8060      if (emboss_image != (Image *) NULL)
8061        {
8062          *image=DestroyImage(*image);
8063          *image=emboss_image;
8064        }
8065      CatchException(&(*image)->exception);
8066      XSetCursorState(display,windows,MagickFalse);
8067      if (windows->image.orphan != MagickFalse)
8068        break;
8069      XConfigureImageColormap(display,resource_info,windows,*image);
8070      (void) XConfigureImage(display,resource_info,windows,*image);
8071      break;
8072    }
8073    case ReduceNoiseCommand:
8074    {
8075      Image
8076        *noise_image;
8077
8078      static char
8079        radius[MaxTextExtent] = "0";
8080
8081      /*
8082        Query user for noise radius.
8083      */
8084      (void) XDialogWidget(display,windows,"Reduce Noise",
8085        "Enter the noise radius:",radius);
8086      if (*radius == '\0')
8087        break;
8088      /*
8089        Reduce noise in the image.
8090      */
8091      XSetCursorState(display,windows,MagickTrue);
8092      XCheckRefreshWindows(display,windows);
8093      flags=ParseGeometry(radius,&geometry_info);
8094      noise_image=StatisticImage(*image,NonpeakStatistic,(size_t)
8095        geometry_info.rho,(size_t) geometry_info.rho,&(*image)->exception);
8096      if (noise_image != (Image *) NULL)
8097        {
8098          *image=DestroyImage(*image);
8099          *image=noise_image;
8100        }
8101      CatchException(&(*image)->exception);
8102      XSetCursorState(display,windows,MagickFalse);
8103      if (windows->image.orphan != MagickFalse)
8104        break;
8105      XConfigureImageColormap(display,resource_info,windows,*image);
8106      (void) XConfigureImage(display,resource_info,windows,*image);
8107      break;
8108    }
8109    case AddNoiseCommand:
8110    {
8111      char
8112        **noises;
8113
8114      Image
8115        *noise_image;
8116
8117      static char
8118        noise_type[MaxTextExtent] = "Gaussian";
8119
8120      /*
8121        Add noise to the image.
8122      */
8123      noises=GetCommandOptions(MagickNoiseOptions);
8124      if (noises == (char **) NULL)
8125        break;
8126      XListBrowserWidget(display,windows,&windows->widget,
8127        (const char **) noises,"Add Noise",
8128        "Select a type of noise to add to your image:",noise_type);
8129      noises=DestroyStringList(noises);
8130      if (*noise_type == '\0')
8131        break;
8132      XSetCursorState(display,windows,MagickTrue);
8133      XCheckRefreshWindows(display,windows);
8134      noise_image=AddNoiseImage(*image,(NoiseType) ParseCommandOption(
8135        MagickNoiseOptions,MagickFalse,noise_type),&(*image)->exception);
8136      if (noise_image != (Image *) NULL)
8137        {
8138          *image=DestroyImage(*image);
8139          *image=noise_image;
8140        }
8141      CatchException(&(*image)->exception);
8142      XSetCursorState(display,windows,MagickFalse);
8143      if (windows->image.orphan != MagickFalse)
8144        break;
8145      XConfigureImageColormap(display,resource_info,windows,*image);
8146      (void) XConfigureImage(display,resource_info,windows,*image);
8147      break;
8148    }
8149    case SharpenCommand:
8150    {
8151      Image
8152        *sharp_image;
8153
8154      static char
8155        radius[MaxTextExtent] = "0.0x1.0";
8156
8157      /*
8158        Query user for sharpen radius.
8159      */
8160      (void) XDialogWidget(display,windows,"Sharpen",
8161        "Enter the sharpen radius and standard deviation:",radius);
8162      if (*radius == '\0')
8163        break;
8164      /*
8165        Sharpen image scanlines.
8166      */
8167      XSetCursorState(display,windows,MagickTrue);
8168      XCheckRefreshWindows(display,windows);
8169      flags=ParseGeometry(radius,&geometry_info);
8170      sharp_image=SharpenImage(*image,geometry_info.rho,geometry_info.sigma,
8171        &(*image)->exception);
8172      if (sharp_image != (Image *) NULL)
8173        {
8174          *image=DestroyImage(*image);
8175          *image=sharp_image;
8176        }
8177      CatchException(&(*image)->exception);
8178      XSetCursorState(display,windows,MagickFalse);
8179      if (windows->image.orphan != MagickFalse)
8180        break;
8181      XConfigureImageColormap(display,resource_info,windows,*image);
8182      (void) XConfigureImage(display,resource_info,windows,*image);
8183      break;
8184    }
8185    case BlurCommand:
8186    {
8187      Image
8188        *blur_image;
8189
8190      static char
8191        radius[MaxTextExtent] = "0.0x1.0";
8192
8193      /*
8194        Query user for blur radius.
8195      */
8196      (void) XDialogWidget(display,windows,"Blur",
8197        "Enter the blur radius and standard deviation:",radius);
8198      if (*radius == '\0')
8199        break;
8200      /*
8201        Blur an image.
8202      */
8203      XSetCursorState(display,windows,MagickTrue);
8204      XCheckRefreshWindows(display,windows);
8205      flags=ParseGeometry(radius,&geometry_info);
8206      blur_image=BlurImage(*image,geometry_info.rho,geometry_info.sigma,
8207        &(*image)->exception);
8208      if (blur_image != (Image *) NULL)
8209        {
8210          *image=DestroyImage(*image);
8211          *image=blur_image;
8212        }
8213      CatchException(&(*image)->exception);
8214      XSetCursorState(display,windows,MagickFalse);
8215      if (windows->image.orphan != MagickFalse)
8216        break;
8217      XConfigureImageColormap(display,resource_info,windows,*image);
8218      (void) XConfigureImage(display,resource_info,windows,*image);
8219      break;
8220    }
8221    case ThresholdCommand:
8222    {
8223      double
8224        threshold;
8225
8226      static char
8227        factor[MaxTextExtent] = "128";
8228
8229      /*
8230        Query user for threshold value.
8231      */
8232      (void) XDialogWidget(display,windows,"Threshold",
8233        "Enter threshold value:",factor);
8234      if (*factor == '\0')
8235        break;
8236      /*
8237        Gamma correct image.
8238      */
8239      XSetCursorState(display,windows,MagickTrue);
8240      XCheckRefreshWindows(display,windows);
8241      threshold=SiPrefixToDouble(factor,QuantumRange);
8242      (void) BilevelImage(*image,threshold);
8243      XSetCursorState(display,windows,MagickFalse);
8244      if (windows->image.orphan != MagickFalse)
8245        break;
8246      XConfigureImageColormap(display,resource_info,windows,*image);
8247      (void) XConfigureImage(display,resource_info,windows,*image);
8248      break;
8249    }
8250    case EdgeDetectCommand:
8251    {
8252      Image
8253        *edge_image;
8254
8255      static char
8256        radius[MaxTextExtent] = "0";
8257
8258      /*
8259        Query user for edge factor.
8260      */
8261      (void) XDialogWidget(display,windows,"Detect Edges",
8262        "Enter the edge detect radius:",radius);
8263      if (*radius == '\0')
8264        break;
8265      /*
8266        Detect edge in image.
8267      */
8268      XSetCursorState(display,windows,MagickTrue);
8269      XCheckRefreshWindows(display,windows);
8270      flags=ParseGeometry(radius,&geometry_info);
8271      edge_image=EdgeImage(*image,geometry_info.rho,&(*image)->exception);
8272      if (edge_image != (Image *) NULL)
8273        {
8274          *image=DestroyImage(*image);
8275          *image=edge_image;
8276        }
8277      CatchException(&(*image)->exception);
8278      XSetCursorState(display,windows,MagickFalse);
8279      if (windows->image.orphan != MagickFalse)
8280        break;
8281      XConfigureImageColormap(display,resource_info,windows,*image);
8282      (void) XConfigureImage(display,resource_info,windows,*image);
8283      break;
8284    }
8285    case SpreadCommand:
8286    {
8287      Image
8288        *spread_image;
8289
8290      static char
8291        amount[MaxTextExtent] = "2";
8292
8293      /*
8294        Query user for spread amount.
8295      */
8296      (void) XDialogWidget(display,windows,"Spread",
8297        "Enter the displacement amount:",amount);
8298      if (*amount == '\0')
8299        break;
8300      /*
8301        Displace image pixels by a random amount.
8302      */
8303      XSetCursorState(display,windows,MagickTrue);
8304      XCheckRefreshWindows(display,windows);
8305      flags=ParseGeometry(amount,&geometry_info);
8306      spread_image=EdgeImage(*image,geometry_info.rho,&(*image)->exception);
8307      if (spread_image != (Image *) NULL)
8308        {
8309          *image=DestroyImage(*image);
8310          *image=spread_image;
8311        }
8312      CatchException(&(*image)->exception);
8313      XSetCursorState(display,windows,MagickFalse);
8314      if (windows->image.orphan != MagickFalse)
8315        break;
8316      XConfigureImageColormap(display,resource_info,windows,*image);
8317      (void) XConfigureImage(display,resource_info,windows,*image);
8318      break;
8319    }
8320    case ShadeCommand:
8321    {
8322      Image
8323        *shade_image;
8324
8325      int
8326        status;
8327
8328      static char
8329        geometry[MaxTextExtent] = "30x30";
8330
8331      /*
8332        Query user for the shade geometry.
8333      */
8334      status=XDialogWidget(display,windows,"Shade",
8335        "Enter the azimuth and elevation of the light source:",geometry);
8336      if (*geometry == '\0')
8337        break;
8338      /*
8339        Shade image pixels.
8340      */
8341      XSetCursorState(display,windows,MagickTrue);
8342      XCheckRefreshWindows(display,windows);
8343      flags=ParseGeometry(geometry,&geometry_info);
8344      if ((flags & SigmaValue) == 0)
8345        geometry_info.sigma=1.0;
8346      shade_image=ShadeImage(*image,status != 0 ? MagickFalse : MagickTrue,
8347        geometry_info.rho,geometry_info.sigma,&(*image)->exception);
8348      if (shade_image != (Image *) NULL)
8349        {
8350          *image=DestroyImage(*image);
8351          *image=shade_image;
8352        }
8353      CatchException(&(*image)->exception);
8354      XSetCursorState(display,windows,MagickFalse);
8355      if (windows->image.orphan != MagickFalse)
8356        break;
8357      XConfigureImageColormap(display,resource_info,windows,*image);
8358      (void) XConfigureImage(display,resource_info,windows,*image);
8359      break;
8360    }
8361    case RaiseCommand:
8362    {
8363      static char
8364        bevel_width[MaxTextExtent] = "10";
8365
8366      /*
8367        Query user for bevel width.
8368      */
8369      (void) XDialogWidget(display,windows,"Raise","Bevel width:",bevel_width);
8370      if (*bevel_width == '\0')
8371        break;
8372      /*
8373        Raise an image.
8374      */
8375      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
8376      XSetCursorState(display,windows,MagickTrue);
8377      XCheckRefreshWindows(display,windows);
8378      (void) ParsePageGeometry(*image,bevel_width,&page_geometry,
8379        &(*image)->exception);
8380      (void) RaiseImage(*image,&page_geometry,MagickTrue);
8381      XSetCursorState(display,windows,MagickFalse);
8382      if (windows->image.orphan != MagickFalse)
8383        break;
8384      XConfigureImageColormap(display,resource_info,windows,*image);
8385      (void) XConfigureImage(display,resource_info,windows,*image);
8386      break;
8387    }
8388    case SegmentCommand:
8389    {
8390      static char
8391        threshold[MaxTextExtent] = "1.0x1.5";
8392
8393      /*
8394        Query user for smoothing threshold.
8395      */
8396      (void) XDialogWidget(display,windows,"Segment","Smooth threshold:",
8397        threshold);
8398      if (*threshold == '\0')
8399        break;
8400      /*
8401        Segment an image.
8402      */
8403      XSetCursorState(display,windows,MagickTrue);
8404      XCheckRefreshWindows(display,windows);
8405      flags=ParseGeometry(threshold,&geometry_info);
8406      if ((flags & SigmaValue) == 0)
8407        geometry_info.sigma=1.0;
8408      (void) SegmentImage(*image,RGBColorspace,MagickFalse,geometry_info.rho,
8409        geometry_info.sigma);
8410      XSetCursorState(display,windows,MagickFalse);
8411      if (windows->image.orphan != MagickFalse)
8412        break;
8413      XConfigureImageColormap(display,resource_info,windows,*image);
8414      (void) XConfigureImage(display,resource_info,windows,*image);
8415      break;
8416    }
8417    case SepiaToneCommand:
8418    {
8419      double
8420        threshold;
8421
8422      Image
8423        *sepia_image;
8424
8425      static char
8426        factor[MaxTextExtent] = "80%";
8427
8428      /*
8429        Query user for sepia-tone factor.
8430      */
8431      (void) XDialogWidget(display,windows,"Sepia Tone",
8432        "Enter the sepia tone factor (0 - 99.9%):",factor);
8433      if (*factor == '\0')
8434        break;
8435      /*
8436        Sepia tone image pixels.
8437      */
8438      XSetCursorState(display,windows,MagickTrue);
8439      XCheckRefreshWindows(display,windows);
8440      threshold=SiPrefixToDouble(factor,QuantumRange);
8441      sepia_image=SepiaToneImage(*image,threshold,&(*image)->exception);
8442      if (sepia_image != (Image *) NULL)
8443        {
8444          *image=DestroyImage(*image);
8445          *image=sepia_image;
8446        }
8447      CatchException(&(*image)->exception);
8448      XSetCursorState(display,windows,MagickFalse);
8449      if (windows->image.orphan != MagickFalse)
8450        break;
8451      XConfigureImageColormap(display,resource_info,windows,*image);
8452      (void) XConfigureImage(display,resource_info,windows,*image);
8453      break;
8454    }
8455    case SolarizeCommand:
8456    {
8457      double
8458        threshold;
8459
8460      static char
8461        factor[MaxTextExtent] = "60%";
8462
8463      /*
8464        Query user for solarize factor.
8465      */
8466      (void) XDialogWidget(display,windows,"Solarize",
8467        "Enter the solarize factor (0 - 99.9%):",factor);
8468      if (*factor == '\0')
8469        break;
8470      /*
8471        Solarize image pixels.
8472      */
8473      XSetCursorState(display,windows,MagickTrue);
8474      XCheckRefreshWindows(display,windows);
8475      threshold=SiPrefixToDouble(factor,QuantumRange);
8476      (void) SolarizeImage(*image,threshold);
8477      XSetCursorState(display,windows,MagickFalse);
8478      if (windows->image.orphan != MagickFalse)
8479        break;
8480      XConfigureImageColormap(display,resource_info,windows,*image);
8481      (void) XConfigureImage(display,resource_info,windows,*image);
8482      break;
8483    }
8484    case SwirlCommand:
8485    {
8486      Image
8487        *swirl_image;
8488
8489      static char
8490        degrees[MaxTextExtent] = "60";
8491
8492      /*
8493        Query user for swirl angle.
8494      */
8495      (void) XDialogWidget(display,windows,"Swirl","Enter the swirl angle:",
8496        degrees);
8497      if (*degrees == '\0')
8498        break;
8499      /*
8500        Swirl image pixels about the center.
8501      */
8502      XSetCursorState(display,windows,MagickTrue);
8503      XCheckRefreshWindows(display,windows);
8504      flags=ParseGeometry(degrees,&geometry_info);
8505      swirl_image=SwirlImage(*image,geometry_info.rho,&(*image)->exception);
8506      if (swirl_image != (Image *) NULL)
8507        {
8508          *image=DestroyImage(*image);
8509          *image=swirl_image;
8510        }
8511      CatchException(&(*image)->exception);
8512      XSetCursorState(display,windows,MagickFalse);
8513      if (windows->image.orphan != MagickFalse)
8514        break;
8515      XConfigureImageColormap(display,resource_info,windows,*image);
8516      (void) XConfigureImage(display,resource_info,windows,*image);
8517      break;
8518    }
8519    case ImplodeCommand:
8520    {
8521      Image
8522        *implode_image;
8523
8524      static char
8525        factor[MaxTextExtent] = "0.3";
8526
8527      /*
8528        Query user for implode factor.
8529      */
8530      (void) XDialogWidget(display,windows,"Implode",
8531        "Enter the implosion/explosion factor (-1.0 - 1.0):",factor);
8532      if (*factor == '\0')
8533        break;
8534      /*
8535        Implode image pixels about the center.
8536      */
8537      XSetCursorState(display,windows,MagickTrue);
8538      XCheckRefreshWindows(display,windows);
8539      flags=ParseGeometry(factor,&geometry_info);
8540      implode_image=ImplodeImage(*image,geometry_info.rho,&(*image)->exception);
8541      if (implode_image != (Image *) NULL)
8542        {
8543          *image=DestroyImage(*image);
8544          *image=implode_image;
8545        }
8546      CatchException(&(*image)->exception);
8547      XSetCursorState(display,windows,MagickFalse);
8548      if (windows->image.orphan != MagickFalse)
8549        break;
8550      XConfigureImageColormap(display,resource_info,windows,*image);
8551      (void) XConfigureImage(display,resource_info,windows,*image);
8552      break;
8553    }
8554    case VignetteCommand:
8555    {
8556      Image
8557        *vignette_image;
8558
8559      static char
8560        geometry[MaxTextExtent] = "0x20";
8561
8562      /*
8563        Query user for the vignette geometry.
8564      */
8565      (void) XDialogWidget(display,windows,"Vignette",
8566        "Enter the radius, sigma, and x and y offsets:",geometry);
8567      if (*geometry == '\0')
8568        break;
8569      /*
8570        Soften the edges of the image in vignette style
8571      */
8572      XSetCursorState(display,windows,MagickTrue);
8573      XCheckRefreshWindows(display,windows);
8574      flags=ParseGeometry(geometry,&geometry_info);
8575      if ((flags & SigmaValue) == 0)
8576        geometry_info.sigma=1.0;
8577      if ((flags & XiValue) == 0)
8578        geometry_info.xi=0.1*(*image)->columns;
8579      if ((flags & PsiValue) == 0)
8580        geometry_info.psi=0.1*(*image)->rows;
8581      vignette_image=VignetteImage(*image,geometry_info.rho,geometry_info.sigma,
8582        (ssize_t) ceil(geometry_info.xi-0.5),(ssize_t) ceil(geometry_info.psi-
8583        0.5),&(*image)->exception);
8584      if (vignette_image != (Image *) NULL)
8585        {
8586          *image=DestroyImage(*image);
8587          *image=vignette_image;
8588        }
8589      CatchException(&(*image)->exception);
8590      XSetCursorState(display,windows,MagickFalse);
8591      if (windows->image.orphan != MagickFalse)
8592        break;
8593      XConfigureImageColormap(display,resource_info,windows,*image);
8594      (void) XConfigureImage(display,resource_info,windows,*image);
8595      break;
8596    }
8597    case WaveCommand:
8598    {
8599      Image
8600        *wave_image;
8601
8602      static char
8603        geometry[MaxTextExtent] = "25x150";
8604
8605      /*
8606        Query user for the wave geometry.
8607      */
8608      (void) XDialogWidget(display,windows,"Wave",
8609        "Enter the amplitude and length of the wave:",geometry);
8610      if (*geometry == '\0')
8611        break;
8612      /*
8613        Alter an image along a sine wave.
8614      */
8615      XSetCursorState(display,windows,MagickTrue);
8616      XCheckRefreshWindows(display,windows);
8617      flags=ParseGeometry(geometry,&geometry_info);
8618      if ((flags & SigmaValue) == 0)
8619        geometry_info.sigma=1.0;
8620      wave_image=WaveImage(*image,geometry_info.rho,geometry_info.sigma,
8621        &(*image)->exception);
8622      if (wave_image != (Image *) NULL)
8623        {
8624          *image=DestroyImage(*image);
8625          *image=wave_image;
8626        }
8627      CatchException(&(*image)->exception);
8628      XSetCursorState(display,windows,MagickFalse);
8629      if (windows->image.orphan != MagickFalse)
8630        break;
8631      XConfigureImageColormap(display,resource_info,windows,*image);
8632      (void) XConfigureImage(display,resource_info,windows,*image);
8633      break;
8634    }
8635    case OilPaintCommand:
8636    {
8637      Image
8638        *paint_image;
8639
8640      static char
8641        radius[MaxTextExtent] = "0";
8642
8643      /*
8644        Query user for circular neighborhood radius.
8645      */
8646      (void) XDialogWidget(display,windows,"Oil Paint",
8647        "Enter the mask radius:",radius);
8648      if (*radius == '\0')
8649        break;
8650      /*
8651        OilPaint image scanlines.
8652      */
8653      XSetCursorState(display,windows,MagickTrue);
8654      XCheckRefreshWindows(display,windows);
8655      flags=ParseGeometry(radius,&geometry_info);
8656      paint_image=OilPaintImage(*image,geometry_info.rho,&(*image)->exception);
8657      if (paint_image != (Image *) NULL)
8658        {
8659          *image=DestroyImage(*image);
8660          *image=paint_image;
8661        }
8662      CatchException(&(*image)->exception);
8663      XSetCursorState(display,windows,MagickFalse);
8664      if (windows->image.orphan != MagickFalse)
8665        break;
8666      XConfigureImageColormap(display,resource_info,windows,*image);
8667      (void) XConfigureImage(display,resource_info,windows,*image);
8668      break;
8669    }
8670    case CharcoalDrawCommand:
8671    {
8672      Image
8673        *charcoal_image;
8674
8675      static char
8676        radius[MaxTextExtent] = "0x1";
8677
8678      /*
8679        Query user for charcoal radius.
8680      */
8681      (void) XDialogWidget(display,windows,"Charcoal Draw",
8682        "Enter the charcoal radius and sigma:",radius);
8683      if (*radius == '\0')
8684        break;
8685      /*
8686        Charcoal the image.
8687      */
8688      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
8689      XSetCursorState(display,windows,MagickTrue);
8690      XCheckRefreshWindows(display,windows);
8691      flags=ParseGeometry(radius,&geometry_info);
8692      if ((flags & SigmaValue) == 0)
8693        geometry_info.sigma=geometry_info.rho;
8694      charcoal_image=CharcoalImage(*image,geometry_info.rho,geometry_info.sigma,
8695        &(*image)->exception);
8696      if (charcoal_image != (Image *) NULL)
8697        {
8698          *image=DestroyImage(*image);
8699          *image=charcoal_image;
8700        }
8701      CatchException(&(*image)->exception);
8702      XSetCursorState(display,windows,MagickFalse);
8703      if (windows->image.orphan != MagickFalse)
8704        break;
8705      XConfigureImageColormap(display,resource_info,windows,*image);
8706      (void) XConfigureImage(display,resource_info,windows,*image);
8707      break;
8708    }
8709    case AnnotateCommand:
8710    {
8711      /*
8712        Annotate the image with text.
8713      */
8714      status=XAnnotateEditImage(display,resource_info,windows,*image);
8715      if (status == MagickFalse)
8716        {
8717          XNoticeWidget(display,windows,"Unable to annotate X image",
8718            (*image)->filename);
8719          break;
8720        }
8721      break;
8722    }
8723    case DrawCommand:
8724    {
8725      /*
8726        Draw image.
8727      */
8728      status=XDrawEditImage(display,resource_info,windows,image);
8729      if (status == MagickFalse)
8730        {
8731          XNoticeWidget(display,windows,"Unable to draw on the X image",
8732            (*image)->filename);
8733          break;
8734        }
8735      break;
8736    }
8737    case ColorCommand:
8738    {
8739      /*
8740        Color edit.
8741      */
8742      status=XColorEditImage(display,resource_info,windows,image);
8743      if (status == MagickFalse)
8744        {
8745          XNoticeWidget(display,windows,"Unable to pixel edit X image",
8746            (*image)->filename);
8747          break;
8748        }
8749      break;
8750    }
8751    case MatteCommand:
8752    {
8753      /*
8754        Matte edit.
8755      */
8756      status=XMatteEditImage(display,resource_info,windows,image);
8757      if (status == MagickFalse)
8758        {
8759          XNoticeWidget(display,windows,"Unable to matte edit X image",
8760            (*image)->filename);
8761          break;
8762        }
8763      break;
8764    }
8765    case CompositeCommand:
8766    {
8767      /*
8768        Composite image.
8769      */
8770      status=XCompositeImage(display,resource_info,windows,*image);
8771      if (status == MagickFalse)
8772        {
8773          XNoticeWidget(display,windows,"Unable to composite X image",
8774            (*image)->filename);
8775          break;
8776        }
8777      break;
8778    }
8779    case AddBorderCommand:
8780    {
8781      Image
8782        *border_image;
8783
8784      static char
8785        geometry[MaxTextExtent] = "6x6";
8786
8787      /*
8788        Query user for border color and geometry.
8789      */
8790      XColorBrowserWidget(display,windows,"Select",color);
8791      if (*color == '\0')
8792        break;
8793      (void) XDialogWidget(display,windows,"Add Border",
8794        "Enter border geometry:",geometry);
8795      if (*geometry == '\0')
8796        break;
8797      /*
8798        Add a border to the image.
8799      */
8800      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
8801      XSetCursorState(display,windows,MagickTrue);
8802      XCheckRefreshWindows(display,windows);
8803      (void) QueryColorDatabase(color,&(*image)->border_color,
8804        &(*image)->exception);
8805      (void) ParsePageGeometry(*image,geometry,&page_geometry,
8806        &(*image)->exception);
8807      border_image=BorderImage(*image,&page_geometry,&(*image)->exception);
8808      if (border_image != (Image *) NULL)
8809        {
8810          *image=DestroyImage(*image);
8811          *image=border_image;
8812        }
8813      CatchException(&(*image)->exception);
8814      XSetCursorState(display,windows,MagickFalse);
8815      if (windows->image.orphan != MagickFalse)
8816        break;
8817      windows->image.window_changes.width=(int) (*image)->columns;
8818      windows->image.window_changes.height=(int) (*image)->rows;
8819      XConfigureImageColormap(display,resource_info,windows,*image);
8820      (void) XConfigureImage(display,resource_info,windows,*image);
8821      break;
8822    }
8823    case AddFrameCommand:
8824    {
8825      FrameInfo
8826        frame_info;
8827
8828      Image
8829        *frame_image;
8830
8831      static char
8832        geometry[MaxTextExtent] = "6x6";
8833
8834      /*
8835        Query user for frame color and geometry.
8836      */
8837      XColorBrowserWidget(display,windows,"Select",color);
8838      if (*color == '\0')
8839        break;
8840      (void) XDialogWidget(display,windows,"Add Frame","Enter frame geometry:",
8841        geometry);
8842      if (*geometry == '\0')
8843        break;
8844      /*
8845        Surround image with an ornamental border.
8846      */
8847      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
8848      XSetCursorState(display,windows,MagickTrue);
8849      XCheckRefreshWindows(display,windows);
8850      (void) QueryColorDatabase(color,&(*image)->matte_color,
8851        &(*image)->exception);
8852      (void) ParsePageGeometry(*image,geometry,&page_geometry,
8853        &(*image)->exception);
8854      frame_info.width=page_geometry.width;
8855      frame_info.height=page_geometry.height;
8856      frame_info.outer_bevel=page_geometry.x;
8857      frame_info.inner_bevel=page_geometry.y;
8858      frame_info.x=(ssize_t) frame_info.width;
8859      frame_info.y=(ssize_t) frame_info.height;
8860      frame_info.width=(*image)->columns+2*frame_info.width;
8861      frame_info.height=(*image)->rows+2*frame_info.height;
8862      frame_image=FrameImage(*image,&frame_info,&(*image)->exception);
8863      if (frame_image != (Image *) NULL)
8864        {
8865          *image=DestroyImage(*image);
8866          *image=frame_image;
8867        }
8868      CatchException(&(*image)->exception);
8869      XSetCursorState(display,windows,MagickFalse);
8870      if (windows->image.orphan != MagickFalse)
8871        break;
8872      windows->image.window_changes.width=(int) (*image)->columns;
8873      windows->image.window_changes.height=(int) (*image)->rows;
8874      XConfigureImageColormap(display,resource_info,windows,*image);
8875      (void) XConfigureImage(display,resource_info,windows,*image);
8876      break;
8877    }
8878    case CommentCommand:
8879    {
8880      const char
8881        *value;
8882
8883      FILE
8884        *file;
8885
8886      int
8887        unique_file;
8888
8889      /*
8890        Edit image comment.
8891      */
8892      unique_file=AcquireUniqueFileResource(image_info->filename);
8893      if (unique_file == -1)
8894        XNoticeWidget(display,windows,"Unable to edit image comment",
8895          image_info->filename);
8896      value=GetImageProperty(*image,"comment");
8897      if (value == (char *) NULL)
8898        unique_file=close(unique_file)-1;
8899      else
8900        {
8901          register const char
8902            *p;
8903
8904          file=fdopen(unique_file,"w");
8905          if (file == (FILE *) NULL)
8906            {
8907              XNoticeWidget(display,windows,"Unable to edit image comment",
8908                image_info->filename);
8909              break;
8910            }
8911          for (p=value; *p != '\0'; p++)
8912            (void) fputc((int) *p,file);
8913          (void) fputc('\n',file);
8914          (void) fclose(file);
8915        }
8916      XSetCursorState(display,windows,MagickTrue);
8917      XCheckRefreshWindows(display,windows);
8918      status=InvokeDelegate(image_info,*image,"edit",(char *) NULL,
8919        &(*image)->exception);
8920      if (status == MagickFalse)
8921        XNoticeWidget(display,windows,"Unable to edit image comment",
8922          (char *) NULL);
8923      else
8924        {
8925          char
8926            *comment;
8927
8928          comment=FileToString(image_info->filename,~0UL,&(*image)->exception);
8929          if (comment != (char *) NULL)
8930            {
8931              (void) SetImageProperty(*image,"comment",comment);
8932              (*image)->taint=MagickTrue;
8933            }
8934        }
8935      (void) RelinquishUniqueFileResource(image_info->filename);
8936      XSetCursorState(display,windows,MagickFalse);
8937      break;
8938    }
8939    case LaunchCommand:
8940    {
8941      /*
8942        Launch program.
8943      */
8944      XSetCursorState(display,windows,MagickTrue);
8945      XCheckRefreshWindows(display,windows);
8946      (void) AcquireUniqueFilename(filename);
8947      (void) FormatLocaleString((*image)->filename,MaxTextExtent,"launch:%s",
8948        filename);
8949      status=WriteImage(image_info,*image);
8950      if (status == MagickFalse)
8951        XNoticeWidget(display,windows,"Unable to launch image editor",
8952          (char *) NULL);
8953      else
8954        {
8955          nexus=ReadImage(resource_info->image_info,&(*image)->exception);
8956          CatchException(&(*image)->exception);
8957          XClientMessage(display,windows->image.id,windows->im_protocols,
8958            windows->im_next_image,CurrentTime);
8959        }
8960      (void) RelinquishUniqueFileResource(filename);
8961      XSetCursorState(display,windows,MagickFalse);
8962      break;
8963    }
8964    case RegionofInterestCommand:
8965    {
8966      /*
8967        Apply an image processing technique to a region of interest.
8968      */
8969      (void) XROIImage(display,resource_info,windows,image);
8970      break;
8971    }
8972    case InfoCommand:
8973      break;
8974    case ZoomCommand:
8975    {
8976      /*
8977        Zoom image.
8978      */
8979      if (windows->magnify.mapped != MagickFalse)
8980        (void) XRaiseWindow(display,windows->magnify.id);
8981      else
8982        {
8983          /*
8984            Make magnify image.
8985          */
8986          XSetCursorState(display,windows,MagickTrue);
8987          (void) XMapRaised(display,windows->magnify.id);
8988          XSetCursorState(display,windows,MagickFalse);
8989        }
8990      break;
8991    }
8992    case ShowPreviewCommand:
8993    {
8994      char
8995        **previews;
8996
8997      Image
8998        *preview_image;
8999
9000      static char
9001        preview_type[MaxTextExtent] = "Gamma";
9002
9003      /*
9004        Select preview type from menu.
9005      */
9006      previews=GetCommandOptions(MagickPreviewOptions);
9007      if (previews == (char **) NULL)
9008        break;
9009      XListBrowserWidget(display,windows,&windows->widget,
9010        (const char **) previews,"Preview",
9011        "Select an enhancement, effect, or F/X:",preview_type);
9012      previews=DestroyStringList(previews);
9013      if (*preview_type == '\0')
9014        break;
9015      /*
9016        Show image preview.
9017      */
9018      XSetCursorState(display,windows,MagickTrue);
9019      XCheckRefreshWindows(display,windows);
9020      image_info->preview_type=(PreviewType)
9021        ParseCommandOption(MagickPreviewOptions,MagickFalse,preview_type);
9022      image_info->group=(ssize_t) windows->image.id;
9023      (void) DeleteImageProperty(*image,"label");
9024      (void) SetImageProperty(*image,"label","Preview");
9025      (void) AcquireUniqueFilename(filename);
9026      (void) FormatLocaleString((*image)->filename,MaxTextExtent,"preview:%s",
9027        filename);
9028      status=WriteImage(image_info,*image);
9029      (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9030      preview_image=ReadImage(image_info,&(*image)->exception);
9031      (void) RelinquishUniqueFileResource(filename);
9032      if (preview_image == (Image *) NULL)
9033        break;
9034      (void) FormatLocaleString(preview_image->filename,MaxTextExtent,"show:%s",
9035        filename);
9036      status=WriteImage(image_info,preview_image);
9037      preview_image=DestroyImage(preview_image);
9038      if (status == MagickFalse)
9039        XNoticeWidget(display,windows,"Unable to show image preview",
9040          (*image)->filename);
9041      XDelay(display,1500);
9042      XSetCursorState(display,windows,MagickFalse);
9043      break;
9044    }
9045    case ShowHistogramCommand:
9046    {
9047      Image
9048        *histogram_image;
9049
9050      /*
9051        Show image histogram.
9052      */
9053      XSetCursorState(display,windows,MagickTrue);
9054      XCheckRefreshWindows(display,windows);
9055      image_info->group=(ssize_t) windows->image.id;
9056      (void) DeleteImageProperty(*image,"label");
9057      (void) SetImageProperty(*image,"label","Histogram");
9058      (void) AcquireUniqueFilename(filename);
9059      (void) FormatLocaleString((*image)->filename,MaxTextExtent,"histogram:%s",
9060        filename);
9061      status=WriteImage(image_info,*image);
9062      (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9063      histogram_image=ReadImage(image_info,&(*image)->exception);
9064      (void) RelinquishUniqueFileResource(filename);
9065      if (histogram_image == (Image *) NULL)
9066        break;
9067      (void) FormatLocaleString(histogram_image->filename,MaxTextExtent,
9068        "show:%s",filename);
9069      status=WriteImage(image_info,histogram_image);
9070      histogram_image=DestroyImage(histogram_image);
9071      if (status == MagickFalse)
9072        XNoticeWidget(display,windows,"Unable to show histogram",
9073          (*image)->filename);
9074      XDelay(display,1500);
9075      XSetCursorState(display,windows,MagickFalse);
9076      break;
9077    }
9078    case ShowMatteCommand:
9079    {
9080      Image
9081        *matte_image;
9082
9083      if ((*image)->matte == MagickFalse)
9084        {
9085          XNoticeWidget(display,windows,
9086            "Image does not have any matte information",(*image)->filename);
9087          break;
9088        }
9089      /*
9090        Show image matte.
9091      */
9092      XSetCursorState(display,windows,MagickTrue);
9093      XCheckRefreshWindows(display,windows);
9094      image_info->group=(ssize_t) windows->image.id;
9095      (void) DeleteImageProperty(*image,"label");
9096      (void) SetImageProperty(*image,"label","Matte");
9097      (void) AcquireUniqueFilename(filename);
9098      (void) FormatLocaleString((*image)->filename,MaxTextExtent,"matte:%s",
9099        filename);
9100      status=WriteImage(image_info,*image);
9101      (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9102      matte_image=ReadImage(image_info,&(*image)->exception);
9103      (void) RelinquishUniqueFileResource(filename);
9104      if (matte_image == (Image *) NULL)
9105        break;
9106      (void) FormatLocaleString(matte_image->filename,MaxTextExtent,"show:%s",
9107        filename);
9108      status=WriteImage(image_info,matte_image);
9109      matte_image=DestroyImage(matte_image);
9110      if (status == MagickFalse)
9111        XNoticeWidget(display,windows,"Unable to show matte",
9112          (*image)->filename);
9113      XDelay(display,1500);
9114      XSetCursorState(display,windows,MagickFalse);
9115      break;
9116    }
9117    case BackgroundCommand:
9118    {
9119      /*
9120        Background image.
9121      */
9122      status=XBackgroundImage(display,resource_info,windows,image);
9123      if (status == MagickFalse)
9124        break;
9125      nexus=CloneImage(*image,0,0,MagickTrue,&(*image)->exception);
9126      if (nexus != (Image *) NULL)
9127        XClientMessage(display,windows->image.id,windows->im_protocols,
9128          windows->im_next_image,CurrentTime);
9129      break;
9130    }
9131    case SlideShowCommand:
9132    {
9133      static char
9134        delay[MaxTextExtent] = "5";
9135
9136      /*
9137        Display next image after pausing.
9138      */
9139      (void) XDialogWidget(display,windows,"Slide Show",
9140        "Pause how many 1/100ths of a second between images:",delay);
9141      if (*delay == '\0')
9142        break;
9143      resource_info->delay=StringToUnsignedLong(delay);
9144      XClientMessage(display,windows->image.id,windows->im_protocols,
9145        windows->im_next_image,CurrentTime);
9146      break;
9147    }
9148    case PreferencesCommand:
9149    {
9150      /*
9151        Set user preferences.
9152      */
9153      status=XPreferencesWidget(display,resource_info,windows);
9154      if (status == MagickFalse)
9155        break;
9156      nexus=CloneImage(*image,0,0,MagickTrue,&(*image)->exception);
9157      if (nexus != (Image *) NULL)
9158        XClientMessage(display,windows->image.id,windows->im_protocols,
9159          windows->im_next_image,CurrentTime);
9160      break;
9161    }
9162    case HelpCommand:
9163    {
9164      /*
9165        User requested help.
9166      */
9167      XTextViewWidget(display,resource_info,windows,MagickFalse,
9168        "Help Viewer - Display",DisplayHelp);
9169      break;
9170    }
9171    case BrowseDocumentationCommand:
9172    {
9173      Atom
9174        mozilla_atom;
9175
9176      Window
9177        mozilla_window,
9178        root_window;
9179
9180      /*
9181        Browse the ImageMagick documentation.
9182      */
9183      root_window=XRootWindow(display,XDefaultScreen(display));
9184      mozilla_atom=XInternAtom(display,"_MOZILLA_VERSION",MagickFalse);
9185      mozilla_window=XWindowByProperty(display,root_window,mozilla_atom);
9186      if (mozilla_window != (Window) NULL)
9187        {
9188          char
9189            command[MaxTextExtent],
9190            *url;
9191
9192          /*
9193            Display documentation using Netscape remote control.
9194          */
9195          url=GetMagickHomeURL();
9196          (void) FormatLocaleString(command,MaxTextExtent,
9197            "openurl(%s,new-tab)",url);
9198          url=DestroyString(url);
9199          mozilla_atom=XInternAtom(display,"_MOZILLA_COMMAND",MagickFalse);
9200          (void) XChangeProperty(display,mozilla_window,mozilla_atom,XA_STRING,
9201            8,PropModeReplace,(unsigned char *) command,(int) strlen(command));
9202          XSetCursorState(display,windows,MagickFalse);
9203          break;
9204        }
9205      XSetCursorState(display,windows,MagickTrue);
9206      XCheckRefreshWindows(display,windows);
9207      status=InvokeDelegate(image_info,*image,"browse",(char *) NULL,
9208        &(*image)->exception);
9209      if (status == MagickFalse)
9210        XNoticeWidget(display,windows,"Unable to browse documentation",
9211          (char *) NULL);
9212      XDelay(display,1500);
9213      XSetCursorState(display,windows,MagickFalse);
9214      break;
9215    }
9216    case VersionCommand:
9217    {
9218      XNoticeWidget(display,windows,GetMagickVersion((size_t *) NULL),
9219        GetMagickCopyright());
9220      break;
9221    }
9222    case SaveToUndoBufferCommand:
9223      break;
9224    default:
9225    {
9226      (void) XBell(display,0);
9227      break;
9228    }
9229  }
9230  image_info=DestroyImageInfo(image_info);
9231  return(nexus);
9232}
9233
9234/*
9235%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9236%                                                                             %
9237%                                                                             %
9238%                                                                             %
9239+   X M a g n i f y I m a g e                                                 %
9240%                                                                             %
9241%                                                                             %
9242%                                                                             %
9243%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9244%
9245%  XMagnifyImage() magnifies portions of the image as indicated by the pointer.
9246%  The magnified portion is displayed in a separate window.
9247%
9248%  The format of the XMagnifyImage method is:
9249%
9250%      void XMagnifyImage(Display *display,XWindows *windows,XEvent *event)
9251%
9252%  A description of each parameter follows:
9253%
9254%    o display: Specifies a connection to an X server;  returned from
9255%      XOpenDisplay.
9256%
9257%    o windows: Specifies a pointer to a XWindows structure.
9258%
9259%    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
9260%      the entire image is refreshed.
9261%
9262*/
9263static void XMagnifyImage(Display *display,XWindows *windows,XEvent *event)
9264{
9265  char
9266    text[MaxTextExtent];
9267
9268  register int
9269    x,
9270    y;
9271
9272  size_t
9273    state;
9274
9275  /*
9276    Update magnified image until the mouse button is released.
9277  */
9278  (void) XCheckDefineCursor(display,windows->image.id,windows->magnify.cursor);
9279  state=DefaultState;
9280  x=event->xbutton.x;
9281  y=event->xbutton.y;
9282  windows->magnify.x=(int) windows->image.x+x;
9283  windows->magnify.y=(int) windows->image.y+y;
9284  do
9285  {
9286    /*
9287      Map and unmap Info widget as text cursor crosses its boundaries.
9288    */
9289    if (windows->info.mapped != MagickFalse)
9290      {
9291        if ((x < (int) (windows->info.x+windows->info.width)) &&
9292            (y < (int) (windows->info.y+windows->info.height)))
9293          (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
9294      }
9295    else
9296      if ((x > (int) (windows->info.x+windows->info.width)) ||
9297          (y > (int) (windows->info.y+windows->info.height)))
9298        (void) XMapWindow(display,windows->info.id);
9299    if (windows->info.mapped != MagickFalse)
9300      {
9301        /*
9302          Display pointer position.
9303        */
9304        (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
9305          windows->magnify.x,windows->magnify.y);
9306        XInfoWidget(display,windows,text);
9307      }
9308    /*
9309      Wait for next event.
9310    */
9311    XScreenEvent(display,windows,event);
9312    switch (event->type)
9313    {
9314      case ButtonPress:
9315        break;
9316      case ButtonRelease:
9317      {
9318        /*
9319          User has finished magnifying image.
9320        */
9321        x=event->xbutton.x;
9322        y=event->xbutton.y;
9323        state|=ExitState;
9324        break;
9325      }
9326      case Expose:
9327        break;
9328      case MotionNotify:
9329      {
9330        x=event->xmotion.x;
9331        y=event->xmotion.y;
9332        break;
9333      }
9334      default:
9335        break;
9336    }
9337    /*
9338      Check boundary conditions.
9339    */
9340    if (x < 0)
9341      x=0;
9342    else
9343      if (x >= (int) windows->image.width)
9344        x=(int) windows->image.width-1;
9345    if (y < 0)
9346      y=0;
9347    else
9348     if (y >= (int) windows->image.height)
9349       y=(int) windows->image.height-1;
9350  } while ((state & ExitState) == 0);
9351  /*
9352    Display magnified image.
9353  */
9354  XSetCursorState(display,windows,MagickFalse);
9355}
9356
9357/*
9358%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9359%                                                                             %
9360%                                                                             %
9361%                                                                             %
9362+   X M a g n i f y W i n d o w C o m m a n d                                 %
9363%                                                                             %
9364%                                                                             %
9365%                                                                             %
9366%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9367%
9368%  XMagnifyWindowCommand() moves the image within an Magnify window by one
9369%  pixel as specified by the key symbol.
9370%
9371%  The format of the XMagnifyWindowCommand method is:
9372%
9373%      void XMagnifyWindowCommand(Display *display,XWindows *windows,
9374%        const MagickStatusType state,const KeySym key_symbol)
9375%
9376%  A description of each parameter follows:
9377%
9378%    o display: Specifies a connection to an X server; returned from
9379%      XOpenDisplay.
9380%
9381%    o windows: Specifies a pointer to a XWindows structure.
9382%
9383%    o state: key mask.
9384%
9385%    o key_symbol: Specifies a KeySym which indicates which side of the image
9386%      to trim.
9387%
9388*/
9389static void XMagnifyWindowCommand(Display *display,XWindows *windows,
9390  const MagickStatusType state,const KeySym key_symbol)
9391{
9392  unsigned int
9393    quantum;
9394
9395  /*
9396    User specified a magnify factor or position.
9397  */
9398  quantum=1;
9399  if ((state & Mod1Mask) != 0)
9400    quantum=10;
9401  switch ((int) key_symbol)
9402  {
9403    case QuitCommand:
9404    {
9405      (void) XWithdrawWindow(display,windows->magnify.id,
9406        windows->magnify.screen);
9407      break;
9408    }
9409    case XK_Home:
9410    case XK_KP_Home:
9411    {
9412      windows->magnify.x=(int) windows->image.width/2;
9413      windows->magnify.y=(int) windows->image.height/2;
9414      break;
9415    }
9416    case XK_Left:
9417    case XK_KP_Left:
9418    {
9419      if (windows->magnify.x > 0)
9420        windows->magnify.x-=quantum;
9421      break;
9422    }
9423    case XK_Up:
9424    case XK_KP_Up:
9425    {
9426      if (windows->magnify.y > 0)
9427        windows->magnify.y-=quantum;
9428      break;
9429    }
9430    case XK_Right:
9431    case XK_KP_Right:
9432    {
9433      if (windows->magnify.x < (int) (windows->image.ximage->width-1))
9434        windows->magnify.x+=quantum;
9435      break;
9436    }
9437    case XK_Down:
9438    case XK_KP_Down:
9439    {
9440      if (windows->magnify.y < (int) (windows->image.ximage->height-1))
9441        windows->magnify.y+=quantum;
9442      break;
9443    }
9444    case XK_0:
9445    case XK_1:
9446    case XK_2:
9447    case XK_3:
9448    case XK_4:
9449    case XK_5:
9450    case XK_6:
9451    case XK_7:
9452    case XK_8:
9453    case XK_9:
9454    {
9455      windows->magnify.data=(key_symbol-XK_0);
9456      break;
9457    }
9458    case XK_KP_0:
9459    case XK_KP_1:
9460    case XK_KP_2:
9461    case XK_KP_3:
9462    case XK_KP_4:
9463    case XK_KP_5:
9464    case XK_KP_6:
9465    case XK_KP_7:
9466    case XK_KP_8:
9467    case XK_KP_9:
9468    {
9469      windows->magnify.data=(key_symbol-XK_KP_0);
9470      break;
9471    }
9472    default:
9473      break;
9474  }
9475  XMakeMagnifyImage(display,windows);
9476}
9477
9478/*
9479%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9480%                                                                             %
9481%                                                                             %
9482%                                                                             %
9483+   X M a k e P a n I m a g e                                                 %
9484%                                                                             %
9485%                                                                             %
9486%                                                                             %
9487%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9488%
9489%  XMakePanImage() creates a thumbnail of the image and displays it in the Pan
9490%  icon window.
9491%
9492%  The format of the XMakePanImage method is:
9493%
9494%        void XMakePanImage(Display *display,XResourceInfo *resource_info,
9495%          XWindows *windows,Image *image)
9496%
9497%  A description of each parameter follows:
9498%
9499%    o display: Specifies a connection to an X server;  returned from
9500%      XOpenDisplay.
9501%
9502%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9503%
9504%    o windows: Specifies a pointer to a XWindows structure.
9505%
9506%    o image: the image.
9507%
9508*/
9509static void XMakePanImage(Display *display,XResourceInfo *resource_info,
9510  XWindows *windows,Image *image)
9511{
9512  MagickStatusType
9513    status;
9514
9515  /*
9516    Create and display image for panning icon.
9517  */
9518  XSetCursorState(display,windows,MagickTrue);
9519  XCheckRefreshWindows(display,windows);
9520  windows->pan.x=(int) windows->image.x;
9521  windows->pan.y=(int) windows->image.y;
9522  status=XMakeImage(display,resource_info,&windows->pan,image,
9523    windows->pan.width,windows->pan.height);
9524  if (status == MagickFalse)
9525    ThrowXWindowFatalException(XServerError,image->exception.reason,
9526      image->exception.description);
9527  (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
9528    windows->pan.pixmap);
9529  (void) XClearWindow(display,windows->pan.id);
9530  XDrawPanRectangle(display,windows);
9531  XSetCursorState(display,windows,MagickFalse);
9532}
9533
9534/*
9535%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9536%                                                                             %
9537%                                                                             %
9538%                                                                             %
9539+   X M a t t a E d i t I m a g e                                             %
9540%                                                                             %
9541%                                                                             %
9542%                                                                             %
9543%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9544%
9545%  XMatteEditImage() allows the user to interactively change the Matte channel
9546%  of an image.  If the image is PseudoClass it is promoted to DirectClass
9547%  before the matte information is stored.
9548%
9549%  The format of the XMatteEditImage method is:
9550%
9551%      MagickBooleanType XMatteEditImage(Display *display,
9552%        XResourceInfo *resource_info,XWindows *windows,Image **image)
9553%
9554%  A description of each parameter follows:
9555%
9556%    o display: Specifies a connection to an X server;  returned from
9557%      XOpenDisplay.
9558%
9559%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9560%
9561%    o windows: Specifies a pointer to a XWindows structure.
9562%
9563%    o image: the image; returned from ReadImage.
9564%
9565*/
9566static MagickBooleanType XMatteEditImage(Display *display,
9567  XResourceInfo *resource_info,XWindows *windows,Image **image)
9568{
9569  static char
9570    matte[MaxTextExtent] = "0";
9571
9572  static const char
9573    *MatteEditMenu[] =
9574    {
9575      "Method",
9576      "Border Color",
9577      "Fuzz",
9578      "Matte Value",
9579      "Undo",
9580      "Help",
9581      "Dismiss",
9582      (char *) NULL
9583    };
9584
9585  static const ModeType
9586    MatteEditCommands[] =
9587    {
9588      MatteEditMethod,
9589      MatteEditBorderCommand,
9590      MatteEditFuzzCommand,
9591      MatteEditValueCommand,
9592      MatteEditUndoCommand,
9593      MatteEditHelpCommand,
9594      MatteEditDismissCommand
9595    };
9596
9597  static PaintMethod
9598    method = PointMethod;
9599
9600  static XColor
9601    border_color = { 0, 0, 0, 0, 0, 0 };
9602
9603  char
9604    command[MaxTextExtent],
9605    text[MaxTextExtent];
9606
9607  Cursor
9608    cursor;
9609
9610  int
9611    entry,
9612    id,
9613    x,
9614    x_offset,
9615    y,
9616    y_offset;
9617
9618  register int
9619    i;
9620
9621  register Quantum
9622    *q;
9623
9624  unsigned int
9625    height,
9626    width;
9627
9628  size_t
9629    state;
9630
9631  XEvent
9632    event;
9633
9634  /*
9635    Map Command widget.
9636  */
9637  (void) CloneString(&windows->command.name,"Matte Edit");
9638  windows->command.data=4;
9639  (void) XCommandWidget(display,windows,MatteEditMenu,(XEvent *) NULL);
9640  (void) XMapRaised(display,windows->command.id);
9641  XClientMessage(display,windows->image.id,windows->im_protocols,
9642    windows->im_update_widget,CurrentTime);
9643  /*
9644    Make cursor.
9645  */
9646  cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
9647    resource_info->background_color,resource_info->foreground_color);
9648  (void) XCheckDefineCursor(display,windows->image.id,cursor);
9649  /*
9650    Track pointer until button 1 is pressed.
9651  */
9652  XQueryPosition(display,windows->image.id,&x,&y);
9653  (void) XSelectInput(display,windows->image.id,
9654    windows->image.attributes.event_mask | PointerMotionMask);
9655  state=DefaultState;
9656  do
9657  {
9658    if (windows->info.mapped != MagickFalse)
9659      {
9660        /*
9661          Display pointer position.
9662        */
9663        (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
9664          x+windows->image.x,y+windows->image.y);
9665        XInfoWidget(display,windows,text);
9666      }
9667    /*
9668      Wait for next event.
9669    */
9670    XScreenEvent(display,windows,&event);
9671    if (event.xany.window == windows->command.id)
9672      {
9673        /*
9674          Select a command from the Command widget.
9675        */
9676        id=XCommandWidget(display,windows,MatteEditMenu,&event);
9677        if (id < 0)
9678          {
9679            (void) XCheckDefineCursor(display,windows->image.id,cursor);
9680            continue;
9681          }
9682        switch (MatteEditCommands[id])
9683        {
9684          case MatteEditMethod:
9685          {
9686            char
9687              **methods;
9688
9689            /*
9690              Select a method from the pop-up menu.
9691            */
9692            methods=GetCommandOptions(MagickMethodOptions);
9693            if (methods == (char **) NULL)
9694              break;
9695            entry=XMenuWidget(display,windows,MatteEditMenu[id],
9696              (const char **) methods,command);
9697            if (entry >= 0)
9698              method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
9699                MagickFalse,methods[entry]);
9700            methods=DestroyStringList(methods);
9701            break;
9702          }
9703          case MatteEditBorderCommand:
9704          {
9705            const char
9706              *ColorMenu[MaxNumberPens];
9707
9708            int
9709              pen_number;
9710
9711            /*
9712              Initialize menu selections.
9713            */
9714            for (i=0; i < (int) (MaxNumberPens-2); i++)
9715              ColorMenu[i]=resource_info->pen_colors[i];
9716            ColorMenu[MaxNumberPens-2]="Browser...";
9717            ColorMenu[MaxNumberPens-1]=(const char *) NULL;
9718            /*
9719              Select a pen color from the pop-up menu.
9720            */
9721            pen_number=XMenuWidget(display,windows,MatteEditMenu[id],
9722              (const char **) ColorMenu,command);
9723            if (pen_number < 0)
9724              break;
9725            if (pen_number == (MaxNumberPens-2))
9726              {
9727                static char
9728                  color_name[MaxTextExtent] = "gray";
9729
9730                /*
9731                  Select a pen color from a dialog.
9732                */
9733                resource_info->pen_colors[pen_number]=color_name;
9734                XColorBrowserWidget(display,windows,"Select",color_name);
9735                if (*color_name == '\0')
9736                  break;
9737              }
9738            /*
9739              Set border color.
9740            */
9741            (void) XParseColor(display,windows->map_info->colormap,
9742              resource_info->pen_colors[pen_number],&border_color);
9743            break;
9744          }
9745          case MatteEditFuzzCommand:
9746          {
9747            static char
9748              fuzz[MaxTextExtent];
9749
9750            static const char
9751              *FuzzMenu[] =
9752              {
9753                "0%",
9754                "2%",
9755                "5%",
9756                "10%",
9757                "15%",
9758                "Dialog...",
9759                (char *) NULL,
9760              };
9761
9762            /*
9763              Select a command from the pop-up menu.
9764            */
9765            entry=XMenuWidget(display,windows,MatteEditMenu[id],FuzzMenu,
9766              command);
9767            if (entry < 0)
9768              break;
9769            if (entry != 5)
9770              {
9771                (*image)->fuzz=SiPrefixToDouble(FuzzMenu[entry],1.0*
9772                  QuantumRange+1.0);
9773                break;
9774              }
9775            (void) CopyMagickString(fuzz,"20%",MaxTextExtent);
9776            (void) XDialogWidget(display,windows,"Ok",
9777              "Enter fuzz factor (0.0 - 99.9%):",fuzz);
9778            if (*fuzz == '\0')
9779              break;
9780            (void) ConcatenateMagickString(fuzz,"%",MaxTextExtent);
9781            (*image)->fuzz=SiPrefixToDouble(fuzz,1.0*QuantumRange+1.0);
9782            break;
9783          }
9784          case MatteEditValueCommand:
9785          {
9786            static char
9787              message[MaxTextExtent];
9788
9789            static const char
9790              *MatteMenu[] =
9791              {
9792                "Opaque",
9793                "Transparent",
9794                "Dialog...",
9795                (char *) NULL,
9796              };
9797
9798            /*
9799              Select a command from the pop-up menu.
9800            */
9801            entry=XMenuWidget(display,windows,MatteEditMenu[id],MatteMenu,
9802              command);
9803            if (entry < 0)
9804              break;
9805            if (entry != 2)
9806              {
9807                (void) FormatLocaleString(matte,MaxTextExtent,QuantumFormat,
9808                  OpaqueAlpha);
9809                if (LocaleCompare(MatteMenu[entry],"Transparent") == 0)
9810                  (void) FormatLocaleString(matte,MaxTextExtent,QuantumFormat,
9811                    (Quantum) TransparentAlpha);
9812                break;
9813              }
9814            (void) FormatLocaleString(message,MaxTextExtent,
9815              "Enter matte value (0 - " QuantumFormat "):",(Quantum)
9816              QuantumRange);
9817            (void) XDialogWidget(display,windows,"Matte",message,matte);
9818            if (*matte == '\0')
9819              break;
9820            break;
9821          }
9822          case MatteEditUndoCommand:
9823          {
9824            (void) XMagickCommand(display,resource_info,windows,UndoCommand,
9825              image);
9826            break;
9827          }
9828          case MatteEditHelpCommand:
9829          {
9830            XTextViewWidget(display,resource_info,windows,MagickFalse,
9831              "Help Viewer - Matte Edit",ImageMatteEditHelp);
9832            break;
9833          }
9834          case MatteEditDismissCommand:
9835          {
9836            /*
9837              Prematurely exit.
9838            */
9839            state|=EscapeState;
9840            state|=ExitState;
9841            break;
9842          }
9843          default:
9844            break;
9845        }
9846        (void) XCheckDefineCursor(display,windows->image.id,cursor);
9847        continue;
9848      }
9849    switch (event.type)
9850    {
9851      case ButtonPress:
9852      {
9853        if (event.xbutton.button != Button1)
9854          break;
9855        if ((event.xbutton.window != windows->image.id) &&
9856            (event.xbutton.window != windows->magnify.id))
9857          break;
9858        /*
9859          Update matte data.
9860        */
9861        x=event.xbutton.x;
9862        y=event.xbutton.y;
9863        (void) XMagickCommand(display,resource_info,windows,
9864          SaveToUndoBufferCommand,image);
9865        state|=UpdateConfigurationState;
9866        break;
9867      }
9868      case ButtonRelease:
9869      {
9870        if (event.xbutton.button != Button1)
9871          break;
9872        if ((event.xbutton.window != windows->image.id) &&
9873            (event.xbutton.window != windows->magnify.id))
9874          break;
9875        /*
9876          Update colormap information.
9877        */
9878        x=event.xbutton.x;
9879        y=event.xbutton.y;
9880        XConfigureImageColormap(display,resource_info,windows,*image);
9881        (void) XConfigureImage(display,resource_info,windows,*image);
9882        XInfoWidget(display,windows,text);
9883        (void) XCheckDefineCursor(display,windows->image.id,cursor);
9884        state&=(~UpdateConfigurationState);
9885        break;
9886      }
9887      case Expose:
9888        break;
9889      case KeyPress:
9890      {
9891        char
9892          command[MaxTextExtent];
9893
9894        KeySym
9895          key_symbol;
9896
9897        if (event.xkey.window == windows->magnify.id)
9898          {
9899            Window
9900              window;
9901
9902            window=windows->magnify.id;
9903            while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
9904          }
9905        if (event.xkey.window != windows->image.id)
9906          break;
9907        /*
9908          Respond to a user key press.
9909        */
9910        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
9911          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
9912        switch ((int) key_symbol)
9913        {
9914          case XK_Escape:
9915          case XK_F20:
9916          {
9917            /*
9918              Prematurely exit.
9919            */
9920            state|=ExitState;
9921            break;
9922          }
9923          case XK_F1:
9924          case XK_Help:
9925          {
9926            XTextViewWidget(display,resource_info,windows,MagickFalse,
9927              "Help Viewer - Matte Edit",ImageMatteEditHelp);
9928            break;
9929          }
9930          default:
9931          {
9932            (void) XBell(display,0);
9933            break;
9934          }
9935        }
9936        break;
9937      }
9938      case MotionNotify:
9939      {
9940        /*
9941          Map and unmap Info widget as cursor crosses its boundaries.
9942        */
9943        x=event.xmotion.x;
9944        y=event.xmotion.y;
9945        if (windows->info.mapped != MagickFalse)
9946          {
9947            if ((x < (int) (windows->info.x+windows->info.width)) &&
9948                (y < (int) (windows->info.y+windows->info.height)))
9949              (void) XWithdrawWindow(display,windows->info.id,
9950                windows->info.screen);
9951          }
9952        else
9953          if ((x > (int) (windows->info.x+windows->info.width)) ||
9954              (y > (int) (windows->info.y+windows->info.height)))
9955            (void) XMapWindow(display,windows->info.id);
9956        break;
9957      }
9958      default:
9959        break;
9960    }
9961    if (event.xany.window == windows->magnify.id)
9962      {
9963        x=windows->magnify.x-windows->image.x;
9964        y=windows->magnify.y-windows->image.y;
9965      }
9966    x_offset=x;
9967    y_offset=y;
9968    if ((state & UpdateConfigurationState) != 0)
9969      {
9970        CacheView
9971          *image_view;
9972
9973        ExceptionInfo
9974          *exception;
9975
9976        int
9977          x,
9978          y;
9979
9980        /*
9981          Matte edit is relative to image configuration.
9982        */
9983        (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
9984          MagickTrue);
9985        XPutPixel(windows->image.ximage,x_offset,y_offset,
9986          windows->pixel_info->background_color.pixel);
9987        width=(unsigned int) (*image)->columns;
9988        height=(unsigned int) (*image)->rows;
9989        x=0;
9990        y=0;
9991        if (windows->image.crop_geometry != (char *) NULL)
9992          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,
9993            &height);
9994        x_offset=(int) (width*(windows->image.x+x_offset)/
9995          windows->image.ximage->width+x);
9996        y_offset=(int) (height*(windows->image.y+y_offset)/
9997          windows->image.ximage->height+y);
9998        if ((x_offset < 0) || (y_offset < 0))
9999          continue;
10000        if ((x_offset >= (int) (*image)->columns) ||
10001            (y_offset >= (int) (*image)->rows))
10002          continue;
10003        exception=(&(*image)->exception);
10004        if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
10005          return(MagickFalse);
10006        (*image)->matte=MagickTrue;
10007        image_view=AcquireCacheView(*image);
10008        switch (method)
10009        {
10010          case PointMethod:
10011          default:
10012          {
10013            /*
10014              Update matte information using point algorithm.
10015            */
10016            q=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,
10017              (ssize_t) y_offset,1,1,exception);
10018            if (q == (Quantum *) NULL)
10019              break;
10020            SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10021            (void) SyncCacheViewAuthenticPixels(image_view,exception);
10022            break;
10023          }
10024          case ReplaceMethod:
10025          {
10026            PixelPacket
10027              pixel,
10028              target;
10029
10030            /*
10031              Update matte information using replace algorithm.
10032            */
10033            (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t) x_offset,
10034              (ssize_t) y_offset,&target,exception);
10035            for (y=0; y < (int) (*image)->rows; y++)
10036            {
10037              q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10038                (*image)->columns,1,&(*image)->exception);
10039              if (q == (Quantum *) NULL)
10040                break;
10041              for (x=0; x < (int) (*image)->columns; x++)
10042              {
10043                GetPixelPacket(*image,q,&pixel);
10044                if (IsFuzzyEquivalencePixelPacket(*image,&pixel,&target))
10045                  SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10046                q+=GetPixelChannels(*image);
10047              }
10048              if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
10049                break;
10050            }
10051            break;
10052          }
10053          case FloodfillMethod:
10054          case FillToBorderMethod:
10055          {
10056            DrawInfo
10057              *draw_info;
10058
10059            PixelInfo
10060              target;
10061
10062            /*
10063              Update matte information using floodfill algorithm.
10064            */
10065            (void) GetOneVirtualMagickPixel(*image,(ssize_t) x_offset,
10066              (ssize_t) y_offset,&target,exception);
10067            if (method == FillToBorderMethod)
10068              {
10069                target.red=(MagickRealType) ScaleShortToQuantum(
10070                  border_color.red);
10071                target.green=(MagickRealType) ScaleShortToQuantum(
10072                  border_color.green);
10073                target.blue=(MagickRealType) ScaleShortToQuantum(
10074                  border_color.blue);
10075              }
10076            draw_info=CloneDrawInfo(resource_info->image_info,
10077              (DrawInfo *) NULL);
10078            draw_info->fill.alpha=ClampToQuantum(InterpretLocaleValue(matte,
10079              (char **) NULL));
10080            PushPixelChannelMap(*image,AlphaChannel);
10081            (void) FloodfillPaintImage(*image,draw_info,&target,(ssize_t)
10082              x_offset,(ssize_t) y_offset,method == FloodfillMethod ?
10083              MagickFalse : MagickTrue);
10084            PopPixelChannelMap(*image);
10085            draw_info=DestroyDrawInfo(draw_info);
10086            break;
10087          }
10088          case ResetMethod:
10089          {
10090            /*
10091              Update matte information using reset algorithm.
10092            */
10093            if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
10094              return(MagickFalse);
10095            for (y=0; y < (int) (*image)->rows; y++)
10096            {
10097              q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10098                (*image)->columns,1,exception);
10099              if (q == (Quantum *) NULL)
10100                break;
10101              for (x=0; x < (int) (*image)->columns; x++)
10102              {
10103                SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10104                q+=GetPixelChannels(*image);
10105              }
10106              if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
10107                break;
10108            }
10109            if (StringToLong(matte) == (long) OpaqueAlpha)
10110              (*image)->matte=MagickFalse;
10111            break;
10112          }
10113        }
10114        image_view=DestroyCacheView(image_view);
10115        state&=(~UpdateConfigurationState);
10116      }
10117  } while ((state & ExitState) == 0);
10118  (void) XSelectInput(display,windows->image.id,
10119    windows->image.attributes.event_mask);
10120  XSetCursorState(display,windows,MagickFalse);
10121  (void) XFreeCursor(display,cursor);
10122  return(MagickTrue);
10123}
10124
10125/*
10126%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10127%                                                                             %
10128%                                                                             %
10129%                                                                             %
10130+   X O p e n I m a g e                                                       %
10131%                                                                             %
10132%                                                                             %
10133%                                                                             %
10134%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10135%
10136%  XOpenImage() loads an image from a file.
10137%
10138%  The format of the XOpenImage method is:
10139%
10140%     Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10141%       XWindows *windows,const unsigned int command)
10142%
10143%  A description of each parameter follows:
10144%
10145%    o display: Specifies a connection to an X server; returned from
10146%      XOpenDisplay.
10147%
10148%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10149%
10150%    o windows: Specifies a pointer to a XWindows structure.
10151%
10152%    o command: A value other than zero indicates that the file is selected
10153%      from the command line argument list.
10154%
10155*/
10156static Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10157  XWindows *windows,const MagickBooleanType command)
10158{
10159  const MagickInfo
10160    *magick_info;
10161
10162  ExceptionInfo
10163    *exception;
10164
10165  Image
10166    *nexus;
10167
10168  ImageInfo
10169    *image_info;
10170
10171  static char
10172    filename[MaxTextExtent] = "\0";
10173
10174  /*
10175    Request file name from user.
10176  */
10177  if (command == MagickFalse)
10178    XFileBrowserWidget(display,windows,"Open",filename);
10179  else
10180    {
10181      char
10182        **filelist,
10183        **files;
10184
10185      int
10186        count,
10187        status;
10188
10189      register int
10190        i,
10191        j;
10192
10193      /*
10194        Select next image from the command line.
10195      */
10196      status=XGetCommand(display,windows->image.id,&files,&count);
10197      if (status == 0)
10198        {
10199          ThrowXWindowFatalException(XServerError,"UnableToGetProperty","...");
10200          return((Image *) NULL);
10201        }
10202      filelist=(char **) AcquireQuantumMemory((size_t) count,sizeof(*filelist));
10203      if (filelist == (char **) NULL)
10204        {
10205          ThrowXWindowFatalException(ResourceLimitError,
10206            "MemoryAllocationFailed","...");
10207          (void) XFreeStringList(files);
10208          return((Image *) NULL);
10209        }
10210      j=0;
10211      for (i=1; i < count; i++)
10212        if (*files[i] != '-')
10213          filelist[j++]=files[i];
10214      filelist[j]=(char *) NULL;
10215      XListBrowserWidget(display,windows,&windows->widget,
10216        (const char **) filelist,"Load","Select Image to Load:",filename);
10217      filelist=(char **) RelinquishMagickMemory(filelist);
10218      (void) XFreeStringList(files);
10219    }
10220  if (*filename == '\0')
10221    return((Image *) NULL);
10222  image_info=CloneImageInfo(resource_info->image_info);
10223  (void) SetImageInfoProgressMonitor(image_info,(MagickProgressMonitor) NULL,
10224    (void *) NULL);
10225  (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
10226  exception=AcquireExceptionInfo();
10227  (void) SetImageInfo(image_info,0,exception);
10228  if (LocaleCompare(image_info->magick,"X") == 0)
10229    {
10230      char
10231        seconds[MaxTextExtent];
10232
10233      /*
10234        User may want to delay the X server screen grab.
10235      */
10236      (void) CopyMagickString(seconds,"0",MaxTextExtent);
10237      (void) XDialogWidget(display,windows,"Grab","Enter any delay in seconds:",
10238        seconds);
10239      if (*seconds == '\0')
10240        return((Image *) NULL);
10241      XDelay(display,(size_t) (1000*StringToLong(seconds)));
10242    }
10243  magick_info=GetMagickInfo(image_info->magick,exception);
10244  if ((magick_info != (const MagickInfo *) NULL) &&
10245      (magick_info->raw != MagickFalse))
10246    {
10247      char
10248        geometry[MaxTextExtent];
10249
10250      /*
10251        Request image size from the user.
10252      */
10253      (void) CopyMagickString(geometry,"512x512",MaxTextExtent);
10254      if (image_info->size != (char *) NULL)
10255        (void) CopyMagickString(geometry,image_info->size,MaxTextExtent);
10256      (void) XDialogWidget(display,windows,"Load","Enter the image geometry:",
10257        geometry);
10258      (void) CloneString(&image_info->size,geometry);
10259    }
10260  /*
10261    Load the image.
10262  */
10263  XSetCursorState(display,windows,MagickTrue);
10264  XCheckRefreshWindows(display,windows);
10265  (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
10266  nexus=ReadImage(image_info,exception);
10267  CatchException(exception);
10268  XSetCursorState(display,windows,MagickFalse);
10269  if (nexus != (Image *) NULL)
10270    XClientMessage(display,windows->image.id,windows->im_protocols,
10271      windows->im_next_image,CurrentTime);
10272  else
10273    {
10274      char
10275        *text,
10276        **textlist;
10277
10278      /*
10279        Unknown image format.
10280      */
10281      text=FileToString(filename,~0,exception);
10282      if (text == (char *) NULL)
10283        return((Image *) NULL);
10284      textlist=StringToList(text);
10285      if (textlist != (char **) NULL)
10286        {
10287          char
10288            title[MaxTextExtent];
10289
10290          register int
10291            i;
10292
10293          (void) FormatLocaleString(title,MaxTextExtent,
10294            "Unknown format: %s",filename);
10295          XTextViewWidget(display,resource_info,windows,MagickTrue,title,
10296            (const char **) textlist);
10297          for (i=0; textlist[i] != (char *) NULL; i++)
10298            textlist[i]=DestroyString(textlist[i]);
10299          textlist=(char **) RelinquishMagickMemory(textlist);
10300        }
10301      text=DestroyString(text);
10302    }
10303  exception=DestroyExceptionInfo(exception);
10304  image_info=DestroyImageInfo(image_info);
10305  return(nexus);
10306}
10307
10308/*
10309%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10310%                                                                             %
10311%                                                                             %
10312%                                                                             %
10313+   X P a n I m a g e                                                         %
10314%                                                                             %
10315%                                                                             %
10316%                                                                             %
10317%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10318%
10319%  XPanImage() pans the image until the mouse button is released.
10320%
10321%  The format of the XPanImage method is:
10322%
10323%      void XPanImage(Display *display,XWindows *windows,XEvent *event)
10324%
10325%  A description of each parameter follows:
10326%
10327%    o display: Specifies a connection to an X server;  returned from
10328%      XOpenDisplay.
10329%
10330%    o windows: Specifies a pointer to a XWindows structure.
10331%
10332%    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
10333%      the entire image is refreshed.
10334%
10335*/
10336static void XPanImage(Display *display,XWindows *windows,XEvent *event)
10337{
10338  char
10339    text[MaxTextExtent];
10340
10341  Cursor
10342    cursor;
10343
10344  MagickRealType
10345    x_factor,
10346    y_factor;
10347
10348  RectangleInfo
10349    pan_info;
10350
10351  size_t
10352    state;
10353
10354  /*
10355    Define cursor.
10356  */
10357  if ((windows->image.ximage->width > (int) windows->image.width) &&
10358      (windows->image.ximage->height > (int) windows->image.height))
10359    cursor=XCreateFontCursor(display,XC_fleur);
10360  else
10361    if (windows->image.ximage->width > (int) windows->image.width)
10362      cursor=XCreateFontCursor(display,XC_sb_h_double_arrow);
10363    else
10364      if (windows->image.ximage->height > (int) windows->image.height)
10365        cursor=XCreateFontCursor(display,XC_sb_v_double_arrow);
10366      else
10367        cursor=XCreateFontCursor(display,XC_arrow);
10368  (void) XCheckDefineCursor(display,windows->pan.id,cursor);
10369  /*
10370    Pan image as pointer moves until the mouse button is released.
10371  */
10372  x_factor=(MagickRealType) windows->image.ximage->width/windows->pan.width;
10373  y_factor=(MagickRealType) windows->image.ximage->height/windows->pan.height;
10374  pan_info.width=windows->pan.width*windows->image.width/
10375    windows->image.ximage->width;
10376  pan_info.height=windows->pan.height*windows->image.height/
10377    windows->image.ximage->height;
10378  pan_info.x=0;
10379  pan_info.y=0;
10380  state=UpdateConfigurationState;
10381  do
10382  {
10383    switch (event->type)
10384    {
10385      case ButtonPress:
10386      {
10387        /*
10388          User choose an initial pan location.
10389        */
10390        pan_info.x=(ssize_t) event->xbutton.x;
10391        pan_info.y=(ssize_t) event->xbutton.y;
10392        state|=UpdateConfigurationState;
10393        break;
10394      }
10395      case ButtonRelease:
10396      {
10397        /*
10398          User has finished panning the image.
10399        */
10400        pan_info.x=(ssize_t) event->xbutton.x;
10401        pan_info.y=(ssize_t) event->xbutton.y;
10402        state|=UpdateConfigurationState | ExitState;
10403        break;
10404      }
10405      case MotionNotify:
10406      {
10407        pan_info.x=(ssize_t) event->xmotion.x;
10408        pan_info.y=(ssize_t) event->xmotion.y;
10409        state|=UpdateConfigurationState;
10410      }
10411      default:
10412        break;
10413    }
10414    if ((state & UpdateConfigurationState) != 0)
10415      {
10416        /*
10417          Check boundary conditions.
10418        */
10419        if (pan_info.x < (ssize_t) (pan_info.width/2))
10420          pan_info.x=0;
10421        else
10422          pan_info.x=(ssize_t) (x_factor*(pan_info.x-(pan_info.width/2)));
10423        if (pan_info.x < 0)
10424          pan_info.x=0;
10425        else
10426          if ((int) (pan_info.x+windows->image.width) >
10427              windows->image.ximage->width)
10428            pan_info.x=(ssize_t)
10429              (windows->image.ximage->width-windows->image.width);
10430        if (pan_info.y < (ssize_t) (pan_info.height/2))
10431          pan_info.y=0;
10432        else
10433          pan_info.y=(ssize_t) (y_factor*(pan_info.y-(pan_info.height/2)));
10434        if (pan_info.y < 0)
10435          pan_info.y=0;
10436        else
10437          if ((int) (pan_info.y+windows->image.height) >
10438              windows->image.ximage->height)
10439            pan_info.y=(ssize_t)
10440              (windows->image.ximage->height-windows->image.height);
10441        if ((windows->image.x != (int) pan_info.x) ||
10442            (windows->image.y != (int) pan_info.y))
10443          {
10444            /*
10445              Display image pan offset.
10446            */
10447            windows->image.x=(int) pan_info.x;
10448            windows->image.y=(int) pan_info.y;
10449            (void) FormatLocaleString(text,MaxTextExtent," %ux%u%+d%+d ",
10450              windows->image.width,windows->image.height,windows->image.x,
10451              windows->image.y);
10452            XInfoWidget(display,windows,text);
10453            /*
10454              Refresh Image window.
10455            */
10456            XDrawPanRectangle(display,windows);
10457            XRefreshWindow(display,&windows->image,(XEvent *) NULL);
10458          }
10459        state&=(~UpdateConfigurationState);
10460      }
10461    /*
10462      Wait for next event.
10463    */
10464    if ((state & ExitState) == 0)
10465      XScreenEvent(display,windows,event);
10466  } while ((state & ExitState) == 0);
10467  /*
10468    Restore cursor.
10469  */
10470  (void) XCheckDefineCursor(display,windows->pan.id,windows->pan.cursor);
10471  (void) XFreeCursor(display,cursor);
10472  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
10473}
10474
10475/*
10476%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10477%                                                                             %
10478%                                                                             %
10479%                                                                             %
10480+   X P a s t e I m a g e                                                     %
10481%                                                                             %
10482%                                                                             %
10483%                                                                             %
10484%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10485%
10486%  XPasteImage() pastes an image previously saved with XCropImage in the X
10487%  window image at a location the user chooses with the pointer.
10488%
10489%  The format of the XPasteImage method is:
10490%
10491%      MagickBooleanType XPasteImage(Display *display,
10492%        XResourceInfo *resource_info,XWindows *windows,Image *image)
10493%
10494%  A description of each parameter follows:
10495%
10496%    o display: Specifies a connection to an X server;  returned from
10497%      XOpenDisplay.
10498%
10499%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10500%
10501%    o windows: Specifies a pointer to a XWindows structure.
10502%
10503%    o image: the image; returned from ReadImage.
10504%
10505*/
10506static MagickBooleanType XPasteImage(Display *display,
10507  XResourceInfo *resource_info,XWindows *windows,Image *image)
10508{
10509  static const char
10510    *PasteMenu[] =
10511    {
10512      "Operator",
10513      "Help",
10514      "Dismiss",
10515      (char *) NULL
10516    };
10517
10518  static const ModeType
10519    PasteCommands[] =
10520    {
10521      PasteOperatorsCommand,
10522      PasteHelpCommand,
10523      PasteDismissCommand
10524    };
10525
10526  static CompositeOperator
10527    compose = CopyCompositeOp;
10528
10529  char
10530    text[MaxTextExtent];
10531
10532  Cursor
10533    cursor;
10534
10535  Image
10536    *paste_image;
10537
10538  int
10539    entry,
10540    id,
10541    x,
10542    y;
10543
10544  MagickRealType
10545    scale_factor;
10546
10547  RectangleInfo
10548    highlight_info,
10549    paste_info;
10550
10551  unsigned int
10552    height,
10553    width;
10554
10555  size_t
10556    state;
10557
10558  XEvent
10559    event;
10560
10561  /*
10562    Copy image.
10563  */
10564  if (resource_info->copy_image == (Image *) NULL)
10565    return(MagickFalse);
10566  paste_image=CloneImage(resource_info->copy_image,0,0,MagickTrue,
10567    &image->exception);
10568  /*
10569    Map Command widget.
10570  */
10571  (void) CloneString(&windows->command.name,"Paste");
10572  windows->command.data=1;
10573  (void) XCommandWidget(display,windows,PasteMenu,(XEvent *) NULL);
10574  (void) XMapRaised(display,windows->command.id);
10575  XClientMessage(display,windows->image.id,windows->im_protocols,
10576    windows->im_update_widget,CurrentTime);
10577  /*
10578    Track pointer until button 1 is pressed.
10579  */
10580  XSetCursorState(display,windows,MagickFalse);
10581  XQueryPosition(display,windows->image.id,&x,&y);
10582  (void) XSelectInput(display,windows->image.id,
10583    windows->image.attributes.event_mask | PointerMotionMask);
10584  paste_info.x=(ssize_t) windows->image.x+x;
10585  paste_info.y=(ssize_t) windows->image.y+y;
10586  paste_info.width=0;
10587  paste_info.height=0;
10588  cursor=XCreateFontCursor(display,XC_ul_angle);
10589  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
10590  state=DefaultState;
10591  do
10592  {
10593    if (windows->info.mapped != MagickFalse)
10594      {
10595        /*
10596          Display pointer position.
10597        */
10598        (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
10599          (long) paste_info.x,(long) paste_info.y);
10600        XInfoWidget(display,windows,text);
10601      }
10602    highlight_info=paste_info;
10603    highlight_info.x=paste_info.x-windows->image.x;
10604    highlight_info.y=paste_info.y-windows->image.y;
10605    XHighlightRectangle(display,windows->image.id,
10606      windows->image.highlight_context,&highlight_info);
10607    /*
10608      Wait for next event.
10609    */
10610    XScreenEvent(display,windows,&event);
10611    XHighlightRectangle(display,windows->image.id,
10612      windows->image.highlight_context,&highlight_info);
10613    if (event.xany.window == windows->command.id)
10614      {
10615        /*
10616          Select a command from the Command widget.
10617        */
10618        id=XCommandWidget(display,windows,PasteMenu,&event);
10619        if (id < 0)
10620          continue;
10621        switch (PasteCommands[id])
10622        {
10623          case PasteOperatorsCommand:
10624          {
10625            char
10626              command[MaxTextExtent],
10627              **operators;
10628
10629            /*
10630              Select a command from the pop-up menu.
10631            */
10632            operators=GetCommandOptions(MagickComposeOptions);
10633            if (operators == (char **) NULL)
10634              break;
10635            entry=XMenuWidget(display,windows,PasteMenu[id],
10636              (const char **) operators,command);
10637            if (entry >= 0)
10638              compose=(CompositeOperator) ParseCommandOption(
10639                MagickComposeOptions,MagickFalse,operators[entry]);
10640            operators=DestroyStringList(operators);
10641            break;
10642          }
10643          case PasteHelpCommand:
10644          {
10645            XTextViewWidget(display,resource_info,windows,MagickFalse,
10646              "Help Viewer - Image Composite",ImagePasteHelp);
10647            break;
10648          }
10649          case PasteDismissCommand:
10650          {
10651            /*
10652              Prematurely exit.
10653            */
10654            state|=EscapeState;
10655            state|=ExitState;
10656            break;
10657          }
10658          default:
10659            break;
10660        }
10661        continue;
10662      }
10663    switch (event.type)
10664    {
10665      case ButtonPress:
10666      {
10667        if (image->debug != MagickFalse)
10668          (void) LogMagickEvent(X11Event,GetMagickModule(),
10669            "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
10670            event.xbutton.button,event.xbutton.x,event.xbutton.y);
10671        if (event.xbutton.button != Button1)
10672          break;
10673        if (event.xbutton.window != windows->image.id)
10674          break;
10675        /*
10676          Paste rectangle is relative to image configuration.
10677        */
10678        width=(unsigned int) image->columns;
10679        height=(unsigned int) image->rows;
10680        x=0;
10681        y=0;
10682        if (windows->image.crop_geometry != (char *) NULL)
10683          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
10684            &width,&height);
10685        scale_factor=(MagickRealType) windows->image.ximage->width/width;
10686        paste_info.width=(unsigned int) (scale_factor*paste_image->columns+0.5);
10687        scale_factor=(MagickRealType) windows->image.ximage->height/height;
10688        paste_info.height=(unsigned int) (scale_factor*paste_image->rows+0.5);
10689        (void) XCheckDefineCursor(display,windows->image.id,cursor);
10690        paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10691        paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10692        break;
10693      }
10694      case ButtonRelease:
10695      {
10696        if (image->debug != MagickFalse)
10697          (void) LogMagickEvent(X11Event,GetMagickModule(),
10698            "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
10699            event.xbutton.button,event.xbutton.x,event.xbutton.y);
10700        if (event.xbutton.button != Button1)
10701          break;
10702        if (event.xbutton.window != windows->image.id)
10703          break;
10704        if ((paste_info.width != 0) && (paste_info.height != 0))
10705          {
10706            /*
10707              User has selected the location of the paste image.
10708            */
10709            paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10710            paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10711            state|=ExitState;
10712          }
10713        break;
10714      }
10715      case Expose:
10716        break;
10717      case KeyPress:
10718      {
10719        char
10720          command[MaxTextExtent];
10721
10722        KeySym
10723          key_symbol;
10724
10725        int
10726          length;
10727
10728        if (event.xkey.window != windows->image.id)
10729          break;
10730        /*
10731          Respond to a user key press.
10732        */
10733        length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
10734          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
10735        *(command+length)='\0';
10736        if (image->debug != MagickFalse)
10737          (void) LogMagickEvent(X11Event,GetMagickModule(),
10738            "Key press: 0x%lx (%s)",(long) key_symbol,command);
10739        switch ((int) key_symbol)
10740        {
10741          case XK_Escape:
10742          case XK_F20:
10743          {
10744            /*
10745              Prematurely exit.
10746            */
10747            paste_image=DestroyImage(paste_image);
10748            state|=EscapeState;
10749            state|=ExitState;
10750            break;
10751          }
10752          case XK_F1:
10753          case XK_Help:
10754          {
10755            (void) XSetFunction(display,windows->image.highlight_context,
10756              GXcopy);
10757            XTextViewWidget(display,resource_info,windows,MagickFalse,
10758              "Help Viewer - Image Composite",ImagePasteHelp);
10759            (void) XSetFunction(display,windows->image.highlight_context,
10760              GXinvert);
10761            break;
10762          }
10763          default:
10764          {
10765            (void) XBell(display,0);
10766            break;
10767          }
10768        }
10769        break;
10770      }
10771      case MotionNotify:
10772      {
10773        /*
10774          Map and unmap Info widget as text cursor crosses its boundaries.
10775        */
10776        x=event.xmotion.x;
10777        y=event.xmotion.y;
10778        if (windows->info.mapped != MagickFalse)
10779          {
10780            if ((x < (int) (windows->info.x+windows->info.width)) &&
10781                (y < (int) (windows->info.y+windows->info.height)))
10782              (void) XWithdrawWindow(display,windows->info.id,
10783                windows->info.screen);
10784          }
10785        else
10786          if ((x > (int) (windows->info.x+windows->info.width)) ||
10787              (y > (int) (windows->info.y+windows->info.height)))
10788            (void) XMapWindow(display,windows->info.id);
10789        paste_info.x=(ssize_t) windows->image.x+x;
10790        paste_info.y=(ssize_t) windows->image.y+y;
10791        break;
10792      }
10793      default:
10794      {
10795        if (image->debug != MagickFalse)
10796          (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
10797            event.type);
10798        break;
10799      }
10800    }
10801  } while ((state & ExitState) == 0);
10802  (void) XSelectInput(display,windows->image.id,
10803    windows->image.attributes.event_mask);
10804  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
10805  XSetCursorState(display,windows,MagickFalse);
10806  (void) XFreeCursor(display,cursor);
10807  if ((state & EscapeState) != 0)
10808    return(MagickTrue);
10809  /*
10810    Image pasting is relative to image configuration.
10811  */
10812  XSetCursorState(display,windows,MagickTrue);
10813  XCheckRefreshWindows(display,windows);
10814  width=(unsigned int) image->columns;
10815  height=(unsigned int) image->rows;
10816  x=0;
10817  y=0;
10818  if (windows->image.crop_geometry != (char *) NULL)
10819    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
10820  scale_factor=(MagickRealType) width/windows->image.ximage->width;
10821  paste_info.x+=x;
10822  paste_info.x=(ssize_t) (scale_factor*paste_info.x+0.5);
10823  paste_info.width=(unsigned int) (scale_factor*paste_info.width+0.5);
10824  scale_factor=(MagickRealType) height/windows->image.ximage->height;
10825  paste_info.y+=y;
10826  paste_info.y=(ssize_t) (scale_factor*paste_info.y*scale_factor+0.5);
10827  paste_info.height=(unsigned int) (scale_factor*paste_info.height+0.5);
10828  /*
10829    Paste image with X Image window.
10830  */
10831  (void) CompositeImage(image,compose,paste_image,paste_info.x,paste_info.y);
10832  paste_image=DestroyImage(paste_image);
10833  XSetCursorState(display,windows,MagickFalse);
10834  /*
10835    Update image colormap.
10836  */
10837  XConfigureImageColormap(display,resource_info,windows,image);
10838  (void) XConfigureImage(display,resource_info,windows,image);
10839  return(MagickTrue);
10840}
10841
10842/*
10843%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10844%                                                                             %
10845%                                                                             %
10846%                                                                             %
10847+   X P r i n t I m a g e                                                     %
10848%                                                                             %
10849%                                                                             %
10850%                                                                             %
10851%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10852%
10853%  XPrintImage() prints an image to a Postscript printer.
10854%
10855%  The format of the XPrintImage method is:
10856%
10857%      MagickBooleanType XPrintImage(Display *display,
10858%        XResourceInfo *resource_info,XWindows *windows,Image *image)
10859%
10860%  A description of each parameter follows:
10861%
10862%    o display: Specifies a connection to an X server; returned from
10863%      XOpenDisplay.
10864%
10865%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10866%
10867%    o windows: Specifies a pointer to a XWindows structure.
10868%
10869%    o image: the image.
10870%
10871*/
10872static MagickBooleanType XPrintImage(Display *display,
10873  XResourceInfo *resource_info,XWindows *windows,Image *image)
10874{
10875  char
10876    filename[MaxTextExtent],
10877    geometry[MaxTextExtent];
10878
10879  Image
10880    *print_image;
10881
10882  ImageInfo
10883    *image_info;
10884
10885  MagickStatusType
10886    status;
10887
10888  /*
10889    Request Postscript page geometry from user.
10890  */
10891  image_info=CloneImageInfo(resource_info->image_info);
10892  (void) FormatLocaleString(geometry,MaxTextExtent,"Letter");
10893  if (image_info->page != (char *) NULL)
10894    (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
10895  XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
10896    "Select Postscript Page Geometry:",geometry);
10897  if (*geometry == '\0')
10898    return(MagickTrue);
10899  image_info->page=GetPageGeometry(geometry);
10900  /*
10901    Apply image transforms.
10902  */
10903  XSetCursorState(display,windows,MagickTrue);
10904  XCheckRefreshWindows(display,windows);
10905  print_image=CloneImage(image,0,0,MagickTrue,&image->exception);
10906  if (print_image == (Image *) NULL)
10907    return(MagickFalse);
10908  (void) FormatLocaleString(geometry,MaxTextExtent,"%dx%d!",
10909    windows->image.ximage->width,windows->image.ximage->height);
10910  (void) TransformImage(&print_image,windows->image.crop_geometry,geometry);
10911  /*
10912    Print image.
10913  */
10914  (void) AcquireUniqueFilename(filename);
10915  (void) FormatLocaleString(print_image->filename,MaxTextExtent,"print:%s",
10916    filename);
10917  status=WriteImage(image_info,print_image);
10918  (void) RelinquishUniqueFileResource(filename);
10919  print_image=DestroyImage(print_image);
10920  image_info=DestroyImageInfo(image_info);
10921  XSetCursorState(display,windows,MagickFalse);
10922  return(status != 0 ? MagickTrue : MagickFalse);
10923}
10924
10925/*
10926%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10927%                                                                             %
10928%                                                                             %
10929%                                                                             %
10930+   X R O I I m a g e                                                         %
10931%                                                                             %
10932%                                                                             %
10933%                                                                             %
10934%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10935%
10936%  XROIImage() applies an image processing technique to a region of interest.
10937%
10938%  The format of the XROIImage method is:
10939%
10940%      MagickBooleanType XROIImage(Display *display,
10941%        XResourceInfo *resource_info,XWindows *windows,Image **image)
10942%
10943%  A description of each parameter follows:
10944%
10945%    o display: Specifies a connection to an X server; returned from
10946%      XOpenDisplay.
10947%
10948%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10949%
10950%    o windows: Specifies a pointer to a XWindows structure.
10951%
10952%    o image: the image; returned from ReadImage.
10953%
10954*/
10955static MagickBooleanType XROIImage(Display *display,
10956  XResourceInfo *resource_info,XWindows *windows,Image **image)
10957{
10958#define ApplyMenus  7
10959
10960  static const char
10961    *ROIMenu[] =
10962    {
10963      "Help",
10964      "Dismiss",
10965      (char *) NULL
10966    },
10967    *ApplyMenu[] =
10968    {
10969      "File",
10970      "Edit",
10971      "Transform",
10972      "Enhance",
10973      "Effects",
10974      "F/X",
10975      "Miscellany",
10976      "Help",
10977      "Dismiss",
10978      (char *) NULL
10979    },
10980    *FileMenu[] =
10981    {
10982      "Save...",
10983      "Print...",
10984      (char *) NULL
10985    },
10986    *EditMenu[] =
10987    {
10988      "Undo",
10989      "Redo",
10990      (char *) NULL
10991    },
10992    *TransformMenu[] =
10993    {
10994      "Flop",
10995      "Flip",
10996      "Rotate Right",
10997      "Rotate Left",
10998      (char *) NULL
10999    },
11000    *EnhanceMenu[] =
11001    {
11002      "Hue...",
11003      "Saturation...",
11004      "Brightness...",
11005      "Gamma...",
11006      "Spiff",
11007      "Dull",
11008      "Contrast Stretch...",
11009      "Sigmoidal Contrast...",
11010      "Normalize",
11011      "Equalize",
11012      "Negate",
11013      "Grayscale",
11014      "Map...",
11015      "Quantize...",
11016      (char *) NULL
11017    },
11018    *EffectsMenu[] =
11019    {
11020      "Despeckle",
11021      "Emboss",
11022      "Reduce Noise",
11023      "Add Noise",
11024      "Sharpen...",
11025      "Blur...",
11026      "Threshold...",
11027      "Edge Detect...",
11028      "Spread...",
11029      "Shade...",
11030      "Raise...",
11031      "Segment...",
11032      (char *) NULL
11033    },
11034    *FXMenu[] =
11035    {
11036      "Solarize...",
11037      "Sepia Tone...",
11038      "Swirl...",
11039      "Implode...",
11040      "Vignette...",
11041      "Wave...",
11042      "Oil Paint...",
11043      "Charcoal Draw...",
11044      (char *) NULL
11045    },
11046    *MiscellanyMenu[] =
11047    {
11048      "Image Info",
11049      "Zoom Image",
11050      "Show Preview...",
11051      "Show Histogram",
11052      "Show Matte",
11053      (char *) NULL
11054    };
11055
11056  static const char
11057    **Menus[ApplyMenus] =
11058    {
11059      FileMenu,
11060      EditMenu,
11061      TransformMenu,
11062      EnhanceMenu,
11063      EffectsMenu,
11064      FXMenu,
11065      MiscellanyMenu
11066    };
11067
11068  static const CommandType
11069    ApplyCommands[] =
11070    {
11071      NullCommand,
11072      NullCommand,
11073      NullCommand,
11074      NullCommand,
11075      NullCommand,
11076      NullCommand,
11077      NullCommand,
11078      HelpCommand,
11079      QuitCommand
11080    },
11081    FileCommands[] =
11082    {
11083      SaveCommand,
11084      PrintCommand
11085    },
11086    EditCommands[] =
11087    {
11088      UndoCommand,
11089      RedoCommand
11090    },
11091    TransformCommands[] =
11092    {
11093      FlopCommand,
11094      FlipCommand,
11095      RotateRightCommand,
11096      RotateLeftCommand
11097    },
11098    EnhanceCommands[] =
11099    {
11100      HueCommand,
11101      SaturationCommand,
11102      BrightnessCommand,
11103      GammaCommand,
11104      SpiffCommand,
11105      DullCommand,
11106      ContrastStretchCommand,
11107      SigmoidalContrastCommand,
11108      NormalizeCommand,
11109      EqualizeCommand,
11110      NegateCommand,
11111      GrayscaleCommand,
11112      MapCommand,
11113      QuantizeCommand
11114    },
11115    EffectsCommands[] =
11116    {
11117      DespeckleCommand,
11118      EmbossCommand,
11119      ReduceNoiseCommand,
11120      AddNoiseCommand,
11121      SharpenCommand,
11122      BlurCommand,
11123      EdgeDetectCommand,
11124      SpreadCommand,
11125      ShadeCommand,
11126      RaiseCommand,
11127      SegmentCommand
11128    },
11129    FXCommands[] =
11130    {
11131      SolarizeCommand,
11132      SepiaToneCommand,
11133      SwirlCommand,
11134      ImplodeCommand,
11135      VignetteCommand,
11136      WaveCommand,
11137      OilPaintCommand,
11138      CharcoalDrawCommand
11139    },
11140    MiscellanyCommands[] =
11141    {
11142      InfoCommand,
11143      ZoomCommand,
11144      ShowPreviewCommand,
11145      ShowHistogramCommand,
11146      ShowMatteCommand
11147    },
11148    ROICommands[] =
11149    {
11150      ROIHelpCommand,
11151      ROIDismissCommand
11152    };
11153
11154  static const CommandType
11155    *Commands[ApplyMenus] =
11156    {
11157      FileCommands,
11158      EditCommands,
11159      TransformCommands,
11160      EnhanceCommands,
11161      EffectsCommands,
11162      FXCommands,
11163      MiscellanyCommands
11164    };
11165
11166  char
11167    command[MaxTextExtent],
11168    text[MaxTextExtent];
11169
11170  CommandType
11171    command_type;
11172
11173  Cursor
11174    cursor;
11175
11176  Image
11177    *roi_image;
11178
11179  int
11180    entry,
11181    id,
11182    x,
11183    y;
11184
11185  MagickRealType
11186    scale_factor;
11187
11188  MagickProgressMonitor
11189    progress_monitor;
11190
11191  RectangleInfo
11192    crop_info,
11193    highlight_info,
11194    roi_info;
11195
11196  unsigned int
11197    height,
11198    width;
11199
11200  size_t
11201    state;
11202
11203  XEvent
11204    event;
11205
11206  /*
11207    Map Command widget.
11208  */
11209  (void) CloneString(&windows->command.name,"ROI");
11210  windows->command.data=0;
11211  (void) XCommandWidget(display,windows,ROIMenu,(XEvent *) NULL);
11212  (void) XMapRaised(display,windows->command.id);
11213  XClientMessage(display,windows->image.id,windows->im_protocols,
11214    windows->im_update_widget,CurrentTime);
11215  /*
11216    Track pointer until button 1 is pressed.
11217  */
11218  XQueryPosition(display,windows->image.id,&x,&y);
11219  (void) XSelectInput(display,windows->image.id,
11220    windows->image.attributes.event_mask | PointerMotionMask);
11221  roi_info.x=(ssize_t) windows->image.x+x;
11222  roi_info.y=(ssize_t) windows->image.y+y;
11223  roi_info.width=0;
11224  roi_info.height=0;
11225  cursor=XCreateFontCursor(display,XC_fleur);
11226  state=DefaultState;
11227  do
11228  {
11229    if (windows->info.mapped != MagickFalse)
11230      {
11231        /*
11232          Display pointer position.
11233        */
11234        (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
11235          (long) roi_info.x,(long) roi_info.y);
11236        XInfoWidget(display,windows,text);
11237      }
11238    /*
11239      Wait for next event.
11240    */
11241    XScreenEvent(display,windows,&event);
11242    if (event.xany.window == windows->command.id)
11243      {
11244        /*
11245          Select a command from the Command widget.
11246        */
11247        id=XCommandWidget(display,windows,ROIMenu,&event);
11248        if (id < 0)
11249          continue;
11250        switch (ROICommands[id])
11251        {
11252          case ROIHelpCommand:
11253          {
11254            XTextViewWidget(display,resource_info,windows,MagickFalse,
11255              "Help Viewer - Region of Interest",ImageROIHelp);
11256            break;
11257          }
11258          case ROIDismissCommand:
11259          {
11260            /*
11261              Prematurely exit.
11262            */
11263            state|=EscapeState;
11264            state|=ExitState;
11265            break;
11266          }
11267          default:
11268            break;
11269        }
11270        continue;
11271      }
11272    switch (event.type)
11273    {
11274      case ButtonPress:
11275      {
11276        if (event.xbutton.button != Button1)
11277          break;
11278        if (event.xbutton.window != windows->image.id)
11279          break;
11280        /*
11281          Note first corner of region of interest rectangle-- exit loop.
11282        */
11283        (void) XCheckDefineCursor(display,windows->image.id,cursor);
11284        roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11285        roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11286        state|=ExitState;
11287        break;
11288      }
11289      case ButtonRelease:
11290        break;
11291      case Expose:
11292        break;
11293      case KeyPress:
11294      {
11295        KeySym
11296          key_symbol;
11297
11298        if (event.xkey.window != windows->image.id)
11299          break;
11300        /*
11301          Respond to a user key press.
11302        */
11303        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11304          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11305        switch ((int) key_symbol)
11306        {
11307          case XK_Escape:
11308          case XK_F20:
11309          {
11310            /*
11311              Prematurely exit.
11312            */
11313            state|=EscapeState;
11314            state|=ExitState;
11315            break;
11316          }
11317          case XK_F1:
11318          case XK_Help:
11319          {
11320            XTextViewWidget(display,resource_info,windows,MagickFalse,
11321              "Help Viewer - Region of Interest",ImageROIHelp);
11322            break;
11323          }
11324          default:
11325          {
11326            (void) XBell(display,0);
11327            break;
11328          }
11329        }
11330        break;
11331      }
11332      case MotionNotify:
11333      {
11334        /*
11335          Map and unmap Info widget as text cursor crosses its boundaries.
11336        */
11337        x=event.xmotion.x;
11338        y=event.xmotion.y;
11339        if (windows->info.mapped != MagickFalse)
11340          {
11341            if ((x < (int) (windows->info.x+windows->info.width)) &&
11342                (y < (int) (windows->info.y+windows->info.height)))
11343              (void) XWithdrawWindow(display,windows->info.id,
11344                windows->info.screen);
11345          }
11346        else
11347          if ((x > (int) (windows->info.x+windows->info.width)) ||
11348              (y > (int) (windows->info.y+windows->info.height)))
11349            (void) XMapWindow(display,windows->info.id);
11350        roi_info.x=(ssize_t) windows->image.x+x;
11351        roi_info.y=(ssize_t) windows->image.y+y;
11352        break;
11353      }
11354      default:
11355        break;
11356    }
11357  } while ((state & ExitState) == 0);
11358  (void) XSelectInput(display,windows->image.id,
11359    windows->image.attributes.event_mask);
11360  if ((state & EscapeState) != 0)
11361    {
11362      /*
11363        User want to exit without region of interest.
11364      */
11365      (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11366      (void) XFreeCursor(display,cursor);
11367      return(MagickTrue);
11368    }
11369  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
11370  do
11371  {
11372    /*
11373      Size rectangle as pointer moves until the mouse button is released.
11374    */
11375    x=(int) roi_info.x;
11376    y=(int) roi_info.y;
11377    roi_info.width=0;
11378    roi_info.height=0;
11379    state=DefaultState;
11380    do
11381    {
11382      highlight_info=roi_info;
11383      highlight_info.x=roi_info.x-windows->image.x;
11384      highlight_info.y=roi_info.y-windows->image.y;
11385      if ((highlight_info.width > 3) && (highlight_info.height > 3))
11386        {
11387          /*
11388            Display info and draw region of interest rectangle.
11389          */
11390          if (windows->info.mapped == MagickFalse)
11391            (void) XMapWindow(display,windows->info.id);
11392          (void) FormatLocaleString(text,MaxTextExtent,
11393            " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11394            roi_info.height,(double) roi_info.x,(double) roi_info.y);
11395          XInfoWidget(display,windows,text);
11396          XHighlightRectangle(display,windows->image.id,
11397            windows->image.highlight_context,&highlight_info);
11398        }
11399      else
11400        if (windows->info.mapped != MagickFalse)
11401          (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11402      /*
11403        Wait for next event.
11404      */
11405      XScreenEvent(display,windows,&event);
11406      if ((highlight_info.width > 3) && (highlight_info.height > 3))
11407        XHighlightRectangle(display,windows->image.id,
11408          windows->image.highlight_context,&highlight_info);
11409      switch (event.type)
11410      {
11411        case ButtonPress:
11412        {
11413          roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11414          roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11415          break;
11416        }
11417        case ButtonRelease:
11418        {
11419          /*
11420            User has committed to region of interest rectangle.
11421          */
11422          roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11423          roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11424          XSetCursorState(display,windows,MagickFalse);
11425          state|=ExitState;
11426          if (LocaleCompare(windows->command.name,"Apply") == 0)
11427            break;
11428          (void) CloneString(&windows->command.name,"Apply");
11429          windows->command.data=ApplyMenus;
11430          (void) XCommandWidget(display,windows,ApplyMenu,(XEvent *) NULL);
11431          break;
11432        }
11433        case Expose:
11434          break;
11435        case MotionNotify:
11436        {
11437          roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11438          roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11439        }
11440        default:
11441          break;
11442      }
11443      if ((((int) roi_info.x != x) && ((int) roi_info.y != y)) ||
11444          ((state & ExitState) != 0))
11445        {
11446          /*
11447            Check boundary conditions.
11448          */
11449          if (roi_info.x < 0)
11450            roi_info.x=0;
11451          else
11452            if (roi_info.x > (ssize_t) windows->image.ximage->width)
11453              roi_info.x=(ssize_t) windows->image.ximage->width;
11454          if ((int) roi_info.x < x)
11455            roi_info.width=(unsigned int) (x-roi_info.x);
11456          else
11457            {
11458              roi_info.width=(unsigned int) (roi_info.x-x);
11459              roi_info.x=(ssize_t) x;
11460            }
11461          if (roi_info.y < 0)
11462            roi_info.y=0;
11463          else
11464            if (roi_info.y > (ssize_t) windows->image.ximage->height)
11465              roi_info.y=(ssize_t) windows->image.ximage->height;
11466          if ((int) roi_info.y < y)
11467            roi_info.height=(unsigned int) (y-roi_info.y);
11468          else
11469            {
11470              roi_info.height=(unsigned int) (roi_info.y-y);
11471              roi_info.y=(ssize_t) y;
11472            }
11473        }
11474    } while ((state & ExitState) == 0);
11475    /*
11476      Wait for user to grab a corner of the rectangle or press return.
11477    */
11478    state=DefaultState;
11479    command_type=NullCommand;
11480    (void) XMapWindow(display,windows->info.id);
11481    do
11482    {
11483      if (windows->info.mapped != MagickFalse)
11484        {
11485          /*
11486            Display pointer position.
11487          */
11488          (void) FormatLocaleString(text,MaxTextExtent,
11489            " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11490            roi_info.height,(double) roi_info.x,(double) roi_info.y);
11491          XInfoWidget(display,windows,text);
11492        }
11493      highlight_info=roi_info;
11494      highlight_info.x=roi_info.x-windows->image.x;
11495      highlight_info.y=roi_info.y-windows->image.y;
11496      if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
11497        {
11498          state|=EscapeState;
11499          state|=ExitState;
11500          break;
11501        }
11502      if ((state & UpdateRegionState) != 0)
11503        {
11504          (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11505          switch (command_type)
11506          {
11507            case UndoCommand:
11508            case RedoCommand:
11509            {
11510              (void) XMagickCommand(display,resource_info,windows,command_type,
11511                image);
11512              break;
11513            }
11514            default:
11515            {
11516              /*
11517                Region of interest is relative to image configuration.
11518              */
11519              progress_monitor=SetImageProgressMonitor(*image,
11520                (MagickProgressMonitor) NULL,(*image)->client_data);
11521              crop_info=roi_info;
11522              width=(unsigned int) (*image)->columns;
11523              height=(unsigned int) (*image)->rows;
11524              x=0;
11525              y=0;
11526              if (windows->image.crop_geometry != (char *) NULL)
11527                (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
11528                  &width,&height);
11529              scale_factor=(MagickRealType) width/windows->image.ximage->width;
11530              crop_info.x+=x;
11531              crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
11532              crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
11533              scale_factor=(MagickRealType)
11534                height/windows->image.ximage->height;
11535              crop_info.y+=y;
11536              crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
11537              crop_info.height=(unsigned int)
11538                (scale_factor*crop_info.height+0.5);
11539              roi_image=CropImage(*image,&crop_info,&(*image)->exception);
11540              (void) SetImageProgressMonitor(*image,progress_monitor,
11541                (*image)->client_data);
11542              if (roi_image == (Image *) NULL)
11543                continue;
11544              /*
11545                Apply image processing technique to the region of interest.
11546              */
11547              windows->image.orphan=MagickTrue;
11548              (void) XMagickCommand(display,resource_info,windows,command_type,
11549                &roi_image);
11550              progress_monitor=SetImageProgressMonitor(*image,
11551                (MagickProgressMonitor) NULL,(*image)->client_data);
11552              (void) XMagickCommand(display,resource_info,windows,
11553                SaveToUndoBufferCommand,image);
11554              windows->image.orphan=MagickFalse;
11555              (void) CompositeImage(*image,CopyCompositeOp,roi_image,
11556                crop_info.x,crop_info.y);
11557              roi_image=DestroyImage(roi_image);
11558              (void) SetImageProgressMonitor(*image,progress_monitor,
11559                (*image)->client_data);
11560              break;
11561            }
11562          }
11563          if (command_type != InfoCommand)
11564            {
11565              XConfigureImageColormap(display,resource_info,windows,*image);
11566              (void) XConfigureImage(display,resource_info,windows,*image);
11567            }
11568          XCheckRefreshWindows(display,windows);
11569          XInfoWidget(display,windows,text);
11570          (void) XSetFunction(display,windows->image.highlight_context,
11571            GXinvert);
11572          state&=(~UpdateRegionState);
11573        }
11574      XHighlightRectangle(display,windows->image.id,
11575        windows->image.highlight_context,&highlight_info);
11576      XScreenEvent(display,windows,&event);
11577      if (event.xany.window == windows->command.id)
11578        {
11579          /*
11580            Select a command from the Command widget.
11581          */
11582          (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11583          command_type=NullCommand;
11584          id=XCommandWidget(display,windows,ApplyMenu,&event);
11585          if (id >= 0)
11586            {
11587              (void) CopyMagickString(command,ApplyMenu[id],MaxTextExtent);
11588              command_type=ApplyCommands[id];
11589              if (id < ApplyMenus)
11590                {
11591                  /*
11592                    Select a command from a pop-up menu.
11593                  */
11594                  entry=XMenuWidget(display,windows,ApplyMenu[id],
11595                    (const char **) Menus[id],command);
11596                  if (entry >= 0)
11597                    {
11598                      (void) CopyMagickString(command,Menus[id][entry],
11599                        MaxTextExtent);
11600                      command_type=Commands[id][entry];
11601                    }
11602                }
11603            }
11604          (void) XSetFunction(display,windows->image.highlight_context,
11605            GXinvert);
11606          XHighlightRectangle(display,windows->image.id,
11607            windows->image.highlight_context,&highlight_info);
11608          if (command_type == HelpCommand)
11609            {
11610              (void) XSetFunction(display,windows->image.highlight_context,
11611                GXcopy);
11612              XTextViewWidget(display,resource_info,windows,MagickFalse,
11613                "Help Viewer - Region of Interest",ImageROIHelp);
11614              (void) XSetFunction(display,windows->image.highlight_context,
11615                GXinvert);
11616              continue;
11617            }
11618          if (command_type == QuitCommand)
11619            {
11620              /*
11621                exit.
11622              */
11623              state|=EscapeState;
11624              state|=ExitState;
11625              continue;
11626            }
11627          if (command_type != NullCommand)
11628            state|=UpdateRegionState;
11629          continue;
11630        }
11631      XHighlightRectangle(display,windows->image.id,
11632        windows->image.highlight_context,&highlight_info);
11633      switch (event.type)
11634      {
11635        case ButtonPress:
11636        {
11637          x=windows->image.x;
11638          y=windows->image.y;
11639          if (event.xbutton.button != Button1)
11640            break;
11641          if (event.xbutton.window != windows->image.id)
11642            break;
11643          x=windows->image.x+event.xbutton.x;
11644          y=windows->image.y+event.xbutton.y;
11645          if ((x < (int) (roi_info.x+RoiDelta)) &&
11646              (x > (int) (roi_info.x-RoiDelta)) &&
11647              (y < (int) (roi_info.y+RoiDelta)) &&
11648              (y > (int) (roi_info.y-RoiDelta)))
11649            {
11650              roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11651              roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11652              state|=UpdateConfigurationState;
11653              break;
11654            }
11655          if ((x < (int) (roi_info.x+RoiDelta)) &&
11656              (x > (int) (roi_info.x-RoiDelta)) &&
11657              (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11658              (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11659            {
11660              roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11661              state|=UpdateConfigurationState;
11662              break;
11663            }
11664          if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11665              (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11666              (y < (int) (roi_info.y+RoiDelta)) &&
11667              (y > (int) (roi_info.y-RoiDelta)))
11668            {
11669              roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11670              state|=UpdateConfigurationState;
11671              break;
11672            }
11673          if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11674              (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11675              (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11676              (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11677            {
11678              state|=UpdateConfigurationState;
11679              break;
11680            }
11681        }
11682        case ButtonRelease:
11683        {
11684          if (event.xbutton.window == windows->pan.id)
11685            if ((highlight_info.x != crop_info.x-windows->image.x) ||
11686                (highlight_info.y != crop_info.y-windows->image.y))
11687              XHighlightRectangle(display,windows->image.id,
11688                windows->image.highlight_context,&highlight_info);
11689          (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11690            event.xbutton.time);
11691          break;
11692        }
11693        case Expose:
11694        {
11695          if (event.xexpose.window == windows->image.id)
11696            if (event.xexpose.count == 0)
11697              {
11698                event.xexpose.x=(int) highlight_info.x;
11699                event.xexpose.y=(int) highlight_info.y;
11700                event.xexpose.width=(int) highlight_info.width;
11701                event.xexpose.height=(int) highlight_info.height;
11702                XRefreshWindow(display,&windows->image,&event);
11703              }
11704          if (event.xexpose.window == windows->info.id)
11705            if (event.xexpose.count == 0)
11706              XInfoWidget(display,windows,text);
11707          break;
11708        }
11709        case KeyPress:
11710        {
11711          KeySym
11712            key_symbol;
11713
11714          if (event.xkey.window != windows->image.id)
11715            break;
11716          /*
11717            Respond to a user key press.
11718          */
11719          (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11720            sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11721          switch ((int) key_symbol)
11722          {
11723            case XK_Shift_L:
11724            case XK_Shift_R:
11725              break;
11726            case XK_Escape:
11727            case XK_F20:
11728              state|=EscapeState;
11729            case XK_Return:
11730            {
11731              state|=ExitState;
11732              break;
11733            }
11734            case XK_Home:
11735            case XK_KP_Home:
11736            {
11737              roi_info.x=(ssize_t) (windows->image.width/2L-roi_info.width/2L);
11738              roi_info.y=(ssize_t) (windows->image.height/2L-
11739                roi_info.height/2L);
11740              break;
11741            }
11742            case XK_Left:
11743            case XK_KP_Left:
11744            {
11745              roi_info.x--;
11746              break;
11747            }
11748            case XK_Up:
11749            case XK_KP_Up:
11750            case XK_Next:
11751            {
11752              roi_info.y--;
11753              break;
11754            }
11755            case XK_Right:
11756            case XK_KP_Right:
11757            {
11758              roi_info.x++;
11759              break;
11760            }
11761            case XK_Prior:
11762            case XK_Down:
11763            case XK_KP_Down:
11764            {
11765              roi_info.y++;
11766              break;
11767            }
11768            case XK_F1:
11769            case XK_Help:
11770            {
11771              (void) XSetFunction(display,windows->image.highlight_context,
11772                GXcopy);
11773              XTextViewWidget(display,resource_info,windows,MagickFalse,
11774                "Help Viewer - Region of Interest",ImageROIHelp);
11775              (void) XSetFunction(display,windows->image.highlight_context,
11776                GXinvert);
11777              break;
11778            }
11779            default:
11780            {
11781              command_type=XImageWindowCommand(display,resource_info,windows,
11782                event.xkey.state,key_symbol,image);
11783              if (command_type != NullCommand)
11784                state|=UpdateRegionState;
11785              break;
11786            }
11787          }
11788          (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11789            event.xkey.time);
11790          break;
11791        }
11792        case KeyRelease:
11793          break;
11794        case MotionNotify:
11795        {
11796          if (event.xbutton.window != windows->image.id)
11797            break;
11798          /*
11799            Map and unmap Info widget as text cursor crosses its boundaries.
11800          */
11801          x=event.xmotion.x;
11802          y=event.xmotion.y;
11803          if (windows->info.mapped != MagickFalse)
11804            {
11805              if ((x < (int) (windows->info.x+windows->info.width)) &&
11806                  (y < (int) (windows->info.y+windows->info.height)))
11807                (void) XWithdrawWindow(display,windows->info.id,
11808                  windows->info.screen);
11809            }
11810          else
11811            if ((x > (int) (windows->info.x+windows->info.width)) ||
11812                (y > (int) (windows->info.y+windows->info.height)))
11813              (void) XMapWindow(display,windows->info.id);
11814          roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11815          roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11816          break;
11817        }
11818        case SelectionRequest:
11819        {
11820          XSelectionEvent
11821            notify;
11822
11823          XSelectionRequestEvent
11824            *request;
11825
11826          /*
11827            Set primary selection.
11828          */
11829          (void) FormatLocaleString(text,MaxTextExtent,
11830            "%.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11831            roi_info.height,(double) roi_info.x,(double) roi_info.y);
11832          request=(&(event.xselectionrequest));
11833          (void) XChangeProperty(request->display,request->requestor,
11834            request->property,request->target,8,PropModeReplace,
11835            (unsigned char *) text,(int) strlen(text));
11836          notify.type=SelectionNotify;
11837          notify.display=request->display;
11838          notify.requestor=request->requestor;
11839          notify.selection=request->selection;
11840          notify.target=request->target;
11841          notify.time=request->time;
11842          if (request->property == None)
11843            notify.property=request->target;
11844          else
11845            notify.property=request->property;
11846          (void) XSendEvent(request->display,request->requestor,False,0,
11847            (XEvent *) &notify);
11848        }
11849        default:
11850          break;
11851      }
11852      if ((state & UpdateConfigurationState) != 0)
11853        {
11854          (void) XPutBackEvent(display,&event);
11855          (void) XCheckDefineCursor(display,windows->image.id,cursor);
11856          break;
11857        }
11858    } while ((state & ExitState) == 0);
11859  } while ((state & ExitState) == 0);
11860  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11861  XSetCursorState(display,windows,MagickFalse);
11862  if ((state & EscapeState) != 0)
11863    return(MagickTrue);
11864  return(MagickTrue);
11865}
11866
11867/*
11868%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11869%                                                                             %
11870%                                                                             %
11871%                                                                             %
11872+   X R o t a t e I m a g e                                                   %
11873%                                                                             %
11874%                                                                             %
11875%                                                                             %
11876%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11877%
11878%  XRotateImage() rotates the X image.  If the degrees parameter if zero, the
11879%  rotation angle is computed from the slope of a line drawn by the user.
11880%
11881%  The format of the XRotateImage method is:
11882%
11883%      MagickBooleanType XRotateImage(Display *display,
11884%        XResourceInfo *resource_info,XWindows *windows,double degrees,
11885%        Image **image)
11886%
11887%  A description of each parameter follows:
11888%
11889%    o display: Specifies a connection to an X server; returned from
11890%      XOpenDisplay.
11891%
11892%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
11893%
11894%    o windows: Specifies a pointer to a XWindows structure.
11895%
11896%    o degrees: Specifies the number of degrees to rotate the image.
11897%
11898%    o image: the image.
11899%
11900*/
11901static MagickBooleanType XRotateImage(Display *display,
11902  XResourceInfo *resource_info,XWindows *windows,double degrees,Image **image)
11903{
11904  static const char
11905    *RotateMenu[] =
11906    {
11907      "Pixel Color",
11908      "Direction",
11909      "Help",
11910      "Dismiss",
11911      (char *) NULL
11912    };
11913
11914  static ModeType
11915    direction = HorizontalRotateCommand;
11916
11917  static const ModeType
11918    DirectionCommands[] =
11919    {
11920      HorizontalRotateCommand,
11921      VerticalRotateCommand
11922    },
11923    RotateCommands[] =
11924    {
11925      RotateColorCommand,
11926      RotateDirectionCommand,
11927      RotateHelpCommand,
11928      RotateDismissCommand
11929    };
11930
11931  static unsigned int
11932    pen_id = 0;
11933
11934  char
11935    command[MaxTextExtent],
11936    text[MaxTextExtent];
11937
11938  Image
11939    *rotate_image;
11940
11941  int
11942    id,
11943    x,
11944    y;
11945
11946  MagickRealType
11947    normalized_degrees;
11948
11949  register int
11950    i;
11951
11952  unsigned int
11953    height,
11954    rotations,
11955    width;
11956
11957  if (degrees == 0.0)
11958    {
11959      unsigned int
11960        distance;
11961
11962      size_t
11963        state;
11964
11965      XEvent
11966        event;
11967
11968      XSegment
11969        rotate_info;
11970
11971      /*
11972        Map Command widget.
11973      */
11974      (void) CloneString(&windows->command.name,"Rotate");
11975      windows->command.data=2;
11976      (void) XCommandWidget(display,windows,RotateMenu,(XEvent *) NULL);
11977      (void) XMapRaised(display,windows->command.id);
11978      XClientMessage(display,windows->image.id,windows->im_protocols,
11979        windows->im_update_widget,CurrentTime);
11980      /*
11981        Wait for first button press.
11982      */
11983      (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
11984      XQueryPosition(display,windows->image.id,&x,&y);
11985      rotate_info.x1=x;
11986      rotate_info.y1=y;
11987      rotate_info.x2=x;
11988      rotate_info.y2=y;
11989      state=DefaultState;
11990      do
11991      {
11992        XHighlightLine(display,windows->image.id,
11993          windows->image.highlight_context,&rotate_info);
11994        /*
11995          Wait for next event.
11996        */
11997        XScreenEvent(display,windows,&event);
11998        XHighlightLine(display,windows->image.id,
11999          windows->image.highlight_context,&rotate_info);
12000        if (event.xany.window == windows->command.id)
12001          {
12002            /*
12003              Select a command from the Command widget.
12004            */
12005            id=XCommandWidget(display,windows,RotateMenu,&event);
12006            if (id < 0)
12007              continue;
12008            (void) XSetFunction(display,windows->image.highlight_context,
12009              GXcopy);
12010            switch (RotateCommands[id])
12011            {
12012              case RotateColorCommand:
12013              {
12014                const char
12015                  *ColorMenu[MaxNumberPens];
12016
12017                int
12018                  pen_number;
12019
12020                XColor
12021                  color;
12022
12023                /*
12024                  Initialize menu selections.
12025                */
12026                for (i=0; i < (int) (MaxNumberPens-2); i++)
12027                  ColorMenu[i]=resource_info->pen_colors[i];
12028                ColorMenu[MaxNumberPens-2]="Browser...";
12029                ColorMenu[MaxNumberPens-1]=(const char *) NULL;
12030                /*
12031                  Select a pen color from the pop-up menu.
12032                */
12033                pen_number=XMenuWidget(display,windows,RotateMenu[id],
12034                  (const char **) ColorMenu,command);
12035                if (pen_number < 0)
12036                  break;
12037                if (pen_number == (MaxNumberPens-2))
12038                  {
12039                    static char
12040                      color_name[MaxTextExtent] = "gray";
12041
12042                    /*
12043                      Select a pen color from a dialog.
12044                    */
12045                    resource_info->pen_colors[pen_number]=color_name;
12046                    XColorBrowserWidget(display,windows,"Select",color_name);
12047                    if (*color_name == '\0')
12048                      break;
12049                  }
12050                /*
12051                  Set pen color.
12052                */
12053                (void) XParseColor(display,windows->map_info->colormap,
12054                  resource_info->pen_colors[pen_number],&color);
12055                XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
12056                  (unsigned int) MaxColors,&color);
12057                windows->pixel_info->pen_colors[pen_number]=color;
12058                pen_id=(unsigned int) pen_number;
12059                break;
12060              }
12061              case RotateDirectionCommand:
12062              {
12063                static const char
12064                  *Directions[] =
12065                  {
12066                    "horizontal",
12067                    "vertical",
12068                    (char *) NULL,
12069                  };
12070
12071                /*
12072                  Select a command from the pop-up menu.
12073                */
12074                id=XMenuWidget(display,windows,RotateMenu[id],
12075                  Directions,command);
12076                if (id >= 0)
12077                  direction=DirectionCommands[id];
12078                break;
12079              }
12080              case RotateHelpCommand:
12081              {
12082                XTextViewWidget(display,resource_info,windows,MagickFalse,
12083                  "Help Viewer - Image Rotation",ImageRotateHelp);
12084                break;
12085              }
12086              case RotateDismissCommand:
12087              {
12088                /*
12089                  Prematurely exit.
12090                */
12091                state|=EscapeState;
12092                state|=ExitState;
12093                break;
12094              }
12095              default:
12096                break;
12097            }
12098            (void) XSetFunction(display,windows->image.highlight_context,
12099              GXinvert);
12100            continue;
12101          }
12102        switch (event.type)
12103        {
12104          case ButtonPress:
12105          {
12106            if (event.xbutton.button != Button1)
12107              break;
12108            if (event.xbutton.window != windows->image.id)
12109              break;
12110            /*
12111              exit loop.
12112            */
12113            (void) XSetFunction(display,windows->image.highlight_context,
12114              GXcopy);
12115            rotate_info.x1=event.xbutton.x;
12116            rotate_info.y1=event.xbutton.y;
12117            state|=ExitState;
12118            break;
12119          }
12120          case ButtonRelease:
12121            break;
12122          case Expose:
12123            break;
12124          case KeyPress:
12125          {
12126            char
12127              command[MaxTextExtent];
12128
12129            KeySym
12130              key_symbol;
12131
12132            if (event.xkey.window != windows->image.id)
12133              break;
12134            /*
12135              Respond to a user key press.
12136            */
12137            (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
12138              sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12139            switch ((int) key_symbol)
12140            {
12141              case XK_Escape:
12142              case XK_F20:
12143              {
12144                /*
12145                  Prematurely exit.
12146                */
12147                state|=EscapeState;
12148                state|=ExitState;
12149                break;
12150              }
12151              case XK_F1:
12152              case XK_Help:
12153              {
12154                (void) XSetFunction(display,windows->image.highlight_context,
12155                  GXcopy);
12156                XTextViewWidget(display,resource_info,windows,MagickFalse,
12157                  "Help Viewer - Image Rotation",ImageRotateHelp);
12158                (void) XSetFunction(display,windows->image.highlight_context,
12159                  GXinvert);
12160                break;
12161              }
12162              default:
12163              {
12164                (void) XBell(display,0);
12165                break;
12166              }
12167            }
12168            break;
12169          }
12170          case MotionNotify:
12171          {
12172            rotate_info.x1=event.xmotion.x;
12173            rotate_info.y1=event.xmotion.y;
12174          }
12175        }
12176        rotate_info.x2=rotate_info.x1;
12177        rotate_info.y2=rotate_info.y1;
12178        if (direction == HorizontalRotateCommand)
12179          rotate_info.x2+=32;
12180        else
12181          rotate_info.y2-=32;
12182      } while ((state & ExitState) == 0);
12183      (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12184      (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12185      if ((state & EscapeState) != 0)
12186        return(MagickTrue);
12187      /*
12188        Draw line as pointer moves until the mouse button is released.
12189      */
12190      distance=0;
12191      (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12192      state=DefaultState;
12193      do
12194      {
12195        if (distance > 9)
12196          {
12197            /*
12198              Display info and draw rotation line.
12199            */
12200            if (windows->info.mapped == MagickFalse)
12201              (void) XMapWindow(display,windows->info.id);
12202            (void) FormatLocaleString(text,MaxTextExtent," %g",
12203              direction == VerticalRotateCommand ? degrees-90.0 : degrees);
12204            XInfoWidget(display,windows,text);
12205            XHighlightLine(display,windows->image.id,
12206              windows->image.highlight_context,&rotate_info);
12207          }
12208        else
12209          if (windows->info.mapped != MagickFalse)
12210            (void) XWithdrawWindow(display,windows->info.id,
12211              windows->info.screen);
12212        /*
12213          Wait for next event.
12214        */
12215        XScreenEvent(display,windows,&event);
12216        if (distance > 9)
12217          XHighlightLine(display,windows->image.id,
12218            windows->image.highlight_context,&rotate_info);
12219        switch (event.type)
12220        {
12221          case ButtonPress:
12222            break;
12223          case ButtonRelease:
12224          {
12225            /*
12226              User has committed to rotation line.
12227            */
12228            rotate_info.x2=event.xbutton.x;
12229            rotate_info.y2=event.xbutton.y;
12230            state|=ExitState;
12231            break;
12232          }
12233          case Expose:
12234            break;
12235          case MotionNotify:
12236          {
12237            rotate_info.x2=event.xmotion.x;
12238            rotate_info.y2=event.xmotion.y;
12239          }
12240          default:
12241            break;
12242        }
12243        /*
12244          Check boundary conditions.
12245        */
12246        if (rotate_info.x2 < 0)
12247          rotate_info.x2=0;
12248        else
12249          if (rotate_info.x2 > (int) windows->image.width)
12250            rotate_info.x2=(short) windows->image.width;
12251        if (rotate_info.y2 < 0)
12252          rotate_info.y2=0;
12253        else
12254          if (rotate_info.y2 > (int) windows->image.height)
12255            rotate_info.y2=(short) windows->image.height;
12256        /*
12257          Compute rotation angle from the slope of the line.
12258        */
12259        degrees=0.0;
12260        distance=(unsigned int)
12261          ((rotate_info.x2-rotate_info.x1+1)*(rotate_info.x2-rotate_info.x1+1))+
12262          ((rotate_info.y2-rotate_info.y1+1)*(rotate_info.y2-rotate_info.y1+1));
12263        if (distance > 9)
12264          degrees=RadiansToDegrees(-atan2((double) (rotate_info.y2-
12265            rotate_info.y1),(double) (rotate_info.x2-rotate_info.x1)));
12266      } while ((state & ExitState) == 0);
12267      (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12268      (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12269      if (distance <= 9)
12270        return(MagickTrue);
12271    }
12272  if (direction == VerticalRotateCommand)
12273    degrees-=90.0;
12274  if (degrees == 0.0)
12275    return(MagickTrue);
12276  /*
12277    Rotate image.
12278  */
12279  normalized_degrees=degrees;
12280  while (normalized_degrees < -45.0)
12281    normalized_degrees+=360.0;
12282  for (rotations=0; normalized_degrees > 45.0; rotations++)
12283    normalized_degrees-=90.0;
12284  if (normalized_degrees != 0.0)
12285    (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
12286  XSetCursorState(display,windows,MagickTrue);
12287  XCheckRefreshWindows(display,windows);
12288  (*image)->background_color.red=ScaleShortToQuantum(
12289    windows->pixel_info->pen_colors[pen_id].red);
12290  (*image)->background_color.green=ScaleShortToQuantum(
12291    windows->pixel_info->pen_colors[pen_id].green);
12292  (*image)->background_color.blue=ScaleShortToQuantum(
12293    windows->pixel_info->pen_colors[pen_id].blue);
12294  rotate_image=RotateImage(*image,degrees,&(*image)->exception);
12295  XSetCursorState(display,windows,MagickFalse);
12296  if (rotate_image == (Image *) NULL)
12297    return(MagickFalse);
12298  *image=DestroyImage(*image);
12299  *image=rotate_image;
12300  if (windows->image.crop_geometry != (char *) NULL)
12301    {
12302      /*
12303        Rotate crop geometry.
12304      */
12305      width=(unsigned int) (*image)->columns;
12306      height=(unsigned int) (*image)->rows;
12307      (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12308      switch (rotations % 4)
12309      {
12310        default:
12311        case 0:
12312          break;
12313        case 1:
12314        {
12315          /*
12316            Rotate 90 degrees.
12317          */
12318          (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12319            "%ux%u%+d%+d",height,width,(int) (*image)->columns-
12320            (int) height-y,x);
12321          break;
12322        }
12323        case 2:
12324        {
12325          /*
12326            Rotate 180 degrees.
12327          */
12328          (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12329            "%ux%u%+d%+d",width,height,(int) width-x,(int) height-y);
12330          break;
12331        }
12332        case 3:
12333        {
12334          /*
12335            Rotate 270 degrees.
12336          */
12337          (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12338            "%ux%u%+d%+d",height,width,y,(int) (*image)->rows-(int) width-x);
12339          break;
12340        }
12341      }
12342    }
12343  if (windows->image.orphan != MagickFalse)
12344    return(MagickTrue);
12345  if (normalized_degrees != 0.0)
12346    {
12347      /*
12348        Update image colormap.
12349      */
12350      windows->image.window_changes.width=(int) (*image)->columns;
12351      windows->image.window_changes.height=(int) (*image)->rows;
12352      if (windows->image.crop_geometry != (char *) NULL)
12353        {
12354          /*
12355            Obtain dimensions of image from crop geometry.
12356          */
12357          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
12358            &width,&height);
12359          windows->image.window_changes.width=(int) width;
12360          windows->image.window_changes.height=(int) height;
12361        }
12362      XConfigureImageColormap(display,resource_info,windows,*image);
12363    }
12364  else
12365    if (((rotations % 4) == 1) || ((rotations % 4) == 3))
12366      {
12367        windows->image.window_changes.width=windows->image.ximage->height;
12368        windows->image.window_changes.height=windows->image.ximage->width;
12369      }
12370  /*
12371    Update image configuration.
12372  */
12373  (void) XConfigureImage(display,resource_info,windows,*image);
12374  return(MagickTrue);
12375}
12376
12377/*
12378%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12379%                                                                             %
12380%                                                                             %
12381%                                                                             %
12382+   X S a v e I m a g e                                                       %
12383%                                                                             %
12384%                                                                             %
12385%                                                                             %
12386%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12387%
12388%  XSaveImage() saves an image to a file.
12389%
12390%  The format of the XSaveImage method is:
12391%
12392%      MagickBooleanType XSaveImage(Display *display,
12393%        XResourceInfo *resource_info,XWindows *windows,Image *image)
12394%
12395%  A description of each parameter follows:
12396%
12397%    o display: Specifies a connection to an X server; returned from
12398%      XOpenDisplay.
12399%
12400%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12401%
12402%    o windows: Specifies a pointer to a XWindows structure.
12403%
12404%    o image: the image.
12405%
12406*/
12407static MagickBooleanType XSaveImage(Display *display,
12408  XResourceInfo *resource_info,XWindows *windows,Image *image)
12409{
12410  char
12411    filename[MaxTextExtent],
12412    geometry[MaxTextExtent];
12413
12414  Image
12415    *save_image;
12416
12417  ImageInfo
12418    *image_info;
12419
12420  MagickStatusType
12421    status;
12422
12423  /*
12424    Request file name from user.
12425  */
12426  if (resource_info->write_filename != (char *) NULL)
12427    (void) CopyMagickString(filename,resource_info->write_filename,
12428      MaxTextExtent);
12429  else
12430    {
12431      char
12432        path[MaxTextExtent];
12433
12434      int
12435        status;
12436
12437      GetPathComponent(image->filename,HeadPath,path);
12438      GetPathComponent(image->filename,TailPath,filename);
12439      status=chdir(path);
12440      if (status == -1)
12441        (void) ThrowMagickException(&image->exception,GetMagickModule(),
12442          FileOpenError,"UnableToOpenFile","%s",path);
12443    }
12444  XFileBrowserWidget(display,windows,"Save",filename);
12445  if (*filename == '\0')
12446    return(MagickTrue);
12447  if (IsPathAccessible(filename) != MagickFalse)
12448    {
12449      int
12450        status;
12451
12452      /*
12453        File exists-- seek user's permission before overwriting.
12454      */
12455      status=XConfirmWidget(display,windows,"Overwrite",filename);
12456      if (status <= 0)
12457        return(MagickTrue);
12458    }
12459  image_info=CloneImageInfo(resource_info->image_info);
12460  (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
12461  (void) SetImageInfo(image_info,1,&image->exception);
12462  if ((LocaleCompare(image_info->magick,"JPEG") == 0) ||
12463      (LocaleCompare(image_info->magick,"JPG") == 0))
12464    {
12465      char
12466        quality[MaxTextExtent];
12467
12468      int
12469        status;
12470
12471      /*
12472        Request JPEG quality from user.
12473      */
12474      (void) FormatLocaleString(quality,MaxTextExtent,"%.20g",(double)
12475        image->quality);
12476      status=XDialogWidget(display,windows,"Save","Enter JPEG quality:",
12477        quality);
12478      if (*quality == '\0')
12479        return(MagickTrue);
12480      image->quality=StringToUnsignedLong(quality);
12481      image_info->interlace=status != 0 ? NoInterlace : PlaneInterlace;
12482    }
12483  if ((LocaleCompare(image_info->magick,"EPS") == 0) ||
12484      (LocaleCompare(image_info->magick,"PDF") == 0) ||
12485      (LocaleCompare(image_info->magick,"PS") == 0) ||
12486      (LocaleCompare(image_info->magick,"PS2") == 0))
12487    {
12488      char
12489        geometry[MaxTextExtent];
12490
12491      /*
12492        Request page geometry from user.
12493      */
12494      (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
12495      if (LocaleCompare(image_info->magick,"PDF") == 0)
12496        (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
12497      if (image_info->page != (char *) NULL)
12498        (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
12499      XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
12500        "Select page geometry:",geometry);
12501      if (*geometry != '\0')
12502        image_info->page=GetPageGeometry(geometry);
12503    }
12504  /*
12505    Apply image transforms.
12506  */
12507  XSetCursorState(display,windows,MagickTrue);
12508  XCheckRefreshWindows(display,windows);
12509  save_image=CloneImage(image,0,0,MagickTrue,&image->exception);
12510  if (save_image == (Image *) NULL)
12511    return(MagickFalse);
12512  (void) FormatLocaleString(geometry,MaxTextExtent,"%dx%d!",
12513    windows->image.ximage->width,windows->image.ximage->height);
12514  (void) TransformImage(&save_image,windows->image.crop_geometry,geometry);
12515  /*
12516    Write image.
12517  */
12518  (void) CopyMagickString(save_image->filename,filename,MaxTextExtent);
12519  status=WriteImage(image_info,save_image);
12520  if (status != MagickFalse)
12521    image->taint=MagickFalse;
12522  save_image=DestroyImage(save_image);
12523  image_info=DestroyImageInfo(image_info);
12524  XSetCursorState(display,windows,MagickFalse);
12525  return(status != 0 ? MagickTrue : MagickFalse);
12526}
12527
12528/*
12529%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12530%                                                                             %
12531%                                                                             %
12532%                                                                             %
12533+   X S c r e e n E v e n t                                                   %
12534%                                                                             %
12535%                                                                             %
12536%                                                                             %
12537%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12538%
12539%  XScreenEvent() handles global events associated with the Pan and Magnify
12540%  windows.
12541%
12542%  The format of the XScreenEvent function is:
12543%
12544%      void XScreenEvent(Display *display,XWindows *windows,XEvent *event)
12545%
12546%  A description of each parameter follows:
12547%
12548%    o display: Specifies a pointer to the Display structure;  returned from
12549%      XOpenDisplay.
12550%
12551%    o windows: Specifies a pointer to a XWindows structure.
12552%
12553%    o event: Specifies a pointer to a X11 XEvent structure.
12554%
12555%
12556*/
12557
12558#if defined(__cplusplus) || defined(c_plusplus)
12559extern "C" {
12560#endif
12561
12562static int XPredicate(Display *magick_unused(display),XEvent *event,char *data)
12563{
12564  register XWindows
12565    *windows;
12566
12567  windows=(XWindows *) data;
12568  if ((event->type == ClientMessage) &&
12569      (event->xclient.window == windows->image.id))
12570    return(MagickFalse);
12571  return(MagickTrue);
12572}
12573
12574#if defined(__cplusplus) || defined(c_plusplus)
12575}
12576#endif
12577
12578static void XScreenEvent(Display *display,XWindows *windows,XEvent *event)
12579{
12580  register int
12581    x,
12582    y;
12583
12584  (void) XIfEvent(display,event,XPredicate,(char *) windows);
12585  if (event->xany.window == windows->command.id)
12586    return;
12587  switch (event->type)
12588  {
12589    case ButtonPress:
12590    case ButtonRelease:
12591    {
12592      if ((event->xbutton.button == Button3) &&
12593          (event->xbutton.state & Mod1Mask))
12594        {
12595          /*
12596            Convert Alt-Button3 to Button2.
12597          */
12598          event->xbutton.button=Button2;
12599          event->xbutton.state&=(~Mod1Mask);
12600        }
12601      if (event->xbutton.window == windows->backdrop.id)
12602        {
12603          (void) XSetInputFocus(display,event->xbutton.window,RevertToParent,
12604            event->xbutton.time);
12605          break;
12606        }
12607      if (event->xbutton.window == windows->pan.id)
12608        {
12609          XPanImage(display,windows,event);
12610          break;
12611        }
12612      if (event->xbutton.window == windows->image.id)
12613        if (event->xbutton.button == Button2)
12614          {
12615            /*
12616              Update magnified image.
12617            */
12618            x=event->xbutton.x;
12619            y=event->xbutton.y;
12620            if (x < 0)
12621              x=0;
12622            else
12623              if (x >= (int) windows->image.width)
12624                x=(int) (windows->image.width-1);
12625            windows->magnify.x=(int) windows->image.x+x;
12626            if (y < 0)
12627              y=0;
12628            else
12629             if (y >= (int) windows->image.height)
12630               y=(int) (windows->image.height-1);
12631            windows->magnify.y=windows->image.y+y;
12632            if (windows->magnify.mapped == MagickFalse)
12633              (void) XMapRaised(display,windows->magnify.id);
12634            XMakeMagnifyImage(display,windows);
12635            if (event->type == ButtonRelease)
12636              (void) XWithdrawWindow(display,windows->info.id,
12637                windows->info.screen);
12638            break;
12639          }
12640      break;
12641    }
12642    case ClientMessage:
12643    {
12644      /*
12645        If client window delete message, exit.
12646      */
12647      if (event->xclient.message_type != windows->wm_protocols)
12648        break;
12649      if (*event->xclient.data.l != (long) windows->wm_delete_window)
12650        break;
12651      if (event->xclient.window == windows->magnify.id)
12652        {
12653          (void) XWithdrawWindow(display,windows->magnify.id,
12654            windows->magnify.screen);
12655          break;
12656        }
12657      break;
12658    }
12659    case ConfigureNotify:
12660    {
12661      if (event->xconfigure.window == windows->magnify.id)
12662        {
12663          unsigned int
12664            magnify;
12665
12666          /*
12667            Magnify window has a new configuration.
12668          */
12669          windows->magnify.width=(unsigned int) event->xconfigure.width;
12670          windows->magnify.height=(unsigned int) event->xconfigure.height;
12671          if (windows->magnify.mapped == MagickFalse)
12672            break;
12673          magnify=1;
12674          while ((int) magnify <= event->xconfigure.width)
12675            magnify<<=1;
12676          while ((int) magnify <= event->xconfigure.height)
12677            magnify<<=1;
12678          magnify>>=1;
12679          if (((int) magnify != event->xconfigure.width) ||
12680              ((int) magnify != event->xconfigure.height))
12681            {
12682              XWindowChanges
12683                window_changes;
12684
12685              window_changes.width=(int) magnify;
12686              window_changes.height=(int) magnify;
12687              (void) XReconfigureWMWindow(display,windows->magnify.id,
12688                windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
12689                &window_changes);
12690              break;
12691            }
12692          XMakeMagnifyImage(display,windows);
12693          break;
12694        }
12695      break;
12696    }
12697    case Expose:
12698    {
12699      if (event->xexpose.window == windows->image.id)
12700        {
12701          XRefreshWindow(display,&windows->image,event);
12702          break;
12703        }
12704      if (event->xexpose.window == windows->pan.id)
12705        if (event->xexpose.count == 0)
12706          {
12707            XDrawPanRectangle(display,windows);
12708            break;
12709          }
12710      if (event->xexpose.window == windows->magnify.id)
12711        if (event->xexpose.count == 0)
12712          {
12713            XMakeMagnifyImage(display,windows);
12714            break;
12715          }
12716      break;
12717    }
12718    case KeyPress:
12719    {
12720      char
12721        command[MaxTextExtent];
12722
12723      KeySym
12724        key_symbol;
12725
12726      if (event->xkey.window != windows->magnify.id)
12727        break;
12728      /*
12729        Respond to a user key press.
12730      */
12731      (void) XLookupString((XKeyEvent *) &event->xkey,command,(int)
12732        sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12733      XMagnifyWindowCommand(display,windows,event->xkey.state,key_symbol);
12734      break;
12735    }
12736    case MapNotify:
12737    {
12738      if (event->xmap.window == windows->magnify.id)
12739        {
12740          windows->magnify.mapped=MagickTrue;
12741          (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12742          break;
12743        }
12744      if (event->xmap.window == windows->info.id)
12745        {
12746          windows->info.mapped=MagickTrue;
12747          break;
12748        }
12749      break;
12750    }
12751    case MotionNotify:
12752    {
12753      while (XCheckMaskEvent(display,ButtonMotionMask,event)) ;
12754      if (event->xmotion.window == windows->image.id)
12755        if (windows->magnify.mapped != MagickFalse)
12756          {
12757            /*
12758              Update magnified image.
12759            */
12760            x=event->xmotion.x;
12761            y=event->xmotion.y;
12762            if (x < 0)
12763              x=0;
12764            else
12765              if (x >= (int) windows->image.width)
12766                x=(int) (windows->image.width-1);
12767            windows->magnify.x=(int) windows->image.x+x;
12768            if (y < 0)
12769              y=0;
12770            else
12771             if (y >= (int) windows->image.height)
12772               y=(int) (windows->image.height-1);
12773            windows->magnify.y=windows->image.y+y;
12774            XMakeMagnifyImage(display,windows);
12775          }
12776      break;
12777    }
12778    case UnmapNotify:
12779    {
12780      if (event->xunmap.window == windows->magnify.id)
12781        {
12782          windows->magnify.mapped=MagickFalse;
12783          break;
12784        }
12785      if (event->xunmap.window == windows->info.id)
12786        {
12787          windows->info.mapped=MagickFalse;
12788          break;
12789        }
12790      break;
12791    }
12792    default:
12793      break;
12794  }
12795}
12796
12797/*
12798%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12799%                                                                             %
12800%                                                                             %
12801%                                                                             %
12802+   X S e t C r o p G e o m e t r y                                           %
12803%                                                                             %
12804%                                                                             %
12805%                                                                             %
12806%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12807%
12808%  XSetCropGeometry() accepts a cropping geometry relative to the Image window
12809%  and translates it to a cropping geometry relative to the image.
12810%
12811%  The format of the XSetCropGeometry method is:
12812%
12813%      void XSetCropGeometry(Display *display,XWindows *windows,
12814%        RectangleInfo *crop_info,Image *image)
12815%
12816%  A description of each parameter follows:
12817%
12818%    o display: Specifies a connection to an X server; returned from
12819%      XOpenDisplay.
12820%
12821%    o windows: Specifies a pointer to a XWindows structure.
12822%
12823%    o crop_info:  A pointer to a RectangleInfo that defines a region of the
12824%      Image window to crop.
12825%
12826%    o image: the image.
12827%
12828*/
12829static void XSetCropGeometry(Display *display,XWindows *windows,
12830  RectangleInfo *crop_info,Image *image)
12831{
12832  char
12833    text[MaxTextExtent];
12834
12835  int
12836    x,
12837    y;
12838
12839  MagickRealType
12840    scale_factor;
12841
12842  unsigned int
12843    height,
12844    width;
12845
12846  if (windows->info.mapped != MagickFalse)
12847    {
12848      /*
12849        Display info on cropping rectangle.
12850      */
12851      (void) FormatLocaleString(text,MaxTextExtent," %.20gx%.20g%+.20g%+.20g",
12852        (double) crop_info->width,(double) crop_info->height,(double)
12853        crop_info->x,(double) crop_info->y);
12854      XInfoWidget(display,windows,text);
12855    }
12856  /*
12857    Cropping geometry is relative to any previous crop geometry.
12858  */
12859  x=0;
12860  y=0;
12861  width=(unsigned int) image->columns;
12862  height=(unsigned int) image->rows;
12863  if (windows->image.crop_geometry != (char *) NULL)
12864    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12865  else
12866    windows->image.crop_geometry=AcquireString((char *) NULL);
12867  /*
12868    Define the crop geometry string from the cropping rectangle.
12869  */
12870  scale_factor=(MagickRealType) width/windows->image.ximage->width;
12871  if (crop_info->x > 0)
12872    x+=(int) (scale_factor*crop_info->x+0.5);
12873  width=(unsigned int) (scale_factor*crop_info->width+0.5);
12874  if (width == 0)
12875    width=1;
12876  scale_factor=(MagickRealType) height/windows->image.ximage->height;
12877  if (crop_info->y > 0)
12878    y+=(int) (scale_factor*crop_info->y+0.5);
12879  height=(unsigned int) (scale_factor*crop_info->height+0.5);
12880  if (height == 0)
12881    height=1;
12882  (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12883    "%ux%u%+d%+d",width,height,x,y);
12884}
12885
12886/*
12887%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12888%                                                                             %
12889%                                                                             %
12890%                                                                             %
12891+   X T i l e I m a g e                                                       %
12892%                                                                             %
12893%                                                                             %
12894%                                                                             %
12895%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12896%
12897%  XTileImage() loads or deletes a selected tile from a visual image directory.
12898%  The load or delete command is chosen from a menu.
12899%
12900%  The format of the XTileImage method is:
12901%
12902%      Image *XTileImage(Display *display,XResourceInfo *resource_info,
12903%        XWindows *windows,Image *image,XEvent *event)
12904%
12905%  A description of each parameter follows:
12906%
12907%    o tile_image:  XTileImage reads or deletes the tile image
12908%      and returns it.  A null image is returned if an error occurs.
12909%
12910%    o display: Specifies a connection to an X server;  returned from
12911%      XOpenDisplay.
12912%
12913%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12914%
12915%    o windows: Specifies a pointer to a XWindows structure.
12916%
12917%    o image: the image; returned from ReadImage.
12918%
12919%    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
12920%      the entire image is refreshed.
12921%
12922*/
12923static Image *XTileImage(Display *display,XResourceInfo *resource_info,
12924  XWindows *windows,Image *image,XEvent *event)
12925{
12926  static const char
12927    *VerbMenu[] =
12928    {
12929      "Load",
12930      "Next",
12931      "Former",
12932      "Delete",
12933      "Update",
12934      (char *) NULL,
12935    };
12936
12937  static const ModeType
12938    TileCommands[] =
12939    {
12940      TileLoadCommand,
12941      TileNextCommand,
12942      TileFormerCommand,
12943      TileDeleteCommand,
12944      TileUpdateCommand
12945    };
12946
12947  char
12948    command[MaxTextExtent],
12949    filename[MaxTextExtent];
12950
12951  Image
12952    *tile_image;
12953
12954  int
12955    id,
12956    status,
12957    tile,
12958    x,
12959    y;
12960
12961  MagickRealType
12962    scale_factor;
12963
12964  register char
12965    *p,
12966    *q;
12967
12968  register int
12969    i;
12970
12971  unsigned int
12972    height,
12973    width;
12974
12975  /*
12976    Tile image is relative to montage image configuration.
12977  */
12978  x=0;
12979  y=0;
12980  width=(unsigned int) image->columns;
12981  height=(unsigned int) image->rows;
12982  if (windows->image.crop_geometry != (char *) NULL)
12983    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12984  scale_factor=(MagickRealType) width/windows->image.ximage->width;
12985  event->xbutton.x+=windows->image.x;
12986  event->xbutton.x=(int) (scale_factor*event->xbutton.x+x+0.5);
12987  scale_factor=(MagickRealType) height/windows->image.ximage->height;
12988  event->xbutton.y+=windows->image.y;
12989  event->xbutton.y=(int) (scale_factor*event->xbutton.y+y+0.5);
12990  /*
12991    Determine size and location of each tile in the visual image directory.
12992  */
12993  width=(unsigned int) image->columns;
12994  height=(unsigned int) image->rows;
12995  x=0;
12996  y=0;
12997  (void) XParseGeometry(image->montage,&x,&y,&width,&height);
12998  tile=((event->xbutton.y-y)/height)*(((int) image->columns-x)/width)+
12999    (event->xbutton.x-x)/width;
13000  if (tile < 0)
13001    {
13002      /*
13003        Button press is outside any tile.
13004      */
13005      (void) XBell(display,0);
13006      return((Image *) NULL);
13007    }
13008  /*
13009    Determine file name from the tile directory.
13010  */
13011  p=image->directory;
13012  for (i=tile; (i != 0) && (*p != '\0'); )
13013  {
13014    if (*p == '\n')
13015      i--;
13016    p++;
13017  }
13018  if (*p == '\0')
13019    {
13020      /*
13021        Button press is outside any tile.
13022      */
13023      (void) XBell(display,0);
13024      return((Image *) NULL);
13025    }
13026  /*
13027    Select a command from the pop-up menu.
13028  */
13029  id=XMenuWidget(display,windows,"Tile Verb",VerbMenu,command);
13030  if (id < 0)
13031    return((Image *) NULL);
13032  q=p;
13033  while ((*q != '\n') && (*q != '\0'))
13034    q++;
13035  (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13036  /*
13037    Perform command for the selected tile.
13038  */
13039  XSetCursorState(display,windows,MagickTrue);
13040  XCheckRefreshWindows(display,windows);
13041  tile_image=NewImageList();
13042  switch (TileCommands[id])
13043  {
13044    case TileLoadCommand:
13045    {
13046      /*
13047        Load tile image.
13048      */
13049      XCheckRefreshWindows(display,windows);
13050      (void) CopyMagickString(resource_info->image_info->magick,"MIFF",
13051        MaxTextExtent);
13052      (void) CopyMagickString(resource_info->image_info->filename,filename,
13053        MaxTextExtent);
13054      tile_image=ReadImage(resource_info->image_info,&image->exception);
13055      CatchException(&image->exception);
13056      (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13057      break;
13058    }
13059    case TileNextCommand:
13060    {
13061      /*
13062        Display next image.
13063      */
13064      XClientMessage(display,windows->image.id,windows->im_protocols,
13065        windows->im_next_image,CurrentTime);
13066      break;
13067    }
13068    case TileFormerCommand:
13069    {
13070      /*
13071        Display former image.
13072      */
13073      XClientMessage(display,windows->image.id,windows->im_protocols,
13074        windows->im_former_image,CurrentTime);
13075      break;
13076    }
13077    case TileDeleteCommand:
13078    {
13079      /*
13080        Delete tile image.
13081      */
13082      if (IsPathAccessible(filename) == MagickFalse)
13083        {
13084          XNoticeWidget(display,windows,"Image file does not exist:",filename);
13085          break;
13086        }
13087      status=XConfirmWidget(display,windows,"Really delete tile",filename);
13088      if (status <= 0)
13089        break;
13090      status=remove(filename) != 0 ? MagickTrue : MagickFalse;
13091      if (status != MagickFalse)
13092        {
13093          XNoticeWidget(display,windows,"Unable to delete image file:",
13094            filename);
13095          break;
13096        }
13097    }
13098    case TileUpdateCommand:
13099    {
13100      ExceptionInfo
13101        *exception;
13102
13103      int
13104        x_offset,
13105        y_offset;
13106
13107      PixelPacket
13108        pixel;
13109
13110      register int
13111        j;
13112
13113      register Quantum
13114        *s;
13115
13116      /*
13117        Ensure all the images exist.
13118      */
13119      tile=0;
13120      for (p=image->directory; *p != '\0'; p++)
13121      {
13122        CacheView
13123          *image_view;
13124
13125        q=p;
13126        while ((*q != '\n') && (*q != '\0'))
13127          q++;
13128        (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13129        p=q;
13130        if (IsPathAccessible(filename) != MagickFalse)
13131          {
13132            tile++;
13133            continue;
13134          }
13135        /*
13136          Overwrite tile with background color.
13137        */
13138        x_offset=(int) (width*(tile % (((int) image->columns-x)/width))+x);
13139        y_offset=(int) (height*(tile/(((int) image->columns-x)/width))+y);
13140        exception=(&image->exception);
13141        image_view=AcquireCacheView(image);
13142        (void) GetOneCacheViewVirtualPixel(image_view,0,0,&pixel,exception);
13143        for (i=0; i < (int) height; i++)
13144        {
13145          s=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,(ssize_t)
13146            y_offset+i,width,1,exception);
13147          if (s == (Quantum *) NULL)
13148            break;
13149          for (j=0; j < (int) width; j++)
13150          {
13151            SetPixelPacket(image,&pixel,s);
13152            s+=GetPixelChannels(image);
13153          }
13154          if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
13155            break;
13156        }
13157        image_view=DestroyCacheView(image_view);
13158        tile++;
13159      }
13160      windows->image.window_changes.width=(int) image->columns;
13161      windows->image.window_changes.height=(int) image->rows;
13162      XConfigureImageColormap(display,resource_info,windows,image);
13163      (void) XConfigureImage(display,resource_info,windows,image);
13164      break;
13165    }
13166    default:
13167      break;
13168  }
13169  XSetCursorState(display,windows,MagickFalse);
13170  return(tile_image);
13171}
13172
13173/*
13174%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13175%                                                                             %
13176%                                                                             %
13177%                                                                             %
13178+   X T r a n s l a t e I m a g e                                             %
13179%                                                                             %
13180%                                                                             %
13181%                                                                             %
13182%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13183%
13184%  XTranslateImage() translates the image within an Image window by one pixel
13185%  as specified by the key symbol.  If the image has a `montage string the
13186%  translation is respect to the width and height contained within the string.
13187%
13188%  The format of the XTranslateImage method is:
13189%
13190%      void XTranslateImage(Display *display,XWindows *windows,
13191%        Image *image,const KeySym key_symbol)
13192%
13193%  A description of each parameter follows:
13194%
13195%    o display: Specifies a connection to an X server; returned from
13196%      XOpenDisplay.
13197%
13198%    o windows: Specifies a pointer to a XWindows structure.
13199%
13200%    o image: the image.
13201%
13202%    o key_symbol: Specifies a KeySym which indicates which side of the image
13203%      to trim.
13204%
13205*/
13206static void XTranslateImage(Display *display,XWindows *windows,
13207  Image *image,const KeySym key_symbol)
13208{
13209  char
13210    text[MaxTextExtent];
13211
13212  int
13213    x,
13214    y;
13215
13216  unsigned int
13217    x_offset,
13218    y_offset;
13219
13220  /*
13221    User specified a pan position offset.
13222  */
13223  x_offset=windows->image.width;
13224  y_offset=windows->image.height;
13225  if (image->montage != (char *) NULL)
13226    (void) XParseGeometry(image->montage,&x,&y,&x_offset,&y_offset);
13227  switch ((int) key_symbol)
13228  {
13229    case XK_Home:
13230    case XK_KP_Home:
13231    {
13232      windows->image.x=(int) windows->image.width/2;
13233      windows->image.y=(int) windows->image.height/2;
13234      break;
13235    }
13236    case XK_Left:
13237    case XK_KP_Left:
13238    {
13239      windows->image.x-=x_offset;
13240      break;
13241    }
13242    case XK_Next:
13243    case XK_Up:
13244    case XK_KP_Up:
13245    {
13246      windows->image.y-=y_offset;
13247      break;
13248    }
13249    case XK_Right:
13250    case XK_KP_Right:
13251    {
13252      windows->image.x+=x_offset;
13253      break;
13254    }
13255    case XK_Prior:
13256    case XK_Down:
13257    case XK_KP_Down:
13258    {
13259      windows->image.y+=y_offset;
13260      break;
13261    }
13262    default:
13263      return;
13264  }
13265  /*
13266    Check boundary conditions.
13267  */
13268  if (windows->image.x < 0)
13269    windows->image.x=0;
13270  else
13271    if ((int) (windows->image.x+windows->image.width) >
13272        windows->image.ximage->width)
13273      windows->image.x=(int) windows->image.ximage->width-windows->image.width;
13274  if (windows->image.y < 0)
13275    windows->image.y=0;
13276  else
13277    if ((int) (windows->image.y+windows->image.height) >
13278        windows->image.ximage->height)
13279      windows->image.y=(int) windows->image.ximage->height-windows->image.height;
13280  /*
13281    Refresh Image window.
13282  */
13283  (void) FormatLocaleString(text,MaxTextExtent," %ux%u%+d%+d ",
13284    windows->image.width,windows->image.height,windows->image.x,
13285    windows->image.y);
13286  XInfoWidget(display,windows,text);
13287  XCheckRefreshWindows(display,windows);
13288  XDrawPanRectangle(display,windows);
13289  XRefreshWindow(display,&windows->image,(XEvent *) NULL);
13290  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13291}
13292
13293/*
13294%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13295%                                                                             %
13296%                                                                             %
13297%                                                                             %
13298+   X T r i m I m a g e                                                       %
13299%                                                                             %
13300%                                                                             %
13301%                                                                             %
13302%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13303%
13304%  XTrimImage() trims the edges from the Image window.
13305%
13306%  The format of the XTrimImage method is:
13307%
13308%      MagickBooleanType XTrimImage(Display *display,
13309%        XResourceInfo *resource_info,XWindows *windows,Image *image)
13310%
13311%  A description of each parameter follows:
13312%
13313%    o display: Specifies a connection to an X server; returned from
13314%      XOpenDisplay.
13315%
13316%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13317%
13318%    o windows: Specifies a pointer to a XWindows structure.
13319%
13320%    o image: the image.
13321%
13322*/
13323static MagickBooleanType XTrimImage(Display *display,
13324  XResourceInfo *resource_info,XWindows *windows,Image *image)
13325{
13326  RectangleInfo
13327    trim_info;
13328
13329  register int
13330    x,
13331    y;
13332
13333  size_t
13334    background,
13335    pixel;
13336
13337  /*
13338    Trim edges from image.
13339  */
13340  XSetCursorState(display,windows,MagickTrue);
13341  XCheckRefreshWindows(display,windows);
13342  /*
13343    Crop the left edge.
13344  */
13345  background=XGetPixel(windows->image.ximage,0,0);
13346  trim_info.width=(size_t) windows->image.ximage->width;
13347  for (x=0; x < windows->image.ximage->width; x++)
13348  {
13349    for (y=0; y < windows->image.ximage->height; y++)
13350    {
13351      pixel=XGetPixel(windows->image.ximage,x,y);
13352      if (pixel != background)
13353        break;
13354    }
13355    if (y < windows->image.ximage->height)
13356      break;
13357  }
13358  trim_info.x=(ssize_t) x;
13359  if (trim_info.x == (ssize_t) windows->image.ximage->width)
13360    {
13361      XSetCursorState(display,windows,MagickFalse);
13362      return(MagickFalse);
13363    }
13364  /*
13365    Crop the right edge.
13366  */
13367  background=XGetPixel(windows->image.ximage,windows->image.ximage->width-1,0);
13368  for (x=windows->image.ximage->width-1; x != 0; x--)
13369  {
13370    for (y=0; y < windows->image.ximage->height; y++)
13371    {
13372      pixel=XGetPixel(windows->image.ximage,x,y);
13373      if (pixel != background)
13374        break;
13375    }
13376    if (y < windows->image.ximage->height)
13377      break;
13378  }
13379  trim_info.width=(size_t) (x-trim_info.x+1);
13380  /*
13381    Crop the top edge.
13382  */
13383  background=XGetPixel(windows->image.ximage,0,0);
13384  trim_info.height=(size_t) windows->image.ximage->height;
13385  for (y=0; y < windows->image.ximage->height; y++)
13386  {
13387    for (x=0; x < windows->image.ximage->width; x++)
13388    {
13389      pixel=XGetPixel(windows->image.ximage,x,y);
13390      if (pixel != background)
13391        break;
13392    }
13393    if (x < windows->image.ximage->width)
13394      break;
13395  }
13396  trim_info.y=(ssize_t) y;
13397  /*
13398    Crop the bottom edge.
13399  */
13400  background=XGetPixel(windows->image.ximage,0,windows->image.ximage->height-1);
13401  for (y=windows->image.ximage->height-1; y != 0; y--)
13402  {
13403    for (x=0; x < windows->image.ximage->width; x++)
13404    {
13405      pixel=XGetPixel(windows->image.ximage,x,y);
13406      if (pixel != background)
13407        break;
13408    }
13409    if (x < windows->image.ximage->width)
13410      break;
13411  }
13412  trim_info.height=(size_t) y-trim_info.y+1;
13413  if (((unsigned int) trim_info.width != windows->image.width) ||
13414      ((unsigned int) trim_info.height != windows->image.height))
13415    {
13416      /*
13417        Reconfigure Image window as defined by the trimming rectangle.
13418      */
13419      XSetCropGeometry(display,windows,&trim_info,image);
13420      windows->image.window_changes.width=(int) trim_info.width;
13421      windows->image.window_changes.height=(int) trim_info.height;
13422      (void) XConfigureImage(display,resource_info,windows,image);
13423    }
13424  XSetCursorState(display,windows,MagickFalse);
13425  return(MagickTrue);
13426}
13427
13428/*
13429%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13430%                                                                             %
13431%                                                                             %
13432%                                                                             %
13433+   X V i s u a l D i r e c t o r y I m a g e                                 %
13434%                                                                             %
13435%                                                                             %
13436%                                                                             %
13437%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13438%
13439%  XVisualDirectoryImage() creates a Visual Image Directory.
13440%
13441%  The format of the XVisualDirectoryImage method is:
13442%
13443%      Image *XVisualDirectoryImage(Display *display,
13444%        XResourceInfo *resource_info,XWindows *windows)
13445%
13446%  A description of each parameter follows:
13447%
13448%    o nexus: Method XVisualDirectoryImage returns a visual image
13449%      directory if it can be created successfully.  Otherwise a null image
13450%      is returned.
13451%
13452%    o display: Specifies a connection to an X server; returned from
13453%      XOpenDisplay.
13454%
13455%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13456%
13457%    o windows: Specifies a pointer to a XWindows structure.
13458%
13459*/
13460static Image *XVisualDirectoryImage(Display *display,
13461  XResourceInfo *resource_info,XWindows *windows)
13462{
13463#define TileImageTag  "Scale/Image"
13464#define XClientName  "montage"
13465
13466  char
13467    **filelist;
13468
13469  ExceptionInfo
13470    *exception;
13471
13472  Image
13473    *images,
13474    *montage_image,
13475    *next_image,
13476    *thumbnail_image;
13477
13478  ImageInfo
13479    *read_info;
13480
13481  int
13482    number_files;
13483
13484  MagickBooleanType
13485    backdrop;
13486
13487  MagickStatusType
13488    status;
13489
13490  MontageInfo
13491    *montage_info;
13492
13493  RectangleInfo
13494    geometry;
13495
13496  register int
13497    i;
13498
13499  static char
13500    filename[MaxTextExtent] = "\0",
13501    filenames[MaxTextExtent] = "*";
13502
13503  XResourceInfo
13504    background_resources;
13505
13506  /*
13507    Request file name from user.
13508  */
13509  XFileBrowserWidget(display,windows,"Directory",filenames);
13510  if (*filenames == '\0')
13511    return((Image *) NULL);
13512  /*
13513    Expand the filenames.
13514  */
13515  filelist=(char **) AcquireMagickMemory(sizeof(*filelist));
13516  if (filelist == (char **) NULL)
13517    {
13518      ThrowXWindowFatalException(ResourceLimitError,"MemoryAllocationFailed",
13519        filenames);
13520      return((Image *) NULL);
13521    }
13522  number_files=1;
13523  filelist[0]=filenames;
13524  status=ExpandFilenames(&number_files,&filelist);
13525  if ((status == MagickFalse) || (number_files == 0))
13526    {
13527      if (number_files == 0)
13528        ThrowXWindowFatalException(ImageError,"NoImagesWereFound",filenames)
13529      else
13530        ThrowXWindowFatalException(ResourceLimitError,"MemoryAllocationFailed",
13531          filenames);
13532      return((Image *) NULL);
13533    }
13534  /*
13535    Set image background resources.
13536  */
13537  background_resources=(*resource_info);
13538  background_resources.window_id=AcquireString("");
13539  (void) FormatLocaleString(background_resources.window_id,MaxTextExtent,
13540    "0x%lx",windows->image.id);
13541  background_resources.backdrop=MagickTrue;
13542  /*
13543    Read each image and convert them to a tile.
13544  */
13545  backdrop=(windows->visual_info->klass == TrueColor) ||
13546    (windows->visual_info->klass == DirectColor) ? MagickTrue : MagickFalse;
13547  read_info=CloneImageInfo(resource_info->image_info);
13548  (void) SetImageOption(read_info,"jpeg:size","120x120");
13549  (void) CloneString(&read_info->size,DefaultTileGeometry);
13550  (void) SetImageInfoProgressMonitor(read_info,(MagickProgressMonitor) NULL,
13551    (void *) NULL);
13552  images=NewImageList();
13553  exception=AcquireExceptionInfo();
13554  XSetCursorState(display,windows,MagickTrue);
13555  XCheckRefreshWindows(display,windows);
13556  for (i=0; i < (int) number_files; i++)
13557  {
13558    (void) CopyMagickString(read_info->filename,filelist[i],MaxTextExtent);
13559    filelist[i]=DestroyString(filelist[i]);
13560    *read_info->magick='\0';
13561    next_image=ReadImage(read_info,exception);
13562    CatchException(exception);
13563    if (next_image != (Image *) NULL)
13564      {
13565        (void) DeleteImageProperty(next_image,"label");
13566        (void) SetImageProperty(next_image,"label",InterpretImageProperties(
13567          read_info,next_image,DefaultTileLabel));
13568        (void) ParseRegionGeometry(next_image,read_info->size,&geometry,
13569          exception);
13570        thumbnail_image=ThumbnailImage(next_image,geometry.width,
13571          geometry.height,exception);
13572        if (thumbnail_image != (Image *) NULL)
13573          {
13574            next_image=DestroyImage(next_image);
13575            next_image=thumbnail_image;
13576          }
13577        if (backdrop)
13578          {
13579            (void) XDisplayBackgroundImage(display,&background_resources,
13580              next_image);
13581            XSetCursorState(display,windows,MagickTrue);
13582          }
13583        AppendImageToList(&images,next_image);
13584        if (images->progress_monitor != (MagickProgressMonitor) NULL)
13585          {
13586            MagickBooleanType
13587              proceed;
13588
13589            proceed=SetImageProgress(images,LoadImageTag,(MagickOffsetType) i,
13590              (MagickSizeType) number_files);
13591            if (proceed == MagickFalse)
13592              break;
13593          }
13594      }
13595  }
13596  exception=DestroyExceptionInfo(exception);
13597  filelist=(char **) RelinquishMagickMemory(filelist);
13598  if (images == (Image *) NULL)
13599    {
13600      read_info=DestroyImageInfo(read_info);
13601      XSetCursorState(display,windows,MagickFalse);
13602      ThrowXWindowFatalException(ImageError,"NoImagesWereLoaded",filenames);
13603      return((Image *) NULL);
13604    }
13605  /*
13606    Create the Visual Image Directory.
13607  */
13608  montage_info=CloneMontageInfo(read_info,(MontageInfo *) NULL);
13609  montage_info->pointsize=10;
13610  if (resource_info->font != (char *) NULL)
13611    (void) CloneString(&montage_info->font,resource_info->font);
13612  (void) CopyMagickString(montage_info->filename,filename,MaxTextExtent);
13613  montage_image=MontageImageList(read_info,montage_info,GetFirstImageInList(
13614    images),&images->exception);
13615  images=DestroyImageList(images);
13616  montage_info=DestroyMontageInfo(montage_info);
13617  read_info=DestroyImageInfo(read_info);
13618  XSetCursorState(display,windows,MagickFalse);
13619  if (montage_image == (Image *) NULL)
13620    return(montage_image);
13621  XClientMessage(display,windows->image.id,windows->im_protocols,
13622    windows->im_next_image,CurrentTime);
13623  return(montage_image);
13624}
13625
13626/*
13627%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13628%                                                                             %
13629%                                                                             %
13630%                                                                             %
13631%   X D i s p l a y B a c k g r o u n d I m a g e                             %
13632%                                                                             %
13633%                                                                             %
13634%                                                                             %
13635%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13636%
13637%  XDisplayBackgroundImage() displays an image in the background of a window.
13638%
13639%  The format of the XDisplayBackgroundImage method is:
13640%
13641%      MagickBooleanType XDisplayBackgroundImage(Display *display,
13642%        XResourceInfo *resource_info,Image *image)
13643%
13644%  A description of each parameter follows:
13645%
13646%    o display: Specifies a connection to an X server;  returned from
13647%      XOpenDisplay.
13648%
13649%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13650%
13651%    o image: the image.
13652%
13653*/
13654MagickExport MagickBooleanType XDisplayBackgroundImage(Display *display,
13655  XResourceInfo *resource_info,Image *image)
13656{
13657  char
13658    geometry[MaxTextExtent],
13659    visual_type[MaxTextExtent];
13660
13661  int
13662    height,
13663    status,
13664    width;
13665
13666  RectangleInfo
13667    geometry_info;
13668
13669  static XPixelInfo
13670    pixel;
13671
13672  static XStandardColormap
13673    *map_info;
13674
13675  static XVisualInfo
13676    *visual_info = (XVisualInfo *) NULL;
13677
13678  static XWindowInfo
13679    window_info;
13680
13681  size_t
13682    delay;
13683
13684  Window
13685    root_window;
13686
13687  XGCValues
13688    context_values;
13689
13690  XResourceInfo
13691    resources;
13692
13693  XWindowAttributes
13694    window_attributes;
13695
13696  /*
13697    Determine target window.
13698  */
13699  assert(image != (Image *) NULL);
13700  assert(image->signature == MagickSignature);
13701  if (image->debug != MagickFalse)
13702    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
13703  resources=(*resource_info);
13704  window_info.id=(Window) NULL;
13705  root_window=XRootWindow(display,XDefaultScreen(display));
13706  if (LocaleCompare(resources.window_id,"root") == 0)
13707    window_info.id=root_window;
13708  else
13709    {
13710      if (isdigit((unsigned char) *resources.window_id) != 0)
13711        window_info.id=XWindowByID(display,root_window,
13712          (Window) strtol((char *) resources.window_id,(char **) NULL,0));
13713      if (window_info.id == (Window) NULL)
13714        window_info.id=XWindowByName(display,root_window,resources.window_id);
13715    }
13716  if (window_info.id == (Window) NULL)
13717    {
13718      ThrowXWindowFatalException(XServerError,"NoWindowWithSpecifiedIDExists",
13719        resources.window_id);
13720      return(MagickFalse);
13721    }
13722  /*
13723    Determine window visual id.
13724  */
13725  window_attributes.width=XDisplayWidth(display,XDefaultScreen(display));
13726  window_attributes.height=XDisplayHeight(display,XDefaultScreen(display));
13727  (void) CopyMagickString(visual_type,"default",MaxTextExtent);
13728  status=XGetWindowAttributes(display,window_info.id,&window_attributes);
13729  if (status != 0)
13730    (void) FormatLocaleString(visual_type,MaxTextExtent,"0x%lx",
13731      XVisualIDFromVisual(window_attributes.visual));
13732  if (visual_info == (XVisualInfo *) NULL)
13733    {
13734      /*
13735        Allocate standard colormap.
13736      */
13737      map_info=XAllocStandardColormap();
13738      if (map_info == (XStandardColormap *) NULL)
13739        ThrowXWindowFatalException(XServerFatalError,"MemoryAllocationFailed",
13740          image->filename);
13741      map_info->colormap=(Colormap) NULL;
13742      pixel.pixels=(unsigned long *) NULL;
13743      /*
13744        Initialize visual info.
13745      */
13746      resources.map_type=(char *) NULL;
13747      resources.visual_type=visual_type;
13748      visual_info=XBestVisualInfo(display,map_info,&resources);
13749      if (visual_info == (XVisualInfo *) NULL)
13750        ThrowXWindowFatalException(XServerFatalError,"UnableToGetVisual",
13751          resources.visual_type);
13752      /*
13753        Initialize window info.
13754      */
13755      window_info.ximage=(XImage *) NULL;
13756      window_info.matte_image=(XImage *) NULL;
13757      window_info.pixmap=(Pixmap) NULL;
13758      window_info.matte_pixmap=(Pixmap) NULL;
13759    }
13760  /*
13761    Free previous root colors.
13762  */
13763  if (window_info.id == root_window)
13764    (void) XDestroyWindowColors(display,root_window);
13765  /*
13766    Initialize Standard Colormap.
13767  */
13768  resources.colormap=SharedColormap;
13769  XMakeStandardColormap(display,visual_info,&resources,image,map_info,&pixel);
13770  /*
13771    Graphic context superclass.
13772  */
13773  context_values.background=pixel.background_color.pixel;
13774  context_values.foreground=pixel.foreground_color.pixel;
13775  pixel.annotate_context=XCreateGC(display,window_info.id,
13776    (size_t) (GCBackground | GCForeground),&context_values);
13777  if (pixel.annotate_context == (GC) NULL)
13778    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
13779      image->filename);
13780  /*
13781    Initialize Image window attributes.
13782  */
13783  window_info.name=AcquireString("\0");
13784  window_info.icon_name=AcquireString("\0");
13785  XGetWindowInfo(display,visual_info,map_info,&pixel,(XFontStruct *) NULL,
13786    &resources,&window_info);
13787  /*
13788    Create the X image.
13789  */
13790  window_info.width=(unsigned int) image->columns;
13791  window_info.height=(unsigned int) image->rows;
13792  if ((image->columns != window_info.width) ||
13793      (image->rows != window_info.height))
13794    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13795      image->filename);
13796  (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>",
13797    window_attributes.width,window_attributes.height);
13798  geometry_info.width=window_info.width;
13799  geometry_info.height=window_info.height;
13800  geometry_info.x=(ssize_t) window_info.x;
13801  geometry_info.y=(ssize_t) window_info.y;
13802  (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
13803    &geometry_info.width,&geometry_info.height);
13804  window_info.width=(unsigned int) geometry_info.width;
13805  window_info.height=(unsigned int) geometry_info.height;
13806  window_info.x=(int) geometry_info.x;
13807  window_info.y=(int) geometry_info.y;
13808  status=XMakeImage(display,&resources,&window_info,image,window_info.width,
13809    window_info.height);
13810  if (status == MagickFalse)
13811    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13812      image->filename);
13813  window_info.x=0;
13814  window_info.y=0;
13815  if (image->debug != MagickFalse)
13816    {
13817      (void) LogMagickEvent(X11Event,GetMagickModule(),
13818        "Image: %s[%.20g] %.20gx%.20g ",image->filename,(double) image->scene,
13819        (double) image->columns,(double) image->rows);
13820      if (image->colors != 0)
13821        (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
13822          image->colors);
13823      (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",image->magick);
13824    }
13825  /*
13826    Adjust image dimensions as specified by backdrop or geometry options.
13827  */
13828  width=(int) window_info.width;
13829  height=(int) window_info.height;
13830  if (resources.backdrop != MagickFalse)
13831    {
13832      /*
13833        Center image on window.
13834      */
13835      window_info.x=(window_attributes.width/2)-
13836        (window_info.ximage->width/2);
13837      window_info.y=(window_attributes.height/2)-
13838        (window_info.ximage->height/2);
13839      width=window_attributes.width;
13840      height=window_attributes.height;
13841    }
13842  if ((resources.image_geometry != (char *) NULL) &&
13843      (*resources.image_geometry != '\0'))
13844    {
13845      char
13846        default_geometry[MaxTextExtent];
13847
13848      int
13849        flags,
13850        gravity;
13851
13852      XSizeHints
13853        *size_hints;
13854
13855      /*
13856        User specified geometry.
13857      */
13858      size_hints=XAllocSizeHints();
13859      if (size_hints == (XSizeHints *) NULL)
13860        ThrowXWindowFatalException(ResourceLimitFatalError,
13861          "MemoryAllocationFailed",image->filename);
13862      size_hints->flags=0L;
13863      (void) FormatLocaleString(default_geometry,MaxTextExtent,"%dx%d",
13864        width,height);
13865      flags=XWMGeometry(display,visual_info->screen,resources.image_geometry,
13866        default_geometry,window_info.border_width,size_hints,&window_info.x,
13867        &window_info.y,&width,&height,&gravity);
13868      if (flags & (XValue | YValue))
13869        {
13870          width=window_attributes.width;
13871          height=window_attributes.height;
13872        }
13873      (void) XFree((void *) size_hints);
13874    }
13875  /*
13876    Create the X pixmap.
13877  */
13878  window_info.pixmap=XCreatePixmap(display,window_info.id,(unsigned int) width,
13879    (unsigned int) height,window_info.depth);
13880  if (window_info.pixmap == (Pixmap) NULL)
13881    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXPixmap",
13882      image->filename);
13883  /*
13884    Display pixmap on the window.
13885  */
13886  if (((unsigned int) width > window_info.width) ||
13887      ((unsigned int) height > window_info.height))
13888    (void) XFillRectangle(display,window_info.pixmap,
13889      window_info.annotate_context,0,0,(unsigned int) width,
13890      (unsigned int) height);
13891  (void) XPutImage(display,window_info.pixmap,window_info.annotate_context,
13892    window_info.ximage,0,0,window_info.x,window_info.y,(unsigned int)
13893    window_info.width,(unsigned int) window_info.height);
13894  (void) XSetWindowBackgroundPixmap(display,window_info.id,window_info.pixmap);
13895  (void) XClearWindow(display,window_info.id);
13896  delay=1000*image->delay/MagickMax(image->ticks_per_second,1L);
13897  XDelay(display,delay == 0UL ? 10UL : delay);
13898  (void) XSync(display,MagickFalse);
13899  return(window_info.id == root_window ? MagickTrue : MagickFalse);
13900}
13901
13902/*
13903%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13904%                                                                             %
13905%                                                                             %
13906%                                                                             %
13907+   X D i s p l a y I m a g e                                                 %
13908%                                                                             %
13909%                                                                             %
13910%                                                                             %
13911%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13912%
13913%  XDisplayImage() displays an image via X11.  A new image is created and
13914%  returned if the user interactively transforms the displayed image.
13915%
13916%  The format of the XDisplayImage method is:
13917%
13918%      Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
13919%        char **argv,int argc,Image **image,size_t *state)
13920%
13921%  A description of each parameter follows:
13922%
13923%    o nexus:  Method XDisplayImage returns an image when the
13924%      user chooses 'Open Image' from the command menu or picks a tile
13925%      from the image directory.  Otherwise a null image is returned.
13926%
13927%    o display: Specifies a connection to an X server;  returned from
13928%      XOpenDisplay.
13929%
13930%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13931%
13932%    o argv: Specifies the application's argument list.
13933%
13934%    o argc: Specifies the number of arguments.
13935%
13936%    o image: Specifies an address to an address of an Image structure;
13937%
13938*/
13939MagickExport Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
13940  char **argv,int argc,Image **image,size_t *state)
13941{
13942#define MagnifySize  256  /* must be a power of 2 */
13943#define MagickMenus  10
13944#define MagickTitle  "Commands"
13945
13946  static const char
13947    *CommandMenu[] =
13948    {
13949      "File",
13950      "Edit",
13951      "View",
13952      "Transform",
13953      "Enhance",
13954      "Effects",
13955      "F/X",
13956      "Image Edit",
13957      "Miscellany",
13958      "Help",
13959      (char *) NULL
13960    },
13961    *FileMenu[] =
13962    {
13963      "Open...",
13964      "Next",
13965      "Former",
13966      "Select...",
13967      "Save...",
13968      "Print...",
13969      "Delete...",
13970      "New...",
13971      "Visual Directory...",
13972      "Quit",
13973      (char *) NULL
13974    },
13975    *EditMenu[] =
13976    {
13977      "Undo",
13978      "Redo",
13979      "Cut",
13980      "Copy",
13981      "Paste",
13982      (char *) NULL
13983    },
13984    *ViewMenu[] =
13985    {
13986      "Half Size",
13987      "Original Size",
13988      "Double Size",
13989      "Resize...",
13990      "Apply",
13991      "Refresh",
13992      "Restore",
13993      (char *) NULL
13994    },
13995    *TransformMenu[] =
13996    {
13997      "Crop",
13998      "Chop",
13999      "Flop",
14000      "Flip",
14001      "Rotate Right",
14002      "Rotate Left",
14003      "Rotate...",
14004      "Shear...",
14005      "Roll...",
14006      "Trim Edges",
14007      (char *) NULL
14008    },
14009    *EnhanceMenu[] =
14010    {
14011      "Hue...",
14012      "Saturation...",
14013      "Brightness...",
14014      "Gamma...",
14015      "Spiff",
14016      "Dull",
14017      "Contrast Stretch...",
14018      "Sigmoidal Contrast...",
14019      "Normalize",
14020      "Equalize",
14021      "Negate",
14022      "Grayscale",
14023      "Map...",
14024      "Quantize...",
14025      (char *) NULL
14026    },
14027    *EffectsMenu[] =
14028    {
14029      "Despeckle",
14030      "Emboss",
14031      "Reduce Noise",
14032      "Add Noise...",
14033      "Sharpen...",
14034      "Blur...",
14035      "Threshold...",
14036      "Edge Detect...",
14037      "Spread...",
14038      "Shade...",
14039      "Raise...",
14040      "Segment...",
14041      (char *) NULL
14042    },
14043    *FXMenu[] =
14044    {
14045      "Solarize...",
14046      "Sepia Tone...",
14047      "Swirl...",
14048      "Implode...",
14049      "Vignette...",
14050      "Wave...",
14051      "Oil Paint...",
14052      "Charcoal Draw...",
14053      (char *) NULL
14054    },
14055    *ImageEditMenu[] =
14056    {
14057      "Annotate...",
14058      "Draw...",
14059      "Color...",
14060      "Matte...",
14061      "Composite...",
14062      "Add Border...",
14063      "Add Frame...",
14064      "Comment...",
14065      "Launch...",
14066      "Region of Interest...",
14067      (char *) NULL
14068    },
14069    *MiscellanyMenu[] =
14070    {
14071      "Image Info",
14072      "Zoom Image",
14073      "Show Preview...",
14074      "Show Histogram",
14075      "Show Matte",
14076      "Background...",
14077      "Slide Show...",
14078      "Preferences...",
14079      (char *) NULL
14080    },
14081    *HelpMenu[] =
14082    {
14083      "Overview",
14084      "Browse Documentation",
14085      "About Display",
14086      (char *) NULL
14087    },
14088    *ShortCutsMenu[] =
14089    {
14090      "Next",
14091      "Former",
14092      "Open...",
14093      "Save...",
14094      "Print...",
14095      "Undo",
14096      "Restore",
14097      "Image Info",
14098      "Quit",
14099      (char *) NULL
14100    },
14101    *VirtualMenu[] =
14102    {
14103      "Image Info",
14104      "Print",
14105      "Next",
14106      "Quit",
14107      (char *) NULL
14108    };
14109
14110  static const char
14111    **Menus[MagickMenus] =
14112    {
14113      FileMenu,
14114      EditMenu,
14115      ViewMenu,
14116      TransformMenu,
14117      EnhanceMenu,
14118      EffectsMenu,
14119      FXMenu,
14120      ImageEditMenu,
14121      MiscellanyMenu,
14122      HelpMenu
14123    };
14124
14125  static CommandType
14126    CommandMenus[] =
14127    {
14128      NullCommand,
14129      NullCommand,
14130      NullCommand,
14131      NullCommand,
14132      NullCommand,
14133      NullCommand,
14134      NullCommand,
14135      NullCommand,
14136      NullCommand,
14137      NullCommand,
14138    },
14139    FileCommands[] =
14140    {
14141      OpenCommand,
14142      NextCommand,
14143      FormerCommand,
14144      SelectCommand,
14145      SaveCommand,
14146      PrintCommand,
14147      DeleteCommand,
14148      NewCommand,
14149      VisualDirectoryCommand,
14150      QuitCommand
14151    },
14152    EditCommands[] =
14153    {
14154      UndoCommand,
14155      RedoCommand,
14156      CutCommand,
14157      CopyCommand,
14158      PasteCommand
14159    },
14160    ViewCommands[] =
14161    {
14162      HalfSizeCommand,
14163      OriginalSizeCommand,
14164      DoubleSizeCommand,
14165      ResizeCommand,
14166      ApplyCommand,
14167      RefreshCommand,
14168      RestoreCommand
14169    },
14170    TransformCommands[] =
14171    {
14172      CropCommand,
14173      ChopCommand,
14174      FlopCommand,
14175      FlipCommand,
14176      RotateRightCommand,
14177      RotateLeftCommand,
14178      RotateCommand,
14179      ShearCommand,
14180      RollCommand,
14181      TrimCommand
14182    },
14183    EnhanceCommands[] =
14184    {
14185      HueCommand,
14186      SaturationCommand,
14187      BrightnessCommand,
14188      GammaCommand,
14189      SpiffCommand,
14190      DullCommand,
14191      ContrastStretchCommand,
14192      SigmoidalContrastCommand,
14193      NormalizeCommand,
14194      EqualizeCommand,
14195      NegateCommand,
14196      GrayscaleCommand,
14197      MapCommand,
14198      QuantizeCommand
14199    },
14200    EffectsCommands[] =
14201    {
14202      DespeckleCommand,
14203      EmbossCommand,
14204      ReduceNoiseCommand,
14205      AddNoiseCommand,
14206      SharpenCommand,
14207      BlurCommand,
14208      ThresholdCommand,
14209      EdgeDetectCommand,
14210      SpreadCommand,
14211      ShadeCommand,
14212      RaiseCommand,
14213      SegmentCommand
14214    },
14215    FXCommands[] =
14216    {
14217      SolarizeCommand,
14218      SepiaToneCommand,
14219      SwirlCommand,
14220      ImplodeCommand,
14221      VignetteCommand,
14222      WaveCommand,
14223      OilPaintCommand,
14224      CharcoalDrawCommand
14225    },
14226    ImageEditCommands[] =
14227    {
14228      AnnotateCommand,
14229      DrawCommand,
14230      ColorCommand,
14231      MatteCommand,
14232      CompositeCommand,
14233      AddBorderCommand,
14234      AddFrameCommand,
14235      CommentCommand,
14236      LaunchCommand,
14237      RegionofInterestCommand
14238    },
14239    MiscellanyCommands[] =
14240    {
14241      InfoCommand,
14242      ZoomCommand,
14243      ShowPreviewCommand,
14244      ShowHistogramCommand,
14245      ShowMatteCommand,
14246      BackgroundCommand,
14247      SlideShowCommand,
14248      PreferencesCommand
14249    },
14250    HelpCommands[] =
14251    {
14252      HelpCommand,
14253      BrowseDocumentationCommand,
14254      VersionCommand
14255    },
14256    ShortCutsCommands[] =
14257    {
14258      NextCommand,
14259      FormerCommand,
14260      OpenCommand,
14261      SaveCommand,
14262      PrintCommand,
14263      UndoCommand,
14264      RestoreCommand,
14265      InfoCommand,
14266      QuitCommand
14267    },
14268    VirtualCommands[] =
14269    {
14270      InfoCommand,
14271      PrintCommand,
14272      NextCommand,
14273      QuitCommand
14274    };
14275
14276  static CommandType
14277    *Commands[MagickMenus] =
14278    {
14279      FileCommands,
14280      EditCommands,
14281      ViewCommands,
14282      TransformCommands,
14283      EnhanceCommands,
14284      EffectsCommands,
14285      FXCommands,
14286      ImageEditCommands,
14287      MiscellanyCommands,
14288      HelpCommands
14289    };
14290
14291  char
14292    command[MaxTextExtent],
14293    *directory,
14294    geometry[MaxTextExtent],
14295    resource_name[MaxTextExtent];
14296
14297  CommandType
14298    command_type;
14299
14300  Image
14301    *display_image,
14302    *nexus;
14303
14304  int
14305    entry,
14306    id;
14307
14308  KeySym
14309    key_symbol;
14310
14311  MagickStatusType
14312    context_mask,
14313    status;
14314
14315  RectangleInfo
14316    geometry_info;
14317
14318  register int
14319    i;
14320
14321  static char
14322    working_directory[MaxTextExtent];
14323
14324  static XPoint
14325    vid_info;
14326
14327  static XWindowInfo
14328    *magick_windows[MaxXWindows];
14329
14330  static unsigned int
14331    number_windows;
14332
14333  struct stat
14334    attributes;
14335
14336  time_t
14337    timer,
14338    timestamp,
14339    update_time;
14340
14341  unsigned int
14342    height,
14343    width;
14344
14345  size_t
14346    delay;
14347
14348  WarningHandler
14349    warning_handler;
14350
14351  Window
14352    root_window;
14353
14354  XClassHint
14355    *class_hints;
14356
14357  XEvent
14358    event;
14359
14360  XFontStruct
14361    *font_info;
14362
14363  XGCValues
14364    context_values;
14365
14366  XPixelInfo
14367    *icon_pixel,
14368    *pixel;
14369
14370  XResourceInfo
14371    *icon_resources;
14372
14373  XStandardColormap
14374    *icon_map,
14375    *map_info;
14376
14377  XVisualInfo
14378    *icon_visual,
14379    *visual_info;
14380
14381  XWindowChanges
14382    window_changes;
14383
14384  XWindows
14385    *windows;
14386
14387  XWMHints
14388    *manager_hints;
14389
14390  assert(image != (Image **) NULL);
14391  assert((*image)->signature == MagickSignature);
14392  if ((*image)->debug != MagickFalse)
14393    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*image)->filename);
14394  display_image=(*image);
14395  warning_handler=(WarningHandler) NULL;
14396  windows=XSetWindows((XWindows *) ~0);
14397  if (windows != (XWindows *) NULL)
14398    {
14399      int
14400        status;
14401
14402      status=chdir(working_directory);
14403      if (status == -1)
14404        (void) ThrowMagickException(&(*image)->exception,GetMagickModule(),
14405          FileOpenError,"UnableToOpenFile","%s",working_directory);
14406      warning_handler=resource_info->display_warnings ?
14407        SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
14408      warning_handler=resource_info->display_warnings ?
14409        SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
14410    }
14411  else
14412    {
14413      /*
14414        Allocate windows structure.
14415      */
14416      resource_info->colors=display_image->colors;
14417      windows=XSetWindows(XInitializeWindows(display,resource_info));
14418      if (windows == (XWindows *) NULL)
14419        ThrowXWindowFatalException(XServerFatalError,"UnableToCreateWindow",
14420          (*image)->filename);
14421      /*
14422        Initialize window id's.
14423      */
14424      number_windows=0;
14425      magick_windows[number_windows++]=(&windows->icon);
14426      magick_windows[number_windows++]=(&windows->backdrop);
14427      magick_windows[number_windows++]=(&windows->image);
14428      magick_windows[number_windows++]=(&windows->info);
14429      magick_windows[number_windows++]=(&windows->command);
14430      magick_windows[number_windows++]=(&windows->widget);
14431      magick_windows[number_windows++]=(&windows->popup);
14432      magick_windows[number_windows++]=(&windows->magnify);
14433      magick_windows[number_windows++]=(&windows->pan);
14434      for (i=0; i < (int) number_windows; i++)
14435        magick_windows[i]->id=(Window) NULL;
14436      vid_info.x=0;
14437      vid_info.y=0;
14438    }
14439  /*
14440    Initialize font info.
14441  */
14442  if (windows->font_info != (XFontStruct *) NULL)
14443    (void) XFreeFont(display,windows->font_info);
14444  windows->font_info=XBestFont(display,resource_info,MagickFalse);
14445  if (windows->font_info == (XFontStruct *) NULL)
14446    ThrowXWindowFatalException(XServerFatalError,"UnableToLoadFont",
14447      resource_info->font);
14448  /*
14449    Initialize Standard Colormap.
14450  */
14451  map_info=windows->map_info;
14452  icon_map=windows->icon_map;
14453  visual_info=windows->visual_info;
14454  icon_visual=windows->icon_visual;
14455  pixel=windows->pixel_info;
14456  icon_pixel=windows->icon_pixel;
14457  font_info=windows->font_info;
14458  icon_resources=windows->icon_resources;
14459  class_hints=windows->class_hints;
14460  manager_hints=windows->manager_hints;
14461  root_window=XRootWindow(display,visual_info->screen);
14462  nexus=NewImageList();
14463  if (display_image->debug != MagickFalse)
14464    {
14465      (void) LogMagickEvent(X11Event,GetMagickModule(),
14466        "Image: %s[%.20g] %.20gx%.20g ",display_image->filename,
14467        (double) display_image->scene,(double) display_image->columns,
14468        (double) display_image->rows);
14469      if (display_image->colors != 0)
14470        (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
14471          display_image->colors);
14472      (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",
14473        display_image->magick);
14474    }
14475  XMakeStandardColormap(display,visual_info,resource_info,display_image,
14476    map_info,pixel);
14477  display_image->taint=MagickFalse;
14478  /*
14479    Initialize graphic context.
14480  */
14481  windows->context.id=(Window) NULL;
14482  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14483    resource_info,&windows->context);
14484  (void) CloneString(&class_hints->res_name,resource_info->client_name);
14485  (void) CloneString(&class_hints->res_class,resource_info->client_name);
14486  class_hints->res_class[0]=(char) toupper((int) class_hints->res_class[0]);
14487  manager_hints->flags=InputHint | StateHint;
14488  manager_hints->input=MagickFalse;
14489  manager_hints->initial_state=WithdrawnState;
14490  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14491    &windows->context);
14492  if (display_image->debug != MagickFalse)
14493    (void) LogMagickEvent(X11Event,GetMagickModule(),
14494      "Window id: 0x%lx (context)",windows->context.id);
14495  context_values.background=pixel->background_color.pixel;
14496  context_values.font=font_info->fid;
14497  context_values.foreground=pixel->foreground_color.pixel;
14498  context_values.graphics_exposures=MagickFalse;
14499  context_mask=(MagickStatusType)
14500    (GCBackground | GCFont | GCForeground | GCGraphicsExposures);
14501  if (pixel->annotate_context != (GC) NULL)
14502    (void) XFreeGC(display,pixel->annotate_context);
14503  pixel->annotate_context=XCreateGC(display,windows->context.id,
14504    context_mask,&context_values);
14505  if (pixel->annotate_context == (GC) NULL)
14506    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14507      display_image->filename);
14508  context_values.background=pixel->depth_color.pixel;
14509  if (pixel->widget_context != (GC) NULL)
14510    (void) XFreeGC(display,pixel->widget_context);
14511  pixel->widget_context=XCreateGC(display,windows->context.id,context_mask,
14512    &context_values);
14513  if (pixel->widget_context == (GC) NULL)
14514    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14515      display_image->filename);
14516  context_values.background=pixel->foreground_color.pixel;
14517  context_values.foreground=pixel->background_color.pixel;
14518  context_values.plane_mask=context_values.background ^
14519    context_values.foreground;
14520  if (pixel->highlight_context != (GC) NULL)
14521    (void) XFreeGC(display,pixel->highlight_context);
14522  pixel->highlight_context=XCreateGC(display,windows->context.id,
14523    (size_t) (context_mask | GCPlaneMask),&context_values);
14524  if (pixel->highlight_context == (GC) NULL)
14525    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14526      display_image->filename);
14527  (void) XDestroyWindow(display,windows->context.id);
14528  /*
14529    Initialize icon window.
14530  */
14531  XGetWindowInfo(display,icon_visual,icon_map,icon_pixel,(XFontStruct *) NULL,
14532    icon_resources,&windows->icon);
14533  windows->icon.geometry=resource_info->icon_geometry;
14534  XBestIconSize(display,&windows->icon,display_image);
14535  windows->icon.attributes.colormap=XDefaultColormap(display,
14536    icon_visual->screen);
14537  windows->icon.attributes.event_mask=ExposureMask | StructureNotifyMask;
14538  manager_hints->flags=InputHint | StateHint;
14539  manager_hints->input=MagickFalse;
14540  manager_hints->initial_state=IconicState;
14541  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14542    &windows->icon);
14543  if (display_image->debug != MagickFalse)
14544    (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (icon)",
14545      windows->icon.id);
14546  /*
14547    Initialize graphic context for icon window.
14548  */
14549  if (icon_pixel->annotate_context != (GC) NULL)
14550    (void) XFreeGC(display,icon_pixel->annotate_context);
14551  context_values.background=icon_pixel->background_color.pixel;
14552  context_values.foreground=icon_pixel->foreground_color.pixel;
14553  icon_pixel->annotate_context=XCreateGC(display,windows->icon.id,
14554    (size_t) (GCBackground | GCForeground),&context_values);
14555  if (icon_pixel->annotate_context == (GC) NULL)
14556    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14557      display_image->filename);
14558  windows->icon.annotate_context=icon_pixel->annotate_context;
14559  /*
14560    Initialize Image window.
14561  */
14562  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14563    &windows->image);
14564  windows->image.shape=MagickTrue;  /* non-rectangular shape hint */
14565  if (resource_info->use_shared_memory == MagickFalse)
14566    windows->image.shared_memory=MagickFalse;
14567  if ((resource_info->title != (char *) NULL) && !(*state & MontageImageState))
14568    {
14569      char
14570        *title;
14571
14572      title=InterpretImageProperties(resource_info->image_info,display_image,
14573        resource_info->title);
14574      (void) CopyMagickString(windows->image.name,title,MaxTextExtent);
14575      (void) CopyMagickString(windows->image.icon_name,title,MaxTextExtent);
14576      title=DestroyString(title);
14577    }
14578  else
14579    {
14580      char
14581        filename[MaxTextExtent];
14582
14583      /*
14584        Window name is the base of the filename.
14585      */
14586      GetPathComponent(display_image->magick_filename,TailPath,filename);
14587      if (GetImageListLength(display_image) == 1)
14588        (void) FormatLocaleString(windows->image.name,MaxTextExtent,
14589          "%s: %s",MagickPackageName,filename);
14590      else
14591        (void) FormatLocaleString(windows->image.name,MaxTextExtent,
14592          "%s: %s[scene: %.20g frames: %.20g]",MagickPackageName,filename,
14593          (double) display_image->scene,(double) GetImageListLength(
14594          display_image));
14595      (void) CopyMagickString(windows->image.icon_name,filename,MaxTextExtent);
14596    }
14597  if (resource_info->immutable)
14598    windows->image.immutable=MagickTrue;
14599  windows->image.use_pixmap=resource_info->use_pixmap;
14600  windows->image.geometry=resource_info->image_geometry;
14601  (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>!",
14602    XDisplayWidth(display,visual_info->screen),
14603    XDisplayHeight(display,visual_info->screen));
14604  geometry_info.width=display_image->columns;
14605  geometry_info.height=display_image->rows;
14606  geometry_info.x=0;
14607  geometry_info.y=0;
14608  (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
14609    &geometry_info.width,&geometry_info.height);
14610  windows->image.width=(unsigned int) geometry_info.width;
14611  windows->image.height=(unsigned int) geometry_info.height;
14612  windows->image.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14613    ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14614    KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14615    PropertyChangeMask | StructureNotifyMask | SubstructureNotifyMask;
14616  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14617    resource_info,&windows->backdrop);
14618  if ((resource_info->backdrop) || (windows->backdrop.id != (Window) NULL))
14619    {
14620      /*
14621        Initialize backdrop window.
14622      */
14623      windows->backdrop.x=0;
14624      windows->backdrop.y=0;
14625      (void) CloneString(&windows->backdrop.name,"Backdrop");
14626      windows->backdrop.flags=(size_t) (USSize | USPosition);
14627      windows->backdrop.width=(unsigned int)
14628        XDisplayWidth(display,visual_info->screen);
14629      windows->backdrop.height=(unsigned int)
14630        XDisplayHeight(display,visual_info->screen);
14631      windows->backdrop.border_width=0;
14632      windows->backdrop.immutable=MagickTrue;
14633      windows->backdrop.attributes.do_not_propagate_mask=ButtonPressMask |
14634        ButtonReleaseMask;
14635      windows->backdrop.attributes.event_mask=ButtonPressMask | KeyPressMask |
14636        StructureNotifyMask;
14637      manager_hints->flags=IconWindowHint | InputHint | StateHint;
14638      manager_hints->icon_window=windows->icon.id;
14639      manager_hints->input=MagickTrue;
14640      manager_hints->initial_state=resource_info->iconic ? IconicState :
14641        NormalState;
14642      XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14643        &windows->backdrop);
14644      if (display_image->debug != MagickFalse)
14645        (void) LogMagickEvent(X11Event,GetMagickModule(),
14646          "Window id: 0x%lx (backdrop)",windows->backdrop.id);
14647      (void) XMapWindow(display,windows->backdrop.id);
14648      (void) XClearWindow(display,windows->backdrop.id);
14649      if (windows->image.id != (Window) NULL)
14650        {
14651          (void) XDestroyWindow(display,windows->image.id);
14652          windows->image.id=(Window) NULL;
14653        }
14654      /*
14655        Position image in the center the backdrop.
14656      */
14657      windows->image.flags|=USPosition;
14658      windows->image.x=(XDisplayWidth(display,visual_info->screen)/2)-
14659        (windows->image.width/2);
14660      windows->image.y=(XDisplayHeight(display,visual_info->screen)/2)-
14661        (windows->image.height/2);
14662    }
14663  manager_hints->flags=IconWindowHint | InputHint | StateHint;
14664  manager_hints->icon_window=windows->icon.id;
14665  manager_hints->input=MagickTrue;
14666  manager_hints->initial_state=resource_info->iconic ? IconicState :
14667    NormalState;
14668  if (windows->group_leader.id != (Window) NULL)
14669    {
14670      /*
14671        Follow the leader.
14672      */
14673      manager_hints->flags|=WindowGroupHint;
14674      manager_hints->window_group=windows->group_leader.id;
14675      (void) XSelectInput(display,windows->group_leader.id,StructureNotifyMask);
14676      if (display_image->debug != MagickFalse)
14677        (void) LogMagickEvent(X11Event,GetMagickModule(),
14678          "Window id: 0x%lx (group leader)",windows->group_leader.id);
14679    }
14680  XMakeWindow(display,
14681    (Window) (resource_info->backdrop ? windows->backdrop.id : root_window),
14682    argv,argc,class_hints,manager_hints,&windows->image);
14683  (void) XChangeProperty(display,windows->image.id,windows->im_protocols,
14684    XA_STRING,8,PropModeReplace,(unsigned char *) NULL,0);
14685  if (windows->group_leader.id != (Window) NULL)
14686    (void) XSetTransientForHint(display,windows->image.id,
14687      windows->group_leader.id);
14688  if (display_image->debug != MagickFalse)
14689    (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (image)",
14690      windows->image.id);
14691  /*
14692    Initialize Info widget.
14693  */
14694  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14695    &windows->info);
14696  (void) CloneString(&windows->info.name,"Info");
14697  (void) CloneString(&windows->info.icon_name,"Info");
14698  windows->info.border_width=1;
14699  windows->info.x=2;
14700  windows->info.y=2;
14701  windows->info.flags|=PPosition;
14702  windows->info.attributes.win_gravity=UnmapGravity;
14703  windows->info.attributes.event_mask=ButtonPressMask | ExposureMask |
14704    StructureNotifyMask;
14705  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14706  manager_hints->input=MagickFalse;
14707  manager_hints->initial_state=NormalState;
14708  manager_hints->window_group=windows->image.id;
14709  XMakeWindow(display,windows->image.id,argv,argc,class_hints,manager_hints,
14710    &windows->info);
14711  windows->info.highlight_stipple=XCreateBitmapFromData(display,
14712    windows->info.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14713  windows->info.shadow_stipple=XCreateBitmapFromData(display,
14714    windows->info.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14715  (void) XSetTransientForHint(display,windows->info.id,windows->image.id);
14716  if (windows->image.mapped != MagickFalse)
14717    (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14718  if (display_image->debug != MagickFalse)
14719    (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (info)",
14720      windows->info.id);
14721  /*
14722    Initialize Command widget.
14723  */
14724  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14725    resource_info,&windows->command);
14726  windows->command.data=MagickMenus;
14727  (void) XCommandWidget(display,windows,CommandMenu,(XEvent *) NULL);
14728  (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.command",
14729    resource_info->client_name);
14730  windows->command.geometry=XGetResourceClass(resource_info->resource_database,
14731    resource_name,"geometry",(char *) NULL);
14732  (void) CloneString(&windows->command.name,MagickTitle);
14733  windows->command.border_width=0;
14734  windows->command.flags|=PPosition;
14735  windows->command.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14736    ButtonReleaseMask | EnterWindowMask | ExposureMask | LeaveWindowMask |
14737    OwnerGrabButtonMask | StructureNotifyMask;
14738  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14739  manager_hints->input=MagickTrue;
14740  manager_hints->initial_state=NormalState;
14741  manager_hints->window_group=windows->image.id;
14742  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14743    &windows->command);
14744  windows->command.highlight_stipple=XCreateBitmapFromData(display,
14745    windows->command.id,(char *) HighlightBitmap,HighlightWidth,
14746    HighlightHeight);
14747  windows->command.shadow_stipple=XCreateBitmapFromData(display,
14748    windows->command.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14749  (void) XSetTransientForHint(display,windows->command.id,windows->image.id);
14750  if (windows->command.mapped != MagickFalse)
14751    (void) XMapRaised(display,windows->command.id);
14752  if (display_image->debug != MagickFalse)
14753    (void) LogMagickEvent(X11Event,GetMagickModule(),
14754      "Window id: 0x%lx (command)",windows->command.id);
14755  /*
14756    Initialize Widget window.
14757  */
14758  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14759    resource_info,&windows->widget);
14760  (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.widget",
14761    resource_info->client_name);
14762  windows->widget.geometry=XGetResourceClass(resource_info->resource_database,
14763    resource_name,"geometry",(char *) NULL);
14764  windows->widget.border_width=0;
14765  windows->widget.flags|=PPosition;
14766  windows->widget.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14767    ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14768    KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14769    StructureNotifyMask;
14770  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14771  manager_hints->input=MagickTrue;
14772  manager_hints->initial_state=NormalState;
14773  manager_hints->window_group=windows->image.id;
14774  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14775    &windows->widget);
14776  windows->widget.highlight_stipple=XCreateBitmapFromData(display,
14777    windows->widget.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14778  windows->widget.shadow_stipple=XCreateBitmapFromData(display,
14779    windows->widget.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14780  (void) XSetTransientForHint(display,windows->widget.id,windows->image.id);
14781  if (display_image->debug != MagickFalse)
14782    (void) LogMagickEvent(X11Event,GetMagickModule(),
14783      "Window id: 0x%lx (widget)",windows->widget.id);
14784  /*
14785    Initialize popup window.
14786  */
14787  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14788    resource_info,&windows->popup);
14789  windows->popup.border_width=0;
14790  windows->popup.flags|=PPosition;
14791  windows->popup.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14792    ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14793    KeyReleaseMask | LeaveWindowMask | StructureNotifyMask;
14794  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14795  manager_hints->input=MagickTrue;
14796  manager_hints->initial_state=NormalState;
14797  manager_hints->window_group=windows->image.id;
14798  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14799    &windows->popup);
14800  windows->popup.highlight_stipple=XCreateBitmapFromData(display,
14801    windows->popup.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14802  windows->popup.shadow_stipple=XCreateBitmapFromData(display,
14803    windows->popup.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14804  (void) XSetTransientForHint(display,windows->popup.id,windows->image.id);
14805  if (display_image->debug != MagickFalse)
14806    (void) LogMagickEvent(X11Event,GetMagickModule(),
14807      "Window id: 0x%lx (pop up)",windows->popup.id);
14808  /*
14809    Initialize Magnify window and cursor.
14810  */
14811  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14812    resource_info,&windows->magnify);
14813  if (resource_info->use_shared_memory == MagickFalse)
14814    windows->magnify.shared_memory=MagickFalse;
14815  (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.magnify",
14816    resource_info->client_name);
14817  windows->magnify.geometry=XGetResourceClass(resource_info->resource_database,
14818    resource_name,"geometry",(char *) NULL);
14819  (void) FormatLocaleString(windows->magnify.name,MaxTextExtent,"Magnify %uX",
14820    resource_info->magnify);
14821  if (windows->magnify.cursor != (Cursor) NULL)
14822    (void) XFreeCursor(display,windows->magnify.cursor);
14823  windows->magnify.cursor=XMakeCursor(display,windows->image.id,
14824    map_info->colormap,resource_info->background_color,
14825    resource_info->foreground_color);
14826  if (windows->magnify.cursor == (Cursor) NULL)
14827    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateCursor",
14828      display_image->filename);
14829  windows->magnify.width=MagnifySize;
14830  windows->magnify.height=MagnifySize;
14831  windows->magnify.flags|=PPosition;
14832  windows->magnify.min_width=MagnifySize;
14833  windows->magnify.min_height=MagnifySize;
14834  windows->magnify.width_inc=MagnifySize;
14835  windows->magnify.height_inc=MagnifySize;
14836  windows->magnify.data=resource_info->magnify;
14837  windows->magnify.attributes.cursor=windows->magnify.cursor;
14838  windows->magnify.attributes.event_mask=ButtonPressMask | ButtonReleaseMask |
14839    ExposureMask | KeyPressMask | KeyReleaseMask | OwnerGrabButtonMask |
14840    StructureNotifyMask;
14841  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14842  manager_hints->input=MagickTrue;
14843  manager_hints->initial_state=NormalState;
14844  manager_hints->window_group=windows->image.id;
14845  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14846    &windows->magnify);
14847  if (display_image->debug != MagickFalse)
14848    (void) LogMagickEvent(X11Event,GetMagickModule(),
14849      "Window id: 0x%lx (magnify)",windows->magnify.id);
14850  (void) XSetTransientForHint(display,windows->magnify.id,windows->image.id);
14851  /*
14852    Initialize panning window.
14853  */
14854  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14855    resource_info,&windows->pan);
14856  (void) CloneString(&windows->pan.name,"Pan Icon");
14857  windows->pan.width=windows->icon.width;
14858  windows->pan.height=windows->icon.height;
14859  (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.pan",
14860    resource_info->client_name);
14861  windows->pan.geometry=XGetResourceClass(resource_info->resource_database,
14862    resource_name,"geometry",(char *) NULL);
14863  (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
14864    &windows->pan.width,&windows->pan.height);
14865  windows->pan.flags|=PPosition;
14866  windows->pan.immutable=MagickTrue;
14867  windows->pan.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14868    ButtonReleaseMask | ExposureMask | KeyPressMask | KeyReleaseMask |
14869    StructureNotifyMask;
14870  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14871  manager_hints->input=MagickFalse;
14872  manager_hints->initial_state=NormalState;
14873  manager_hints->window_group=windows->image.id;
14874  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14875    &windows->pan);
14876  if (display_image->debug != MagickFalse)
14877    (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (pan)",
14878      windows->pan.id);
14879  (void) XSetTransientForHint(display,windows->pan.id,windows->image.id);
14880  if (windows->info.mapped != MagickFalse)
14881    (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14882  if ((windows->image.mapped == MagickFalse) ||
14883      (windows->backdrop.id != (Window) NULL))
14884    (void) XMapWindow(display,windows->image.id);
14885  /*
14886    Set our progress monitor and warning handlers.
14887  */
14888  if (warning_handler == (WarningHandler) NULL)
14889    {
14890      warning_handler=resource_info->display_warnings ?
14891        SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
14892      warning_handler=resource_info->display_warnings ?
14893        SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
14894    }
14895  /*
14896    Initialize Image and Magnify X images.
14897  */
14898  windows->image.x=0;
14899  windows->image.y=0;
14900  windows->magnify.shape=MagickFalse;
14901  width=(unsigned int) display_image->columns;
14902  height=(unsigned int) display_image->rows;
14903  if ((display_image->columns != width) || (display_image->rows != height))
14904    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
14905      display_image->filename);
14906  status=XMakeImage(display,resource_info,&windows->image,display_image,
14907    width,height);
14908  if (status == MagickFalse)
14909    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
14910      display_image->filename);
14911  status=XMakeImage(display,resource_info,&windows->magnify,(Image *) NULL,
14912    windows->magnify.width,windows->magnify.height);
14913  if (status == MagickFalse)
14914    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
14915      display_image->filename);
14916  if (windows->magnify.mapped != MagickFalse)
14917    (void) XMapRaised(display,windows->magnify.id);
14918  if (windows->pan.mapped != MagickFalse)
14919    (void) XMapRaised(display,windows->pan.id);
14920  windows->image.window_changes.width=(int) display_image->columns;
14921  windows->image.window_changes.height=(int) display_image->rows;
14922  (void) XConfigureImage(display,resource_info,windows,display_image);
14923  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14924  (void) XSync(display,MagickFalse);
14925  /*
14926    Respond to events.
14927  */
14928  delay=display_image->delay/MagickMax(display_image->ticks_per_second,1L);
14929  timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
14930  update_time=0;
14931  if (resource_info->update != MagickFalse)
14932    {
14933      MagickBooleanType
14934        status;
14935
14936      /*
14937        Determine when file data was last modified.
14938      */
14939      status=GetPathAttributes(display_image->filename,&attributes);
14940      if (status != MagickFalse)
14941        update_time=attributes.st_mtime;
14942    }
14943  *state&=(~FormerImageState);
14944  *state&=(~MontageImageState);
14945  *state&=(~NextImageState);
14946  do
14947  {
14948    /*
14949      Handle a window event.
14950    */
14951    if (windows->image.mapped != MagickFalse)
14952      if ((display_image->delay != 0) || (resource_info->update != 0))
14953        {
14954          if (timer < time((time_t *) NULL))
14955            {
14956              if (resource_info->update == MagickFalse)
14957                *state|=NextImageState | ExitState;
14958              else
14959                {
14960                  MagickBooleanType
14961                    status;
14962
14963                  /*
14964                    Determine if image file was modified.
14965                  */
14966                  status=GetPathAttributes(display_image->filename,&attributes);
14967                  if (status != MagickFalse)
14968                    if (update_time != attributes.st_mtime)
14969                      {
14970                        /*
14971                          Redisplay image.
14972                        */
14973                        (void) FormatLocaleString(
14974                          resource_info->image_info->filename,MaxTextExtent,
14975                          "%s:%s",display_image->magick,
14976                          display_image->filename);
14977                        nexus=ReadImage(resource_info->image_info,
14978                          &display_image->exception);
14979                        if (nexus != (Image *) NULL)
14980                          {
14981                            nexus=DestroyImage(nexus);
14982                            *state|=NextImageState | ExitState;
14983                          }
14984                      }
14985                  delay=display_image->delay/MagickMax(
14986                    display_image->ticks_per_second,1L);
14987                  timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
14988                }
14989            }
14990          if (XEventsQueued(display,QueuedAfterFlush) == 0)
14991            {
14992              /*
14993                Do not block if delay > 0.
14994              */
14995              XDelay(display,SuspendTime << 2);
14996              continue;
14997            }
14998        }
14999    timestamp=time((time_t *) NULL);
15000    (void) XNextEvent(display,&event);
15001    if (windows->image.stasis == MagickFalse)
15002      windows->image.stasis=(time((time_t *) NULL)-timestamp) > 0 ?
15003        MagickTrue : MagickFalse;
15004    if (windows->magnify.stasis == MagickFalse)
15005      windows->magnify.stasis=(time((time_t *) NULL)-timestamp) > 0 ?
15006        MagickTrue : MagickFalse;
15007    if (event.xany.window == windows->command.id)
15008      {
15009        /*
15010          Select a command from the Command widget.
15011        */
15012        id=XCommandWidget(display,windows,CommandMenu,&event);
15013        if (id < 0)
15014          continue;
15015        (void) CopyMagickString(command,CommandMenu[id],MaxTextExtent);
15016        command_type=CommandMenus[id];
15017        if (id < MagickMenus)
15018          {
15019            /*
15020              Select a command from a pop-up menu.
15021            */
15022            entry=XMenuWidget(display,windows,CommandMenu[id],Menus[id],
15023              command);
15024            if (entry < 0)
15025              continue;
15026            (void) CopyMagickString(command,Menus[id][entry],MaxTextExtent);
15027            command_type=Commands[id][entry];
15028          }
15029        if (command_type != NullCommand)
15030          nexus=XMagickCommand(display,resource_info,windows,command_type,
15031            &display_image);
15032        continue;
15033      }
15034    switch (event.type)
15035    {
15036      case ButtonPress:
15037      {
15038        if (display_image->debug != MagickFalse)
15039          (void) LogMagickEvent(X11Event,GetMagickModule(),
15040            "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
15041            event.xbutton.button,event.xbutton.x,event.xbutton.y);
15042        if ((event.xbutton.button == Button3) &&
15043            (event.xbutton.state & Mod1Mask))
15044          {
15045            /*
15046              Convert Alt-Button3 to Button2.
15047            */
15048            event.xbutton.button=Button2;
15049            event.xbutton.state&=(~Mod1Mask);
15050          }
15051        if (event.xbutton.window == windows->backdrop.id)
15052          {
15053            (void) XSetInputFocus(display,event.xbutton.window,RevertToParent,
15054              event.xbutton.time);
15055            break;
15056          }
15057        if (event.xbutton.window == windows->image.id)
15058          {
15059            switch (event.xbutton.button)
15060            {
15061              case Button1:
15062              {
15063                if (resource_info->immutable)
15064                  {
15065                    /*
15066                      Select a command from the Virtual menu.
15067                    */
15068                    entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15069                      command);
15070                    if (entry >= 0)
15071                      nexus=XMagickCommand(display,resource_info,windows,
15072                        VirtualCommands[entry],&display_image);
15073                    break;
15074                  }
15075                /*
15076                  Map/unmap Command widget.
15077                */
15078                if (windows->command.mapped != MagickFalse)
15079                  (void) XWithdrawWindow(display,windows->command.id,
15080                    windows->command.screen);
15081                else
15082                  {
15083                    (void) XCommandWidget(display,windows,CommandMenu,
15084                      (XEvent *) NULL);
15085                    (void) XMapRaised(display,windows->command.id);
15086                  }
15087                break;
15088              }
15089              case Button2:
15090              {
15091                /*
15092                  User pressed the image magnify button.
15093                */
15094                (void) XMagickCommand(display,resource_info,windows,ZoomCommand,
15095                  &display_image);
15096                XMagnifyImage(display,windows,&event);
15097                break;
15098              }
15099              case Button3:
15100              {
15101                if (resource_info->immutable)
15102                  {
15103                    /*
15104                      Select a command from the Virtual menu.
15105                    */
15106                    entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15107                      command);
15108                    if (entry >= 0)
15109                      nexus=XMagickCommand(display,resource_info,windows,
15110                        VirtualCommands[entry],&display_image);
15111                    break;
15112                  }
15113                if (display_image->montage != (char *) NULL)
15114                  {
15115                    /*
15116                      Open or delete a tile from a visual image directory.
15117                    */
15118                    nexus=XTileImage(display,resource_info,windows,
15119                      display_image,&event);
15120                    if (nexus != (Image *) NULL)
15121                      *state|=MontageImageState | NextImageState | ExitState;
15122                    vid_info.x=(short int) windows->image.x;
15123                    vid_info.y=(short int) windows->image.y;
15124                    break;
15125                  }
15126                /*
15127                  Select a command from the Short Cuts menu.
15128                */
15129                entry=XMenuWidget(display,windows,"Short Cuts",ShortCutsMenu,
15130                  command);
15131                if (entry >= 0)
15132                  nexus=XMagickCommand(display,resource_info,windows,
15133                    ShortCutsCommands[entry],&display_image);
15134                break;
15135              }
15136              case Button4:
15137              {
15138                /*
15139                  Wheel up.
15140                */
15141                XTranslateImage(display,windows,*image,XK_Up);
15142                break;
15143              }
15144              case Button5:
15145              {
15146                /*
15147                  Wheel down.
15148                */
15149                XTranslateImage(display,windows,*image,XK_Down);
15150                break;
15151              }
15152              default:
15153                break;
15154            }
15155            break;
15156          }
15157        if (event.xbutton.window == windows->magnify.id)
15158          {
15159            int
15160              factor;
15161
15162            static const char
15163              *MagnifyMenu[] =
15164              {
15165                "2",
15166                "4",
15167                "5",
15168                "6",
15169                "7",
15170                "8",
15171                "9",
15172                "3",
15173                (char *) NULL,
15174              };
15175
15176            static KeySym
15177              MagnifyCommands[] =
15178              {
15179                XK_2,
15180                XK_4,
15181                XK_5,
15182                XK_6,
15183                XK_7,
15184                XK_8,
15185                XK_9,
15186                XK_3
15187              };
15188
15189            /*
15190              Select a magnify factor from the pop-up menu.
15191            */
15192            factor=XMenuWidget(display,windows,"Magnify",MagnifyMenu,command);
15193            if (factor >= 0)
15194              XMagnifyWindowCommand(display,windows,0,MagnifyCommands[factor]);
15195            break;
15196          }
15197        if (event.xbutton.window == windows->pan.id)
15198          {
15199            switch (event.xbutton.button)
15200            {
15201              case Button4:
15202              {
15203                /*
15204                  Wheel up.
15205                */
15206                XTranslateImage(display,windows,*image,XK_Up);
15207                break;
15208              }
15209              case Button5:
15210              {
15211                /*
15212                  Wheel down.
15213                */
15214                XTranslateImage(display,windows,*image,XK_Down);
15215                break;
15216              }
15217              default:
15218              {
15219                XPanImage(display,windows,&event);
15220                break;
15221              }
15222            }
15223            break;
15224          }
15225        delay=display_image->delay/MagickMax(display_image->ticks_per_second,
15226          1L);
15227        timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15228        break;
15229      }
15230      case ButtonRelease:
15231      {
15232        if (display_image->debug != MagickFalse)
15233          (void) LogMagickEvent(X11Event,GetMagickModule(),
15234            "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
15235            event.xbutton.button,event.xbutton.x,event.xbutton.y);
15236        break;
15237      }
15238      case ClientMessage:
15239      {
15240        if (display_image->debug != MagickFalse)
15241          (void) LogMagickEvent(X11Event,GetMagickModule(),
15242            "Client Message: 0x%lx 0x%lx %d 0x%lx",event.xclient.window,
15243            event.xclient.message_type,event.xclient.format,(unsigned long)
15244            event.xclient.data.l[0]);
15245        if (event.xclient.message_type == windows->im_protocols)
15246          {
15247            if (*event.xclient.data.l == (long) windows->im_update_widget)
15248              {
15249                (void) CloneString(&windows->command.name,MagickTitle);
15250                windows->command.data=MagickMenus;
15251                (void) XCommandWidget(display,windows,CommandMenu,
15252                  (XEvent *) NULL);
15253                break;
15254              }
15255            if (*event.xclient.data.l == (long) windows->im_update_colormap)
15256              {
15257                /*
15258                  Update graphic context and window colormap.
15259                */
15260                for (i=0; i < (int) number_windows; i++)
15261                {
15262                  if (magick_windows[i]->id == windows->icon.id)
15263                    continue;
15264                  context_values.background=pixel->background_color.pixel;
15265                  context_values.foreground=pixel->foreground_color.pixel;
15266                  (void) XChangeGC(display,magick_windows[i]->annotate_context,
15267                    context_mask,&context_values);
15268                  (void) XChangeGC(display,magick_windows[i]->widget_context,
15269                    context_mask,&context_values);
15270                  context_values.background=pixel->foreground_color.pixel;
15271                  context_values.foreground=pixel->background_color.pixel;
15272                  context_values.plane_mask=context_values.background ^
15273                    context_values.foreground;
15274                  (void) XChangeGC(display,magick_windows[i]->highlight_context,
15275                    (size_t) (context_mask | GCPlaneMask),
15276                    &context_values);
15277                  magick_windows[i]->attributes.background_pixel=
15278                    pixel->background_color.pixel;
15279                  magick_windows[i]->attributes.border_pixel=
15280                    pixel->border_color.pixel;
15281                  magick_windows[i]->attributes.colormap=map_info->colormap;
15282                  (void) XChangeWindowAttributes(display,magick_windows[i]->id,
15283                    (unsigned long) magick_windows[i]->mask,
15284                    &magick_windows[i]->attributes);
15285                }
15286                if (windows->pan.mapped != MagickFalse)
15287                  {
15288                    (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
15289                      windows->pan.pixmap);
15290                    (void) XClearWindow(display,windows->pan.id);
15291                    XDrawPanRectangle(display,windows);
15292                  }
15293                if (windows->backdrop.id != (Window) NULL)
15294                  (void) XInstallColormap(display,map_info->colormap);
15295                break;
15296              }
15297            if (*event.xclient.data.l == (long) windows->im_former_image)
15298              {
15299                *state|=FormerImageState | ExitState;
15300                break;
15301              }
15302            if (*event.xclient.data.l == (long) windows->im_next_image)
15303              {
15304                *state|=NextImageState | ExitState;
15305                break;
15306              }
15307            if (*event.xclient.data.l == (long) windows->im_retain_colors)
15308              {
15309                *state|=RetainColorsState;
15310                break;
15311              }
15312            if (*event.xclient.data.l == (long) windows->im_exit)
15313              {
15314                *state|=ExitState;
15315                break;
15316              }
15317            break;
15318          }
15319        if (event.xclient.message_type == windows->dnd_protocols)
15320          {
15321            Atom
15322              selection,
15323              type;
15324
15325            int
15326              format,
15327              status;
15328
15329            unsigned char
15330              *data;
15331
15332            unsigned long
15333              after,
15334              length;
15335
15336            /*
15337              Display image named by the Drag-and-Drop selection.
15338            */
15339            if ((*event.xclient.data.l != 2) && (*event.xclient.data.l != 128))
15340              break;
15341            selection=XInternAtom(display,"DndSelection",MagickFalse);
15342            status=XGetWindowProperty(display,root_window,selection,0L,(long)
15343              MaxTextExtent,MagickFalse,(Atom) AnyPropertyType,&type,&format,
15344              &length,&after,&data);
15345            if ((status != Success) || (length == 0))
15346              break;
15347            if (*event.xclient.data.l == 2)
15348              {
15349                /*
15350                  Offix DND.
15351                */
15352                (void) CopyMagickString(resource_info->image_info->filename,
15353                  (char *) data,MaxTextExtent);
15354              }
15355            else
15356              {
15357                /*
15358                  XDND.
15359                */
15360                if (strncmp((char *) data, "file:", 5) != 0)
15361                  {
15362                    (void) XFree((void *) data);
15363                    break;
15364                  }
15365                (void) CopyMagickString(resource_info->image_info->filename,
15366                  ((char *) data)+5,MaxTextExtent);
15367              }
15368            nexus=ReadImage(resource_info->image_info,
15369              &display_image->exception);
15370            CatchException(&display_image->exception);
15371            if (nexus != (Image *) NULL)
15372              *state|=NextImageState | ExitState;
15373            (void) XFree((void *) data);
15374            break;
15375          }
15376        /*
15377          If client window delete message, exit.
15378        */
15379        if (event.xclient.message_type != windows->wm_protocols)
15380          break;
15381        if (*event.xclient.data.l != (long) windows->wm_delete_window)
15382          break;
15383        (void) XWithdrawWindow(display,event.xclient.window,
15384          visual_info->screen);
15385        if (event.xclient.window == windows->image.id)
15386          {
15387            *state|=ExitState;
15388            break;
15389          }
15390        if (event.xclient.window == windows->pan.id)
15391          {
15392            /*
15393              Restore original image size when pan window is deleted.
15394            */
15395            windows->image.window_changes.width=windows->image.ximage->width;
15396            windows->image.window_changes.height=windows->image.ximage->height;
15397            (void) XConfigureImage(display,resource_info,windows,
15398              display_image);
15399          }
15400        break;
15401      }
15402      case ConfigureNotify:
15403      {
15404        if (display_image->debug != MagickFalse)
15405          (void) LogMagickEvent(X11Event,GetMagickModule(),
15406            "Configure Notify: 0x%lx %dx%d+%d+%d %d",event.xconfigure.window,
15407            event.xconfigure.width,event.xconfigure.height,event.xconfigure.x,
15408            event.xconfigure.y,event.xconfigure.send_event);
15409        if (event.xconfigure.window == windows->image.id)
15410          {
15411            /*
15412              Image window has a new configuration.
15413            */
15414            if (event.xconfigure.send_event != 0)
15415              {
15416                XWindowChanges
15417                  window_changes;
15418
15419                /*
15420                  Position the transient windows relative of the Image window.
15421                */
15422                if (windows->command.geometry == (char *) NULL)
15423                  if (windows->command.mapped == MagickFalse)
15424                    {
15425                      windows->command.x=event.xconfigure.x-
15426                        windows->command.width-25;
15427                      windows->command.y=event.xconfigure.y;
15428                      XConstrainWindowPosition(display,&windows->command);
15429                      window_changes.x=windows->command.x;
15430                      window_changes.y=windows->command.y;
15431                      (void) XReconfigureWMWindow(display,windows->command.id,
15432                        windows->command.screen,(unsigned int) (CWX | CWY),
15433                        &window_changes);
15434                    }
15435                if (windows->widget.geometry == (char *) NULL)
15436                  if (windows->widget.mapped == MagickFalse)
15437                    {
15438                      windows->widget.x=event.xconfigure.x+
15439                        event.xconfigure.width/10;
15440                      windows->widget.y=event.xconfigure.y+
15441                        event.xconfigure.height/10;
15442                      XConstrainWindowPosition(display,&windows->widget);
15443                      window_changes.x=windows->widget.x;
15444                      window_changes.y=windows->widget.y;
15445                      (void) XReconfigureWMWindow(display,windows->widget.id,
15446                        windows->widget.screen,(unsigned int) (CWX | CWY),
15447                        &window_changes);
15448                    }
15449                if (windows->magnify.geometry == (char *) NULL)
15450                  if (windows->magnify.mapped == MagickFalse)
15451                    {
15452                      windows->magnify.x=event.xconfigure.x+
15453                        event.xconfigure.width+25;
15454                      windows->magnify.y=event.xconfigure.y;
15455                      XConstrainWindowPosition(display,&windows->magnify);
15456                      window_changes.x=windows->magnify.x;
15457                      window_changes.y=windows->magnify.y;
15458                      (void) XReconfigureWMWindow(display,windows->magnify.id,
15459                        windows->magnify.screen,(unsigned int) (CWX | CWY),
15460                        &window_changes);
15461                    }
15462                if (windows->pan.geometry == (char *) NULL)
15463                  if (windows->pan.mapped == MagickFalse)
15464                    {
15465                      windows->pan.x=event.xconfigure.x+
15466                        event.xconfigure.width+25;
15467                      windows->pan.y=event.xconfigure.y+
15468                        windows->magnify.height+50;
15469                      XConstrainWindowPosition(display,&windows->pan);
15470                      window_changes.x=windows->pan.x;
15471                      window_changes.y=windows->pan.y;
15472                      (void) XReconfigureWMWindow(display,windows->pan.id,
15473                        windows->pan.screen,(unsigned int) (CWX | CWY),
15474                        &window_changes);
15475                    }
15476              }
15477            if ((event.xconfigure.width == (int) windows->image.width) &&
15478                (event.xconfigure.height == (int) windows->image.height))
15479              break;
15480            windows->image.width=(unsigned int) event.xconfigure.width;
15481            windows->image.height=(unsigned int) event.xconfigure.height;
15482            windows->image.x=0;
15483            windows->image.y=0;
15484            if (display_image->montage != (char *) NULL)
15485              {
15486                windows->image.x=vid_info.x;
15487                windows->image.y=vid_info.y;
15488              }
15489            if ((windows->image.mapped != MagickFalse) &&
15490                (windows->image.stasis != MagickFalse))
15491              {
15492                /*
15493                  Update image window configuration.
15494                */
15495                windows->image.window_changes.width=event.xconfigure.width;
15496                windows->image.window_changes.height=event.xconfigure.height;
15497                (void) XConfigureImage(display,resource_info,windows,
15498                  display_image);
15499              }
15500            /*
15501              Update pan window configuration.
15502            */
15503            if ((event.xconfigure.width < windows->image.ximage->width) ||
15504                (event.xconfigure.height < windows->image.ximage->height))
15505              {
15506                (void) XMapRaised(display,windows->pan.id);
15507                XDrawPanRectangle(display,windows);
15508              }
15509            else
15510              if (windows->pan.mapped != MagickFalse)
15511                (void) XWithdrawWindow(display,windows->pan.id,
15512                  windows->pan.screen);
15513            break;
15514          }
15515        if (event.xconfigure.window == windows->magnify.id)
15516          {
15517            unsigned int
15518              magnify;
15519
15520            /*
15521              Magnify window has a new configuration.
15522            */
15523            windows->magnify.width=(unsigned int) event.xconfigure.width;
15524            windows->magnify.height=(unsigned int) event.xconfigure.height;
15525            if (windows->magnify.mapped == MagickFalse)
15526              break;
15527            magnify=1;
15528            while ((int) magnify <= event.xconfigure.width)
15529              magnify<<=1;
15530            while ((int) magnify <= event.xconfigure.height)
15531              magnify<<=1;
15532            magnify>>=1;
15533            if (((int) magnify != event.xconfigure.width) ||
15534                ((int) magnify != event.xconfigure.height))
15535              {
15536                window_changes.width=(int) magnify;
15537                window_changes.height=(int) magnify;
15538                (void) XReconfigureWMWindow(display,windows->magnify.id,
15539                  windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
15540                  &window_changes);
15541                break;
15542              }
15543            if ((windows->magnify.mapped != MagickFalse) &&
15544                (windows->magnify.stasis != MagickFalse))
15545              {
15546                status=XMakeImage(display,resource_info,&windows->magnify,
15547                  display_image,windows->magnify.width,windows->magnify.height);
15548                XMakeMagnifyImage(display,windows);
15549              }
15550            break;
15551          }
15552        if ((windows->magnify.mapped != MagickFalse) &&
15553            (event.xconfigure.window == windows->pan.id))
15554          {
15555            /*
15556              Pan icon window has a new configuration.
15557            */
15558            if (event.xconfigure.send_event != 0)
15559              {
15560                windows->pan.x=event.xconfigure.x;
15561                windows->pan.y=event.xconfigure.y;
15562              }
15563            windows->pan.width=(unsigned int) event.xconfigure.width;
15564            windows->pan.height=(unsigned int) event.xconfigure.height;
15565            break;
15566          }
15567        if (event.xconfigure.window == windows->icon.id)
15568          {
15569            /*
15570              Icon window has a new configuration.
15571            */
15572            windows->icon.width=(unsigned int) event.xconfigure.width;
15573            windows->icon.height=(unsigned int) event.xconfigure.height;
15574            break;
15575          }
15576        break;
15577      }
15578      case DestroyNotify:
15579      {
15580        /*
15581          Group leader has exited.
15582        */
15583        if (display_image->debug != MagickFalse)
15584          (void) LogMagickEvent(X11Event,GetMagickModule(),
15585            "Destroy Notify: 0x%lx",event.xdestroywindow.window);
15586        if (event.xdestroywindow.window == windows->group_leader.id)
15587          {
15588            *state|=ExitState;
15589            break;
15590          }
15591        break;
15592      }
15593      case EnterNotify:
15594      {
15595        /*
15596          Selectively install colormap.
15597        */
15598        if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15599          if (event.xcrossing.mode != NotifyUngrab)
15600            XInstallColormap(display,map_info->colormap);
15601        break;
15602      }
15603      case Expose:
15604      {
15605        if (display_image->debug != MagickFalse)
15606          (void) LogMagickEvent(X11Event,GetMagickModule(),
15607            "Expose: 0x%lx %dx%d+%d+%d",event.xexpose.window,
15608            event.xexpose.width,event.xexpose.height,event.xexpose.x,
15609            event.xexpose.y);
15610        /*
15611          Refresh windows that are now exposed.
15612        */
15613        if ((event.xexpose.window == windows->image.id) &&
15614            (windows->image.mapped != MagickFalse))
15615          {
15616            XRefreshWindow(display,&windows->image,&event);
15617            delay=display_image->delay/MagickMax(
15618              display_image->ticks_per_second,1L);
15619            timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15620            break;
15621          }
15622        if ((event.xexpose.window == windows->magnify.id) &&
15623            (windows->magnify.mapped != MagickFalse))
15624          {
15625            XMakeMagnifyImage(display,windows);
15626            break;
15627          }
15628        if (event.xexpose.window == windows->pan.id)
15629          {
15630            XDrawPanRectangle(display,windows);
15631            break;
15632          }
15633        if (event.xexpose.window == windows->icon.id)
15634          {
15635            XRefreshWindow(display,&windows->icon,&event);
15636            break;
15637          }
15638        break;
15639      }
15640      case KeyPress:
15641      {
15642        int
15643          length;
15644
15645        /*
15646          Respond to a user key press.
15647        */
15648        length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
15649          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15650        *(command+length)='\0';
15651        if (display_image->debug != MagickFalse)
15652          (void) LogMagickEvent(X11Event,GetMagickModule(),
15653            "Key press: %d 0x%lx (%s)",event.xkey.state,(unsigned long)
15654            key_symbol,command);
15655        if (event.xkey.window == windows->image.id)
15656          {
15657            command_type=XImageWindowCommand(display,resource_info,windows,
15658              event.xkey.state,key_symbol,&display_image);
15659            if (command_type != NullCommand)
15660              nexus=XMagickCommand(display,resource_info,windows,command_type,
15661                &display_image);
15662          }
15663        if (event.xkey.window == windows->magnify.id)
15664          XMagnifyWindowCommand(display,windows,event.xkey.state,key_symbol);
15665        if (event.xkey.window == windows->pan.id)
15666          {
15667            if ((key_symbol == XK_q) || (key_symbol == XK_Escape))
15668              (void) XWithdrawWindow(display,windows->pan.id,
15669                windows->pan.screen);
15670            else
15671              if ((key_symbol == XK_F1) || (key_symbol == XK_Help))
15672                XTextViewWidget(display,resource_info,windows,MagickFalse,
15673                  "Help Viewer - Image Pan",ImagePanHelp);
15674              else
15675                XTranslateImage(display,windows,*image,key_symbol);
15676          }
15677        delay=display_image->delay/MagickMax(
15678          display_image->ticks_per_second,1L);
15679        timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15680        break;
15681      }
15682      case KeyRelease:
15683      {
15684        /*
15685          Respond to a user key release.
15686        */
15687        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
15688          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15689        if (display_image->debug != MagickFalse)
15690          (void) LogMagickEvent(X11Event,GetMagickModule(),
15691            "Key release: 0x%lx (%c)",(unsigned long) key_symbol,*command);
15692        break;
15693      }
15694      case LeaveNotify:
15695      {
15696        /*
15697          Selectively uninstall colormap.
15698        */
15699        if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15700          if (event.xcrossing.mode != NotifyUngrab)
15701            XUninstallColormap(display,map_info->colormap);
15702        break;
15703      }
15704      case MapNotify:
15705      {
15706        if (display_image->debug != MagickFalse)
15707          (void) LogMagickEvent(X11Event,GetMagickModule(),"Map Notify: 0x%lx",
15708            event.xmap.window);
15709        if (event.xmap.window == windows->backdrop.id)
15710          {
15711            (void) XSetInputFocus(display,event.xmap.window,RevertToParent,
15712              CurrentTime);
15713            windows->backdrop.mapped=MagickTrue;
15714            break;
15715          }
15716        if (event.xmap.window == windows->image.id)
15717          {
15718            if (windows->backdrop.id != (Window) NULL)
15719              (void) XInstallColormap(display,map_info->colormap);
15720            if (LocaleCompare(display_image->magick,"LOGO") == 0)
15721              {
15722                if (LocaleCompare(display_image->filename,"LOGO") == 0)
15723                  nexus=XOpenImage(display,resource_info,windows,MagickFalse);
15724              }
15725            if (((int) windows->image.width < windows->image.ximage->width) ||
15726                ((int) windows->image.height < windows->image.ximage->height))
15727              (void) XMapRaised(display,windows->pan.id);
15728            windows->image.mapped=MagickTrue;
15729            break;
15730          }
15731        if (event.xmap.window == windows->magnify.id)
15732          {
15733            XMakeMagnifyImage(display,windows);
15734            windows->magnify.mapped=MagickTrue;
15735            (void) XWithdrawWindow(display,windows->info.id,
15736              windows->info.screen);
15737            break;
15738          }
15739        if (event.xmap.window == windows->pan.id)
15740          {
15741            XMakePanImage(display,resource_info,windows,display_image);
15742            windows->pan.mapped=MagickTrue;
15743            break;
15744          }
15745        if (event.xmap.window == windows->info.id)
15746          {
15747            windows->info.mapped=MagickTrue;
15748            break;
15749          }
15750        if (event.xmap.window == windows->icon.id)
15751          {
15752            MagickBooleanType
15753              taint;
15754
15755            /*
15756              Create an icon image.
15757            */
15758            taint=display_image->taint;
15759            XMakeStandardColormap(display,icon_visual,icon_resources,
15760              display_image,icon_map,icon_pixel);
15761            (void) XMakeImage(display,icon_resources,&windows->icon,
15762              display_image,windows->icon.width,windows->icon.height);
15763            display_image->taint=taint;
15764            (void) XSetWindowBackgroundPixmap(display,windows->icon.id,
15765              windows->icon.pixmap);
15766            (void) XClearWindow(display,windows->icon.id);
15767            (void) XWithdrawWindow(display,windows->info.id,
15768              windows->info.screen);
15769            windows->icon.mapped=MagickTrue;
15770            break;
15771          }
15772        if (event.xmap.window == windows->command.id)
15773          {
15774            windows->command.mapped=MagickTrue;
15775            break;
15776          }
15777        if (event.xmap.window == windows->popup.id)
15778          {
15779            windows->popup.mapped=MagickTrue;
15780            break;
15781          }
15782        if (event.xmap.window == windows->widget.id)
15783          {
15784            windows->widget.mapped=MagickTrue;
15785            break;
15786          }
15787        break;
15788      }
15789      case MappingNotify:
15790      {
15791        (void) XRefreshKeyboardMapping(&event.xmapping);
15792        break;
15793      }
15794      case NoExpose:
15795        break;
15796      case PropertyNotify:
15797      {
15798        Atom
15799          type;
15800
15801        int
15802          format,
15803          status;
15804
15805        unsigned char
15806          *data;
15807
15808        unsigned long
15809          after,
15810          length;
15811
15812        if (display_image->debug != MagickFalse)
15813          (void) LogMagickEvent(X11Event,GetMagickModule(),
15814            "Property Notify: 0x%lx 0x%lx %d",event.xproperty.window,
15815            event.xproperty.atom,event.xproperty.state);
15816        if (event.xproperty.atom != windows->im_remote_command)
15817          break;
15818        /*
15819          Display image named by the remote command protocol.
15820        */
15821        status=XGetWindowProperty(display,event.xproperty.window,
15822          event.xproperty.atom,0L,(long) MaxTextExtent,MagickFalse,(Atom)
15823          AnyPropertyType,&type,&format,&length,&after,&data);
15824        if ((status != Success) || (length == 0))
15825          break;
15826        if (LocaleCompare((char *) data,"-quit") == 0)
15827          {
15828            XClientMessage(display,windows->image.id,windows->im_protocols,
15829              windows->im_exit,CurrentTime);
15830            (void) XFree((void *) data);
15831            break;
15832          }
15833        (void) CopyMagickString(resource_info->image_info->filename,
15834          (char *) data,MaxTextExtent);
15835        (void) XFree((void *) data);
15836        nexus=ReadImage(resource_info->image_info,&display_image->exception);
15837        CatchException(&display_image->exception);
15838        if (nexus != (Image *) NULL)
15839          *state|=NextImageState | ExitState;
15840        break;
15841      }
15842      case ReparentNotify:
15843      {
15844        if (display_image->debug != MagickFalse)
15845          (void) LogMagickEvent(X11Event,GetMagickModule(),
15846            "Reparent Notify: 0x%lx=>0x%lx",event.xreparent.parent,
15847            event.xreparent.window);
15848        break;
15849      }
15850      case UnmapNotify:
15851      {
15852        if (display_image->debug != MagickFalse)
15853          (void) LogMagickEvent(X11Event,GetMagickModule(),
15854            "Unmap Notify: 0x%lx",event.xunmap.window);
15855        if (event.xunmap.window == windows->backdrop.id)
15856          {
15857            windows->backdrop.mapped=MagickFalse;
15858            break;
15859          }
15860        if (event.xunmap.window == windows->image.id)
15861          {
15862            windows->image.mapped=MagickFalse;
15863            break;
15864          }
15865        if (event.xunmap.window == windows->magnify.id)
15866          {
15867            windows->magnify.mapped=MagickFalse;
15868            break;
15869          }
15870        if (event.xunmap.window == windows->pan.id)
15871          {
15872            windows->pan.mapped=MagickFalse;
15873            break;
15874          }
15875        if (event.xunmap.window == windows->info.id)
15876          {
15877            windows->info.mapped=MagickFalse;
15878            break;
15879          }
15880        if (event.xunmap.window == windows->icon.id)
15881          {
15882            if (map_info->colormap == icon_map->colormap)
15883              XConfigureImageColormap(display,resource_info,windows,
15884                display_image);
15885            (void) XFreeStandardColormap(display,icon_visual,icon_map,
15886              icon_pixel);
15887            windows->icon.mapped=MagickFalse;
15888            break;
15889          }
15890        if (event.xunmap.window == windows->command.id)
15891          {
15892            windows->command.mapped=MagickFalse;
15893            break;
15894          }
15895        if (event.xunmap.window == windows->popup.id)
15896          {
15897            if (windows->backdrop.id != (Window) NULL)
15898              (void) XSetInputFocus(display,windows->image.id,RevertToParent,
15899                CurrentTime);
15900            windows->popup.mapped=MagickFalse;
15901            break;
15902          }
15903        if (event.xunmap.window == windows->widget.id)
15904          {
15905            if (windows->backdrop.id != (Window) NULL)
15906              (void) XSetInputFocus(display,windows->image.id,RevertToParent,
15907                CurrentTime);
15908            windows->widget.mapped=MagickFalse;
15909            break;
15910          }
15911        break;
15912      }
15913      default:
15914      {
15915        if (display_image->debug != MagickFalse)
15916          (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
15917            event.type);
15918        break;
15919      }
15920    }
15921  } while (!(*state & ExitState));
15922  if ((*state & ExitState) == 0)
15923    (void) XMagickCommand(display,resource_info,windows,FreeBuffersCommand,
15924      &display_image);
15925  else
15926    if (resource_info->confirm_edit != MagickFalse)
15927      {
15928        /*
15929          Query user if image has changed.
15930        */
15931        if ((resource_info->immutable == MagickFalse) &&
15932            (display_image->taint != MagickFalse))
15933          {
15934            int
15935              status;
15936
15937            status=XConfirmWidget(display,windows,"Your image changed.",
15938              "Do you want to save it");
15939            if (status == 0)
15940              *state&=(~ExitState);
15941            else
15942              if (status > 0)
15943                (void) XMagickCommand(display,resource_info,windows,SaveCommand,
15944                  &display_image);
15945          }
15946      }
15947  if ((windows->visual_info->klass == GrayScale) ||
15948      (windows->visual_info->klass == PseudoColor) ||
15949      (windows->visual_info->klass == DirectColor))
15950    {
15951      /*
15952        Withdraw pan and Magnify window.
15953      */
15954      if (windows->info.mapped != MagickFalse)
15955        (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15956      if (windows->magnify.mapped != MagickFalse)
15957        (void) XWithdrawWindow(display,windows->magnify.id,
15958          windows->magnify.screen);
15959      if (windows->command.mapped != MagickFalse)
15960        (void) XWithdrawWindow(display,windows->command.id,
15961          windows->command.screen);
15962    }
15963  if (windows->pan.mapped != MagickFalse)
15964    (void) XWithdrawWindow(display,windows->pan.id,windows->pan.screen);
15965  if (resource_info->backdrop == MagickFalse)
15966    if (windows->backdrop.mapped)
15967      {
15968        (void) XWithdrawWindow(display,windows->backdrop.id,
15969          windows->backdrop.screen);
15970        (void) XDestroyWindow(display,windows->backdrop.id);
15971        windows->backdrop.id=(Window) NULL;
15972        (void) XWithdrawWindow(display,windows->image.id,
15973          windows->image.screen);
15974        (void) XDestroyWindow(display,windows->image.id);
15975        windows->image.id=(Window) NULL;
15976      }
15977  XSetCursorState(display,windows,MagickTrue);
15978  XCheckRefreshWindows(display,windows);
15979  if (((*state & FormerImageState) != 0) || ((*state & NextImageState) != 0))
15980    *state&=(~ExitState);
15981  if (*state & ExitState)
15982    {
15983      /*
15984        Free Standard Colormap.
15985      */
15986      (void) XFreeStandardColormap(display,icon_visual,icon_map,icon_pixel);
15987      if (resource_info->map_type == (char *) NULL)
15988        (void) XFreeStandardColormap(display,visual_info,map_info,pixel);
15989      /*
15990        Free X resources.
15991      */
15992      if (resource_info->copy_image != (Image *) NULL)
15993        {
15994          resource_info->copy_image=DestroyImage(resource_info->copy_image);
15995          resource_info->copy_image=NewImageList();
15996        }
15997      DestroyXResources();
15998    }
15999  (void) XSync(display,MagickFalse);
16000  /*
16001    Restore our progress monitor and warning handlers.
16002  */
16003  (void) SetErrorHandler(warning_handler);
16004  (void) SetWarningHandler(warning_handler);
16005  /*
16006    Change to home directory.
16007  */
16008  directory=getcwd(working_directory,MaxTextExtent);
16009  (void) directory;
16010  {
16011    int
16012      status;
16013
16014    status=chdir(resource_info->home_directory);
16015    if (status == -1)
16016      (void) ThrowMagickException(&display_image->exception,GetMagickModule(),
16017        FileOpenError,"UnableToOpenFile","%s",resource_info->home_directory);
16018  }
16019  *image=display_image;
16020  return(nexus);
16021}
16022#else
16023
16024/*
16025%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16026%                                                                             %
16027%                                                                             %
16028%                                                                             %
16029+   D i s p l a y I m a g e s                                                 %
16030%                                                                             %
16031%                                                                             %
16032%                                                                             %
16033%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16034%
16035%  DisplayImages() displays an image sequence to any X window screen.  It
16036%  returns a value other than 0 if successful.  Check the exception member
16037%  of image to determine the reason for any failure.
16038%
16039%  The format of the DisplayImages method is:
16040%
16041%      MagickBooleanType DisplayImages(const ImageInfo *image_info,
16042%        Image *images)
16043%
16044%  A description of each parameter follows:
16045%
16046%    o image_info: the image info.
16047%
16048%    o image: the image.
16049%
16050*/
16051MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
16052  Image *image)
16053{
16054  assert(image_info != (const ImageInfo *) NULL);
16055  assert(image_info->signature == MagickSignature);
16056  assert(image != (Image *) NULL);
16057  assert(image->signature == MagickSignature);
16058  if (image->debug != MagickFalse)
16059    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
16060  (void) ThrowMagickException(&image->exception,GetMagickModule(),
16061    MissingDelegateError,"DelegateLibrarySupportNotBuiltIn","`%s' (X11)",
16062    image->filename);
16063  return(MagickFalse);
16064}
16065
16066/*
16067%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16068%                                                                             %
16069%                                                                             %
16070%                                                                             %
16071+   R e m o t e D i s p l a y C o m m a n d                                   %
16072%                                                                             %
16073%                                                                             %
16074%                                                                             %
16075%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16076%
16077%  RemoteDisplayCommand() encourages a remote display program to display the
16078%  specified image filename.
16079%
16080%  The format of the RemoteDisplayCommand method is:
16081%
16082%      MagickBooleanType RemoteDisplayCommand(const ImageInfo *image,
16083%        const char *window,const char *filename,ExceptionInfo *exception)
16084%
16085%  A description of each parameter follows:
16086%
16087%    o image_info: the image info.
16088%
16089%    o window: Specifies the name or id of an X window.
16090%
16091%    o filename: the name of the image filename to display.
16092%
16093%    o exception: return any errors or warnings in this structure.
16094%
16095*/
16096MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
16097  const char *window,const char *filename,ExceptionInfo *exception)
16098{
16099  assert(image_info != (const ImageInfo *) NULL);
16100  assert(image_info->signature == MagickSignature);
16101  assert(filename != (char *) NULL);
16102  (void) window;
16103  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
16104  (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16105    "DelegateLibrarySupportNotBuiltIn","`%s' (X11)",image_info->filename);
16106  return(MagickFalse);
16107}
16108#endif
16109