display.c revision 4c08aed51c5899665ade97263692328eea4af106
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) == 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,DefaultChannels,draw_info,&target,
3805              (ssize_t) x_offset,(ssize_t) y_offset,
3806              method == FloodfillMethod ? 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) == 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      (void) SetImageAlphaChannel(composite_image,OpaqueAlphaChannel);
4299      opacity=(Quantum) (ScaleQuantumToChar((Quantum) QuantumRange)-
4300        ((ssize_t) ScaleQuantumToChar((Quantum) QuantumRange)*blend)/100);
4301      if (SetImageStorageClass(image,DirectClass) == MagickFalse)
4302        return(MagickFalse);
4303      image->matte=MagickTrue;
4304      exception=(&image->exception);
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  if (SetImageStorageClass(image,DirectClass) == MagickFalse)
5320    return(MagickFalse);
5321  image->matte=MagickTrue;
5322  exception=(&image->exception);
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.0,1.0,1.6):",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,factor);
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) ContrastStretchImageChannel(*image,DefaultChannels,black_point,
7842        white_point);
7843      XSetCursorState(display,windows,MagickFalse);
7844      if (windows->image.orphan != MagickFalse)
7845        break;
7846      XConfigureImageColormap(display,resource_info,windows,*image);
7847      (void) XConfigureImage(display,resource_info,windows,*image);
7848      break;
7849    }
7850    case SigmoidalContrastCommand:
7851    {
7852      static char
7853        levels[MaxTextExtent] = "3x50%";
7854
7855      /*
7856        Query user for gamma value.
7857      */
7858      (void) XDialogWidget(display,windows,"Sigmoidal Contrast",
7859        "Enter contrast and midpoint:",levels);
7860      if (*levels == '\0')
7861        break;
7862      /*
7863        Contrast stretch image.
7864      */
7865      XSetCursorState(display,windows,MagickTrue);
7866      XCheckRefreshWindows(display,windows);
7867      (void) SigmoidalContrastImage(*image,MagickTrue,levels);
7868      XSetCursorState(display,windows,MagickFalse);
7869      if (windows->image.orphan != MagickFalse)
7870        break;
7871      XConfigureImageColormap(display,resource_info,windows,*image);
7872      (void) XConfigureImage(display,resource_info,windows,*image);
7873      break;
7874    }
7875    case NormalizeCommand:
7876    {
7877      /*
7878        Perform histogram normalization on the image.
7879      */
7880      XSetCursorState(display,windows,MagickTrue);
7881      XCheckRefreshWindows(display,windows);
7882      (void) NormalizeImage(*image);
7883      XSetCursorState(display,windows,MagickFalse);
7884      if (windows->image.orphan != MagickFalse)
7885        break;
7886      XConfigureImageColormap(display,resource_info,windows,*image);
7887      (void) XConfigureImage(display,resource_info,windows,*image);
7888      break;
7889    }
7890    case EqualizeCommand:
7891    {
7892      /*
7893        Perform histogram equalization on the image.
7894      */
7895      XSetCursorState(display,windows,MagickTrue);
7896      XCheckRefreshWindows(display,windows);
7897      (void) EqualizeImage(*image);
7898      XSetCursorState(display,windows,MagickFalse);
7899      if (windows->image.orphan != MagickFalse)
7900        break;
7901      XConfigureImageColormap(display,resource_info,windows,*image);
7902      (void) XConfigureImage(display,resource_info,windows,*image);
7903      break;
7904    }
7905    case NegateCommand:
7906    {
7907      /*
7908        Negate colors in image.
7909      */
7910      XSetCursorState(display,windows,MagickTrue);
7911      XCheckRefreshWindows(display,windows);
7912      (void) NegateImage(*image,MagickFalse);
7913      XSetCursorState(display,windows,MagickFalse);
7914      if (windows->image.orphan != MagickFalse)
7915        break;
7916      XConfigureImageColormap(display,resource_info,windows,*image);
7917      (void) XConfigureImage(display,resource_info,windows,*image);
7918      break;
7919    }
7920    case GrayscaleCommand:
7921    {
7922      /*
7923        Convert image to grayscale.
7924      */
7925      XSetCursorState(display,windows,MagickTrue);
7926      XCheckRefreshWindows(display,windows);
7927      (void) SetImageType(*image,(*image)->matte == MagickFalse ?
7928        GrayscaleType : GrayscaleMatteType);
7929      XSetCursorState(display,windows,MagickFalse);
7930      if (windows->image.orphan != MagickFalse)
7931        break;
7932      XConfigureImageColormap(display,resource_info,windows,*image);
7933      (void) XConfigureImage(display,resource_info,windows,*image);
7934      break;
7935    }
7936    case MapCommand:
7937    {
7938      Image
7939        *affinity_image;
7940
7941      static char
7942        filename[MaxTextExtent] = "\0";
7943
7944      /*
7945        Request image file name from user.
7946      */
7947      XFileBrowserWidget(display,windows,"Map",filename);
7948      if (*filename == '\0')
7949        break;
7950      /*
7951        Map image.
7952      */
7953      XSetCursorState(display,windows,MagickTrue);
7954      XCheckRefreshWindows(display,windows);
7955      (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
7956      affinity_image=ReadImage(image_info,&(*image)->exception);
7957      if (affinity_image != (Image *) NULL)
7958        {
7959          (void) RemapImage(&quantize_info,*image,affinity_image);
7960          affinity_image=DestroyImage(affinity_image);
7961        }
7962      CatchException(&(*image)->exception);
7963      XSetCursorState(display,windows,MagickFalse);
7964      if (windows->image.orphan != MagickFalse)
7965        break;
7966      XConfigureImageColormap(display,resource_info,windows,*image);
7967      (void) XConfigureImage(display,resource_info,windows,*image);
7968      break;
7969    }
7970    case QuantizeCommand:
7971    {
7972      int
7973        status;
7974
7975      static char
7976        colors[MaxTextExtent] = "256";
7977
7978      /*
7979        Query user for maximum number of colors.
7980      */
7981      status=XDialogWidget(display,windows,"Quantize",
7982        "Maximum number of colors:",colors);
7983      if (*colors == '\0')
7984        break;
7985      /*
7986        Color reduce the image.
7987      */
7988      XSetCursorState(display,windows,MagickTrue);
7989      XCheckRefreshWindows(display,windows);
7990      quantize_info.number_colors=StringToUnsignedLong(colors);
7991      quantize_info.dither=status != 0 ? MagickTrue : MagickFalse;
7992      (void) QuantizeImage(&quantize_info,*image);
7993      XSetCursorState(display,windows,MagickFalse);
7994      if (windows->image.orphan != MagickFalse)
7995        break;
7996      XConfigureImageColormap(display,resource_info,windows,*image);
7997      (void) XConfigureImage(display,resource_info,windows,*image);
7998      break;
7999    }
8000    case DespeckleCommand:
8001    {
8002      Image
8003        *despeckle_image;
8004
8005      /*
8006        Despeckle image.
8007      */
8008      XSetCursorState(display,windows,MagickTrue);
8009      XCheckRefreshWindows(display,windows);
8010      despeckle_image=DespeckleImage(*image,&(*image)->exception);
8011      if (despeckle_image != (Image *) NULL)
8012        {
8013          *image=DestroyImage(*image);
8014          *image=despeckle_image;
8015        }
8016      CatchException(&(*image)->exception);
8017      XSetCursorState(display,windows,MagickFalse);
8018      if (windows->image.orphan != MagickFalse)
8019        break;
8020      XConfigureImageColormap(display,resource_info,windows,*image);
8021      (void) XConfigureImage(display,resource_info,windows,*image);
8022      break;
8023    }
8024    case EmbossCommand:
8025    {
8026      Image
8027        *emboss_image;
8028
8029      static char
8030        radius[MaxTextExtent] = "0.0x1.0";
8031
8032      /*
8033        Query user for emboss radius.
8034      */
8035      (void) XDialogWidget(display,windows,"Emboss",
8036        "Enter the emboss radius and standard deviation:",radius);
8037      if (*radius == '\0')
8038        break;
8039      /*
8040        Reduce noise in the image.
8041      */
8042      XSetCursorState(display,windows,MagickTrue);
8043      XCheckRefreshWindows(display,windows);
8044      flags=ParseGeometry(radius,&geometry_info);
8045      if ((flags & SigmaValue) == 0)
8046        geometry_info.sigma=1.0;
8047      emboss_image=EmbossImage(*image,geometry_info.rho,geometry_info.sigma,
8048        &(*image)->exception);
8049      if (emboss_image != (Image *) NULL)
8050        {
8051          *image=DestroyImage(*image);
8052          *image=emboss_image;
8053        }
8054      CatchException(&(*image)->exception);
8055      XSetCursorState(display,windows,MagickFalse);
8056      if (windows->image.orphan != MagickFalse)
8057        break;
8058      XConfigureImageColormap(display,resource_info,windows,*image);
8059      (void) XConfigureImage(display,resource_info,windows,*image);
8060      break;
8061    }
8062    case ReduceNoiseCommand:
8063    {
8064      Image
8065        *noise_image;
8066
8067      static char
8068        radius[MaxTextExtent] = "0";
8069
8070      /*
8071        Query user for noise radius.
8072      */
8073      (void) XDialogWidget(display,windows,"Reduce Noise",
8074        "Enter the noise radius:",radius);
8075      if (*radius == '\0')
8076        break;
8077      /*
8078        Reduce noise in the image.
8079      */
8080      XSetCursorState(display,windows,MagickTrue);
8081      XCheckRefreshWindows(display,windows);
8082      flags=ParseGeometry(radius,&geometry_info);
8083      noise_image=StatisticImage(*image,NonpeakStatistic,(size_t)
8084        geometry_info.rho,(size_t) geometry_info.rho,&(*image)->exception);
8085      if (noise_image != (Image *) NULL)
8086        {
8087          *image=DestroyImage(*image);
8088          *image=noise_image;
8089        }
8090      CatchException(&(*image)->exception);
8091      XSetCursorState(display,windows,MagickFalse);
8092      if (windows->image.orphan != MagickFalse)
8093        break;
8094      XConfigureImageColormap(display,resource_info,windows,*image);
8095      (void) XConfigureImage(display,resource_info,windows,*image);
8096      break;
8097    }
8098    case AddNoiseCommand:
8099    {
8100      char
8101        **noises;
8102
8103      Image
8104        *noise_image;
8105
8106      static char
8107        noise_type[MaxTextExtent] = "Gaussian";
8108
8109      /*
8110        Add noise to the image.
8111      */
8112      noises=GetCommandOptions(MagickNoiseOptions);
8113      if (noises == (char **) NULL)
8114        break;
8115      XListBrowserWidget(display,windows,&windows->widget,
8116        (const char **) noises,"Add Noise",
8117        "Select a type of noise to add to your image:",noise_type);
8118      noises=DestroyStringList(noises);
8119      if (*noise_type == '\0')
8120        break;
8121      XSetCursorState(display,windows,MagickTrue);
8122      XCheckRefreshWindows(display,windows);
8123      noise_image=AddNoiseImage(*image,(NoiseType) ParseCommandOption(
8124        MagickNoiseOptions,MagickFalse,noise_type),&(*image)->exception);
8125      if (noise_image != (Image *) NULL)
8126        {
8127          *image=DestroyImage(*image);
8128          *image=noise_image;
8129        }
8130      CatchException(&(*image)->exception);
8131      XSetCursorState(display,windows,MagickFalse);
8132      if (windows->image.orphan != MagickFalse)
8133        break;
8134      XConfigureImageColormap(display,resource_info,windows,*image);
8135      (void) XConfigureImage(display,resource_info,windows,*image);
8136      break;
8137    }
8138    case SharpenCommand:
8139    {
8140      Image
8141        *sharp_image;
8142
8143      static char
8144        radius[MaxTextExtent] = "0.0x1.0";
8145
8146      /*
8147        Query user for sharpen radius.
8148      */
8149      (void) XDialogWidget(display,windows,"Sharpen",
8150        "Enter the sharpen radius and standard deviation:",radius);
8151      if (*radius == '\0')
8152        break;
8153      /*
8154        Sharpen image scanlines.
8155      */
8156      XSetCursorState(display,windows,MagickTrue);
8157      XCheckRefreshWindows(display,windows);
8158      flags=ParseGeometry(radius,&geometry_info);
8159      sharp_image=SharpenImage(*image,geometry_info.rho,geometry_info.sigma,
8160        &(*image)->exception);
8161      if (sharp_image != (Image *) NULL)
8162        {
8163          *image=DestroyImage(*image);
8164          *image=sharp_image;
8165        }
8166      CatchException(&(*image)->exception);
8167      XSetCursorState(display,windows,MagickFalse);
8168      if (windows->image.orphan != MagickFalse)
8169        break;
8170      XConfigureImageColormap(display,resource_info,windows,*image);
8171      (void) XConfigureImage(display,resource_info,windows,*image);
8172      break;
8173    }
8174    case BlurCommand:
8175    {
8176      Image
8177        *blur_image;
8178
8179      static char
8180        radius[MaxTextExtent] = "0.0x1.0";
8181
8182      /*
8183        Query user for blur radius.
8184      */
8185      (void) XDialogWidget(display,windows,"Blur",
8186        "Enter the blur radius and standard deviation:",radius);
8187      if (*radius == '\0')
8188        break;
8189      /*
8190        Blur an image.
8191      */
8192      XSetCursorState(display,windows,MagickTrue);
8193      XCheckRefreshWindows(display,windows);
8194      flags=ParseGeometry(radius,&geometry_info);
8195      blur_image=BlurImage(*image,geometry_info.rho,geometry_info.sigma,
8196        &(*image)->exception);
8197      if (blur_image != (Image *) NULL)
8198        {
8199          *image=DestroyImage(*image);
8200          *image=blur_image;
8201        }
8202      CatchException(&(*image)->exception);
8203      XSetCursorState(display,windows,MagickFalse);
8204      if (windows->image.orphan != MagickFalse)
8205        break;
8206      XConfigureImageColormap(display,resource_info,windows,*image);
8207      (void) XConfigureImage(display,resource_info,windows,*image);
8208      break;
8209    }
8210    case ThresholdCommand:
8211    {
8212      double
8213        threshold;
8214
8215      static char
8216        factor[MaxTextExtent] = "128";
8217
8218      /*
8219        Query user for threshold value.
8220      */
8221      (void) XDialogWidget(display,windows,"Threshold",
8222        "Enter threshold value:",factor);
8223      if (*factor == '\0')
8224        break;
8225      /*
8226        Gamma correct image.
8227      */
8228      XSetCursorState(display,windows,MagickTrue);
8229      XCheckRefreshWindows(display,windows);
8230      threshold=SiPrefixToDouble(factor,QuantumRange);
8231      (void) BilevelImage(*image,threshold);
8232      XSetCursorState(display,windows,MagickFalse);
8233      if (windows->image.orphan != MagickFalse)
8234        break;
8235      XConfigureImageColormap(display,resource_info,windows,*image);
8236      (void) XConfigureImage(display,resource_info,windows,*image);
8237      break;
8238    }
8239    case EdgeDetectCommand:
8240    {
8241      Image
8242        *edge_image;
8243
8244      static char
8245        radius[MaxTextExtent] = "0";
8246
8247      /*
8248        Query user for edge factor.
8249      */
8250      (void) XDialogWidget(display,windows,"Detect Edges",
8251        "Enter the edge detect radius:",radius);
8252      if (*radius == '\0')
8253        break;
8254      /*
8255        Detect edge in image.
8256      */
8257      XSetCursorState(display,windows,MagickTrue);
8258      XCheckRefreshWindows(display,windows);
8259      flags=ParseGeometry(radius,&geometry_info);
8260      edge_image=EdgeImage(*image,geometry_info.rho,&(*image)->exception);
8261      if (edge_image != (Image *) NULL)
8262        {
8263          *image=DestroyImage(*image);
8264          *image=edge_image;
8265        }
8266      CatchException(&(*image)->exception);
8267      XSetCursorState(display,windows,MagickFalse);
8268      if (windows->image.orphan != MagickFalse)
8269        break;
8270      XConfigureImageColormap(display,resource_info,windows,*image);
8271      (void) XConfigureImage(display,resource_info,windows,*image);
8272      break;
8273    }
8274    case SpreadCommand:
8275    {
8276      Image
8277        *spread_image;
8278
8279      static char
8280        amount[MaxTextExtent] = "2";
8281
8282      /*
8283        Query user for spread amount.
8284      */
8285      (void) XDialogWidget(display,windows,"Spread",
8286        "Enter the displacement amount:",amount);
8287      if (*amount == '\0')
8288        break;
8289      /*
8290        Displace image pixels by a random amount.
8291      */
8292      XSetCursorState(display,windows,MagickTrue);
8293      XCheckRefreshWindows(display,windows);
8294      flags=ParseGeometry(amount,&geometry_info);
8295      spread_image=EdgeImage(*image,geometry_info.rho,&(*image)->exception);
8296      if (spread_image != (Image *) NULL)
8297        {
8298          *image=DestroyImage(*image);
8299          *image=spread_image;
8300        }
8301      CatchException(&(*image)->exception);
8302      XSetCursorState(display,windows,MagickFalse);
8303      if (windows->image.orphan != MagickFalse)
8304        break;
8305      XConfigureImageColormap(display,resource_info,windows,*image);
8306      (void) XConfigureImage(display,resource_info,windows,*image);
8307      break;
8308    }
8309    case ShadeCommand:
8310    {
8311      Image
8312        *shade_image;
8313
8314      int
8315        status;
8316
8317      static char
8318        geometry[MaxTextExtent] = "30x30";
8319
8320      /*
8321        Query user for the shade geometry.
8322      */
8323      status=XDialogWidget(display,windows,"Shade",
8324        "Enter the azimuth and elevation of the light source:",geometry);
8325      if (*geometry == '\0')
8326        break;
8327      /*
8328        Shade image pixels.
8329      */
8330      XSetCursorState(display,windows,MagickTrue);
8331      XCheckRefreshWindows(display,windows);
8332      flags=ParseGeometry(geometry,&geometry_info);
8333      if ((flags & SigmaValue) == 0)
8334        geometry_info.sigma=1.0;
8335      shade_image=ShadeImage(*image,status != 0 ? MagickFalse : MagickTrue,
8336        geometry_info.rho,geometry_info.sigma,&(*image)->exception);
8337      if (shade_image != (Image *) NULL)
8338        {
8339          *image=DestroyImage(*image);
8340          *image=shade_image;
8341        }
8342      CatchException(&(*image)->exception);
8343      XSetCursorState(display,windows,MagickFalse);
8344      if (windows->image.orphan != MagickFalse)
8345        break;
8346      XConfigureImageColormap(display,resource_info,windows,*image);
8347      (void) XConfigureImage(display,resource_info,windows,*image);
8348      break;
8349    }
8350    case RaiseCommand:
8351    {
8352      static char
8353        bevel_width[MaxTextExtent] = "10";
8354
8355      /*
8356        Query user for bevel width.
8357      */
8358      (void) XDialogWidget(display,windows,"Raise","Bevel width:",bevel_width);
8359      if (*bevel_width == '\0')
8360        break;
8361      /*
8362        Raise an image.
8363      */
8364      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
8365      XSetCursorState(display,windows,MagickTrue);
8366      XCheckRefreshWindows(display,windows);
8367      (void) ParsePageGeometry(*image,bevel_width,&page_geometry,
8368        &(*image)->exception);
8369      (void) RaiseImage(*image,&page_geometry,MagickTrue);
8370      XSetCursorState(display,windows,MagickFalse);
8371      if (windows->image.orphan != MagickFalse)
8372        break;
8373      XConfigureImageColormap(display,resource_info,windows,*image);
8374      (void) XConfigureImage(display,resource_info,windows,*image);
8375      break;
8376    }
8377    case SegmentCommand:
8378    {
8379      static char
8380        threshold[MaxTextExtent] = "1.0x1.5";
8381
8382      /*
8383        Query user for smoothing threshold.
8384      */
8385      (void) XDialogWidget(display,windows,"Segment","Smooth threshold:",
8386        threshold);
8387      if (*threshold == '\0')
8388        break;
8389      /*
8390        Segment an image.
8391      */
8392      XSetCursorState(display,windows,MagickTrue);
8393      XCheckRefreshWindows(display,windows);
8394      flags=ParseGeometry(threshold,&geometry_info);
8395      if ((flags & SigmaValue) == 0)
8396        geometry_info.sigma=1.0;
8397      (void) SegmentImage(*image,RGBColorspace,MagickFalse,geometry_info.rho,
8398        geometry_info.sigma);
8399      XSetCursorState(display,windows,MagickFalse);
8400      if (windows->image.orphan != MagickFalse)
8401        break;
8402      XConfigureImageColormap(display,resource_info,windows,*image);
8403      (void) XConfigureImage(display,resource_info,windows,*image);
8404      break;
8405    }
8406    case SepiaToneCommand:
8407    {
8408      double
8409        threshold;
8410
8411      Image
8412        *sepia_image;
8413
8414      static char
8415        factor[MaxTextExtent] = "80%";
8416
8417      /*
8418        Query user for sepia-tone factor.
8419      */
8420      (void) XDialogWidget(display,windows,"Sepia Tone",
8421        "Enter the sepia tone factor (0 - 99.9%):",factor);
8422      if (*factor == '\0')
8423        break;
8424      /*
8425        Sepia tone image pixels.
8426      */
8427      XSetCursorState(display,windows,MagickTrue);
8428      XCheckRefreshWindows(display,windows);
8429      threshold=SiPrefixToDouble(factor,QuantumRange);
8430      sepia_image=SepiaToneImage(*image,threshold,&(*image)->exception);
8431      if (sepia_image != (Image *) NULL)
8432        {
8433          *image=DestroyImage(*image);
8434          *image=sepia_image;
8435        }
8436      CatchException(&(*image)->exception);
8437      XSetCursorState(display,windows,MagickFalse);
8438      if (windows->image.orphan != MagickFalse)
8439        break;
8440      XConfigureImageColormap(display,resource_info,windows,*image);
8441      (void) XConfigureImage(display,resource_info,windows,*image);
8442      break;
8443    }
8444    case SolarizeCommand:
8445    {
8446      double
8447        threshold;
8448
8449      static char
8450        factor[MaxTextExtent] = "60%";
8451
8452      /*
8453        Query user for solarize factor.
8454      */
8455      (void) XDialogWidget(display,windows,"Solarize",
8456        "Enter the solarize factor (0 - 99.9%):",factor);
8457      if (*factor == '\0')
8458        break;
8459      /*
8460        Solarize image pixels.
8461      */
8462      XSetCursorState(display,windows,MagickTrue);
8463      XCheckRefreshWindows(display,windows);
8464      threshold=SiPrefixToDouble(factor,QuantumRange);
8465      (void) SolarizeImage(*image,threshold);
8466      XSetCursorState(display,windows,MagickFalse);
8467      if (windows->image.orphan != MagickFalse)
8468        break;
8469      XConfigureImageColormap(display,resource_info,windows,*image);
8470      (void) XConfigureImage(display,resource_info,windows,*image);
8471      break;
8472    }
8473    case SwirlCommand:
8474    {
8475      Image
8476        *swirl_image;
8477
8478      static char
8479        degrees[MaxTextExtent] = "60";
8480
8481      /*
8482        Query user for swirl angle.
8483      */
8484      (void) XDialogWidget(display,windows,"Swirl","Enter the swirl angle:",
8485        degrees);
8486      if (*degrees == '\0')
8487        break;
8488      /*
8489        Swirl image pixels about the center.
8490      */
8491      XSetCursorState(display,windows,MagickTrue);
8492      XCheckRefreshWindows(display,windows);
8493      flags=ParseGeometry(degrees,&geometry_info);
8494      swirl_image=SwirlImage(*image,geometry_info.rho,&(*image)->exception);
8495      if (swirl_image != (Image *) NULL)
8496        {
8497          *image=DestroyImage(*image);
8498          *image=swirl_image;
8499        }
8500      CatchException(&(*image)->exception);
8501      XSetCursorState(display,windows,MagickFalse);
8502      if (windows->image.orphan != MagickFalse)
8503        break;
8504      XConfigureImageColormap(display,resource_info,windows,*image);
8505      (void) XConfigureImage(display,resource_info,windows,*image);
8506      break;
8507    }
8508    case ImplodeCommand:
8509    {
8510      Image
8511        *implode_image;
8512
8513      static char
8514        factor[MaxTextExtent] = "0.3";
8515
8516      /*
8517        Query user for implode factor.
8518      */
8519      (void) XDialogWidget(display,windows,"Implode",
8520        "Enter the implosion/explosion factor (-1.0 - 1.0):",factor);
8521      if (*factor == '\0')
8522        break;
8523      /*
8524        Implode image pixels about the center.
8525      */
8526      XSetCursorState(display,windows,MagickTrue);
8527      XCheckRefreshWindows(display,windows);
8528      flags=ParseGeometry(factor,&geometry_info);
8529      implode_image=ImplodeImage(*image,geometry_info.rho,&(*image)->exception);
8530      if (implode_image != (Image *) NULL)
8531        {
8532          *image=DestroyImage(*image);
8533          *image=implode_image;
8534        }
8535      CatchException(&(*image)->exception);
8536      XSetCursorState(display,windows,MagickFalse);
8537      if (windows->image.orphan != MagickFalse)
8538        break;
8539      XConfigureImageColormap(display,resource_info,windows,*image);
8540      (void) XConfigureImage(display,resource_info,windows,*image);
8541      break;
8542    }
8543    case VignetteCommand:
8544    {
8545      Image
8546        *vignette_image;
8547
8548      static char
8549        geometry[MaxTextExtent] = "0x20";
8550
8551      /*
8552        Query user for the vignette geometry.
8553      */
8554      (void) XDialogWidget(display,windows,"Vignette",
8555        "Enter the radius, sigma, and x and y offsets:",geometry);
8556      if (*geometry == '\0')
8557        break;
8558      /*
8559        Soften the edges of the image in vignette style
8560      */
8561      XSetCursorState(display,windows,MagickTrue);
8562      XCheckRefreshWindows(display,windows);
8563      flags=ParseGeometry(geometry,&geometry_info);
8564      if ((flags & SigmaValue) == 0)
8565        geometry_info.sigma=1.0;
8566      if ((flags & XiValue) == 0)
8567        geometry_info.xi=0.1*(*image)->columns;
8568      if ((flags & PsiValue) == 0)
8569        geometry_info.psi=0.1*(*image)->rows;
8570      vignette_image=VignetteImage(*image,geometry_info.rho,geometry_info.sigma,
8571        (ssize_t) ceil(geometry_info.xi-0.5),(ssize_t) ceil(geometry_info.psi-
8572        0.5),&(*image)->exception);
8573      if (vignette_image != (Image *) NULL)
8574        {
8575          *image=DestroyImage(*image);
8576          *image=vignette_image;
8577        }
8578      CatchException(&(*image)->exception);
8579      XSetCursorState(display,windows,MagickFalse);
8580      if (windows->image.orphan != MagickFalse)
8581        break;
8582      XConfigureImageColormap(display,resource_info,windows,*image);
8583      (void) XConfigureImage(display,resource_info,windows,*image);
8584      break;
8585    }
8586    case WaveCommand:
8587    {
8588      Image
8589        *wave_image;
8590
8591      static char
8592        geometry[MaxTextExtent] = "25x150";
8593
8594      /*
8595        Query user for the wave geometry.
8596      */
8597      (void) XDialogWidget(display,windows,"Wave",
8598        "Enter the amplitude and length of the wave:",geometry);
8599      if (*geometry == '\0')
8600        break;
8601      /*
8602        Alter an image along a sine wave.
8603      */
8604      XSetCursorState(display,windows,MagickTrue);
8605      XCheckRefreshWindows(display,windows);
8606      flags=ParseGeometry(geometry,&geometry_info);
8607      if ((flags & SigmaValue) == 0)
8608        geometry_info.sigma=1.0;
8609      wave_image=WaveImage(*image,geometry_info.rho,geometry_info.sigma,
8610        &(*image)->exception);
8611      if (wave_image != (Image *) NULL)
8612        {
8613          *image=DestroyImage(*image);
8614          *image=wave_image;
8615        }
8616      CatchException(&(*image)->exception);
8617      XSetCursorState(display,windows,MagickFalse);
8618      if (windows->image.orphan != MagickFalse)
8619        break;
8620      XConfigureImageColormap(display,resource_info,windows,*image);
8621      (void) XConfigureImage(display,resource_info,windows,*image);
8622      break;
8623    }
8624    case OilPaintCommand:
8625    {
8626      Image
8627        *paint_image;
8628
8629      static char
8630        radius[MaxTextExtent] = "0";
8631
8632      /*
8633        Query user for circular neighborhood radius.
8634      */
8635      (void) XDialogWidget(display,windows,"Oil Paint",
8636        "Enter the mask radius:",radius);
8637      if (*radius == '\0')
8638        break;
8639      /*
8640        OilPaint image scanlines.
8641      */
8642      XSetCursorState(display,windows,MagickTrue);
8643      XCheckRefreshWindows(display,windows);
8644      flags=ParseGeometry(radius,&geometry_info);
8645      paint_image=OilPaintImage(*image,geometry_info.rho,&(*image)->exception);
8646      if (paint_image != (Image *) NULL)
8647        {
8648          *image=DestroyImage(*image);
8649          *image=paint_image;
8650        }
8651      CatchException(&(*image)->exception);
8652      XSetCursorState(display,windows,MagickFalse);
8653      if (windows->image.orphan != MagickFalse)
8654        break;
8655      XConfigureImageColormap(display,resource_info,windows,*image);
8656      (void) XConfigureImage(display,resource_info,windows,*image);
8657      break;
8658    }
8659    case CharcoalDrawCommand:
8660    {
8661      Image
8662        *charcoal_image;
8663
8664      static char
8665        radius[MaxTextExtent] = "0x1";
8666
8667      /*
8668        Query user for charcoal radius.
8669      */
8670      (void) XDialogWidget(display,windows,"Charcoal Draw",
8671        "Enter the charcoal radius and sigma:",radius);
8672      if (*radius == '\0')
8673        break;
8674      /*
8675        Charcoal the image.
8676      */
8677      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
8678      XSetCursorState(display,windows,MagickTrue);
8679      XCheckRefreshWindows(display,windows);
8680      flags=ParseGeometry(radius,&geometry_info);
8681      if ((flags & SigmaValue) == 0)
8682        geometry_info.sigma=geometry_info.rho;
8683      charcoal_image=CharcoalImage(*image,geometry_info.rho,geometry_info.sigma,
8684        &(*image)->exception);
8685      if (charcoal_image != (Image *) NULL)
8686        {
8687          *image=DestroyImage(*image);
8688          *image=charcoal_image;
8689        }
8690      CatchException(&(*image)->exception);
8691      XSetCursorState(display,windows,MagickFalse);
8692      if (windows->image.orphan != MagickFalse)
8693        break;
8694      XConfigureImageColormap(display,resource_info,windows,*image);
8695      (void) XConfigureImage(display,resource_info,windows,*image);
8696      break;
8697    }
8698    case AnnotateCommand:
8699    {
8700      /*
8701        Annotate the image with text.
8702      */
8703      status=XAnnotateEditImage(display,resource_info,windows,*image);
8704      if (status == MagickFalse)
8705        {
8706          XNoticeWidget(display,windows,"Unable to annotate X image",
8707            (*image)->filename);
8708          break;
8709        }
8710      break;
8711    }
8712    case DrawCommand:
8713    {
8714      /*
8715        Draw image.
8716      */
8717      status=XDrawEditImage(display,resource_info,windows,image);
8718      if (status == MagickFalse)
8719        {
8720          XNoticeWidget(display,windows,"Unable to draw on the X image",
8721            (*image)->filename);
8722          break;
8723        }
8724      break;
8725    }
8726    case ColorCommand:
8727    {
8728      /*
8729        Color edit.
8730      */
8731      status=XColorEditImage(display,resource_info,windows,image);
8732      if (status == MagickFalse)
8733        {
8734          XNoticeWidget(display,windows,"Unable to pixel edit X image",
8735            (*image)->filename);
8736          break;
8737        }
8738      break;
8739    }
8740    case MatteCommand:
8741    {
8742      /*
8743        Matte edit.
8744      */
8745      status=XMatteEditImage(display,resource_info,windows,image);
8746      if (status == MagickFalse)
8747        {
8748          XNoticeWidget(display,windows,"Unable to matte edit X image",
8749            (*image)->filename);
8750          break;
8751        }
8752      break;
8753    }
8754    case CompositeCommand:
8755    {
8756      /*
8757        Composite image.
8758      */
8759      status=XCompositeImage(display,resource_info,windows,*image);
8760      if (status == MagickFalse)
8761        {
8762          XNoticeWidget(display,windows,"Unable to composite X image",
8763            (*image)->filename);
8764          break;
8765        }
8766      break;
8767    }
8768    case AddBorderCommand:
8769    {
8770      Image
8771        *border_image;
8772
8773      static char
8774        geometry[MaxTextExtent] = "6x6";
8775
8776      /*
8777        Query user for border color and geometry.
8778      */
8779      XColorBrowserWidget(display,windows,"Select",color);
8780      if (*color == '\0')
8781        break;
8782      (void) XDialogWidget(display,windows,"Add Border",
8783        "Enter border geometry:",geometry);
8784      if (*geometry == '\0')
8785        break;
8786      /*
8787        Add a border to the image.
8788      */
8789      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
8790      XSetCursorState(display,windows,MagickTrue);
8791      XCheckRefreshWindows(display,windows);
8792      (void) QueryColorDatabase(color,&(*image)->border_color,
8793        &(*image)->exception);
8794      (void) ParsePageGeometry(*image,geometry,&page_geometry,
8795        &(*image)->exception);
8796      border_image=BorderImage(*image,&page_geometry,&(*image)->exception);
8797      if (border_image != (Image *) NULL)
8798        {
8799          *image=DestroyImage(*image);
8800          *image=border_image;
8801        }
8802      CatchException(&(*image)->exception);
8803      XSetCursorState(display,windows,MagickFalse);
8804      if (windows->image.orphan != MagickFalse)
8805        break;
8806      windows->image.window_changes.width=(int) (*image)->columns;
8807      windows->image.window_changes.height=(int) (*image)->rows;
8808      XConfigureImageColormap(display,resource_info,windows,*image);
8809      (void) XConfigureImage(display,resource_info,windows,*image);
8810      break;
8811    }
8812    case AddFrameCommand:
8813    {
8814      FrameInfo
8815        frame_info;
8816
8817      Image
8818        *frame_image;
8819
8820      static char
8821        geometry[MaxTextExtent] = "6x6";
8822
8823      /*
8824        Query user for frame color and geometry.
8825      */
8826      XColorBrowserWidget(display,windows,"Select",color);
8827      if (*color == '\0')
8828        break;
8829      (void) XDialogWidget(display,windows,"Add Frame","Enter frame geometry:",
8830        geometry);
8831      if (*geometry == '\0')
8832        break;
8833      /*
8834        Surround image with an ornamental border.
8835      */
8836      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
8837      XSetCursorState(display,windows,MagickTrue);
8838      XCheckRefreshWindows(display,windows);
8839      (void) QueryColorDatabase(color,&(*image)->matte_color,
8840        &(*image)->exception);
8841      (void) ParsePageGeometry(*image,geometry,&page_geometry,
8842        &(*image)->exception);
8843      frame_info.width=page_geometry.width;
8844      frame_info.height=page_geometry.height;
8845      frame_info.outer_bevel=page_geometry.x;
8846      frame_info.inner_bevel=page_geometry.y;
8847      frame_info.x=(ssize_t) frame_info.width;
8848      frame_info.y=(ssize_t) frame_info.height;
8849      frame_info.width=(*image)->columns+2*frame_info.width;
8850      frame_info.height=(*image)->rows+2*frame_info.height;
8851      frame_image=FrameImage(*image,&frame_info,&(*image)->exception);
8852      if (frame_image != (Image *) NULL)
8853        {
8854          *image=DestroyImage(*image);
8855          *image=frame_image;
8856        }
8857      CatchException(&(*image)->exception);
8858      XSetCursorState(display,windows,MagickFalse);
8859      if (windows->image.orphan != MagickFalse)
8860        break;
8861      windows->image.window_changes.width=(int) (*image)->columns;
8862      windows->image.window_changes.height=(int) (*image)->rows;
8863      XConfigureImageColormap(display,resource_info,windows,*image);
8864      (void) XConfigureImage(display,resource_info,windows,*image);
8865      break;
8866    }
8867    case CommentCommand:
8868    {
8869      const char
8870        *value;
8871
8872      FILE
8873        *file;
8874
8875      int
8876        unique_file;
8877
8878      /*
8879        Edit image comment.
8880      */
8881      unique_file=AcquireUniqueFileResource(image_info->filename);
8882      if (unique_file == -1)
8883        XNoticeWidget(display,windows,"Unable to edit image comment",
8884          image_info->filename);
8885      value=GetImageProperty(*image,"comment");
8886      if (value == (char *) NULL)
8887        unique_file=close(unique_file)-1;
8888      else
8889        {
8890          register const char
8891            *p;
8892
8893          file=fdopen(unique_file,"w");
8894          if (file == (FILE *) NULL)
8895            {
8896              XNoticeWidget(display,windows,"Unable to edit image comment",
8897                image_info->filename);
8898              break;
8899            }
8900          for (p=value; *p != '\0'; p++)
8901            (void) fputc((int) *p,file);
8902          (void) fputc('\n',file);
8903          (void) fclose(file);
8904        }
8905      XSetCursorState(display,windows,MagickTrue);
8906      XCheckRefreshWindows(display,windows);
8907      status=InvokeDelegate(image_info,*image,"edit",(char *) NULL,
8908        &(*image)->exception);
8909      if (status == MagickFalse)
8910        XNoticeWidget(display,windows,"Unable to edit image comment",
8911          (char *) NULL);
8912      else
8913        {
8914          char
8915            *comment;
8916
8917          comment=FileToString(image_info->filename,~0UL,&(*image)->exception);
8918          if (comment != (char *) NULL)
8919            {
8920              (void) SetImageProperty(*image,"comment",comment);
8921              (*image)->taint=MagickTrue;
8922            }
8923        }
8924      (void) RelinquishUniqueFileResource(image_info->filename);
8925      XSetCursorState(display,windows,MagickFalse);
8926      break;
8927    }
8928    case LaunchCommand:
8929    {
8930      /*
8931        Launch program.
8932      */
8933      XSetCursorState(display,windows,MagickTrue);
8934      XCheckRefreshWindows(display,windows);
8935      (void) AcquireUniqueFilename(filename);
8936      (void) FormatLocaleString((*image)->filename,MaxTextExtent,"launch:%s",
8937        filename);
8938      status=WriteImage(image_info,*image);
8939      if (status == MagickFalse)
8940        XNoticeWidget(display,windows,"Unable to launch image editor",
8941          (char *) NULL);
8942      else
8943        {
8944          nexus=ReadImage(resource_info->image_info,&(*image)->exception);
8945          CatchException(&(*image)->exception);
8946          XClientMessage(display,windows->image.id,windows->im_protocols,
8947            windows->im_next_image,CurrentTime);
8948        }
8949      (void) RelinquishUniqueFileResource(filename);
8950      XSetCursorState(display,windows,MagickFalse);
8951      break;
8952    }
8953    case RegionofInterestCommand:
8954    {
8955      /*
8956        Apply an image processing technique to a region of interest.
8957      */
8958      (void) XROIImage(display,resource_info,windows,image);
8959      break;
8960    }
8961    case InfoCommand:
8962      break;
8963    case ZoomCommand:
8964    {
8965      /*
8966        Zoom image.
8967      */
8968      if (windows->magnify.mapped != MagickFalse)
8969        (void) XRaiseWindow(display,windows->magnify.id);
8970      else
8971        {
8972          /*
8973            Make magnify image.
8974          */
8975          XSetCursorState(display,windows,MagickTrue);
8976          (void) XMapRaised(display,windows->magnify.id);
8977          XSetCursorState(display,windows,MagickFalse);
8978        }
8979      break;
8980    }
8981    case ShowPreviewCommand:
8982    {
8983      char
8984        **previews;
8985
8986      Image
8987        *preview_image;
8988
8989      static char
8990        preview_type[MaxTextExtent] = "Gamma";
8991
8992      /*
8993        Select preview type from menu.
8994      */
8995      previews=GetCommandOptions(MagickPreviewOptions);
8996      if (previews == (char **) NULL)
8997        break;
8998      XListBrowserWidget(display,windows,&windows->widget,
8999        (const char **) previews,"Preview",
9000        "Select an enhancement, effect, or F/X:",preview_type);
9001      previews=DestroyStringList(previews);
9002      if (*preview_type == '\0')
9003        break;
9004      /*
9005        Show image preview.
9006      */
9007      XSetCursorState(display,windows,MagickTrue);
9008      XCheckRefreshWindows(display,windows);
9009      image_info->preview_type=(PreviewType)
9010        ParseCommandOption(MagickPreviewOptions,MagickFalse,preview_type);
9011      image_info->group=(ssize_t) windows->image.id;
9012      (void) DeleteImageProperty(*image,"label");
9013      (void) SetImageProperty(*image,"label","Preview");
9014      (void) AcquireUniqueFilename(filename);
9015      (void) FormatLocaleString((*image)->filename,MaxTextExtent,"preview:%s",
9016        filename);
9017      status=WriteImage(image_info,*image);
9018      (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9019      preview_image=ReadImage(image_info,&(*image)->exception);
9020      (void) RelinquishUniqueFileResource(filename);
9021      if (preview_image == (Image *) NULL)
9022        break;
9023      (void) FormatLocaleString(preview_image->filename,MaxTextExtent,"show:%s",
9024        filename);
9025      status=WriteImage(image_info,preview_image);
9026      preview_image=DestroyImage(preview_image);
9027      if (status == MagickFalse)
9028        XNoticeWidget(display,windows,"Unable to show image preview",
9029          (*image)->filename);
9030      XDelay(display,1500);
9031      XSetCursorState(display,windows,MagickFalse);
9032      break;
9033    }
9034    case ShowHistogramCommand:
9035    {
9036      Image
9037        *histogram_image;
9038
9039      /*
9040        Show image histogram.
9041      */
9042      XSetCursorState(display,windows,MagickTrue);
9043      XCheckRefreshWindows(display,windows);
9044      image_info->group=(ssize_t) windows->image.id;
9045      (void) DeleteImageProperty(*image,"label");
9046      (void) SetImageProperty(*image,"label","Histogram");
9047      (void) AcquireUniqueFilename(filename);
9048      (void) FormatLocaleString((*image)->filename,MaxTextExtent,"histogram:%s",
9049        filename);
9050      status=WriteImage(image_info,*image);
9051      (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9052      histogram_image=ReadImage(image_info,&(*image)->exception);
9053      (void) RelinquishUniqueFileResource(filename);
9054      if (histogram_image == (Image *) NULL)
9055        break;
9056      (void) FormatLocaleString(histogram_image->filename,MaxTextExtent,
9057        "show:%s",filename);
9058      status=WriteImage(image_info,histogram_image);
9059      histogram_image=DestroyImage(histogram_image);
9060      if (status == MagickFalse)
9061        XNoticeWidget(display,windows,"Unable to show histogram",
9062          (*image)->filename);
9063      XDelay(display,1500);
9064      XSetCursorState(display,windows,MagickFalse);
9065      break;
9066    }
9067    case ShowMatteCommand:
9068    {
9069      Image
9070        *matte_image;
9071
9072      if ((*image)->matte == MagickFalse)
9073        {
9074          XNoticeWidget(display,windows,
9075            "Image does not have any matte information",(*image)->filename);
9076          break;
9077        }
9078      /*
9079        Show image matte.
9080      */
9081      XSetCursorState(display,windows,MagickTrue);
9082      XCheckRefreshWindows(display,windows);
9083      image_info->group=(ssize_t) windows->image.id;
9084      (void) DeleteImageProperty(*image,"label");
9085      (void) SetImageProperty(*image,"label","Matte");
9086      (void) AcquireUniqueFilename(filename);
9087      (void) FormatLocaleString((*image)->filename,MaxTextExtent,"matte:%s",
9088        filename);
9089      status=WriteImage(image_info,*image);
9090      (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9091      matte_image=ReadImage(image_info,&(*image)->exception);
9092      (void) RelinquishUniqueFileResource(filename);
9093      if (matte_image == (Image *) NULL)
9094        break;
9095      (void) FormatLocaleString(matte_image->filename,MaxTextExtent,"show:%s",
9096        filename);
9097      status=WriteImage(image_info,matte_image);
9098      matte_image=DestroyImage(matte_image);
9099      if (status == MagickFalse)
9100        XNoticeWidget(display,windows,"Unable to show matte",
9101          (*image)->filename);
9102      XDelay(display,1500);
9103      XSetCursorState(display,windows,MagickFalse);
9104      break;
9105    }
9106    case BackgroundCommand:
9107    {
9108      /*
9109        Background image.
9110      */
9111      status=XBackgroundImage(display,resource_info,windows,image);
9112      if (status == MagickFalse)
9113        break;
9114      nexus=CloneImage(*image,0,0,MagickTrue,&(*image)->exception);
9115      if (nexus != (Image *) NULL)
9116        XClientMessage(display,windows->image.id,windows->im_protocols,
9117          windows->im_next_image,CurrentTime);
9118      break;
9119    }
9120    case SlideShowCommand:
9121    {
9122      static char
9123        delay[MaxTextExtent] = "5";
9124
9125      /*
9126        Display next image after pausing.
9127      */
9128      (void) XDialogWidget(display,windows,"Slide Show",
9129        "Pause how many 1/100ths of a second between images:",delay);
9130      if (*delay == '\0')
9131        break;
9132      resource_info->delay=StringToUnsignedLong(delay);
9133      XClientMessage(display,windows->image.id,windows->im_protocols,
9134        windows->im_next_image,CurrentTime);
9135      break;
9136    }
9137    case PreferencesCommand:
9138    {
9139      /*
9140        Set user preferences.
9141      */
9142      status=XPreferencesWidget(display,resource_info,windows);
9143      if (status == MagickFalse)
9144        break;
9145      nexus=CloneImage(*image,0,0,MagickTrue,&(*image)->exception);
9146      if (nexus != (Image *) NULL)
9147        XClientMessage(display,windows->image.id,windows->im_protocols,
9148          windows->im_next_image,CurrentTime);
9149      break;
9150    }
9151    case HelpCommand:
9152    {
9153      /*
9154        User requested help.
9155      */
9156      XTextViewWidget(display,resource_info,windows,MagickFalse,
9157        "Help Viewer - Display",DisplayHelp);
9158      break;
9159    }
9160    case BrowseDocumentationCommand:
9161    {
9162      Atom
9163        mozilla_atom;
9164
9165      Window
9166        mozilla_window,
9167        root_window;
9168
9169      /*
9170        Browse the ImageMagick documentation.
9171      */
9172      root_window=XRootWindow(display,XDefaultScreen(display));
9173      mozilla_atom=XInternAtom(display,"_MOZILLA_VERSION",MagickFalse);
9174      mozilla_window=XWindowByProperty(display,root_window,mozilla_atom);
9175      if (mozilla_window != (Window) NULL)
9176        {
9177          char
9178            command[MaxTextExtent],
9179            *url;
9180
9181          /*
9182            Display documentation using Netscape remote control.
9183          */
9184          url=GetMagickHomeURL();
9185          (void) FormatLocaleString(command,MaxTextExtent,
9186            "openurl(%s,new-tab)",url);
9187          url=DestroyString(url);
9188          mozilla_atom=XInternAtom(display,"_MOZILLA_COMMAND",MagickFalse);
9189          (void) XChangeProperty(display,mozilla_window,mozilla_atom,XA_STRING,
9190            8,PropModeReplace,(unsigned char *) command,(int) strlen(command));
9191          XSetCursorState(display,windows,MagickFalse);
9192          break;
9193        }
9194      XSetCursorState(display,windows,MagickTrue);
9195      XCheckRefreshWindows(display,windows);
9196      status=InvokeDelegate(image_info,*image,"browse",(char *) NULL,
9197        &(*image)->exception);
9198      if (status == MagickFalse)
9199        XNoticeWidget(display,windows,"Unable to browse documentation",
9200          (char *) NULL);
9201      XDelay(display,1500);
9202      XSetCursorState(display,windows,MagickFalse);
9203      break;
9204    }
9205    case VersionCommand:
9206    {
9207      XNoticeWidget(display,windows,GetMagickVersion((size_t *) NULL),
9208        GetMagickCopyright());
9209      break;
9210    }
9211    case SaveToUndoBufferCommand:
9212      break;
9213    default:
9214    {
9215      (void) XBell(display,0);
9216      break;
9217    }
9218  }
9219  image_info=DestroyImageInfo(image_info);
9220  return(nexus);
9221}
9222
9223/*
9224%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9225%                                                                             %
9226%                                                                             %
9227%                                                                             %
9228+   X M a g n i f y I m a g e                                                 %
9229%                                                                             %
9230%                                                                             %
9231%                                                                             %
9232%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9233%
9234%  XMagnifyImage() magnifies portions of the image as indicated by the pointer.
9235%  The magnified portion is displayed in a separate window.
9236%
9237%  The format of the XMagnifyImage method is:
9238%
9239%      void XMagnifyImage(Display *display,XWindows *windows,XEvent *event)
9240%
9241%  A description of each parameter follows:
9242%
9243%    o display: Specifies a connection to an X server;  returned from
9244%      XOpenDisplay.
9245%
9246%    o windows: Specifies a pointer to a XWindows structure.
9247%
9248%    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
9249%      the entire image is refreshed.
9250%
9251*/
9252static void XMagnifyImage(Display *display,XWindows *windows,XEvent *event)
9253{
9254  char
9255    text[MaxTextExtent];
9256
9257  register int
9258    x,
9259    y;
9260
9261  size_t
9262    state;
9263
9264  /*
9265    Update magnified image until the mouse button is released.
9266  */
9267  (void) XCheckDefineCursor(display,windows->image.id,windows->magnify.cursor);
9268  state=DefaultState;
9269  x=event->xbutton.x;
9270  y=event->xbutton.y;
9271  windows->magnify.x=(int) windows->image.x+x;
9272  windows->magnify.y=(int) windows->image.y+y;
9273  do
9274  {
9275    /*
9276      Map and unmap Info widget as text cursor crosses its boundaries.
9277    */
9278    if (windows->info.mapped != MagickFalse)
9279      {
9280        if ((x < (int) (windows->info.x+windows->info.width)) &&
9281            (y < (int) (windows->info.y+windows->info.height)))
9282          (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
9283      }
9284    else
9285      if ((x > (int) (windows->info.x+windows->info.width)) ||
9286          (y > (int) (windows->info.y+windows->info.height)))
9287        (void) XMapWindow(display,windows->info.id);
9288    if (windows->info.mapped != MagickFalse)
9289      {
9290        /*
9291          Display pointer position.
9292        */
9293        (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
9294          windows->magnify.x,windows->magnify.y);
9295        XInfoWidget(display,windows,text);
9296      }
9297    /*
9298      Wait for next event.
9299    */
9300    XScreenEvent(display,windows,event);
9301    switch (event->type)
9302    {
9303      case ButtonPress:
9304        break;
9305      case ButtonRelease:
9306      {
9307        /*
9308          User has finished magnifying image.
9309        */
9310        x=event->xbutton.x;
9311        y=event->xbutton.y;
9312        state|=ExitState;
9313        break;
9314      }
9315      case Expose:
9316        break;
9317      case MotionNotify:
9318      {
9319        x=event->xmotion.x;
9320        y=event->xmotion.y;
9321        break;
9322      }
9323      default:
9324        break;
9325    }
9326    /*
9327      Check boundary conditions.
9328    */
9329    if (x < 0)
9330      x=0;
9331    else
9332      if (x >= (int) windows->image.width)
9333        x=(int) windows->image.width-1;
9334    if (y < 0)
9335      y=0;
9336    else
9337     if (y >= (int) windows->image.height)
9338       y=(int) windows->image.height-1;
9339  } while ((state & ExitState) == 0);
9340  /*
9341    Display magnified image.
9342  */
9343  XSetCursorState(display,windows,MagickFalse);
9344}
9345
9346/*
9347%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9348%                                                                             %
9349%                                                                             %
9350%                                                                             %
9351+   X M a g n i f y W i n d o w C o m m a n d                                 %
9352%                                                                             %
9353%                                                                             %
9354%                                                                             %
9355%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9356%
9357%  XMagnifyWindowCommand() moves the image within an Magnify window by one
9358%  pixel as specified by the key symbol.
9359%
9360%  The format of the XMagnifyWindowCommand method is:
9361%
9362%      void XMagnifyWindowCommand(Display *display,XWindows *windows,
9363%        const MagickStatusType state,const KeySym key_symbol)
9364%
9365%  A description of each parameter follows:
9366%
9367%    o display: Specifies a connection to an X server; returned from
9368%      XOpenDisplay.
9369%
9370%    o windows: Specifies a pointer to a XWindows structure.
9371%
9372%    o state: key mask.
9373%
9374%    o key_symbol: Specifies a KeySym which indicates which side of the image
9375%      to trim.
9376%
9377*/
9378static void XMagnifyWindowCommand(Display *display,XWindows *windows,
9379  const MagickStatusType state,const KeySym key_symbol)
9380{
9381  unsigned int
9382    quantum;
9383
9384  /*
9385    User specified a magnify factor or position.
9386  */
9387  quantum=1;
9388  if ((state & Mod1Mask) != 0)
9389    quantum=10;
9390  switch ((int) key_symbol)
9391  {
9392    case QuitCommand:
9393    {
9394      (void) XWithdrawWindow(display,windows->magnify.id,
9395        windows->magnify.screen);
9396      break;
9397    }
9398    case XK_Home:
9399    case XK_KP_Home:
9400    {
9401      windows->magnify.x=(int) windows->image.width/2;
9402      windows->magnify.y=(int) windows->image.height/2;
9403      break;
9404    }
9405    case XK_Left:
9406    case XK_KP_Left:
9407    {
9408      if (windows->magnify.x > 0)
9409        windows->magnify.x-=quantum;
9410      break;
9411    }
9412    case XK_Up:
9413    case XK_KP_Up:
9414    {
9415      if (windows->magnify.y > 0)
9416        windows->magnify.y-=quantum;
9417      break;
9418    }
9419    case XK_Right:
9420    case XK_KP_Right:
9421    {
9422      if (windows->magnify.x < (int) (windows->image.ximage->width-1))
9423        windows->magnify.x+=quantum;
9424      break;
9425    }
9426    case XK_Down:
9427    case XK_KP_Down:
9428    {
9429      if (windows->magnify.y < (int) (windows->image.ximage->height-1))
9430        windows->magnify.y+=quantum;
9431      break;
9432    }
9433    case XK_0:
9434    case XK_1:
9435    case XK_2:
9436    case XK_3:
9437    case XK_4:
9438    case XK_5:
9439    case XK_6:
9440    case XK_7:
9441    case XK_8:
9442    case XK_9:
9443    {
9444      windows->magnify.data=(key_symbol-XK_0);
9445      break;
9446    }
9447    case XK_KP_0:
9448    case XK_KP_1:
9449    case XK_KP_2:
9450    case XK_KP_3:
9451    case XK_KP_4:
9452    case XK_KP_5:
9453    case XK_KP_6:
9454    case XK_KP_7:
9455    case XK_KP_8:
9456    case XK_KP_9:
9457    {
9458      windows->magnify.data=(key_symbol-XK_KP_0);
9459      break;
9460    }
9461    default:
9462      break;
9463  }
9464  XMakeMagnifyImage(display,windows);
9465}
9466
9467/*
9468%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9469%                                                                             %
9470%                                                                             %
9471%                                                                             %
9472+   X M a k e P a n I m a g e                                                 %
9473%                                                                             %
9474%                                                                             %
9475%                                                                             %
9476%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9477%
9478%  XMakePanImage() creates a thumbnail of the image and displays it in the Pan
9479%  icon window.
9480%
9481%  The format of the XMakePanImage method is:
9482%
9483%        void XMakePanImage(Display *display,XResourceInfo *resource_info,
9484%          XWindows *windows,Image *image)
9485%
9486%  A description of each parameter follows:
9487%
9488%    o display: Specifies a connection to an X server;  returned from
9489%      XOpenDisplay.
9490%
9491%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9492%
9493%    o windows: Specifies a pointer to a XWindows structure.
9494%
9495%    o image: the image.
9496%
9497*/
9498static void XMakePanImage(Display *display,XResourceInfo *resource_info,
9499  XWindows *windows,Image *image)
9500{
9501  MagickStatusType
9502    status;
9503
9504  /*
9505    Create and display image for panning icon.
9506  */
9507  XSetCursorState(display,windows,MagickTrue);
9508  XCheckRefreshWindows(display,windows);
9509  windows->pan.x=(int) windows->image.x;
9510  windows->pan.y=(int) windows->image.y;
9511  status=XMakeImage(display,resource_info,&windows->pan,image,
9512    windows->pan.width,windows->pan.height);
9513  if (status == MagickFalse)
9514    ThrowXWindowFatalException(XServerError,image->exception.reason,
9515      image->exception.description);
9516  (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
9517    windows->pan.pixmap);
9518  (void) XClearWindow(display,windows->pan.id);
9519  XDrawPanRectangle(display,windows);
9520  XSetCursorState(display,windows,MagickFalse);
9521}
9522
9523/*
9524%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9525%                                                                             %
9526%                                                                             %
9527%                                                                             %
9528+   X M a t t a E d i t I m a g e                                             %
9529%                                                                             %
9530%                                                                             %
9531%                                                                             %
9532%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9533%
9534%  XMatteEditImage() allows the user to interactively change the Matte channel
9535%  of an image.  If the image is PseudoClass it is promoted to DirectClass
9536%  before the matte information is stored.
9537%
9538%  The format of the XMatteEditImage method is:
9539%
9540%      MagickBooleanType XMatteEditImage(Display *display,
9541%        XResourceInfo *resource_info,XWindows *windows,Image **image)
9542%
9543%  A description of each parameter follows:
9544%
9545%    o display: Specifies a connection to an X server;  returned from
9546%      XOpenDisplay.
9547%
9548%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9549%
9550%    o windows: Specifies a pointer to a XWindows structure.
9551%
9552%    o image: the image; returned from ReadImage.
9553%
9554*/
9555static MagickBooleanType XMatteEditImage(Display *display,
9556  XResourceInfo *resource_info,XWindows *windows,Image **image)
9557{
9558  static char
9559    matte[MaxTextExtent] = "0";
9560
9561  static const char
9562    *MatteEditMenu[] =
9563    {
9564      "Method",
9565      "Border Color",
9566      "Fuzz",
9567      "Matte Value",
9568      "Undo",
9569      "Help",
9570      "Dismiss",
9571      (char *) NULL
9572    };
9573
9574  static const ModeType
9575    MatteEditCommands[] =
9576    {
9577      MatteEditMethod,
9578      MatteEditBorderCommand,
9579      MatteEditFuzzCommand,
9580      MatteEditValueCommand,
9581      MatteEditUndoCommand,
9582      MatteEditHelpCommand,
9583      MatteEditDismissCommand
9584    };
9585
9586  static PaintMethod
9587    method = PointMethod;
9588
9589  static XColor
9590    border_color = { 0, 0, 0, 0, 0, 0 };
9591
9592  char
9593    command[MaxTextExtent],
9594    text[MaxTextExtent];
9595
9596  Cursor
9597    cursor;
9598
9599  int
9600    entry,
9601    id,
9602    x,
9603    x_offset,
9604    y,
9605    y_offset;
9606
9607  register int
9608    i;
9609
9610  register Quantum
9611    *q;
9612
9613  unsigned int
9614    height,
9615    width;
9616
9617  size_t
9618    state;
9619
9620  XEvent
9621    event;
9622
9623  /*
9624    Map Command widget.
9625  */
9626  (void) CloneString(&windows->command.name,"Matte Edit");
9627  windows->command.data=4;
9628  (void) XCommandWidget(display,windows,MatteEditMenu,(XEvent *) NULL);
9629  (void) XMapRaised(display,windows->command.id);
9630  XClientMessage(display,windows->image.id,windows->im_protocols,
9631    windows->im_update_widget,CurrentTime);
9632  /*
9633    Make cursor.
9634  */
9635  cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
9636    resource_info->background_color,resource_info->foreground_color);
9637  (void) XCheckDefineCursor(display,windows->image.id,cursor);
9638  /*
9639    Track pointer until button 1 is pressed.
9640  */
9641  XQueryPosition(display,windows->image.id,&x,&y);
9642  (void) XSelectInput(display,windows->image.id,
9643    windows->image.attributes.event_mask | PointerMotionMask);
9644  state=DefaultState;
9645  do
9646  {
9647    if (windows->info.mapped != MagickFalse)
9648      {
9649        /*
9650          Display pointer position.
9651        */
9652        (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
9653          x+windows->image.x,y+windows->image.y);
9654        XInfoWidget(display,windows,text);
9655      }
9656    /*
9657      Wait for next event.
9658    */
9659    XScreenEvent(display,windows,&event);
9660    if (event.xany.window == windows->command.id)
9661      {
9662        /*
9663          Select a command from the Command widget.
9664        */
9665        id=XCommandWidget(display,windows,MatteEditMenu,&event);
9666        if (id < 0)
9667          {
9668            (void) XCheckDefineCursor(display,windows->image.id,cursor);
9669            continue;
9670          }
9671        switch (MatteEditCommands[id])
9672        {
9673          case MatteEditMethod:
9674          {
9675            char
9676              **methods;
9677
9678            /*
9679              Select a method from the pop-up menu.
9680            */
9681            methods=GetCommandOptions(MagickMethodOptions);
9682            if (methods == (char **) NULL)
9683              break;
9684            entry=XMenuWidget(display,windows,MatteEditMenu[id],
9685              (const char **) methods,command);
9686            if (entry >= 0)
9687              method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
9688                MagickFalse,methods[entry]);
9689            methods=DestroyStringList(methods);
9690            break;
9691          }
9692          case MatteEditBorderCommand:
9693          {
9694            const char
9695              *ColorMenu[MaxNumberPens];
9696
9697            int
9698              pen_number;
9699
9700            /*
9701              Initialize menu selections.
9702            */
9703            for (i=0; i < (int) (MaxNumberPens-2); i++)
9704              ColorMenu[i]=resource_info->pen_colors[i];
9705            ColorMenu[MaxNumberPens-2]="Browser...";
9706            ColorMenu[MaxNumberPens-1]=(const char *) NULL;
9707            /*
9708              Select a pen color from the pop-up menu.
9709            */
9710            pen_number=XMenuWidget(display,windows,MatteEditMenu[id],
9711              (const char **) ColorMenu,command);
9712            if (pen_number < 0)
9713              break;
9714            if (pen_number == (MaxNumberPens-2))
9715              {
9716                static char
9717                  color_name[MaxTextExtent] = "gray";
9718
9719                /*
9720                  Select a pen color from a dialog.
9721                */
9722                resource_info->pen_colors[pen_number]=color_name;
9723                XColorBrowserWidget(display,windows,"Select",color_name);
9724                if (*color_name == '\0')
9725                  break;
9726              }
9727            /*
9728              Set border color.
9729            */
9730            (void) XParseColor(display,windows->map_info->colormap,
9731              resource_info->pen_colors[pen_number],&border_color);
9732            break;
9733          }
9734          case MatteEditFuzzCommand:
9735          {
9736            static char
9737              fuzz[MaxTextExtent];
9738
9739            static const char
9740              *FuzzMenu[] =
9741              {
9742                "0%",
9743                "2%",
9744                "5%",
9745                "10%",
9746                "15%",
9747                "Dialog...",
9748                (char *) NULL,
9749              };
9750
9751            /*
9752              Select a command from the pop-up menu.
9753            */
9754            entry=XMenuWidget(display,windows,MatteEditMenu[id],FuzzMenu,
9755              command);
9756            if (entry < 0)
9757              break;
9758            if (entry != 5)
9759              {
9760                (*image)->fuzz=SiPrefixToDouble(FuzzMenu[entry],1.0*
9761                  QuantumRange+1.0);
9762                break;
9763              }
9764            (void) CopyMagickString(fuzz,"20%",MaxTextExtent);
9765            (void) XDialogWidget(display,windows,"Ok",
9766              "Enter fuzz factor (0.0 - 99.9%):",fuzz);
9767            if (*fuzz == '\0')
9768              break;
9769            (void) ConcatenateMagickString(fuzz,"%",MaxTextExtent);
9770            (*image)->fuzz=SiPrefixToDouble(fuzz,1.0*QuantumRange+1.0);
9771            break;
9772          }
9773          case MatteEditValueCommand:
9774          {
9775            static char
9776              message[MaxTextExtent];
9777
9778            static const char
9779              *MatteMenu[] =
9780              {
9781                "Opaque",
9782                "Transparent",
9783                "Dialog...",
9784                (char *) NULL,
9785              };
9786
9787            /*
9788              Select a command from the pop-up menu.
9789            */
9790            entry=XMenuWidget(display,windows,MatteEditMenu[id],MatteMenu,
9791              command);
9792            if (entry < 0)
9793              break;
9794            if (entry != 2)
9795              {
9796                (void) FormatLocaleString(matte,MaxTextExtent,QuantumFormat,
9797                  OpaqueAlpha);
9798                if (LocaleCompare(MatteMenu[entry],"Transparent") == 0)
9799                  (void) FormatLocaleString(matte,MaxTextExtent,QuantumFormat,
9800                    (Quantum) TransparentAlpha);
9801                break;
9802              }
9803            (void) FormatLocaleString(message,MaxTextExtent,
9804              "Enter matte value (0 - " QuantumFormat "):",(Quantum)
9805              QuantumRange);
9806            (void) XDialogWidget(display,windows,"Matte",message,matte);
9807            if (*matte == '\0')
9808              break;
9809            break;
9810          }
9811          case MatteEditUndoCommand:
9812          {
9813            (void) XMagickCommand(display,resource_info,windows,UndoCommand,
9814              image);
9815            break;
9816          }
9817          case MatteEditHelpCommand:
9818          {
9819            XTextViewWidget(display,resource_info,windows,MagickFalse,
9820              "Help Viewer - Matte Edit",ImageMatteEditHelp);
9821            break;
9822          }
9823          case MatteEditDismissCommand:
9824          {
9825            /*
9826              Prematurely exit.
9827            */
9828            state|=EscapeState;
9829            state|=ExitState;
9830            break;
9831          }
9832          default:
9833            break;
9834        }
9835        (void) XCheckDefineCursor(display,windows->image.id,cursor);
9836        continue;
9837      }
9838    switch (event.type)
9839    {
9840      case ButtonPress:
9841      {
9842        if (event.xbutton.button != Button1)
9843          break;
9844        if ((event.xbutton.window != windows->image.id) &&
9845            (event.xbutton.window != windows->magnify.id))
9846          break;
9847        /*
9848          Update matte data.
9849        */
9850        x=event.xbutton.x;
9851        y=event.xbutton.y;
9852        (void) XMagickCommand(display,resource_info,windows,
9853          SaveToUndoBufferCommand,image);
9854        state|=UpdateConfigurationState;
9855        break;
9856      }
9857      case ButtonRelease:
9858      {
9859        if (event.xbutton.button != Button1)
9860          break;
9861        if ((event.xbutton.window != windows->image.id) &&
9862            (event.xbutton.window != windows->magnify.id))
9863          break;
9864        /*
9865          Update colormap information.
9866        */
9867        x=event.xbutton.x;
9868        y=event.xbutton.y;
9869        XConfigureImageColormap(display,resource_info,windows,*image);
9870        (void) XConfigureImage(display,resource_info,windows,*image);
9871        XInfoWidget(display,windows,text);
9872        (void) XCheckDefineCursor(display,windows->image.id,cursor);
9873        state&=(~UpdateConfigurationState);
9874        break;
9875      }
9876      case Expose:
9877        break;
9878      case KeyPress:
9879      {
9880        char
9881          command[MaxTextExtent];
9882
9883        KeySym
9884          key_symbol;
9885
9886        if (event.xkey.window == windows->magnify.id)
9887          {
9888            Window
9889              window;
9890
9891            window=windows->magnify.id;
9892            while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
9893          }
9894        if (event.xkey.window != windows->image.id)
9895          break;
9896        /*
9897          Respond to a user key press.
9898        */
9899        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
9900          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
9901        switch ((int) key_symbol)
9902        {
9903          case XK_Escape:
9904          case XK_F20:
9905          {
9906            /*
9907              Prematurely exit.
9908            */
9909            state|=ExitState;
9910            break;
9911          }
9912          case XK_F1:
9913          case XK_Help:
9914          {
9915            XTextViewWidget(display,resource_info,windows,MagickFalse,
9916              "Help Viewer - Matte Edit",ImageMatteEditHelp);
9917            break;
9918          }
9919          default:
9920          {
9921            (void) XBell(display,0);
9922            break;
9923          }
9924        }
9925        break;
9926      }
9927      case MotionNotify:
9928      {
9929        /*
9930          Map and unmap Info widget as cursor crosses its boundaries.
9931        */
9932        x=event.xmotion.x;
9933        y=event.xmotion.y;
9934        if (windows->info.mapped != MagickFalse)
9935          {
9936            if ((x < (int) (windows->info.x+windows->info.width)) &&
9937                (y < (int) (windows->info.y+windows->info.height)))
9938              (void) XWithdrawWindow(display,windows->info.id,
9939                windows->info.screen);
9940          }
9941        else
9942          if ((x > (int) (windows->info.x+windows->info.width)) ||
9943              (y > (int) (windows->info.y+windows->info.height)))
9944            (void) XMapWindow(display,windows->info.id);
9945        break;
9946      }
9947      default:
9948        break;
9949    }
9950    if (event.xany.window == windows->magnify.id)
9951      {
9952        x=windows->magnify.x-windows->image.x;
9953        y=windows->magnify.y-windows->image.y;
9954      }
9955    x_offset=x;
9956    y_offset=y;
9957    if ((state & UpdateConfigurationState) != 0)
9958      {
9959        CacheView
9960          *image_view;
9961
9962        ExceptionInfo
9963          *exception;
9964
9965        int
9966          x,
9967          y;
9968
9969        /*
9970          Matte edit is relative to image configuration.
9971        */
9972        (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
9973          MagickTrue);
9974        XPutPixel(windows->image.ximage,x_offset,y_offset,
9975          windows->pixel_info->background_color.pixel);
9976        width=(unsigned int) (*image)->columns;
9977        height=(unsigned int) (*image)->rows;
9978        x=0;
9979        y=0;
9980        if (windows->image.crop_geometry != (char *) NULL)
9981          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,
9982            &height);
9983        x_offset=(int) (width*(windows->image.x+x_offset)/
9984          windows->image.ximage->width+x);
9985        y_offset=(int) (height*(windows->image.y+y_offset)/
9986          windows->image.ximage->height+y);
9987        if ((x_offset < 0) || (y_offset < 0))
9988          continue;
9989        if ((x_offset >= (int) (*image)->columns) ||
9990            (y_offset >= (int) (*image)->rows))
9991          continue;
9992        if (SetImageStorageClass(*image,DirectClass) == MagickFalse)
9993          return(MagickFalse);
9994        (*image)->matte=MagickTrue;
9995        exception=(&(*image)->exception);
9996        image_view=AcquireCacheView(*image);
9997        switch (method)
9998        {
9999          case PointMethod:
10000          default:
10001          {
10002            /*
10003              Update matte information using point algorithm.
10004            */
10005            q=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,
10006              (ssize_t) y_offset,1,1,exception);
10007            if (q == (Quantum *) NULL)
10008              break;
10009            SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10010            (void) SyncCacheViewAuthenticPixels(image_view,exception);
10011            break;
10012          }
10013          case ReplaceMethod:
10014          {
10015            PixelPacket
10016              pixel,
10017              target;
10018
10019            /*
10020              Update matte information using replace algorithm.
10021            */
10022            (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t) x_offset,
10023              (ssize_t) y_offset,&target,exception);
10024            for (y=0; y < (int) (*image)->rows; y++)
10025            {
10026              q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10027                (*image)->columns,1,&(*image)->exception);
10028              if (q == (Quantum *) NULL)
10029                break;
10030              for (x=0; x < (int) (*image)->columns; x++)
10031              {
10032                GetPixelPacket(*image,q,&pixel);
10033                if (IsFuzzyEquivalencePixelPacket(*image,&pixel,&target))
10034                  SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10035                q+=GetPixelChannels(*image);
10036              }
10037              if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
10038                break;
10039            }
10040            break;
10041          }
10042          case FloodfillMethod:
10043          case FillToBorderMethod:
10044          {
10045            DrawInfo
10046              *draw_info;
10047
10048            PixelInfo
10049              target;
10050
10051            /*
10052              Update matte information using floodfill algorithm.
10053            */
10054            (void) GetOneVirtualMagickPixel(*image,(ssize_t) x_offset,
10055              (ssize_t) y_offset,&target,exception);
10056            if (method == FillToBorderMethod)
10057              {
10058                target.red=(MagickRealType) ScaleShortToQuantum(
10059                  border_color.red);
10060                target.green=(MagickRealType) ScaleShortToQuantum(
10061                  border_color.green);
10062                target.blue=(MagickRealType) ScaleShortToQuantum(
10063                  border_color.blue);
10064              }
10065            draw_info=CloneDrawInfo(resource_info->image_info,
10066              (DrawInfo *) NULL);
10067            draw_info->fill.alpha=ClampToQuantum(InterpretLocaleValue(matte,
10068              (char **) NULL));
10069            (void) FloodfillPaintImage(*image,OpacityChannel,draw_info,&target,
10070              (ssize_t) x_offset,(ssize_t) y_offset,
10071              method == FloodfillMethod ? MagickFalse : MagickTrue);
10072            draw_info=DestroyDrawInfo(draw_info);
10073            break;
10074          }
10075          case ResetMethod:
10076          {
10077            /*
10078              Update matte information using reset algorithm.
10079            */
10080            if (SetImageStorageClass(*image,DirectClass) == MagickFalse)
10081              return(MagickFalse);
10082            for (y=0; y < (int) (*image)->rows; y++)
10083            {
10084              q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10085                (*image)->columns,1,exception);
10086              if (q == (Quantum *) NULL)
10087                break;
10088              for (x=0; x < (int) (*image)->columns; x++)
10089              {
10090                SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10091                q+=GetPixelChannels(*image);
10092              }
10093              if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
10094                break;
10095            }
10096            if (StringToLong(matte) == (long) OpaqueAlpha)
10097              (*image)->matte=MagickFalse;
10098            break;
10099          }
10100        }
10101        image_view=DestroyCacheView(image_view);
10102        state&=(~UpdateConfigurationState);
10103      }
10104  } while ((state & ExitState) == 0);
10105  (void) XSelectInput(display,windows->image.id,
10106    windows->image.attributes.event_mask);
10107  XSetCursorState(display,windows,MagickFalse);
10108  (void) XFreeCursor(display,cursor);
10109  return(MagickTrue);
10110}
10111
10112/*
10113%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10114%                                                                             %
10115%                                                                             %
10116%                                                                             %
10117+   X O p e n I m a g e                                                       %
10118%                                                                             %
10119%                                                                             %
10120%                                                                             %
10121%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10122%
10123%  XOpenImage() loads an image from a file.
10124%
10125%  The format of the XOpenImage method is:
10126%
10127%     Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10128%       XWindows *windows,const unsigned int command)
10129%
10130%  A description of each parameter follows:
10131%
10132%    o display: Specifies a connection to an X server; returned from
10133%      XOpenDisplay.
10134%
10135%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10136%
10137%    o windows: Specifies a pointer to a XWindows structure.
10138%
10139%    o command: A value other than zero indicates that the file is selected
10140%      from the command line argument list.
10141%
10142*/
10143static Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10144  XWindows *windows,const MagickBooleanType command)
10145{
10146  const MagickInfo
10147    *magick_info;
10148
10149  ExceptionInfo
10150    *exception;
10151
10152  Image
10153    *nexus;
10154
10155  ImageInfo
10156    *image_info;
10157
10158  static char
10159    filename[MaxTextExtent] = "\0";
10160
10161  /*
10162    Request file name from user.
10163  */
10164  if (command == MagickFalse)
10165    XFileBrowserWidget(display,windows,"Open",filename);
10166  else
10167    {
10168      char
10169        **filelist,
10170        **files;
10171
10172      int
10173        count,
10174        status;
10175
10176      register int
10177        i,
10178        j;
10179
10180      /*
10181        Select next image from the command line.
10182      */
10183      status=XGetCommand(display,windows->image.id,&files,&count);
10184      if (status == 0)
10185        {
10186          ThrowXWindowFatalException(XServerError,"UnableToGetProperty","...");
10187          return((Image *) NULL);
10188        }
10189      filelist=(char **) AcquireQuantumMemory((size_t) count,sizeof(*filelist));
10190      if (filelist == (char **) NULL)
10191        {
10192          ThrowXWindowFatalException(ResourceLimitError,
10193            "MemoryAllocationFailed","...");
10194          (void) XFreeStringList(files);
10195          return((Image *) NULL);
10196        }
10197      j=0;
10198      for (i=1; i < count; i++)
10199        if (*files[i] != '-')
10200          filelist[j++]=files[i];
10201      filelist[j]=(char *) NULL;
10202      XListBrowserWidget(display,windows,&windows->widget,
10203        (const char **) filelist,"Load","Select Image to Load:",filename);
10204      filelist=(char **) RelinquishMagickMemory(filelist);
10205      (void) XFreeStringList(files);
10206    }
10207  if (*filename == '\0')
10208    return((Image *) NULL);
10209  image_info=CloneImageInfo(resource_info->image_info);
10210  (void) SetImageInfoProgressMonitor(image_info,(MagickProgressMonitor) NULL,
10211    (void *) NULL);
10212  (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
10213  exception=AcquireExceptionInfo();
10214  (void) SetImageInfo(image_info,0,exception);
10215  if (LocaleCompare(image_info->magick,"X") == 0)
10216    {
10217      char
10218        seconds[MaxTextExtent];
10219
10220      /*
10221        User may want to delay the X server screen grab.
10222      */
10223      (void) CopyMagickString(seconds,"0",MaxTextExtent);
10224      (void) XDialogWidget(display,windows,"Grab","Enter any delay in seconds:",
10225        seconds);
10226      if (*seconds == '\0')
10227        return((Image *) NULL);
10228      XDelay(display,(size_t) (1000*StringToLong(seconds)));
10229    }
10230  magick_info=GetMagickInfo(image_info->magick,exception);
10231  if ((magick_info != (const MagickInfo *) NULL) &&
10232      (magick_info->raw != MagickFalse))
10233    {
10234      char
10235        geometry[MaxTextExtent];
10236
10237      /*
10238        Request image size from the user.
10239      */
10240      (void) CopyMagickString(geometry,"512x512",MaxTextExtent);
10241      if (image_info->size != (char *) NULL)
10242        (void) CopyMagickString(geometry,image_info->size,MaxTextExtent);
10243      (void) XDialogWidget(display,windows,"Load","Enter the image geometry:",
10244        geometry);
10245      (void) CloneString(&image_info->size,geometry);
10246    }
10247  /*
10248    Load the image.
10249  */
10250  XSetCursorState(display,windows,MagickTrue);
10251  XCheckRefreshWindows(display,windows);
10252  (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
10253  nexus=ReadImage(image_info,exception);
10254  CatchException(exception);
10255  XSetCursorState(display,windows,MagickFalse);
10256  if (nexus != (Image *) NULL)
10257    XClientMessage(display,windows->image.id,windows->im_protocols,
10258      windows->im_next_image,CurrentTime);
10259  else
10260    {
10261      char
10262        *text,
10263        **textlist;
10264
10265      /*
10266        Unknown image format.
10267      */
10268      text=FileToString(filename,~0,exception);
10269      if (text == (char *) NULL)
10270        return((Image *) NULL);
10271      textlist=StringToList(text);
10272      if (textlist != (char **) NULL)
10273        {
10274          char
10275            title[MaxTextExtent];
10276
10277          register int
10278            i;
10279
10280          (void) FormatLocaleString(title,MaxTextExtent,
10281            "Unknown format: %s",filename);
10282          XTextViewWidget(display,resource_info,windows,MagickTrue,title,
10283            (const char **) textlist);
10284          for (i=0; textlist[i] != (char *) NULL; i++)
10285            textlist[i]=DestroyString(textlist[i]);
10286          textlist=(char **) RelinquishMagickMemory(textlist);
10287        }
10288      text=DestroyString(text);
10289    }
10290  exception=DestroyExceptionInfo(exception);
10291  image_info=DestroyImageInfo(image_info);
10292  return(nexus);
10293}
10294
10295/*
10296%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10297%                                                                             %
10298%                                                                             %
10299%                                                                             %
10300+   X P a n I m a g e                                                         %
10301%                                                                             %
10302%                                                                             %
10303%                                                                             %
10304%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10305%
10306%  XPanImage() pans the image until the mouse button is released.
10307%
10308%  The format of the XPanImage method is:
10309%
10310%      void XPanImage(Display *display,XWindows *windows,XEvent *event)
10311%
10312%  A description of each parameter follows:
10313%
10314%    o display: Specifies a connection to an X server;  returned from
10315%      XOpenDisplay.
10316%
10317%    o windows: Specifies a pointer to a XWindows structure.
10318%
10319%    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
10320%      the entire image is refreshed.
10321%
10322*/
10323static void XPanImage(Display *display,XWindows *windows,XEvent *event)
10324{
10325  char
10326    text[MaxTextExtent];
10327
10328  Cursor
10329    cursor;
10330
10331  MagickRealType
10332    x_factor,
10333    y_factor;
10334
10335  RectangleInfo
10336    pan_info;
10337
10338  size_t
10339    state;
10340
10341  /*
10342    Define cursor.
10343  */
10344  if ((windows->image.ximage->width > (int) windows->image.width) &&
10345      (windows->image.ximage->height > (int) windows->image.height))
10346    cursor=XCreateFontCursor(display,XC_fleur);
10347  else
10348    if (windows->image.ximage->width > (int) windows->image.width)
10349      cursor=XCreateFontCursor(display,XC_sb_h_double_arrow);
10350    else
10351      if (windows->image.ximage->height > (int) windows->image.height)
10352        cursor=XCreateFontCursor(display,XC_sb_v_double_arrow);
10353      else
10354        cursor=XCreateFontCursor(display,XC_arrow);
10355  (void) XCheckDefineCursor(display,windows->pan.id,cursor);
10356  /*
10357    Pan image as pointer moves until the mouse button is released.
10358  */
10359  x_factor=(MagickRealType) windows->image.ximage->width/windows->pan.width;
10360  y_factor=(MagickRealType) windows->image.ximage->height/windows->pan.height;
10361  pan_info.width=windows->pan.width*windows->image.width/
10362    windows->image.ximage->width;
10363  pan_info.height=windows->pan.height*windows->image.height/
10364    windows->image.ximage->height;
10365  pan_info.x=0;
10366  pan_info.y=0;
10367  state=UpdateConfigurationState;
10368  do
10369  {
10370    switch (event->type)
10371    {
10372      case ButtonPress:
10373      {
10374        /*
10375          User choose an initial pan location.
10376        */
10377        pan_info.x=(ssize_t) event->xbutton.x;
10378        pan_info.y=(ssize_t) event->xbutton.y;
10379        state|=UpdateConfigurationState;
10380        break;
10381      }
10382      case ButtonRelease:
10383      {
10384        /*
10385          User has finished panning the image.
10386        */
10387        pan_info.x=(ssize_t) event->xbutton.x;
10388        pan_info.y=(ssize_t) event->xbutton.y;
10389        state|=UpdateConfigurationState | ExitState;
10390        break;
10391      }
10392      case MotionNotify:
10393      {
10394        pan_info.x=(ssize_t) event->xmotion.x;
10395        pan_info.y=(ssize_t) event->xmotion.y;
10396        state|=UpdateConfigurationState;
10397      }
10398      default:
10399        break;
10400    }
10401    if ((state & UpdateConfigurationState) != 0)
10402      {
10403        /*
10404          Check boundary conditions.
10405        */
10406        if (pan_info.x < (ssize_t) (pan_info.width/2))
10407          pan_info.x=0;
10408        else
10409          pan_info.x=(ssize_t) (x_factor*(pan_info.x-(pan_info.width/2)));
10410        if (pan_info.x < 0)
10411          pan_info.x=0;
10412        else
10413          if ((int) (pan_info.x+windows->image.width) >
10414              windows->image.ximage->width)
10415            pan_info.x=(ssize_t)
10416              (windows->image.ximage->width-windows->image.width);
10417        if (pan_info.y < (ssize_t) (pan_info.height/2))
10418          pan_info.y=0;
10419        else
10420          pan_info.y=(ssize_t) (y_factor*(pan_info.y-(pan_info.height/2)));
10421        if (pan_info.y < 0)
10422          pan_info.y=0;
10423        else
10424          if ((int) (pan_info.y+windows->image.height) >
10425              windows->image.ximage->height)
10426            pan_info.y=(ssize_t)
10427              (windows->image.ximage->height-windows->image.height);
10428        if ((windows->image.x != (int) pan_info.x) ||
10429            (windows->image.y != (int) pan_info.y))
10430          {
10431            /*
10432              Display image pan offset.
10433            */
10434            windows->image.x=(int) pan_info.x;
10435            windows->image.y=(int) pan_info.y;
10436            (void) FormatLocaleString(text,MaxTextExtent," %ux%u%+d%+d ",
10437              windows->image.width,windows->image.height,windows->image.x,
10438              windows->image.y);
10439            XInfoWidget(display,windows,text);
10440            /*
10441              Refresh Image window.
10442            */
10443            XDrawPanRectangle(display,windows);
10444            XRefreshWindow(display,&windows->image,(XEvent *) NULL);
10445          }
10446        state&=(~UpdateConfigurationState);
10447      }
10448    /*
10449      Wait for next event.
10450    */
10451    if ((state & ExitState) == 0)
10452      XScreenEvent(display,windows,event);
10453  } while ((state & ExitState) == 0);
10454  /*
10455    Restore cursor.
10456  */
10457  (void) XCheckDefineCursor(display,windows->pan.id,windows->pan.cursor);
10458  (void) XFreeCursor(display,cursor);
10459  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
10460}
10461
10462/*
10463%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10464%                                                                             %
10465%                                                                             %
10466%                                                                             %
10467+   X P a s t e I m a g e                                                     %
10468%                                                                             %
10469%                                                                             %
10470%                                                                             %
10471%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10472%
10473%  XPasteImage() pastes an image previously saved with XCropImage in the X
10474%  window image at a location the user chooses with the pointer.
10475%
10476%  The format of the XPasteImage method is:
10477%
10478%      MagickBooleanType XPasteImage(Display *display,
10479%        XResourceInfo *resource_info,XWindows *windows,Image *image)
10480%
10481%  A description of each parameter follows:
10482%
10483%    o display: Specifies a connection to an X server;  returned from
10484%      XOpenDisplay.
10485%
10486%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10487%
10488%    o windows: Specifies a pointer to a XWindows structure.
10489%
10490%    o image: the image; returned from ReadImage.
10491%
10492*/
10493static MagickBooleanType XPasteImage(Display *display,
10494  XResourceInfo *resource_info,XWindows *windows,Image *image)
10495{
10496  static const char
10497    *PasteMenu[] =
10498    {
10499      "Operator",
10500      "Help",
10501      "Dismiss",
10502      (char *) NULL
10503    };
10504
10505  static const ModeType
10506    PasteCommands[] =
10507    {
10508      PasteOperatorsCommand,
10509      PasteHelpCommand,
10510      PasteDismissCommand
10511    };
10512
10513  static CompositeOperator
10514    compose = CopyCompositeOp;
10515
10516  char
10517    text[MaxTextExtent];
10518
10519  Cursor
10520    cursor;
10521
10522  Image
10523    *paste_image;
10524
10525  int
10526    entry,
10527    id,
10528    x,
10529    y;
10530
10531  MagickRealType
10532    scale_factor;
10533
10534  RectangleInfo
10535    highlight_info,
10536    paste_info;
10537
10538  unsigned int
10539    height,
10540    width;
10541
10542  size_t
10543    state;
10544
10545  XEvent
10546    event;
10547
10548  /*
10549    Copy image.
10550  */
10551  if (resource_info->copy_image == (Image *) NULL)
10552    return(MagickFalse);
10553  paste_image=CloneImage(resource_info->copy_image,0,0,MagickTrue,
10554    &image->exception);
10555  /*
10556    Map Command widget.
10557  */
10558  (void) CloneString(&windows->command.name,"Paste");
10559  windows->command.data=1;
10560  (void) XCommandWidget(display,windows,PasteMenu,(XEvent *) NULL);
10561  (void) XMapRaised(display,windows->command.id);
10562  XClientMessage(display,windows->image.id,windows->im_protocols,
10563    windows->im_update_widget,CurrentTime);
10564  /*
10565    Track pointer until button 1 is pressed.
10566  */
10567  XSetCursorState(display,windows,MagickFalse);
10568  XQueryPosition(display,windows->image.id,&x,&y);
10569  (void) XSelectInput(display,windows->image.id,
10570    windows->image.attributes.event_mask | PointerMotionMask);
10571  paste_info.x=(ssize_t) windows->image.x+x;
10572  paste_info.y=(ssize_t) windows->image.y+y;
10573  paste_info.width=0;
10574  paste_info.height=0;
10575  cursor=XCreateFontCursor(display,XC_ul_angle);
10576  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
10577  state=DefaultState;
10578  do
10579  {
10580    if (windows->info.mapped != MagickFalse)
10581      {
10582        /*
10583          Display pointer position.
10584        */
10585        (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
10586          (long) paste_info.x,(long) paste_info.y);
10587        XInfoWidget(display,windows,text);
10588      }
10589    highlight_info=paste_info;
10590    highlight_info.x=paste_info.x-windows->image.x;
10591    highlight_info.y=paste_info.y-windows->image.y;
10592    XHighlightRectangle(display,windows->image.id,
10593      windows->image.highlight_context,&highlight_info);
10594    /*
10595      Wait for next event.
10596    */
10597    XScreenEvent(display,windows,&event);
10598    XHighlightRectangle(display,windows->image.id,
10599      windows->image.highlight_context,&highlight_info);
10600    if (event.xany.window == windows->command.id)
10601      {
10602        /*
10603          Select a command from the Command widget.
10604        */
10605        id=XCommandWidget(display,windows,PasteMenu,&event);
10606        if (id < 0)
10607          continue;
10608        switch (PasteCommands[id])
10609        {
10610          case PasteOperatorsCommand:
10611          {
10612            char
10613              command[MaxTextExtent],
10614              **operators;
10615
10616            /*
10617              Select a command from the pop-up menu.
10618            */
10619            operators=GetCommandOptions(MagickComposeOptions);
10620            if (operators == (char **) NULL)
10621              break;
10622            entry=XMenuWidget(display,windows,PasteMenu[id],
10623              (const char **) operators,command);
10624            if (entry >= 0)
10625              compose=(CompositeOperator) ParseCommandOption(
10626                MagickComposeOptions,MagickFalse,operators[entry]);
10627            operators=DestroyStringList(operators);
10628            break;
10629          }
10630          case PasteHelpCommand:
10631          {
10632            XTextViewWidget(display,resource_info,windows,MagickFalse,
10633              "Help Viewer - Image Composite",ImagePasteHelp);
10634            break;
10635          }
10636          case PasteDismissCommand:
10637          {
10638            /*
10639              Prematurely exit.
10640            */
10641            state|=EscapeState;
10642            state|=ExitState;
10643            break;
10644          }
10645          default:
10646            break;
10647        }
10648        continue;
10649      }
10650    switch (event.type)
10651    {
10652      case ButtonPress:
10653      {
10654        if (image->debug != MagickFalse)
10655          (void) LogMagickEvent(X11Event,GetMagickModule(),
10656            "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
10657            event.xbutton.button,event.xbutton.x,event.xbutton.y);
10658        if (event.xbutton.button != Button1)
10659          break;
10660        if (event.xbutton.window != windows->image.id)
10661          break;
10662        /*
10663          Paste rectangle is relative to image configuration.
10664        */
10665        width=(unsigned int) image->columns;
10666        height=(unsigned int) image->rows;
10667        x=0;
10668        y=0;
10669        if (windows->image.crop_geometry != (char *) NULL)
10670          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
10671            &width,&height);
10672        scale_factor=(MagickRealType) windows->image.ximage->width/width;
10673        paste_info.width=(unsigned int) (scale_factor*paste_image->columns+0.5);
10674        scale_factor=(MagickRealType) windows->image.ximage->height/height;
10675        paste_info.height=(unsigned int) (scale_factor*paste_image->rows+0.5);
10676        (void) XCheckDefineCursor(display,windows->image.id,cursor);
10677        paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10678        paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10679        break;
10680      }
10681      case ButtonRelease:
10682      {
10683        if (image->debug != MagickFalse)
10684          (void) LogMagickEvent(X11Event,GetMagickModule(),
10685            "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
10686            event.xbutton.button,event.xbutton.x,event.xbutton.y);
10687        if (event.xbutton.button != Button1)
10688          break;
10689        if (event.xbutton.window != windows->image.id)
10690          break;
10691        if ((paste_info.width != 0) && (paste_info.height != 0))
10692          {
10693            /*
10694              User has selected the location of the paste image.
10695            */
10696            paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10697            paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10698            state|=ExitState;
10699          }
10700        break;
10701      }
10702      case Expose:
10703        break;
10704      case KeyPress:
10705      {
10706        char
10707          command[MaxTextExtent];
10708
10709        KeySym
10710          key_symbol;
10711
10712        int
10713          length;
10714
10715        if (event.xkey.window != windows->image.id)
10716          break;
10717        /*
10718          Respond to a user key press.
10719        */
10720        length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
10721          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
10722        *(command+length)='\0';
10723        if (image->debug != MagickFalse)
10724          (void) LogMagickEvent(X11Event,GetMagickModule(),
10725            "Key press: 0x%lx (%s)",(long) key_symbol,command);
10726        switch ((int) key_symbol)
10727        {
10728          case XK_Escape:
10729          case XK_F20:
10730          {
10731            /*
10732              Prematurely exit.
10733            */
10734            paste_image=DestroyImage(paste_image);
10735            state|=EscapeState;
10736            state|=ExitState;
10737            break;
10738          }
10739          case XK_F1:
10740          case XK_Help:
10741          {
10742            (void) XSetFunction(display,windows->image.highlight_context,
10743              GXcopy);
10744            XTextViewWidget(display,resource_info,windows,MagickFalse,
10745              "Help Viewer - Image Composite",ImagePasteHelp);
10746            (void) XSetFunction(display,windows->image.highlight_context,
10747              GXinvert);
10748            break;
10749          }
10750          default:
10751          {
10752            (void) XBell(display,0);
10753            break;
10754          }
10755        }
10756        break;
10757      }
10758      case MotionNotify:
10759      {
10760        /*
10761          Map and unmap Info widget as text cursor crosses its boundaries.
10762        */
10763        x=event.xmotion.x;
10764        y=event.xmotion.y;
10765        if (windows->info.mapped != MagickFalse)
10766          {
10767            if ((x < (int) (windows->info.x+windows->info.width)) &&
10768                (y < (int) (windows->info.y+windows->info.height)))
10769              (void) XWithdrawWindow(display,windows->info.id,
10770                windows->info.screen);
10771          }
10772        else
10773          if ((x > (int) (windows->info.x+windows->info.width)) ||
10774              (y > (int) (windows->info.y+windows->info.height)))
10775            (void) XMapWindow(display,windows->info.id);
10776        paste_info.x=(ssize_t) windows->image.x+x;
10777        paste_info.y=(ssize_t) windows->image.y+y;
10778        break;
10779      }
10780      default:
10781      {
10782        if (image->debug != MagickFalse)
10783          (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
10784            event.type);
10785        break;
10786      }
10787    }
10788  } while ((state & ExitState) == 0);
10789  (void) XSelectInput(display,windows->image.id,
10790    windows->image.attributes.event_mask);
10791  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
10792  XSetCursorState(display,windows,MagickFalse);
10793  (void) XFreeCursor(display,cursor);
10794  if ((state & EscapeState) != 0)
10795    return(MagickTrue);
10796  /*
10797    Image pasting is relative to image configuration.
10798  */
10799  XSetCursorState(display,windows,MagickTrue);
10800  XCheckRefreshWindows(display,windows);
10801  width=(unsigned int) image->columns;
10802  height=(unsigned int) image->rows;
10803  x=0;
10804  y=0;
10805  if (windows->image.crop_geometry != (char *) NULL)
10806    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
10807  scale_factor=(MagickRealType) width/windows->image.ximage->width;
10808  paste_info.x+=x;
10809  paste_info.x=(ssize_t) (scale_factor*paste_info.x+0.5);
10810  paste_info.width=(unsigned int) (scale_factor*paste_info.width+0.5);
10811  scale_factor=(MagickRealType) height/windows->image.ximage->height;
10812  paste_info.y+=y;
10813  paste_info.y=(ssize_t) (scale_factor*paste_info.y*scale_factor+0.5);
10814  paste_info.height=(unsigned int) (scale_factor*paste_info.height+0.5);
10815  /*
10816    Paste image with X Image window.
10817  */
10818  (void) CompositeImage(image,compose,paste_image,paste_info.x,paste_info.y);
10819  paste_image=DestroyImage(paste_image);
10820  XSetCursorState(display,windows,MagickFalse);
10821  /*
10822    Update image colormap.
10823  */
10824  XConfigureImageColormap(display,resource_info,windows,image);
10825  (void) XConfigureImage(display,resource_info,windows,image);
10826  return(MagickTrue);
10827}
10828
10829/*
10830%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10831%                                                                             %
10832%                                                                             %
10833%                                                                             %
10834+   X P r i n t I m a g e                                                     %
10835%                                                                             %
10836%                                                                             %
10837%                                                                             %
10838%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10839%
10840%  XPrintImage() prints an image to a Postscript printer.
10841%
10842%  The format of the XPrintImage method is:
10843%
10844%      MagickBooleanType XPrintImage(Display *display,
10845%        XResourceInfo *resource_info,XWindows *windows,Image *image)
10846%
10847%  A description of each parameter follows:
10848%
10849%    o display: Specifies a connection to an X server; returned from
10850%      XOpenDisplay.
10851%
10852%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10853%
10854%    o windows: Specifies a pointer to a XWindows structure.
10855%
10856%    o image: the image.
10857%
10858*/
10859static MagickBooleanType XPrintImage(Display *display,
10860  XResourceInfo *resource_info,XWindows *windows,Image *image)
10861{
10862  char
10863    filename[MaxTextExtent],
10864    geometry[MaxTextExtent];
10865
10866  Image
10867    *print_image;
10868
10869  ImageInfo
10870    *image_info;
10871
10872  MagickStatusType
10873    status;
10874
10875  /*
10876    Request Postscript page geometry from user.
10877  */
10878  image_info=CloneImageInfo(resource_info->image_info);
10879  (void) FormatLocaleString(geometry,MaxTextExtent,"Letter");
10880  if (image_info->page != (char *) NULL)
10881    (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
10882  XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
10883    "Select Postscript Page Geometry:",geometry);
10884  if (*geometry == '\0')
10885    return(MagickTrue);
10886  image_info->page=GetPageGeometry(geometry);
10887  /*
10888    Apply image transforms.
10889  */
10890  XSetCursorState(display,windows,MagickTrue);
10891  XCheckRefreshWindows(display,windows);
10892  print_image=CloneImage(image,0,0,MagickTrue,&image->exception);
10893  if (print_image == (Image *) NULL)
10894    return(MagickFalse);
10895  (void) FormatLocaleString(geometry,MaxTextExtent,"%dx%d!",
10896    windows->image.ximage->width,windows->image.ximage->height);
10897  (void) TransformImage(&print_image,windows->image.crop_geometry,geometry);
10898  /*
10899    Print image.
10900  */
10901  (void) AcquireUniqueFilename(filename);
10902  (void) FormatLocaleString(print_image->filename,MaxTextExtent,"print:%s",
10903    filename);
10904  status=WriteImage(image_info,print_image);
10905  (void) RelinquishUniqueFileResource(filename);
10906  print_image=DestroyImage(print_image);
10907  image_info=DestroyImageInfo(image_info);
10908  XSetCursorState(display,windows,MagickFalse);
10909  return(status != 0 ? MagickTrue : MagickFalse);
10910}
10911
10912/*
10913%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10914%                                                                             %
10915%                                                                             %
10916%                                                                             %
10917+   X R O I I m a g e                                                         %
10918%                                                                             %
10919%                                                                             %
10920%                                                                             %
10921%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10922%
10923%  XROIImage() applies an image processing technique to a region of interest.
10924%
10925%  The format of the XROIImage method is:
10926%
10927%      MagickBooleanType XROIImage(Display *display,
10928%        XResourceInfo *resource_info,XWindows *windows,Image **image)
10929%
10930%  A description of each parameter follows:
10931%
10932%    o display: Specifies a connection to an X server; returned from
10933%      XOpenDisplay.
10934%
10935%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10936%
10937%    o windows: Specifies a pointer to a XWindows structure.
10938%
10939%    o image: the image; returned from ReadImage.
10940%
10941*/
10942static MagickBooleanType XROIImage(Display *display,
10943  XResourceInfo *resource_info,XWindows *windows,Image **image)
10944{
10945#define ApplyMenus  7
10946
10947  static const char
10948    *ROIMenu[] =
10949    {
10950      "Help",
10951      "Dismiss",
10952      (char *) NULL
10953    },
10954    *ApplyMenu[] =
10955    {
10956      "File",
10957      "Edit",
10958      "Transform",
10959      "Enhance",
10960      "Effects",
10961      "F/X",
10962      "Miscellany",
10963      "Help",
10964      "Dismiss",
10965      (char *) NULL
10966    },
10967    *FileMenu[] =
10968    {
10969      "Save...",
10970      "Print...",
10971      (char *) NULL
10972    },
10973    *EditMenu[] =
10974    {
10975      "Undo",
10976      "Redo",
10977      (char *) NULL
10978    },
10979    *TransformMenu[] =
10980    {
10981      "Flop",
10982      "Flip",
10983      "Rotate Right",
10984      "Rotate Left",
10985      (char *) NULL
10986    },
10987    *EnhanceMenu[] =
10988    {
10989      "Hue...",
10990      "Saturation...",
10991      "Brightness...",
10992      "Gamma...",
10993      "Spiff",
10994      "Dull",
10995      "Contrast Stretch...",
10996      "Sigmoidal Contrast...",
10997      "Normalize",
10998      "Equalize",
10999      "Negate",
11000      "Grayscale",
11001      "Map...",
11002      "Quantize...",
11003      (char *) NULL
11004    },
11005    *EffectsMenu[] =
11006    {
11007      "Despeckle",
11008      "Emboss",
11009      "Reduce Noise",
11010      "Add Noise",
11011      "Sharpen...",
11012      "Blur...",
11013      "Threshold...",
11014      "Edge Detect...",
11015      "Spread...",
11016      "Shade...",
11017      "Raise...",
11018      "Segment...",
11019      (char *) NULL
11020    },
11021    *FXMenu[] =
11022    {
11023      "Solarize...",
11024      "Sepia Tone...",
11025      "Swirl...",
11026      "Implode...",
11027      "Vignette...",
11028      "Wave...",
11029      "Oil Paint...",
11030      "Charcoal Draw...",
11031      (char *) NULL
11032    },
11033    *MiscellanyMenu[] =
11034    {
11035      "Image Info",
11036      "Zoom Image",
11037      "Show Preview...",
11038      "Show Histogram",
11039      "Show Matte",
11040      (char *) NULL
11041    };
11042
11043  static const char
11044    **Menus[ApplyMenus] =
11045    {
11046      FileMenu,
11047      EditMenu,
11048      TransformMenu,
11049      EnhanceMenu,
11050      EffectsMenu,
11051      FXMenu,
11052      MiscellanyMenu
11053    };
11054
11055  static const CommandType
11056    ApplyCommands[] =
11057    {
11058      NullCommand,
11059      NullCommand,
11060      NullCommand,
11061      NullCommand,
11062      NullCommand,
11063      NullCommand,
11064      NullCommand,
11065      HelpCommand,
11066      QuitCommand
11067    },
11068    FileCommands[] =
11069    {
11070      SaveCommand,
11071      PrintCommand
11072    },
11073    EditCommands[] =
11074    {
11075      UndoCommand,
11076      RedoCommand
11077    },
11078    TransformCommands[] =
11079    {
11080      FlopCommand,
11081      FlipCommand,
11082      RotateRightCommand,
11083      RotateLeftCommand
11084    },
11085    EnhanceCommands[] =
11086    {
11087      HueCommand,
11088      SaturationCommand,
11089      BrightnessCommand,
11090      GammaCommand,
11091      SpiffCommand,
11092      DullCommand,
11093      ContrastStretchCommand,
11094      SigmoidalContrastCommand,
11095      NormalizeCommand,
11096      EqualizeCommand,
11097      NegateCommand,
11098      GrayscaleCommand,
11099      MapCommand,
11100      QuantizeCommand
11101    },
11102    EffectsCommands[] =
11103    {
11104      DespeckleCommand,
11105      EmbossCommand,
11106      ReduceNoiseCommand,
11107      AddNoiseCommand,
11108      SharpenCommand,
11109      BlurCommand,
11110      EdgeDetectCommand,
11111      SpreadCommand,
11112      ShadeCommand,
11113      RaiseCommand,
11114      SegmentCommand
11115    },
11116    FXCommands[] =
11117    {
11118      SolarizeCommand,
11119      SepiaToneCommand,
11120      SwirlCommand,
11121      ImplodeCommand,
11122      VignetteCommand,
11123      WaveCommand,
11124      OilPaintCommand,
11125      CharcoalDrawCommand
11126    },
11127    MiscellanyCommands[] =
11128    {
11129      InfoCommand,
11130      ZoomCommand,
11131      ShowPreviewCommand,
11132      ShowHistogramCommand,
11133      ShowMatteCommand
11134    },
11135    ROICommands[] =
11136    {
11137      ROIHelpCommand,
11138      ROIDismissCommand
11139    };
11140
11141  static const CommandType
11142    *Commands[ApplyMenus] =
11143    {
11144      FileCommands,
11145      EditCommands,
11146      TransformCommands,
11147      EnhanceCommands,
11148      EffectsCommands,
11149      FXCommands,
11150      MiscellanyCommands
11151    };
11152
11153  char
11154    command[MaxTextExtent],
11155    text[MaxTextExtent];
11156
11157  CommandType
11158    command_type;
11159
11160  Cursor
11161    cursor;
11162
11163  Image
11164    *roi_image;
11165
11166  int
11167    entry,
11168    id,
11169    x,
11170    y;
11171
11172  MagickRealType
11173    scale_factor;
11174
11175  MagickProgressMonitor
11176    progress_monitor;
11177
11178  RectangleInfo
11179    crop_info,
11180    highlight_info,
11181    roi_info;
11182
11183  unsigned int
11184    height,
11185    width;
11186
11187  size_t
11188    state;
11189
11190  XEvent
11191    event;
11192
11193  /*
11194    Map Command widget.
11195  */
11196  (void) CloneString(&windows->command.name,"ROI");
11197  windows->command.data=0;
11198  (void) XCommandWidget(display,windows,ROIMenu,(XEvent *) NULL);
11199  (void) XMapRaised(display,windows->command.id);
11200  XClientMessage(display,windows->image.id,windows->im_protocols,
11201    windows->im_update_widget,CurrentTime);
11202  /*
11203    Track pointer until button 1 is pressed.
11204  */
11205  XQueryPosition(display,windows->image.id,&x,&y);
11206  (void) XSelectInput(display,windows->image.id,
11207    windows->image.attributes.event_mask | PointerMotionMask);
11208  roi_info.x=(ssize_t) windows->image.x+x;
11209  roi_info.y=(ssize_t) windows->image.y+y;
11210  roi_info.width=0;
11211  roi_info.height=0;
11212  cursor=XCreateFontCursor(display,XC_fleur);
11213  state=DefaultState;
11214  do
11215  {
11216    if (windows->info.mapped != MagickFalse)
11217      {
11218        /*
11219          Display pointer position.
11220        */
11221        (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
11222          (long) roi_info.x,(long) roi_info.y);
11223        XInfoWidget(display,windows,text);
11224      }
11225    /*
11226      Wait for next event.
11227    */
11228    XScreenEvent(display,windows,&event);
11229    if (event.xany.window == windows->command.id)
11230      {
11231        /*
11232          Select a command from the Command widget.
11233        */
11234        id=XCommandWidget(display,windows,ROIMenu,&event);
11235        if (id < 0)
11236          continue;
11237        switch (ROICommands[id])
11238        {
11239          case ROIHelpCommand:
11240          {
11241            XTextViewWidget(display,resource_info,windows,MagickFalse,
11242              "Help Viewer - Region of Interest",ImageROIHelp);
11243            break;
11244          }
11245          case ROIDismissCommand:
11246          {
11247            /*
11248              Prematurely exit.
11249            */
11250            state|=EscapeState;
11251            state|=ExitState;
11252            break;
11253          }
11254          default:
11255            break;
11256        }
11257        continue;
11258      }
11259    switch (event.type)
11260    {
11261      case ButtonPress:
11262      {
11263        if (event.xbutton.button != Button1)
11264          break;
11265        if (event.xbutton.window != windows->image.id)
11266          break;
11267        /*
11268          Note first corner of region of interest rectangle-- exit loop.
11269        */
11270        (void) XCheckDefineCursor(display,windows->image.id,cursor);
11271        roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11272        roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11273        state|=ExitState;
11274        break;
11275      }
11276      case ButtonRelease:
11277        break;
11278      case Expose:
11279        break;
11280      case KeyPress:
11281      {
11282        KeySym
11283          key_symbol;
11284
11285        if (event.xkey.window != windows->image.id)
11286          break;
11287        /*
11288          Respond to a user key press.
11289        */
11290        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11291          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11292        switch ((int) key_symbol)
11293        {
11294          case XK_Escape:
11295          case XK_F20:
11296          {
11297            /*
11298              Prematurely exit.
11299            */
11300            state|=EscapeState;
11301            state|=ExitState;
11302            break;
11303          }
11304          case XK_F1:
11305          case XK_Help:
11306          {
11307            XTextViewWidget(display,resource_info,windows,MagickFalse,
11308              "Help Viewer - Region of Interest",ImageROIHelp);
11309            break;
11310          }
11311          default:
11312          {
11313            (void) XBell(display,0);
11314            break;
11315          }
11316        }
11317        break;
11318      }
11319      case MotionNotify:
11320      {
11321        /*
11322          Map and unmap Info widget as text cursor crosses its boundaries.
11323        */
11324        x=event.xmotion.x;
11325        y=event.xmotion.y;
11326        if (windows->info.mapped != MagickFalse)
11327          {
11328            if ((x < (int) (windows->info.x+windows->info.width)) &&
11329                (y < (int) (windows->info.y+windows->info.height)))
11330              (void) XWithdrawWindow(display,windows->info.id,
11331                windows->info.screen);
11332          }
11333        else
11334          if ((x > (int) (windows->info.x+windows->info.width)) ||
11335              (y > (int) (windows->info.y+windows->info.height)))
11336            (void) XMapWindow(display,windows->info.id);
11337        roi_info.x=(ssize_t) windows->image.x+x;
11338        roi_info.y=(ssize_t) windows->image.y+y;
11339        break;
11340      }
11341      default:
11342        break;
11343    }
11344  } while ((state & ExitState) == 0);
11345  (void) XSelectInput(display,windows->image.id,
11346    windows->image.attributes.event_mask);
11347  if ((state & EscapeState) != 0)
11348    {
11349      /*
11350        User want to exit without region of interest.
11351      */
11352      (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11353      (void) XFreeCursor(display,cursor);
11354      return(MagickTrue);
11355    }
11356  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
11357  do
11358  {
11359    /*
11360      Size rectangle as pointer moves until the mouse button is released.
11361    */
11362    x=(int) roi_info.x;
11363    y=(int) roi_info.y;
11364    roi_info.width=0;
11365    roi_info.height=0;
11366    state=DefaultState;
11367    do
11368    {
11369      highlight_info=roi_info;
11370      highlight_info.x=roi_info.x-windows->image.x;
11371      highlight_info.y=roi_info.y-windows->image.y;
11372      if ((highlight_info.width > 3) && (highlight_info.height > 3))
11373        {
11374          /*
11375            Display info and draw region of interest rectangle.
11376          */
11377          if (windows->info.mapped == MagickFalse)
11378            (void) XMapWindow(display,windows->info.id);
11379          (void) FormatLocaleString(text,MaxTextExtent,
11380            " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11381            roi_info.height,(double) roi_info.x,(double) roi_info.y);
11382          XInfoWidget(display,windows,text);
11383          XHighlightRectangle(display,windows->image.id,
11384            windows->image.highlight_context,&highlight_info);
11385        }
11386      else
11387        if (windows->info.mapped != MagickFalse)
11388          (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11389      /*
11390        Wait for next event.
11391      */
11392      XScreenEvent(display,windows,&event);
11393      if ((highlight_info.width > 3) && (highlight_info.height > 3))
11394        XHighlightRectangle(display,windows->image.id,
11395          windows->image.highlight_context,&highlight_info);
11396      switch (event.type)
11397      {
11398        case ButtonPress:
11399        {
11400          roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11401          roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11402          break;
11403        }
11404        case ButtonRelease:
11405        {
11406          /*
11407            User has committed to region of interest rectangle.
11408          */
11409          roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11410          roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11411          XSetCursorState(display,windows,MagickFalse);
11412          state|=ExitState;
11413          if (LocaleCompare(windows->command.name,"Apply") == 0)
11414            break;
11415          (void) CloneString(&windows->command.name,"Apply");
11416          windows->command.data=ApplyMenus;
11417          (void) XCommandWidget(display,windows,ApplyMenu,(XEvent *) NULL);
11418          break;
11419        }
11420        case Expose:
11421          break;
11422        case MotionNotify:
11423        {
11424          roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11425          roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11426        }
11427        default:
11428          break;
11429      }
11430      if ((((int) roi_info.x != x) && ((int) roi_info.y != y)) ||
11431          ((state & ExitState) != 0))
11432        {
11433          /*
11434            Check boundary conditions.
11435          */
11436          if (roi_info.x < 0)
11437            roi_info.x=0;
11438          else
11439            if (roi_info.x > (ssize_t) windows->image.ximage->width)
11440              roi_info.x=(ssize_t) windows->image.ximage->width;
11441          if ((int) roi_info.x < x)
11442            roi_info.width=(unsigned int) (x-roi_info.x);
11443          else
11444            {
11445              roi_info.width=(unsigned int) (roi_info.x-x);
11446              roi_info.x=(ssize_t) x;
11447            }
11448          if (roi_info.y < 0)
11449            roi_info.y=0;
11450          else
11451            if (roi_info.y > (ssize_t) windows->image.ximage->height)
11452              roi_info.y=(ssize_t) windows->image.ximage->height;
11453          if ((int) roi_info.y < y)
11454            roi_info.height=(unsigned int) (y-roi_info.y);
11455          else
11456            {
11457              roi_info.height=(unsigned int) (roi_info.y-y);
11458              roi_info.y=(ssize_t) y;
11459            }
11460        }
11461    } while ((state & ExitState) == 0);
11462    /*
11463      Wait for user to grab a corner of the rectangle or press return.
11464    */
11465    state=DefaultState;
11466    command_type=NullCommand;
11467    (void) XMapWindow(display,windows->info.id);
11468    do
11469    {
11470      if (windows->info.mapped != MagickFalse)
11471        {
11472          /*
11473            Display pointer position.
11474          */
11475          (void) FormatLocaleString(text,MaxTextExtent,
11476            " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11477            roi_info.height,(double) roi_info.x,(double) roi_info.y);
11478          XInfoWidget(display,windows,text);
11479        }
11480      highlight_info=roi_info;
11481      highlight_info.x=roi_info.x-windows->image.x;
11482      highlight_info.y=roi_info.y-windows->image.y;
11483      if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
11484        {
11485          state|=EscapeState;
11486          state|=ExitState;
11487          break;
11488        }
11489      if ((state & UpdateRegionState) != 0)
11490        {
11491          (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11492          switch (command_type)
11493          {
11494            case UndoCommand:
11495            case RedoCommand:
11496            {
11497              (void) XMagickCommand(display,resource_info,windows,command_type,
11498                image);
11499              break;
11500            }
11501            default:
11502            {
11503              /*
11504                Region of interest is relative to image configuration.
11505              */
11506              progress_monitor=SetImageProgressMonitor(*image,
11507                (MagickProgressMonitor) NULL,(*image)->client_data);
11508              crop_info=roi_info;
11509              width=(unsigned int) (*image)->columns;
11510              height=(unsigned int) (*image)->rows;
11511              x=0;
11512              y=0;
11513              if (windows->image.crop_geometry != (char *) NULL)
11514                (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
11515                  &width,&height);
11516              scale_factor=(MagickRealType) width/windows->image.ximage->width;
11517              crop_info.x+=x;
11518              crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
11519              crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
11520              scale_factor=(MagickRealType)
11521                height/windows->image.ximage->height;
11522              crop_info.y+=y;
11523              crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
11524              crop_info.height=(unsigned int)
11525                (scale_factor*crop_info.height+0.5);
11526              roi_image=CropImage(*image,&crop_info,&(*image)->exception);
11527              (void) SetImageProgressMonitor(*image,progress_monitor,
11528                (*image)->client_data);
11529              if (roi_image == (Image *) NULL)
11530                continue;
11531              /*
11532                Apply image processing technique to the region of interest.
11533              */
11534              windows->image.orphan=MagickTrue;
11535              (void) XMagickCommand(display,resource_info,windows,command_type,
11536                &roi_image);
11537              progress_monitor=SetImageProgressMonitor(*image,
11538                (MagickProgressMonitor) NULL,(*image)->client_data);
11539              (void) XMagickCommand(display,resource_info,windows,
11540                SaveToUndoBufferCommand,image);
11541              windows->image.orphan=MagickFalse;
11542              (void) CompositeImage(*image,CopyCompositeOp,roi_image,
11543                crop_info.x,crop_info.y);
11544              roi_image=DestroyImage(roi_image);
11545              (void) SetImageProgressMonitor(*image,progress_monitor,
11546                (*image)->client_data);
11547              break;
11548            }
11549          }
11550          if (command_type != InfoCommand)
11551            {
11552              XConfigureImageColormap(display,resource_info,windows,*image);
11553              (void) XConfigureImage(display,resource_info,windows,*image);
11554            }
11555          XCheckRefreshWindows(display,windows);
11556          XInfoWidget(display,windows,text);
11557          (void) XSetFunction(display,windows->image.highlight_context,
11558            GXinvert);
11559          state&=(~UpdateRegionState);
11560        }
11561      XHighlightRectangle(display,windows->image.id,
11562        windows->image.highlight_context,&highlight_info);
11563      XScreenEvent(display,windows,&event);
11564      if (event.xany.window == windows->command.id)
11565        {
11566          /*
11567            Select a command from the Command widget.
11568          */
11569          (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11570          command_type=NullCommand;
11571          id=XCommandWidget(display,windows,ApplyMenu,&event);
11572          if (id >= 0)
11573            {
11574              (void) CopyMagickString(command,ApplyMenu[id],MaxTextExtent);
11575              command_type=ApplyCommands[id];
11576              if (id < ApplyMenus)
11577                {
11578                  /*
11579                    Select a command from a pop-up menu.
11580                  */
11581                  entry=XMenuWidget(display,windows,ApplyMenu[id],
11582                    (const char **) Menus[id],command);
11583                  if (entry >= 0)
11584                    {
11585                      (void) CopyMagickString(command,Menus[id][entry],
11586                        MaxTextExtent);
11587                      command_type=Commands[id][entry];
11588                    }
11589                }
11590            }
11591          (void) XSetFunction(display,windows->image.highlight_context,
11592            GXinvert);
11593          XHighlightRectangle(display,windows->image.id,
11594            windows->image.highlight_context,&highlight_info);
11595          if (command_type == HelpCommand)
11596            {
11597              (void) XSetFunction(display,windows->image.highlight_context,
11598                GXcopy);
11599              XTextViewWidget(display,resource_info,windows,MagickFalse,
11600                "Help Viewer - Region of Interest",ImageROIHelp);
11601              (void) XSetFunction(display,windows->image.highlight_context,
11602                GXinvert);
11603              continue;
11604            }
11605          if (command_type == QuitCommand)
11606            {
11607              /*
11608                exit.
11609              */
11610              state|=EscapeState;
11611              state|=ExitState;
11612              continue;
11613            }
11614          if (command_type != NullCommand)
11615            state|=UpdateRegionState;
11616          continue;
11617        }
11618      XHighlightRectangle(display,windows->image.id,
11619        windows->image.highlight_context,&highlight_info);
11620      switch (event.type)
11621      {
11622        case ButtonPress:
11623        {
11624          x=windows->image.x;
11625          y=windows->image.y;
11626          if (event.xbutton.button != Button1)
11627            break;
11628          if (event.xbutton.window != windows->image.id)
11629            break;
11630          x=windows->image.x+event.xbutton.x;
11631          y=windows->image.y+event.xbutton.y;
11632          if ((x < (int) (roi_info.x+RoiDelta)) &&
11633              (x > (int) (roi_info.x-RoiDelta)) &&
11634              (y < (int) (roi_info.y+RoiDelta)) &&
11635              (y > (int) (roi_info.y-RoiDelta)))
11636            {
11637              roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11638              roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11639              state|=UpdateConfigurationState;
11640              break;
11641            }
11642          if ((x < (int) (roi_info.x+RoiDelta)) &&
11643              (x > (int) (roi_info.x-RoiDelta)) &&
11644              (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11645              (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11646            {
11647              roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11648              state|=UpdateConfigurationState;
11649              break;
11650            }
11651          if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11652              (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11653              (y < (int) (roi_info.y+RoiDelta)) &&
11654              (y > (int) (roi_info.y-RoiDelta)))
11655            {
11656              roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11657              state|=UpdateConfigurationState;
11658              break;
11659            }
11660          if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11661              (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11662              (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11663              (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11664            {
11665              state|=UpdateConfigurationState;
11666              break;
11667            }
11668        }
11669        case ButtonRelease:
11670        {
11671          if (event.xbutton.window == windows->pan.id)
11672            if ((highlight_info.x != crop_info.x-windows->image.x) ||
11673                (highlight_info.y != crop_info.y-windows->image.y))
11674              XHighlightRectangle(display,windows->image.id,
11675                windows->image.highlight_context,&highlight_info);
11676          (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11677            event.xbutton.time);
11678          break;
11679        }
11680        case Expose:
11681        {
11682          if (event.xexpose.window == windows->image.id)
11683            if (event.xexpose.count == 0)
11684              {
11685                event.xexpose.x=(int) highlight_info.x;
11686                event.xexpose.y=(int) highlight_info.y;
11687                event.xexpose.width=(int) highlight_info.width;
11688                event.xexpose.height=(int) highlight_info.height;
11689                XRefreshWindow(display,&windows->image,&event);
11690              }
11691          if (event.xexpose.window == windows->info.id)
11692            if (event.xexpose.count == 0)
11693              XInfoWidget(display,windows,text);
11694          break;
11695        }
11696        case KeyPress:
11697        {
11698          KeySym
11699            key_symbol;
11700
11701          if (event.xkey.window != windows->image.id)
11702            break;
11703          /*
11704            Respond to a user key press.
11705          */
11706          (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11707            sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11708          switch ((int) key_symbol)
11709          {
11710            case XK_Shift_L:
11711            case XK_Shift_R:
11712              break;
11713            case XK_Escape:
11714            case XK_F20:
11715              state|=EscapeState;
11716            case XK_Return:
11717            {
11718              state|=ExitState;
11719              break;
11720            }
11721            case XK_Home:
11722            case XK_KP_Home:
11723            {
11724              roi_info.x=(ssize_t) (windows->image.width/2L-roi_info.width/2L);
11725              roi_info.y=(ssize_t) (windows->image.height/2L-
11726                roi_info.height/2L);
11727              break;
11728            }
11729            case XK_Left:
11730            case XK_KP_Left:
11731            {
11732              roi_info.x--;
11733              break;
11734            }
11735            case XK_Up:
11736            case XK_KP_Up:
11737            case XK_Next:
11738            {
11739              roi_info.y--;
11740              break;
11741            }
11742            case XK_Right:
11743            case XK_KP_Right:
11744            {
11745              roi_info.x++;
11746              break;
11747            }
11748            case XK_Prior:
11749            case XK_Down:
11750            case XK_KP_Down:
11751            {
11752              roi_info.y++;
11753              break;
11754            }
11755            case XK_F1:
11756            case XK_Help:
11757            {
11758              (void) XSetFunction(display,windows->image.highlight_context,
11759                GXcopy);
11760              XTextViewWidget(display,resource_info,windows,MagickFalse,
11761                "Help Viewer - Region of Interest",ImageROIHelp);
11762              (void) XSetFunction(display,windows->image.highlight_context,
11763                GXinvert);
11764              break;
11765            }
11766            default:
11767            {
11768              command_type=XImageWindowCommand(display,resource_info,windows,
11769                event.xkey.state,key_symbol,image);
11770              if (command_type != NullCommand)
11771                state|=UpdateRegionState;
11772              break;
11773            }
11774          }
11775          (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11776            event.xkey.time);
11777          break;
11778        }
11779        case KeyRelease:
11780          break;
11781        case MotionNotify:
11782        {
11783          if (event.xbutton.window != windows->image.id)
11784            break;
11785          /*
11786            Map and unmap Info widget as text cursor crosses its boundaries.
11787          */
11788          x=event.xmotion.x;
11789          y=event.xmotion.y;
11790          if (windows->info.mapped != MagickFalse)
11791            {
11792              if ((x < (int) (windows->info.x+windows->info.width)) &&
11793                  (y < (int) (windows->info.y+windows->info.height)))
11794                (void) XWithdrawWindow(display,windows->info.id,
11795                  windows->info.screen);
11796            }
11797          else
11798            if ((x > (int) (windows->info.x+windows->info.width)) ||
11799                (y > (int) (windows->info.y+windows->info.height)))
11800              (void) XMapWindow(display,windows->info.id);
11801          roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11802          roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11803          break;
11804        }
11805        case SelectionRequest:
11806        {
11807          XSelectionEvent
11808            notify;
11809
11810          XSelectionRequestEvent
11811            *request;
11812
11813          /*
11814            Set primary selection.
11815          */
11816          (void) FormatLocaleString(text,MaxTextExtent,
11817            "%.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11818            roi_info.height,(double) roi_info.x,(double) roi_info.y);
11819          request=(&(event.xselectionrequest));
11820          (void) XChangeProperty(request->display,request->requestor,
11821            request->property,request->target,8,PropModeReplace,
11822            (unsigned char *) text,(int) strlen(text));
11823          notify.type=SelectionNotify;
11824          notify.display=request->display;
11825          notify.requestor=request->requestor;
11826          notify.selection=request->selection;
11827          notify.target=request->target;
11828          notify.time=request->time;
11829          if (request->property == None)
11830            notify.property=request->target;
11831          else
11832            notify.property=request->property;
11833          (void) XSendEvent(request->display,request->requestor,False,0,
11834            (XEvent *) &notify);
11835        }
11836        default:
11837          break;
11838      }
11839      if ((state & UpdateConfigurationState) != 0)
11840        {
11841          (void) XPutBackEvent(display,&event);
11842          (void) XCheckDefineCursor(display,windows->image.id,cursor);
11843          break;
11844        }
11845    } while ((state & ExitState) == 0);
11846  } while ((state & ExitState) == 0);
11847  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11848  XSetCursorState(display,windows,MagickFalse);
11849  if ((state & EscapeState) != 0)
11850    return(MagickTrue);
11851  return(MagickTrue);
11852}
11853
11854/*
11855%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11856%                                                                             %
11857%                                                                             %
11858%                                                                             %
11859+   X R o t a t e I m a g e                                                   %
11860%                                                                             %
11861%                                                                             %
11862%                                                                             %
11863%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11864%
11865%  XRotateImage() rotates the X image.  If the degrees parameter if zero, the
11866%  rotation angle is computed from the slope of a line drawn by the user.
11867%
11868%  The format of the XRotateImage method is:
11869%
11870%      MagickBooleanType XRotateImage(Display *display,
11871%        XResourceInfo *resource_info,XWindows *windows,double degrees,
11872%        Image **image)
11873%
11874%  A description of each parameter follows:
11875%
11876%    o display: Specifies a connection to an X server; returned from
11877%      XOpenDisplay.
11878%
11879%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
11880%
11881%    o windows: Specifies a pointer to a XWindows structure.
11882%
11883%    o degrees: Specifies the number of degrees to rotate the image.
11884%
11885%    o image: the image.
11886%
11887*/
11888static MagickBooleanType XRotateImage(Display *display,
11889  XResourceInfo *resource_info,XWindows *windows,double degrees,Image **image)
11890{
11891  static const char
11892    *RotateMenu[] =
11893    {
11894      "Pixel Color",
11895      "Direction",
11896      "Help",
11897      "Dismiss",
11898      (char *) NULL
11899    };
11900
11901  static ModeType
11902    direction = HorizontalRotateCommand;
11903
11904  static const ModeType
11905    DirectionCommands[] =
11906    {
11907      HorizontalRotateCommand,
11908      VerticalRotateCommand
11909    },
11910    RotateCommands[] =
11911    {
11912      RotateColorCommand,
11913      RotateDirectionCommand,
11914      RotateHelpCommand,
11915      RotateDismissCommand
11916    };
11917
11918  static unsigned int
11919    pen_id = 0;
11920
11921  char
11922    command[MaxTextExtent],
11923    text[MaxTextExtent];
11924
11925  Image
11926    *rotate_image;
11927
11928  int
11929    id,
11930    x,
11931    y;
11932
11933  MagickRealType
11934    normalized_degrees;
11935
11936  register int
11937    i;
11938
11939  unsigned int
11940    height,
11941    rotations,
11942    width;
11943
11944  if (degrees == 0.0)
11945    {
11946      unsigned int
11947        distance;
11948
11949      size_t
11950        state;
11951
11952      XEvent
11953        event;
11954
11955      XSegment
11956        rotate_info;
11957
11958      /*
11959        Map Command widget.
11960      */
11961      (void) CloneString(&windows->command.name,"Rotate");
11962      windows->command.data=2;
11963      (void) XCommandWidget(display,windows,RotateMenu,(XEvent *) NULL);
11964      (void) XMapRaised(display,windows->command.id);
11965      XClientMessage(display,windows->image.id,windows->im_protocols,
11966        windows->im_update_widget,CurrentTime);
11967      /*
11968        Wait for first button press.
11969      */
11970      (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
11971      XQueryPosition(display,windows->image.id,&x,&y);
11972      rotate_info.x1=x;
11973      rotate_info.y1=y;
11974      rotate_info.x2=x;
11975      rotate_info.y2=y;
11976      state=DefaultState;
11977      do
11978      {
11979        XHighlightLine(display,windows->image.id,
11980          windows->image.highlight_context,&rotate_info);
11981        /*
11982          Wait for next event.
11983        */
11984        XScreenEvent(display,windows,&event);
11985        XHighlightLine(display,windows->image.id,
11986          windows->image.highlight_context,&rotate_info);
11987        if (event.xany.window == windows->command.id)
11988          {
11989            /*
11990              Select a command from the Command widget.
11991            */
11992            id=XCommandWidget(display,windows,RotateMenu,&event);
11993            if (id < 0)
11994              continue;
11995            (void) XSetFunction(display,windows->image.highlight_context,
11996              GXcopy);
11997            switch (RotateCommands[id])
11998            {
11999              case RotateColorCommand:
12000              {
12001                const char
12002                  *ColorMenu[MaxNumberPens];
12003
12004                int
12005                  pen_number;
12006
12007                XColor
12008                  color;
12009
12010                /*
12011                  Initialize menu selections.
12012                */
12013                for (i=0; i < (int) (MaxNumberPens-2); i++)
12014                  ColorMenu[i]=resource_info->pen_colors[i];
12015                ColorMenu[MaxNumberPens-2]="Browser...";
12016                ColorMenu[MaxNumberPens-1]=(const char *) NULL;
12017                /*
12018                  Select a pen color from the pop-up menu.
12019                */
12020                pen_number=XMenuWidget(display,windows,RotateMenu[id],
12021                  (const char **) ColorMenu,command);
12022                if (pen_number < 0)
12023                  break;
12024                if (pen_number == (MaxNumberPens-2))
12025                  {
12026                    static char
12027                      color_name[MaxTextExtent] = "gray";
12028
12029                    /*
12030                      Select a pen color from a dialog.
12031                    */
12032                    resource_info->pen_colors[pen_number]=color_name;
12033                    XColorBrowserWidget(display,windows,"Select",color_name);
12034                    if (*color_name == '\0')
12035                      break;
12036                  }
12037                /*
12038                  Set pen color.
12039                */
12040                (void) XParseColor(display,windows->map_info->colormap,
12041                  resource_info->pen_colors[pen_number],&color);
12042                XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
12043                  (unsigned int) MaxColors,&color);
12044                windows->pixel_info->pen_colors[pen_number]=color;
12045                pen_id=(unsigned int) pen_number;
12046                break;
12047              }
12048              case RotateDirectionCommand:
12049              {
12050                static const char
12051                  *Directions[] =
12052                  {
12053                    "horizontal",
12054                    "vertical",
12055                    (char *) NULL,
12056                  };
12057
12058                /*
12059                  Select a command from the pop-up menu.
12060                */
12061                id=XMenuWidget(display,windows,RotateMenu[id],
12062                  Directions,command);
12063                if (id >= 0)
12064                  direction=DirectionCommands[id];
12065                break;
12066              }
12067              case RotateHelpCommand:
12068              {
12069                XTextViewWidget(display,resource_info,windows,MagickFalse,
12070                  "Help Viewer - Image Rotation",ImageRotateHelp);
12071                break;
12072              }
12073              case RotateDismissCommand:
12074              {
12075                /*
12076                  Prematurely exit.
12077                */
12078                state|=EscapeState;
12079                state|=ExitState;
12080                break;
12081              }
12082              default:
12083                break;
12084            }
12085            (void) XSetFunction(display,windows->image.highlight_context,
12086              GXinvert);
12087            continue;
12088          }
12089        switch (event.type)
12090        {
12091          case ButtonPress:
12092          {
12093            if (event.xbutton.button != Button1)
12094              break;
12095            if (event.xbutton.window != windows->image.id)
12096              break;
12097            /*
12098              exit loop.
12099            */
12100            (void) XSetFunction(display,windows->image.highlight_context,
12101              GXcopy);
12102            rotate_info.x1=event.xbutton.x;
12103            rotate_info.y1=event.xbutton.y;
12104            state|=ExitState;
12105            break;
12106          }
12107          case ButtonRelease:
12108            break;
12109          case Expose:
12110            break;
12111          case KeyPress:
12112          {
12113            char
12114              command[MaxTextExtent];
12115
12116            KeySym
12117              key_symbol;
12118
12119            if (event.xkey.window != windows->image.id)
12120              break;
12121            /*
12122              Respond to a user key press.
12123            */
12124            (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
12125              sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12126            switch ((int) key_symbol)
12127            {
12128              case XK_Escape:
12129              case XK_F20:
12130              {
12131                /*
12132                  Prematurely exit.
12133                */
12134                state|=EscapeState;
12135                state|=ExitState;
12136                break;
12137              }
12138              case XK_F1:
12139              case XK_Help:
12140              {
12141                (void) XSetFunction(display,windows->image.highlight_context,
12142                  GXcopy);
12143                XTextViewWidget(display,resource_info,windows,MagickFalse,
12144                  "Help Viewer - Image Rotation",ImageRotateHelp);
12145                (void) XSetFunction(display,windows->image.highlight_context,
12146                  GXinvert);
12147                break;
12148              }
12149              default:
12150              {
12151                (void) XBell(display,0);
12152                break;
12153              }
12154            }
12155            break;
12156          }
12157          case MotionNotify:
12158          {
12159            rotate_info.x1=event.xmotion.x;
12160            rotate_info.y1=event.xmotion.y;
12161          }
12162        }
12163        rotate_info.x2=rotate_info.x1;
12164        rotate_info.y2=rotate_info.y1;
12165        if (direction == HorizontalRotateCommand)
12166          rotate_info.x2+=32;
12167        else
12168          rotate_info.y2-=32;
12169      } while ((state & ExitState) == 0);
12170      (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12171      (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12172      if ((state & EscapeState) != 0)
12173        return(MagickTrue);
12174      /*
12175        Draw line as pointer moves until the mouse button is released.
12176      */
12177      distance=0;
12178      (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12179      state=DefaultState;
12180      do
12181      {
12182        if (distance > 9)
12183          {
12184            /*
12185              Display info and draw rotation line.
12186            */
12187            if (windows->info.mapped == MagickFalse)
12188              (void) XMapWindow(display,windows->info.id);
12189            (void) FormatLocaleString(text,MaxTextExtent," %g",
12190              direction == VerticalRotateCommand ? degrees-90.0 : degrees);
12191            XInfoWidget(display,windows,text);
12192            XHighlightLine(display,windows->image.id,
12193              windows->image.highlight_context,&rotate_info);
12194          }
12195        else
12196          if (windows->info.mapped != MagickFalse)
12197            (void) XWithdrawWindow(display,windows->info.id,
12198              windows->info.screen);
12199        /*
12200          Wait for next event.
12201        */
12202        XScreenEvent(display,windows,&event);
12203        if (distance > 9)
12204          XHighlightLine(display,windows->image.id,
12205            windows->image.highlight_context,&rotate_info);
12206        switch (event.type)
12207        {
12208          case ButtonPress:
12209            break;
12210          case ButtonRelease:
12211          {
12212            /*
12213              User has committed to rotation line.
12214            */
12215            rotate_info.x2=event.xbutton.x;
12216            rotate_info.y2=event.xbutton.y;
12217            state|=ExitState;
12218            break;
12219          }
12220          case Expose:
12221            break;
12222          case MotionNotify:
12223          {
12224            rotate_info.x2=event.xmotion.x;
12225            rotate_info.y2=event.xmotion.y;
12226          }
12227          default:
12228            break;
12229        }
12230        /*
12231          Check boundary conditions.
12232        */
12233        if (rotate_info.x2 < 0)
12234          rotate_info.x2=0;
12235        else
12236          if (rotate_info.x2 > (int) windows->image.width)
12237            rotate_info.x2=(short) windows->image.width;
12238        if (rotate_info.y2 < 0)
12239          rotate_info.y2=0;
12240        else
12241          if (rotate_info.y2 > (int) windows->image.height)
12242            rotate_info.y2=(short) windows->image.height;
12243        /*
12244          Compute rotation angle from the slope of the line.
12245        */
12246        degrees=0.0;
12247        distance=(unsigned int)
12248          ((rotate_info.x2-rotate_info.x1+1)*(rotate_info.x2-rotate_info.x1+1))+
12249          ((rotate_info.y2-rotate_info.y1+1)*(rotate_info.y2-rotate_info.y1+1));
12250        if (distance > 9)
12251          degrees=RadiansToDegrees(-atan2((double) (rotate_info.y2-
12252            rotate_info.y1),(double) (rotate_info.x2-rotate_info.x1)));
12253      } while ((state & ExitState) == 0);
12254      (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12255      (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12256      if (distance <= 9)
12257        return(MagickTrue);
12258    }
12259  if (direction == VerticalRotateCommand)
12260    degrees-=90.0;
12261  if (degrees == 0.0)
12262    return(MagickTrue);
12263  /*
12264    Rotate image.
12265  */
12266  normalized_degrees=degrees;
12267  while (normalized_degrees < -45.0)
12268    normalized_degrees+=360.0;
12269  for (rotations=0; normalized_degrees > 45.0; rotations++)
12270    normalized_degrees-=90.0;
12271  if (normalized_degrees != 0.0)
12272    (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
12273  XSetCursorState(display,windows,MagickTrue);
12274  XCheckRefreshWindows(display,windows);
12275  (*image)->background_color.red=ScaleShortToQuantum(
12276    windows->pixel_info->pen_colors[pen_id].red);
12277  (*image)->background_color.green=ScaleShortToQuantum(
12278    windows->pixel_info->pen_colors[pen_id].green);
12279  (*image)->background_color.blue=ScaleShortToQuantum(
12280    windows->pixel_info->pen_colors[pen_id].blue);
12281  rotate_image=RotateImage(*image,degrees,&(*image)->exception);
12282  XSetCursorState(display,windows,MagickFalse);
12283  if (rotate_image == (Image *) NULL)
12284    return(MagickFalse);
12285  *image=DestroyImage(*image);
12286  *image=rotate_image;
12287  if (windows->image.crop_geometry != (char *) NULL)
12288    {
12289      /*
12290        Rotate crop geometry.
12291      */
12292      width=(unsigned int) (*image)->columns;
12293      height=(unsigned int) (*image)->rows;
12294      (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12295      switch (rotations % 4)
12296      {
12297        default:
12298        case 0:
12299          break;
12300        case 1:
12301        {
12302          /*
12303            Rotate 90 degrees.
12304          */
12305          (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12306            "%ux%u%+d%+d",height,width,(int) (*image)->columns-
12307            (int) height-y,x);
12308          break;
12309        }
12310        case 2:
12311        {
12312          /*
12313            Rotate 180 degrees.
12314          */
12315          (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12316            "%ux%u%+d%+d",width,height,(int) width-x,(int) height-y);
12317          break;
12318        }
12319        case 3:
12320        {
12321          /*
12322            Rotate 270 degrees.
12323          */
12324          (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12325            "%ux%u%+d%+d",height,width,y,(int) (*image)->rows-(int) width-x);
12326          break;
12327        }
12328      }
12329    }
12330  if (windows->image.orphan != MagickFalse)
12331    return(MagickTrue);
12332  if (normalized_degrees != 0.0)
12333    {
12334      /*
12335        Update image colormap.
12336      */
12337      windows->image.window_changes.width=(int) (*image)->columns;
12338      windows->image.window_changes.height=(int) (*image)->rows;
12339      if (windows->image.crop_geometry != (char *) NULL)
12340        {
12341          /*
12342            Obtain dimensions of image from crop geometry.
12343          */
12344          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
12345            &width,&height);
12346          windows->image.window_changes.width=(int) width;
12347          windows->image.window_changes.height=(int) height;
12348        }
12349      XConfigureImageColormap(display,resource_info,windows,*image);
12350    }
12351  else
12352    if (((rotations % 4) == 1) || ((rotations % 4) == 3))
12353      {
12354        windows->image.window_changes.width=windows->image.ximage->height;
12355        windows->image.window_changes.height=windows->image.ximage->width;
12356      }
12357  /*
12358    Update image configuration.
12359  */
12360  (void) XConfigureImage(display,resource_info,windows,*image);
12361  return(MagickTrue);
12362}
12363
12364/*
12365%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12366%                                                                             %
12367%                                                                             %
12368%                                                                             %
12369+   X S a v e I m a g e                                                       %
12370%                                                                             %
12371%                                                                             %
12372%                                                                             %
12373%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12374%
12375%  XSaveImage() saves an image to a file.
12376%
12377%  The format of the XSaveImage method is:
12378%
12379%      MagickBooleanType XSaveImage(Display *display,
12380%        XResourceInfo *resource_info,XWindows *windows,Image *image)
12381%
12382%  A description of each parameter follows:
12383%
12384%    o display: Specifies a connection to an X server; returned from
12385%      XOpenDisplay.
12386%
12387%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12388%
12389%    o windows: Specifies a pointer to a XWindows structure.
12390%
12391%    o image: the image.
12392%
12393*/
12394static MagickBooleanType XSaveImage(Display *display,
12395  XResourceInfo *resource_info,XWindows *windows,Image *image)
12396{
12397  char
12398    filename[MaxTextExtent],
12399    geometry[MaxTextExtent];
12400
12401  Image
12402    *save_image;
12403
12404  ImageInfo
12405    *image_info;
12406
12407  MagickStatusType
12408    status;
12409
12410  /*
12411    Request file name from user.
12412  */
12413  if (resource_info->write_filename != (char *) NULL)
12414    (void) CopyMagickString(filename,resource_info->write_filename,
12415      MaxTextExtent);
12416  else
12417    {
12418      char
12419        path[MaxTextExtent];
12420
12421      int
12422        status;
12423
12424      GetPathComponent(image->filename,HeadPath,path);
12425      GetPathComponent(image->filename,TailPath,filename);
12426      status=chdir(path);
12427      if (status == -1)
12428        (void) ThrowMagickException(&image->exception,GetMagickModule(),
12429          FileOpenError,"UnableToOpenFile","%s",path);
12430    }
12431  XFileBrowserWidget(display,windows,"Save",filename);
12432  if (*filename == '\0')
12433    return(MagickTrue);
12434  if (IsPathAccessible(filename) != MagickFalse)
12435    {
12436      int
12437        status;
12438
12439      /*
12440        File exists-- seek user's permission before overwriting.
12441      */
12442      status=XConfirmWidget(display,windows,"Overwrite",filename);
12443      if (status <= 0)
12444        return(MagickTrue);
12445    }
12446  image_info=CloneImageInfo(resource_info->image_info);
12447  (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
12448  (void) SetImageInfo(image_info,1,&image->exception);
12449  if ((LocaleCompare(image_info->magick,"JPEG") == 0) ||
12450      (LocaleCompare(image_info->magick,"JPG") == 0))
12451    {
12452      char
12453        quality[MaxTextExtent];
12454
12455      int
12456        status;
12457
12458      /*
12459        Request JPEG quality from user.
12460      */
12461      (void) FormatLocaleString(quality,MaxTextExtent,"%.20g",(double)
12462        image->quality);
12463      status=XDialogWidget(display,windows,"Save","Enter JPEG quality:",
12464        quality);
12465      if (*quality == '\0')
12466        return(MagickTrue);
12467      image->quality=StringToUnsignedLong(quality);
12468      image_info->interlace=status != 0 ? NoInterlace : PlaneInterlace;
12469    }
12470  if ((LocaleCompare(image_info->magick,"EPS") == 0) ||
12471      (LocaleCompare(image_info->magick,"PDF") == 0) ||
12472      (LocaleCompare(image_info->magick,"PS") == 0) ||
12473      (LocaleCompare(image_info->magick,"PS2") == 0))
12474    {
12475      char
12476        geometry[MaxTextExtent];
12477
12478      /*
12479        Request page geometry from user.
12480      */
12481      (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
12482      if (LocaleCompare(image_info->magick,"PDF") == 0)
12483        (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
12484      if (image_info->page != (char *) NULL)
12485        (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
12486      XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
12487        "Select page geometry:",geometry);
12488      if (*geometry != '\0')
12489        image_info->page=GetPageGeometry(geometry);
12490    }
12491  /*
12492    Apply image transforms.
12493  */
12494  XSetCursorState(display,windows,MagickTrue);
12495  XCheckRefreshWindows(display,windows);
12496  save_image=CloneImage(image,0,0,MagickTrue,&image->exception);
12497  if (save_image == (Image *) NULL)
12498    return(MagickFalse);
12499  (void) FormatLocaleString(geometry,MaxTextExtent,"%dx%d!",
12500    windows->image.ximage->width,windows->image.ximage->height);
12501  (void) TransformImage(&save_image,windows->image.crop_geometry,geometry);
12502  /*
12503    Write image.
12504  */
12505  (void) CopyMagickString(save_image->filename,filename,MaxTextExtent);
12506  status=WriteImage(image_info,save_image);
12507  if (status != MagickFalse)
12508    image->taint=MagickFalse;
12509  save_image=DestroyImage(save_image);
12510  image_info=DestroyImageInfo(image_info);
12511  XSetCursorState(display,windows,MagickFalse);
12512  return(status != 0 ? MagickTrue : MagickFalse);
12513}
12514
12515/*
12516%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12517%                                                                             %
12518%                                                                             %
12519%                                                                             %
12520+   X S c r e e n E v e n t                                                   %
12521%                                                                             %
12522%                                                                             %
12523%                                                                             %
12524%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12525%
12526%  XScreenEvent() handles global events associated with the Pan and Magnify
12527%  windows.
12528%
12529%  The format of the XScreenEvent function is:
12530%
12531%      void XScreenEvent(Display *display,XWindows *windows,XEvent *event)
12532%
12533%  A description of each parameter follows:
12534%
12535%    o display: Specifies a pointer to the Display structure;  returned from
12536%      XOpenDisplay.
12537%
12538%    o windows: Specifies a pointer to a XWindows structure.
12539%
12540%    o event: Specifies a pointer to a X11 XEvent structure.
12541%
12542%
12543*/
12544
12545#if defined(__cplusplus) || defined(c_plusplus)
12546extern "C" {
12547#endif
12548
12549static int XPredicate(Display *magick_unused(display),XEvent *event,char *data)
12550{
12551  register XWindows
12552    *windows;
12553
12554  windows=(XWindows *) data;
12555  if ((event->type == ClientMessage) &&
12556      (event->xclient.window == windows->image.id))
12557    return(MagickFalse);
12558  return(MagickTrue);
12559}
12560
12561#if defined(__cplusplus) || defined(c_plusplus)
12562}
12563#endif
12564
12565static void XScreenEvent(Display *display,XWindows *windows,XEvent *event)
12566{
12567  register int
12568    x,
12569    y;
12570
12571  (void) XIfEvent(display,event,XPredicate,(char *) windows);
12572  if (event->xany.window == windows->command.id)
12573    return;
12574  switch (event->type)
12575  {
12576    case ButtonPress:
12577    case ButtonRelease:
12578    {
12579      if ((event->xbutton.button == Button3) &&
12580          (event->xbutton.state & Mod1Mask))
12581        {
12582          /*
12583            Convert Alt-Button3 to Button2.
12584          */
12585          event->xbutton.button=Button2;
12586          event->xbutton.state&=(~Mod1Mask);
12587        }
12588      if (event->xbutton.window == windows->backdrop.id)
12589        {
12590          (void) XSetInputFocus(display,event->xbutton.window,RevertToParent,
12591            event->xbutton.time);
12592          break;
12593        }
12594      if (event->xbutton.window == windows->pan.id)
12595        {
12596          XPanImage(display,windows,event);
12597          break;
12598        }
12599      if (event->xbutton.window == windows->image.id)
12600        if (event->xbutton.button == Button2)
12601          {
12602            /*
12603              Update magnified image.
12604            */
12605            x=event->xbutton.x;
12606            y=event->xbutton.y;
12607            if (x < 0)
12608              x=0;
12609            else
12610              if (x >= (int) windows->image.width)
12611                x=(int) (windows->image.width-1);
12612            windows->magnify.x=(int) windows->image.x+x;
12613            if (y < 0)
12614              y=0;
12615            else
12616             if (y >= (int) windows->image.height)
12617               y=(int) (windows->image.height-1);
12618            windows->magnify.y=windows->image.y+y;
12619            if (windows->magnify.mapped == MagickFalse)
12620              (void) XMapRaised(display,windows->magnify.id);
12621            XMakeMagnifyImage(display,windows);
12622            if (event->type == ButtonRelease)
12623              (void) XWithdrawWindow(display,windows->info.id,
12624                windows->info.screen);
12625            break;
12626          }
12627      break;
12628    }
12629    case ClientMessage:
12630    {
12631      /*
12632        If client window delete message, exit.
12633      */
12634      if (event->xclient.message_type != windows->wm_protocols)
12635        break;
12636      if (*event->xclient.data.l != (long) windows->wm_delete_window)
12637        break;
12638      if (event->xclient.window == windows->magnify.id)
12639        {
12640          (void) XWithdrawWindow(display,windows->magnify.id,
12641            windows->magnify.screen);
12642          break;
12643        }
12644      break;
12645    }
12646    case ConfigureNotify:
12647    {
12648      if (event->xconfigure.window == windows->magnify.id)
12649        {
12650          unsigned int
12651            magnify;
12652
12653          /*
12654            Magnify window has a new configuration.
12655          */
12656          windows->magnify.width=(unsigned int) event->xconfigure.width;
12657          windows->magnify.height=(unsigned int) event->xconfigure.height;
12658          if (windows->magnify.mapped == MagickFalse)
12659            break;
12660          magnify=1;
12661          while ((int) magnify <= event->xconfigure.width)
12662            magnify<<=1;
12663          while ((int) magnify <= event->xconfigure.height)
12664            magnify<<=1;
12665          magnify>>=1;
12666          if (((int) magnify != event->xconfigure.width) ||
12667              ((int) magnify != event->xconfigure.height))
12668            {
12669              XWindowChanges
12670                window_changes;
12671
12672              window_changes.width=(int) magnify;
12673              window_changes.height=(int) magnify;
12674              (void) XReconfigureWMWindow(display,windows->magnify.id,
12675                windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
12676                &window_changes);
12677              break;
12678            }
12679          XMakeMagnifyImage(display,windows);
12680          break;
12681        }
12682      break;
12683    }
12684    case Expose:
12685    {
12686      if (event->xexpose.window == windows->image.id)
12687        {
12688          XRefreshWindow(display,&windows->image,event);
12689          break;
12690        }
12691      if (event->xexpose.window == windows->pan.id)
12692        if (event->xexpose.count == 0)
12693          {
12694            XDrawPanRectangle(display,windows);
12695            break;
12696          }
12697      if (event->xexpose.window == windows->magnify.id)
12698        if (event->xexpose.count == 0)
12699          {
12700            XMakeMagnifyImage(display,windows);
12701            break;
12702          }
12703      break;
12704    }
12705    case KeyPress:
12706    {
12707      char
12708        command[MaxTextExtent];
12709
12710      KeySym
12711        key_symbol;
12712
12713      if (event->xkey.window != windows->magnify.id)
12714        break;
12715      /*
12716        Respond to a user key press.
12717      */
12718      (void) XLookupString((XKeyEvent *) &event->xkey,command,(int)
12719        sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12720      XMagnifyWindowCommand(display,windows,event->xkey.state,key_symbol);
12721      break;
12722    }
12723    case MapNotify:
12724    {
12725      if (event->xmap.window == windows->magnify.id)
12726        {
12727          windows->magnify.mapped=MagickTrue;
12728          (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12729          break;
12730        }
12731      if (event->xmap.window == windows->info.id)
12732        {
12733          windows->info.mapped=MagickTrue;
12734          break;
12735        }
12736      break;
12737    }
12738    case MotionNotify:
12739    {
12740      while (XCheckMaskEvent(display,ButtonMotionMask,event)) ;
12741      if (event->xmotion.window == windows->image.id)
12742        if (windows->magnify.mapped != MagickFalse)
12743          {
12744            /*
12745              Update magnified image.
12746            */
12747            x=event->xmotion.x;
12748            y=event->xmotion.y;
12749            if (x < 0)
12750              x=0;
12751            else
12752              if (x >= (int) windows->image.width)
12753                x=(int) (windows->image.width-1);
12754            windows->magnify.x=(int) windows->image.x+x;
12755            if (y < 0)
12756              y=0;
12757            else
12758             if (y >= (int) windows->image.height)
12759               y=(int) (windows->image.height-1);
12760            windows->magnify.y=windows->image.y+y;
12761            XMakeMagnifyImage(display,windows);
12762          }
12763      break;
12764    }
12765    case UnmapNotify:
12766    {
12767      if (event->xunmap.window == windows->magnify.id)
12768        {
12769          windows->magnify.mapped=MagickFalse;
12770          break;
12771        }
12772      if (event->xunmap.window == windows->info.id)
12773        {
12774          windows->info.mapped=MagickFalse;
12775          break;
12776        }
12777      break;
12778    }
12779    default:
12780      break;
12781  }
12782}
12783
12784/*
12785%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12786%                                                                             %
12787%                                                                             %
12788%                                                                             %
12789+   X S e t C r o p G e o m e t r y                                           %
12790%                                                                             %
12791%                                                                             %
12792%                                                                             %
12793%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12794%
12795%  XSetCropGeometry() accepts a cropping geometry relative to the Image window
12796%  and translates it to a cropping geometry relative to the image.
12797%
12798%  The format of the XSetCropGeometry method is:
12799%
12800%      void XSetCropGeometry(Display *display,XWindows *windows,
12801%        RectangleInfo *crop_info,Image *image)
12802%
12803%  A description of each parameter follows:
12804%
12805%    o display: Specifies a connection to an X server; returned from
12806%      XOpenDisplay.
12807%
12808%    o windows: Specifies a pointer to a XWindows structure.
12809%
12810%    o crop_info:  A pointer to a RectangleInfo that defines a region of the
12811%      Image window to crop.
12812%
12813%    o image: the image.
12814%
12815*/
12816static void XSetCropGeometry(Display *display,XWindows *windows,
12817  RectangleInfo *crop_info,Image *image)
12818{
12819  char
12820    text[MaxTextExtent];
12821
12822  int
12823    x,
12824    y;
12825
12826  MagickRealType
12827    scale_factor;
12828
12829  unsigned int
12830    height,
12831    width;
12832
12833  if (windows->info.mapped != MagickFalse)
12834    {
12835      /*
12836        Display info on cropping rectangle.
12837      */
12838      (void) FormatLocaleString(text,MaxTextExtent," %.20gx%.20g%+.20g%+.20g",
12839        (double) crop_info->width,(double) crop_info->height,(double)
12840        crop_info->x,(double) crop_info->y);
12841      XInfoWidget(display,windows,text);
12842    }
12843  /*
12844    Cropping geometry is relative to any previous crop geometry.
12845  */
12846  x=0;
12847  y=0;
12848  width=(unsigned int) image->columns;
12849  height=(unsigned int) image->rows;
12850  if (windows->image.crop_geometry != (char *) NULL)
12851    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12852  else
12853    windows->image.crop_geometry=AcquireString((char *) NULL);
12854  /*
12855    Define the crop geometry string from the cropping rectangle.
12856  */
12857  scale_factor=(MagickRealType) width/windows->image.ximage->width;
12858  if (crop_info->x > 0)
12859    x+=(int) (scale_factor*crop_info->x+0.5);
12860  width=(unsigned int) (scale_factor*crop_info->width+0.5);
12861  if (width == 0)
12862    width=1;
12863  scale_factor=(MagickRealType) height/windows->image.ximage->height;
12864  if (crop_info->y > 0)
12865    y+=(int) (scale_factor*crop_info->y+0.5);
12866  height=(unsigned int) (scale_factor*crop_info->height+0.5);
12867  if (height == 0)
12868    height=1;
12869  (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12870    "%ux%u%+d%+d",width,height,x,y);
12871}
12872
12873/*
12874%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12875%                                                                             %
12876%                                                                             %
12877%                                                                             %
12878+   X T i l e I m a g e                                                       %
12879%                                                                             %
12880%                                                                             %
12881%                                                                             %
12882%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12883%
12884%  XTileImage() loads or deletes a selected tile from a visual image directory.
12885%  The load or delete command is chosen from a menu.
12886%
12887%  The format of the XTileImage method is:
12888%
12889%      Image *XTileImage(Display *display,XResourceInfo *resource_info,
12890%        XWindows *windows,Image *image,XEvent *event)
12891%
12892%  A description of each parameter follows:
12893%
12894%    o tile_image:  XTileImage reads or deletes the tile image
12895%      and returns it.  A null image is returned if an error occurs.
12896%
12897%    o display: Specifies a connection to an X server;  returned from
12898%      XOpenDisplay.
12899%
12900%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12901%
12902%    o windows: Specifies a pointer to a XWindows structure.
12903%
12904%    o image: the image; returned from ReadImage.
12905%
12906%    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
12907%      the entire image is refreshed.
12908%
12909*/
12910static Image *XTileImage(Display *display,XResourceInfo *resource_info,
12911  XWindows *windows,Image *image,XEvent *event)
12912{
12913  static const char
12914    *VerbMenu[] =
12915    {
12916      "Load",
12917      "Next",
12918      "Former",
12919      "Delete",
12920      "Update",
12921      (char *) NULL,
12922    };
12923
12924  static const ModeType
12925    TileCommands[] =
12926    {
12927      TileLoadCommand,
12928      TileNextCommand,
12929      TileFormerCommand,
12930      TileDeleteCommand,
12931      TileUpdateCommand
12932    };
12933
12934  char
12935    command[MaxTextExtent],
12936    filename[MaxTextExtent];
12937
12938  Image
12939    *tile_image;
12940
12941  int
12942    id,
12943    status,
12944    tile,
12945    x,
12946    y;
12947
12948  MagickRealType
12949    scale_factor;
12950
12951  register char
12952    *p,
12953    *q;
12954
12955  register int
12956    i;
12957
12958  unsigned int
12959    height,
12960    width;
12961
12962  /*
12963    Tile image is relative to montage image configuration.
12964  */
12965  x=0;
12966  y=0;
12967  width=(unsigned int) image->columns;
12968  height=(unsigned int) image->rows;
12969  if (windows->image.crop_geometry != (char *) NULL)
12970    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12971  scale_factor=(MagickRealType) width/windows->image.ximage->width;
12972  event->xbutton.x+=windows->image.x;
12973  event->xbutton.x=(int) (scale_factor*event->xbutton.x+x+0.5);
12974  scale_factor=(MagickRealType) height/windows->image.ximage->height;
12975  event->xbutton.y+=windows->image.y;
12976  event->xbutton.y=(int) (scale_factor*event->xbutton.y+y+0.5);
12977  /*
12978    Determine size and location of each tile in the visual image directory.
12979  */
12980  width=(unsigned int) image->columns;
12981  height=(unsigned int) image->rows;
12982  x=0;
12983  y=0;
12984  (void) XParseGeometry(image->montage,&x,&y,&width,&height);
12985  tile=((event->xbutton.y-y)/height)*(((int) image->columns-x)/width)+
12986    (event->xbutton.x-x)/width;
12987  if (tile < 0)
12988    {
12989      /*
12990        Button press is outside any tile.
12991      */
12992      (void) XBell(display,0);
12993      return((Image *) NULL);
12994    }
12995  /*
12996    Determine file name from the tile directory.
12997  */
12998  p=image->directory;
12999  for (i=tile; (i != 0) && (*p != '\0'); )
13000  {
13001    if (*p == '\n')
13002      i--;
13003    p++;
13004  }
13005  if (*p == '\0')
13006    {
13007      /*
13008        Button press is outside any tile.
13009      */
13010      (void) XBell(display,0);
13011      return((Image *) NULL);
13012    }
13013  /*
13014    Select a command from the pop-up menu.
13015  */
13016  id=XMenuWidget(display,windows,"Tile Verb",VerbMenu,command);
13017  if (id < 0)
13018    return((Image *) NULL);
13019  q=p;
13020  while ((*q != '\n') && (*q != '\0'))
13021    q++;
13022  (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13023  /*
13024    Perform command for the selected tile.
13025  */
13026  XSetCursorState(display,windows,MagickTrue);
13027  XCheckRefreshWindows(display,windows);
13028  tile_image=NewImageList();
13029  switch (TileCommands[id])
13030  {
13031    case TileLoadCommand:
13032    {
13033      /*
13034        Load tile image.
13035      */
13036      XCheckRefreshWindows(display,windows);
13037      (void) CopyMagickString(resource_info->image_info->magick,"MIFF",
13038        MaxTextExtent);
13039      (void) CopyMagickString(resource_info->image_info->filename,filename,
13040        MaxTextExtent);
13041      tile_image=ReadImage(resource_info->image_info,&image->exception);
13042      CatchException(&image->exception);
13043      (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13044      break;
13045    }
13046    case TileNextCommand:
13047    {
13048      /*
13049        Display next image.
13050      */
13051      XClientMessage(display,windows->image.id,windows->im_protocols,
13052        windows->im_next_image,CurrentTime);
13053      break;
13054    }
13055    case TileFormerCommand:
13056    {
13057      /*
13058        Display former image.
13059      */
13060      XClientMessage(display,windows->image.id,windows->im_protocols,
13061        windows->im_former_image,CurrentTime);
13062      break;
13063    }
13064    case TileDeleteCommand:
13065    {
13066      /*
13067        Delete tile image.
13068      */
13069      if (IsPathAccessible(filename) == MagickFalse)
13070        {
13071          XNoticeWidget(display,windows,"Image file does not exist:",filename);
13072          break;
13073        }
13074      status=XConfirmWidget(display,windows,"Really delete tile",filename);
13075      if (status <= 0)
13076        break;
13077      status=remove(filename) != 0 ? MagickTrue : MagickFalse;
13078      if (status != MagickFalse)
13079        {
13080          XNoticeWidget(display,windows,"Unable to delete image file:",
13081            filename);
13082          break;
13083        }
13084    }
13085    case TileUpdateCommand:
13086    {
13087      ExceptionInfo
13088        *exception;
13089
13090      int
13091        x_offset,
13092        y_offset;
13093
13094      PixelPacket
13095        pixel;
13096
13097      register int
13098        j;
13099
13100      register Quantum
13101        *s;
13102
13103      /*
13104        Ensure all the images exist.
13105      */
13106      tile=0;
13107      for (p=image->directory; *p != '\0'; p++)
13108      {
13109        CacheView
13110          *image_view;
13111
13112        q=p;
13113        while ((*q != '\n') && (*q != '\0'))
13114          q++;
13115        (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13116        p=q;
13117        if (IsPathAccessible(filename) != MagickFalse)
13118          {
13119            tile++;
13120            continue;
13121          }
13122        /*
13123          Overwrite tile with background color.
13124        */
13125        x_offset=(int) (width*(tile % (((int) image->columns-x)/width))+x);
13126        y_offset=(int) (height*(tile/(((int) image->columns-x)/width))+y);
13127        exception=(&image->exception);
13128        image_view=AcquireCacheView(image);
13129        (void) GetOneCacheViewVirtualPixel(image_view,0,0,&pixel,exception);
13130        for (i=0; i < (int) height; i++)
13131        {
13132          s=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,(ssize_t)
13133            y_offset+i,width,1,exception);
13134          if (s == (Quantum *) NULL)
13135            break;
13136          for (j=0; j < (int) width; j++)
13137          {
13138            SetPixelPacket(image,&pixel,s);
13139            s+=GetPixelChannels(image);
13140          }
13141          if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
13142            break;
13143        }
13144        image_view=DestroyCacheView(image_view);
13145        tile++;
13146      }
13147      windows->image.window_changes.width=(int) image->columns;
13148      windows->image.window_changes.height=(int) image->rows;
13149      XConfigureImageColormap(display,resource_info,windows,image);
13150      (void) XConfigureImage(display,resource_info,windows,image);
13151      break;
13152    }
13153    default:
13154      break;
13155  }
13156  XSetCursorState(display,windows,MagickFalse);
13157  return(tile_image);
13158}
13159
13160/*
13161%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13162%                                                                             %
13163%                                                                             %
13164%                                                                             %
13165+   X T r a n s l a t e I m a g e                                             %
13166%                                                                             %
13167%                                                                             %
13168%                                                                             %
13169%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13170%
13171%  XTranslateImage() translates the image within an Image window by one pixel
13172%  as specified by the key symbol.  If the image has a `montage string the
13173%  translation is respect to the width and height contained within the string.
13174%
13175%  The format of the XTranslateImage method is:
13176%
13177%      void XTranslateImage(Display *display,XWindows *windows,
13178%        Image *image,const KeySym key_symbol)
13179%
13180%  A description of each parameter follows:
13181%
13182%    o display: Specifies a connection to an X server; returned from
13183%      XOpenDisplay.
13184%
13185%    o windows: Specifies a pointer to a XWindows structure.
13186%
13187%    o image: the image.
13188%
13189%    o key_symbol: Specifies a KeySym which indicates which side of the image
13190%      to trim.
13191%
13192*/
13193static void XTranslateImage(Display *display,XWindows *windows,
13194  Image *image,const KeySym key_symbol)
13195{
13196  char
13197    text[MaxTextExtent];
13198
13199  int
13200    x,
13201    y;
13202
13203  unsigned int
13204    x_offset,
13205    y_offset;
13206
13207  /*
13208    User specified a pan position offset.
13209  */
13210  x_offset=windows->image.width;
13211  y_offset=windows->image.height;
13212  if (image->montage != (char *) NULL)
13213    (void) XParseGeometry(image->montage,&x,&y,&x_offset,&y_offset);
13214  switch ((int) key_symbol)
13215  {
13216    case XK_Home:
13217    case XK_KP_Home:
13218    {
13219      windows->image.x=(int) windows->image.width/2;
13220      windows->image.y=(int) windows->image.height/2;
13221      break;
13222    }
13223    case XK_Left:
13224    case XK_KP_Left:
13225    {
13226      windows->image.x-=x_offset;
13227      break;
13228    }
13229    case XK_Next:
13230    case XK_Up:
13231    case XK_KP_Up:
13232    {
13233      windows->image.y-=y_offset;
13234      break;
13235    }
13236    case XK_Right:
13237    case XK_KP_Right:
13238    {
13239      windows->image.x+=x_offset;
13240      break;
13241    }
13242    case XK_Prior:
13243    case XK_Down:
13244    case XK_KP_Down:
13245    {
13246      windows->image.y+=y_offset;
13247      break;
13248    }
13249    default:
13250      return;
13251  }
13252  /*
13253    Check boundary conditions.
13254  */
13255  if (windows->image.x < 0)
13256    windows->image.x=0;
13257  else
13258    if ((int) (windows->image.x+windows->image.width) >
13259        windows->image.ximage->width)
13260      windows->image.x=(int) windows->image.ximage->width-windows->image.width;
13261  if (windows->image.y < 0)
13262    windows->image.y=0;
13263  else
13264    if ((int) (windows->image.y+windows->image.height) >
13265        windows->image.ximage->height)
13266      windows->image.y=(int) windows->image.ximage->height-windows->image.height;
13267  /*
13268    Refresh Image window.
13269  */
13270  (void) FormatLocaleString(text,MaxTextExtent," %ux%u%+d%+d ",
13271    windows->image.width,windows->image.height,windows->image.x,
13272    windows->image.y);
13273  XInfoWidget(display,windows,text);
13274  XCheckRefreshWindows(display,windows);
13275  XDrawPanRectangle(display,windows);
13276  XRefreshWindow(display,&windows->image,(XEvent *) NULL);
13277  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13278}
13279
13280/*
13281%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13282%                                                                             %
13283%                                                                             %
13284%                                                                             %
13285+   X T r i m I m a g e                                                       %
13286%                                                                             %
13287%                                                                             %
13288%                                                                             %
13289%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13290%
13291%  XTrimImage() trims the edges from the Image window.
13292%
13293%  The format of the XTrimImage method is:
13294%
13295%      MagickBooleanType XTrimImage(Display *display,
13296%        XResourceInfo *resource_info,XWindows *windows,Image *image)
13297%
13298%  A description of each parameter follows:
13299%
13300%    o display: Specifies a connection to an X server; returned from
13301%      XOpenDisplay.
13302%
13303%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13304%
13305%    o windows: Specifies a pointer to a XWindows structure.
13306%
13307%    o image: the image.
13308%
13309*/
13310static MagickBooleanType XTrimImage(Display *display,
13311  XResourceInfo *resource_info,XWindows *windows,Image *image)
13312{
13313  RectangleInfo
13314    trim_info;
13315
13316  register int
13317    x,
13318    y;
13319
13320  size_t
13321    background,
13322    pixel;
13323
13324  /*
13325    Trim edges from image.
13326  */
13327  XSetCursorState(display,windows,MagickTrue);
13328  XCheckRefreshWindows(display,windows);
13329  /*
13330    Crop the left edge.
13331  */
13332  background=XGetPixel(windows->image.ximage,0,0);
13333  trim_info.width=(size_t) windows->image.ximage->width;
13334  for (x=0; x < windows->image.ximage->width; x++)
13335  {
13336    for (y=0; y < windows->image.ximage->height; y++)
13337    {
13338      pixel=XGetPixel(windows->image.ximage,x,y);
13339      if (pixel != background)
13340        break;
13341    }
13342    if (y < windows->image.ximage->height)
13343      break;
13344  }
13345  trim_info.x=(ssize_t) x;
13346  if (trim_info.x == (ssize_t) windows->image.ximage->width)
13347    {
13348      XSetCursorState(display,windows,MagickFalse);
13349      return(MagickFalse);
13350    }
13351  /*
13352    Crop the right edge.
13353  */
13354  background=XGetPixel(windows->image.ximage,windows->image.ximage->width-1,0);
13355  for (x=windows->image.ximage->width-1; x != 0; x--)
13356  {
13357    for (y=0; y < windows->image.ximage->height; y++)
13358    {
13359      pixel=XGetPixel(windows->image.ximage,x,y);
13360      if (pixel != background)
13361        break;
13362    }
13363    if (y < windows->image.ximage->height)
13364      break;
13365  }
13366  trim_info.width=(size_t) (x-trim_info.x+1);
13367  /*
13368    Crop the top edge.
13369  */
13370  background=XGetPixel(windows->image.ximage,0,0);
13371  trim_info.height=(size_t) windows->image.ximage->height;
13372  for (y=0; y < windows->image.ximage->height; y++)
13373  {
13374    for (x=0; x < windows->image.ximage->width; x++)
13375    {
13376      pixel=XGetPixel(windows->image.ximage,x,y);
13377      if (pixel != background)
13378        break;
13379    }
13380    if (x < windows->image.ximage->width)
13381      break;
13382  }
13383  trim_info.y=(ssize_t) y;
13384  /*
13385    Crop the bottom edge.
13386  */
13387  background=XGetPixel(windows->image.ximage,0,windows->image.ximage->height-1);
13388  for (y=windows->image.ximage->height-1; y != 0; y--)
13389  {
13390    for (x=0; x < windows->image.ximage->width; x++)
13391    {
13392      pixel=XGetPixel(windows->image.ximage,x,y);
13393      if (pixel != background)
13394        break;
13395    }
13396    if (x < windows->image.ximage->width)
13397      break;
13398  }
13399  trim_info.height=(size_t) y-trim_info.y+1;
13400  if (((unsigned int) trim_info.width != windows->image.width) ||
13401      ((unsigned int) trim_info.height != windows->image.height))
13402    {
13403      /*
13404        Reconfigure Image window as defined by the trimming rectangle.
13405      */
13406      XSetCropGeometry(display,windows,&trim_info,image);
13407      windows->image.window_changes.width=(int) trim_info.width;
13408      windows->image.window_changes.height=(int) trim_info.height;
13409      (void) XConfigureImage(display,resource_info,windows,image);
13410    }
13411  XSetCursorState(display,windows,MagickFalse);
13412  return(MagickTrue);
13413}
13414
13415/*
13416%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13417%                                                                             %
13418%                                                                             %
13419%                                                                             %
13420+   X V i s u a l D i r e c t o r y I m a g e                                 %
13421%                                                                             %
13422%                                                                             %
13423%                                                                             %
13424%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13425%
13426%  XVisualDirectoryImage() creates a Visual Image Directory.
13427%
13428%  The format of the XVisualDirectoryImage method is:
13429%
13430%      Image *XVisualDirectoryImage(Display *display,
13431%        XResourceInfo *resource_info,XWindows *windows)
13432%
13433%  A description of each parameter follows:
13434%
13435%    o nexus: Method XVisualDirectoryImage returns a visual image
13436%      directory if it can be created successfully.  Otherwise a null image
13437%      is returned.
13438%
13439%    o display: Specifies a connection to an X server; returned from
13440%      XOpenDisplay.
13441%
13442%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13443%
13444%    o windows: Specifies a pointer to a XWindows structure.
13445%
13446*/
13447static Image *XVisualDirectoryImage(Display *display,
13448  XResourceInfo *resource_info,XWindows *windows)
13449{
13450#define TileImageTag  "Scale/Image"
13451#define XClientName  "montage"
13452
13453  char
13454    **filelist;
13455
13456  ExceptionInfo
13457    *exception;
13458
13459  Image
13460    *images,
13461    *montage_image,
13462    *next_image,
13463    *thumbnail_image;
13464
13465  ImageInfo
13466    *read_info;
13467
13468  int
13469    number_files;
13470
13471  MagickBooleanType
13472    backdrop;
13473
13474  MagickStatusType
13475    status;
13476
13477  MontageInfo
13478    *montage_info;
13479
13480  RectangleInfo
13481    geometry;
13482
13483  register int
13484    i;
13485
13486  static char
13487    filename[MaxTextExtent] = "\0",
13488    filenames[MaxTextExtent] = "*";
13489
13490  XResourceInfo
13491    background_resources;
13492
13493  /*
13494    Request file name from user.
13495  */
13496  XFileBrowserWidget(display,windows,"Directory",filenames);
13497  if (*filenames == '\0')
13498    return((Image *) NULL);
13499  /*
13500    Expand the filenames.
13501  */
13502  filelist=(char **) AcquireMagickMemory(sizeof(*filelist));
13503  if (filelist == (char **) NULL)
13504    {
13505      ThrowXWindowFatalException(ResourceLimitError,"MemoryAllocationFailed",
13506        filenames);
13507      return((Image *) NULL);
13508    }
13509  number_files=1;
13510  filelist[0]=filenames;
13511  status=ExpandFilenames(&number_files,&filelist);
13512  if ((status == MagickFalse) || (number_files == 0))
13513    {
13514      if (number_files == 0)
13515        ThrowXWindowFatalException(ImageError,"NoImagesWereFound",filenames)
13516      else
13517        ThrowXWindowFatalException(ResourceLimitError,"MemoryAllocationFailed",
13518          filenames);
13519      return((Image *) NULL);
13520    }
13521  /*
13522    Set image background resources.
13523  */
13524  background_resources=(*resource_info);
13525  background_resources.window_id=AcquireString("");
13526  (void) FormatLocaleString(background_resources.window_id,MaxTextExtent,
13527    "0x%lx",windows->image.id);
13528  background_resources.backdrop=MagickTrue;
13529  /*
13530    Read each image and convert them to a tile.
13531  */
13532  backdrop=(windows->visual_info->klass == TrueColor) ||
13533    (windows->visual_info->klass == DirectColor) ? MagickTrue : MagickFalse;
13534  read_info=CloneImageInfo(resource_info->image_info);
13535  (void) SetImageOption(read_info,"jpeg:size","120x120");
13536  (void) CloneString(&read_info->size,DefaultTileGeometry);
13537  (void) SetImageInfoProgressMonitor(read_info,(MagickProgressMonitor) NULL,
13538    (void *) NULL);
13539  images=NewImageList();
13540  exception=AcquireExceptionInfo();
13541  XSetCursorState(display,windows,MagickTrue);
13542  XCheckRefreshWindows(display,windows);
13543  for (i=0; i < (int) number_files; i++)
13544  {
13545    (void) CopyMagickString(read_info->filename,filelist[i],MaxTextExtent);
13546    filelist[i]=DestroyString(filelist[i]);
13547    *read_info->magick='\0';
13548    next_image=ReadImage(read_info,exception);
13549    CatchException(exception);
13550    if (next_image != (Image *) NULL)
13551      {
13552        (void) DeleteImageProperty(next_image,"label");
13553        (void) SetImageProperty(next_image,"label",InterpretImageProperties(
13554          read_info,next_image,DefaultTileLabel));
13555        (void) ParseRegionGeometry(next_image,read_info->size,&geometry,
13556          exception);
13557        thumbnail_image=ThumbnailImage(next_image,geometry.width,
13558          geometry.height,exception);
13559        if (thumbnail_image != (Image *) NULL)
13560          {
13561            next_image=DestroyImage(next_image);
13562            next_image=thumbnail_image;
13563          }
13564        if (backdrop)
13565          {
13566            (void) XDisplayBackgroundImage(display,&background_resources,
13567              next_image);
13568            XSetCursorState(display,windows,MagickTrue);
13569          }
13570        AppendImageToList(&images,next_image);
13571        if (images->progress_monitor != (MagickProgressMonitor) NULL)
13572          {
13573            MagickBooleanType
13574              proceed;
13575
13576            proceed=SetImageProgress(images,LoadImageTag,(MagickOffsetType) i,
13577              (MagickSizeType) number_files);
13578            if (proceed == MagickFalse)
13579              break;
13580          }
13581      }
13582  }
13583  exception=DestroyExceptionInfo(exception);
13584  filelist=(char **) RelinquishMagickMemory(filelist);
13585  if (images == (Image *) NULL)
13586    {
13587      read_info=DestroyImageInfo(read_info);
13588      XSetCursorState(display,windows,MagickFalse);
13589      ThrowXWindowFatalException(ImageError,"NoImagesWereLoaded",filenames);
13590      return((Image *) NULL);
13591    }
13592  /*
13593    Create the Visual Image Directory.
13594  */
13595  montage_info=CloneMontageInfo(read_info,(MontageInfo *) NULL);
13596  montage_info->pointsize=10;
13597  if (resource_info->font != (char *) NULL)
13598    (void) CloneString(&montage_info->font,resource_info->font);
13599  (void) CopyMagickString(montage_info->filename,filename,MaxTextExtent);
13600  montage_image=MontageImageList(read_info,montage_info,GetFirstImageInList(
13601    images),&images->exception);
13602  images=DestroyImageList(images);
13603  montage_info=DestroyMontageInfo(montage_info);
13604  read_info=DestroyImageInfo(read_info);
13605  XSetCursorState(display,windows,MagickFalse);
13606  if (montage_image == (Image *) NULL)
13607    return(montage_image);
13608  XClientMessage(display,windows->image.id,windows->im_protocols,
13609    windows->im_next_image,CurrentTime);
13610  return(montage_image);
13611}
13612
13613/*
13614%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13615%                                                                             %
13616%                                                                             %
13617%                                                                             %
13618%   X D i s p l a y B a c k g r o u n d I m a g e                             %
13619%                                                                             %
13620%                                                                             %
13621%                                                                             %
13622%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13623%
13624%  XDisplayBackgroundImage() displays an image in the background of a window.
13625%
13626%  The format of the XDisplayBackgroundImage method is:
13627%
13628%      MagickBooleanType XDisplayBackgroundImage(Display *display,
13629%        XResourceInfo *resource_info,Image *image)
13630%
13631%  A description of each parameter follows:
13632%
13633%    o display: Specifies a connection to an X server;  returned from
13634%      XOpenDisplay.
13635%
13636%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13637%
13638%    o image: the image.
13639%
13640*/
13641MagickExport MagickBooleanType XDisplayBackgroundImage(Display *display,
13642  XResourceInfo *resource_info,Image *image)
13643{
13644  char
13645    geometry[MaxTextExtent],
13646    visual_type[MaxTextExtent];
13647
13648  int
13649    height,
13650    status,
13651    width;
13652
13653  RectangleInfo
13654    geometry_info;
13655
13656  static XPixelInfo
13657    pixel;
13658
13659  static XStandardColormap
13660    *map_info;
13661
13662  static XVisualInfo
13663    *visual_info = (XVisualInfo *) NULL;
13664
13665  static XWindowInfo
13666    window_info;
13667
13668  size_t
13669    delay;
13670
13671  Window
13672    root_window;
13673
13674  XGCValues
13675    context_values;
13676
13677  XResourceInfo
13678    resources;
13679
13680  XWindowAttributes
13681    window_attributes;
13682
13683  /*
13684    Determine target window.
13685  */
13686  assert(image != (Image *) NULL);
13687  assert(image->signature == MagickSignature);
13688  if (image->debug != MagickFalse)
13689    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
13690  resources=(*resource_info);
13691  window_info.id=(Window) NULL;
13692  root_window=XRootWindow(display,XDefaultScreen(display));
13693  if (LocaleCompare(resources.window_id,"root") == 0)
13694    window_info.id=root_window;
13695  else
13696    {
13697      if (isdigit((unsigned char) *resources.window_id) != 0)
13698        window_info.id=XWindowByID(display,root_window,
13699          (Window) strtol((char *) resources.window_id,(char **) NULL,0));
13700      if (window_info.id == (Window) NULL)
13701        window_info.id=XWindowByName(display,root_window,resources.window_id);
13702    }
13703  if (window_info.id == (Window) NULL)
13704    {
13705      ThrowXWindowFatalException(XServerError,"NoWindowWithSpecifiedIDExists",
13706        resources.window_id);
13707      return(MagickFalse);
13708    }
13709  /*
13710    Determine window visual id.
13711  */
13712  window_attributes.width=XDisplayWidth(display,XDefaultScreen(display));
13713  window_attributes.height=XDisplayHeight(display,XDefaultScreen(display));
13714  (void) CopyMagickString(visual_type,"default",MaxTextExtent);
13715  status=XGetWindowAttributes(display,window_info.id,&window_attributes);
13716  if (status != 0)
13717    (void) FormatLocaleString(visual_type,MaxTextExtent,"0x%lx",
13718      XVisualIDFromVisual(window_attributes.visual));
13719  if (visual_info == (XVisualInfo *) NULL)
13720    {
13721      /*
13722        Allocate standard colormap.
13723      */
13724      map_info=XAllocStandardColormap();
13725      if (map_info == (XStandardColormap *) NULL)
13726        ThrowXWindowFatalException(XServerFatalError,"MemoryAllocationFailed",
13727          image->filename);
13728      map_info->colormap=(Colormap) NULL;
13729      pixel.pixels=(unsigned long *) NULL;
13730      /*
13731        Initialize visual info.
13732      */
13733      resources.map_type=(char *) NULL;
13734      resources.visual_type=visual_type;
13735      visual_info=XBestVisualInfo(display,map_info,&resources);
13736      if (visual_info == (XVisualInfo *) NULL)
13737        ThrowXWindowFatalException(XServerFatalError,"UnableToGetVisual",
13738          resources.visual_type);
13739      /*
13740        Initialize window info.
13741      */
13742      window_info.ximage=(XImage *) NULL;
13743      window_info.matte_image=(XImage *) NULL;
13744      window_info.pixmap=(Pixmap) NULL;
13745      window_info.matte_pixmap=(Pixmap) NULL;
13746    }
13747  /*
13748    Free previous root colors.
13749  */
13750  if (window_info.id == root_window)
13751    (void) XDestroyWindowColors(display,root_window);
13752  /*
13753    Initialize Standard Colormap.
13754  */
13755  resources.colormap=SharedColormap;
13756  XMakeStandardColormap(display,visual_info,&resources,image,map_info,&pixel);
13757  /*
13758    Graphic context superclass.
13759  */
13760  context_values.background=pixel.background_color.pixel;
13761  context_values.foreground=pixel.foreground_color.pixel;
13762  pixel.annotate_context=XCreateGC(display,window_info.id,
13763    (size_t) (GCBackground | GCForeground),&context_values);
13764  if (pixel.annotate_context == (GC) NULL)
13765    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
13766      image->filename);
13767  /*
13768    Initialize Image window attributes.
13769  */
13770  window_info.name=AcquireString("\0");
13771  window_info.icon_name=AcquireString("\0");
13772  XGetWindowInfo(display,visual_info,map_info,&pixel,(XFontStruct *) NULL,
13773    &resources,&window_info);
13774  /*
13775    Create the X image.
13776  */
13777  window_info.width=(unsigned int) image->columns;
13778  window_info.height=(unsigned int) image->rows;
13779  if ((image->columns != window_info.width) ||
13780      (image->rows != window_info.height))
13781    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13782      image->filename);
13783  (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>",
13784    window_attributes.width,window_attributes.height);
13785  geometry_info.width=window_info.width;
13786  geometry_info.height=window_info.height;
13787  geometry_info.x=(ssize_t) window_info.x;
13788  geometry_info.y=(ssize_t) window_info.y;
13789  (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
13790    &geometry_info.width,&geometry_info.height);
13791  window_info.width=(unsigned int) geometry_info.width;
13792  window_info.height=(unsigned int) geometry_info.height;
13793  window_info.x=(int) geometry_info.x;
13794  window_info.y=(int) geometry_info.y;
13795  status=XMakeImage(display,&resources,&window_info,image,window_info.width,
13796    window_info.height);
13797  if (status == MagickFalse)
13798    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13799      image->filename);
13800  window_info.x=0;
13801  window_info.y=0;
13802  if (image->debug != MagickFalse)
13803    {
13804      (void) LogMagickEvent(X11Event,GetMagickModule(),
13805        "Image: %s[%.20g] %.20gx%.20g ",image->filename,(double) image->scene,
13806        (double) image->columns,(double) image->rows);
13807      if (image->colors != 0)
13808        (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
13809          image->colors);
13810      (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",image->magick);
13811    }
13812  /*
13813    Adjust image dimensions as specified by backdrop or geometry options.
13814  */
13815  width=(int) window_info.width;
13816  height=(int) window_info.height;
13817  if (resources.backdrop != MagickFalse)
13818    {
13819      /*
13820        Center image on window.
13821      */
13822      window_info.x=(window_attributes.width/2)-
13823        (window_info.ximage->width/2);
13824      window_info.y=(window_attributes.height/2)-
13825        (window_info.ximage->height/2);
13826      width=window_attributes.width;
13827      height=window_attributes.height;
13828    }
13829  if ((resources.image_geometry != (char *) NULL) &&
13830      (*resources.image_geometry != '\0'))
13831    {
13832      char
13833        default_geometry[MaxTextExtent];
13834
13835      int
13836        flags,
13837        gravity;
13838
13839      XSizeHints
13840        *size_hints;
13841
13842      /*
13843        User specified geometry.
13844      */
13845      size_hints=XAllocSizeHints();
13846      if (size_hints == (XSizeHints *) NULL)
13847        ThrowXWindowFatalException(ResourceLimitFatalError,
13848          "MemoryAllocationFailed",image->filename);
13849      size_hints->flags=0L;
13850      (void) FormatLocaleString(default_geometry,MaxTextExtent,"%dx%d",
13851        width,height);
13852      flags=XWMGeometry(display,visual_info->screen,resources.image_geometry,
13853        default_geometry,window_info.border_width,size_hints,&window_info.x,
13854        &window_info.y,&width,&height,&gravity);
13855      if (flags & (XValue | YValue))
13856        {
13857          width=window_attributes.width;
13858          height=window_attributes.height;
13859        }
13860      (void) XFree((void *) size_hints);
13861    }
13862  /*
13863    Create the X pixmap.
13864  */
13865  window_info.pixmap=XCreatePixmap(display,window_info.id,(unsigned int) width,
13866    (unsigned int) height,window_info.depth);
13867  if (window_info.pixmap == (Pixmap) NULL)
13868    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXPixmap",
13869      image->filename);
13870  /*
13871    Display pixmap on the window.
13872  */
13873  if (((unsigned int) width > window_info.width) ||
13874      ((unsigned int) height > window_info.height))
13875    (void) XFillRectangle(display,window_info.pixmap,
13876      window_info.annotate_context,0,0,(unsigned int) width,
13877      (unsigned int) height);
13878  (void) XPutImage(display,window_info.pixmap,window_info.annotate_context,
13879    window_info.ximage,0,0,window_info.x,window_info.y,(unsigned int)
13880    window_info.width,(unsigned int) window_info.height);
13881  (void) XSetWindowBackgroundPixmap(display,window_info.id,window_info.pixmap);
13882  (void) XClearWindow(display,window_info.id);
13883  delay=1000*image->delay/MagickMax(image->ticks_per_second,1L);
13884  XDelay(display,delay == 0UL ? 10UL : delay);
13885  (void) XSync(display,MagickFalse);
13886  return(window_info.id == root_window ? MagickTrue : MagickFalse);
13887}
13888
13889/*
13890%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13891%                                                                             %
13892%                                                                             %
13893%                                                                             %
13894+   X D i s p l a y I m a g e                                                 %
13895%                                                                             %
13896%                                                                             %
13897%                                                                             %
13898%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13899%
13900%  XDisplayImage() displays an image via X11.  A new image is created and
13901%  returned if the user interactively transforms the displayed image.
13902%
13903%  The format of the XDisplayImage method is:
13904%
13905%      Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
13906%        char **argv,int argc,Image **image,size_t *state)
13907%
13908%  A description of each parameter follows:
13909%
13910%    o nexus:  Method XDisplayImage returns an image when the
13911%      user chooses 'Open Image' from the command menu or picks a tile
13912%      from the image directory.  Otherwise a null image is returned.
13913%
13914%    o display: Specifies a connection to an X server;  returned from
13915%      XOpenDisplay.
13916%
13917%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13918%
13919%    o argv: Specifies the application's argument list.
13920%
13921%    o argc: Specifies the number of arguments.
13922%
13923%    o image: Specifies an address to an address of an Image structure;
13924%
13925*/
13926MagickExport Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
13927  char **argv,int argc,Image **image,size_t *state)
13928{
13929#define MagnifySize  256  /* must be a power of 2 */
13930#define MagickMenus  10
13931#define MagickTitle  "Commands"
13932
13933  static const char
13934    *CommandMenu[] =
13935    {
13936      "File",
13937      "Edit",
13938      "View",
13939      "Transform",
13940      "Enhance",
13941      "Effects",
13942      "F/X",
13943      "Image Edit",
13944      "Miscellany",
13945      "Help",
13946      (char *) NULL
13947    },
13948    *FileMenu[] =
13949    {
13950      "Open...",
13951      "Next",
13952      "Former",
13953      "Select...",
13954      "Save...",
13955      "Print...",
13956      "Delete...",
13957      "New...",
13958      "Visual Directory...",
13959      "Quit",
13960      (char *) NULL
13961    },
13962    *EditMenu[] =
13963    {
13964      "Undo",
13965      "Redo",
13966      "Cut",
13967      "Copy",
13968      "Paste",
13969      (char *) NULL
13970    },
13971    *ViewMenu[] =
13972    {
13973      "Half Size",
13974      "Original Size",
13975      "Double Size",
13976      "Resize...",
13977      "Apply",
13978      "Refresh",
13979      "Restore",
13980      (char *) NULL
13981    },
13982    *TransformMenu[] =
13983    {
13984      "Crop",
13985      "Chop",
13986      "Flop",
13987      "Flip",
13988      "Rotate Right",
13989      "Rotate Left",
13990      "Rotate...",
13991      "Shear...",
13992      "Roll...",
13993      "Trim Edges",
13994      (char *) NULL
13995    },
13996    *EnhanceMenu[] =
13997    {
13998      "Hue...",
13999      "Saturation...",
14000      "Brightness...",
14001      "Gamma...",
14002      "Spiff",
14003      "Dull",
14004      "Contrast Stretch...",
14005      "Sigmoidal Contrast...",
14006      "Normalize",
14007      "Equalize",
14008      "Negate",
14009      "Grayscale",
14010      "Map...",
14011      "Quantize...",
14012      (char *) NULL
14013    },
14014    *EffectsMenu[] =
14015    {
14016      "Despeckle",
14017      "Emboss",
14018      "Reduce Noise",
14019      "Add Noise...",
14020      "Sharpen...",
14021      "Blur...",
14022      "Threshold...",
14023      "Edge Detect...",
14024      "Spread...",
14025      "Shade...",
14026      "Raise...",
14027      "Segment...",
14028      (char *) NULL
14029    },
14030    *FXMenu[] =
14031    {
14032      "Solarize...",
14033      "Sepia Tone...",
14034      "Swirl...",
14035      "Implode...",
14036      "Vignette...",
14037      "Wave...",
14038      "Oil Paint...",
14039      "Charcoal Draw...",
14040      (char *) NULL
14041    },
14042    *ImageEditMenu[] =
14043    {
14044      "Annotate...",
14045      "Draw...",
14046      "Color...",
14047      "Matte...",
14048      "Composite...",
14049      "Add Border...",
14050      "Add Frame...",
14051      "Comment...",
14052      "Launch...",
14053      "Region of Interest...",
14054      (char *) NULL
14055    },
14056    *MiscellanyMenu[] =
14057    {
14058      "Image Info",
14059      "Zoom Image",
14060      "Show Preview...",
14061      "Show Histogram",
14062      "Show Matte",
14063      "Background...",
14064      "Slide Show...",
14065      "Preferences...",
14066      (char *) NULL
14067    },
14068    *HelpMenu[] =
14069    {
14070      "Overview",
14071      "Browse Documentation",
14072      "About Display",
14073      (char *) NULL
14074    },
14075    *ShortCutsMenu[] =
14076    {
14077      "Next",
14078      "Former",
14079      "Open...",
14080      "Save...",
14081      "Print...",
14082      "Undo",
14083      "Restore",
14084      "Image Info",
14085      "Quit",
14086      (char *) NULL
14087    },
14088    *VirtualMenu[] =
14089    {
14090      "Image Info",
14091      "Print",
14092      "Next",
14093      "Quit",
14094      (char *) NULL
14095    };
14096
14097  static const char
14098    **Menus[MagickMenus] =
14099    {
14100      FileMenu,
14101      EditMenu,
14102      ViewMenu,
14103      TransformMenu,
14104      EnhanceMenu,
14105      EffectsMenu,
14106      FXMenu,
14107      ImageEditMenu,
14108      MiscellanyMenu,
14109      HelpMenu
14110    };
14111
14112  static CommandType
14113    CommandMenus[] =
14114    {
14115      NullCommand,
14116      NullCommand,
14117      NullCommand,
14118      NullCommand,
14119      NullCommand,
14120      NullCommand,
14121      NullCommand,
14122      NullCommand,
14123      NullCommand,
14124      NullCommand,
14125    },
14126    FileCommands[] =
14127    {
14128      OpenCommand,
14129      NextCommand,
14130      FormerCommand,
14131      SelectCommand,
14132      SaveCommand,
14133      PrintCommand,
14134      DeleteCommand,
14135      NewCommand,
14136      VisualDirectoryCommand,
14137      QuitCommand
14138    },
14139    EditCommands[] =
14140    {
14141      UndoCommand,
14142      RedoCommand,
14143      CutCommand,
14144      CopyCommand,
14145      PasteCommand
14146    },
14147    ViewCommands[] =
14148    {
14149      HalfSizeCommand,
14150      OriginalSizeCommand,
14151      DoubleSizeCommand,
14152      ResizeCommand,
14153      ApplyCommand,
14154      RefreshCommand,
14155      RestoreCommand
14156    },
14157    TransformCommands[] =
14158    {
14159      CropCommand,
14160      ChopCommand,
14161      FlopCommand,
14162      FlipCommand,
14163      RotateRightCommand,
14164      RotateLeftCommand,
14165      RotateCommand,
14166      ShearCommand,
14167      RollCommand,
14168      TrimCommand
14169    },
14170    EnhanceCommands[] =
14171    {
14172      HueCommand,
14173      SaturationCommand,
14174      BrightnessCommand,
14175      GammaCommand,
14176      SpiffCommand,
14177      DullCommand,
14178      ContrastStretchCommand,
14179      SigmoidalContrastCommand,
14180      NormalizeCommand,
14181      EqualizeCommand,
14182      NegateCommand,
14183      GrayscaleCommand,
14184      MapCommand,
14185      QuantizeCommand
14186    },
14187    EffectsCommands[] =
14188    {
14189      DespeckleCommand,
14190      EmbossCommand,
14191      ReduceNoiseCommand,
14192      AddNoiseCommand,
14193      SharpenCommand,
14194      BlurCommand,
14195      ThresholdCommand,
14196      EdgeDetectCommand,
14197      SpreadCommand,
14198      ShadeCommand,
14199      RaiseCommand,
14200      SegmentCommand
14201    },
14202    FXCommands[] =
14203    {
14204      SolarizeCommand,
14205      SepiaToneCommand,
14206      SwirlCommand,
14207      ImplodeCommand,
14208      VignetteCommand,
14209      WaveCommand,
14210      OilPaintCommand,
14211      CharcoalDrawCommand
14212    },
14213    ImageEditCommands[] =
14214    {
14215      AnnotateCommand,
14216      DrawCommand,
14217      ColorCommand,
14218      MatteCommand,
14219      CompositeCommand,
14220      AddBorderCommand,
14221      AddFrameCommand,
14222      CommentCommand,
14223      LaunchCommand,
14224      RegionofInterestCommand
14225    },
14226    MiscellanyCommands[] =
14227    {
14228      InfoCommand,
14229      ZoomCommand,
14230      ShowPreviewCommand,
14231      ShowHistogramCommand,
14232      ShowMatteCommand,
14233      BackgroundCommand,
14234      SlideShowCommand,
14235      PreferencesCommand
14236    },
14237    HelpCommands[] =
14238    {
14239      HelpCommand,
14240      BrowseDocumentationCommand,
14241      VersionCommand
14242    },
14243    ShortCutsCommands[] =
14244    {
14245      NextCommand,
14246      FormerCommand,
14247      OpenCommand,
14248      SaveCommand,
14249      PrintCommand,
14250      UndoCommand,
14251      RestoreCommand,
14252      InfoCommand,
14253      QuitCommand
14254    },
14255    VirtualCommands[] =
14256    {
14257      InfoCommand,
14258      PrintCommand,
14259      NextCommand,
14260      QuitCommand
14261    };
14262
14263  static CommandType
14264    *Commands[MagickMenus] =
14265    {
14266      FileCommands,
14267      EditCommands,
14268      ViewCommands,
14269      TransformCommands,
14270      EnhanceCommands,
14271      EffectsCommands,
14272      FXCommands,
14273      ImageEditCommands,
14274      MiscellanyCommands,
14275      HelpCommands
14276    };
14277
14278  char
14279    command[MaxTextExtent],
14280    *directory,
14281    geometry[MaxTextExtent],
14282    resource_name[MaxTextExtent];
14283
14284  CommandType
14285    command_type;
14286
14287  Image
14288    *display_image,
14289    *nexus;
14290
14291  int
14292    entry,
14293    id;
14294
14295  KeySym
14296    key_symbol;
14297
14298  MagickStatusType
14299    context_mask,
14300    status;
14301
14302  RectangleInfo
14303    geometry_info;
14304
14305  register int
14306    i;
14307
14308  static char
14309    working_directory[MaxTextExtent];
14310
14311  static XPoint
14312    vid_info;
14313
14314  static XWindowInfo
14315    *magick_windows[MaxXWindows];
14316
14317  static unsigned int
14318    number_windows;
14319
14320  struct stat
14321    attributes;
14322
14323  time_t
14324    timer,
14325    timestamp,
14326    update_time;
14327
14328  unsigned int
14329    height,
14330    width;
14331
14332  size_t
14333    delay;
14334
14335  WarningHandler
14336    warning_handler;
14337
14338  Window
14339    root_window;
14340
14341  XClassHint
14342    *class_hints;
14343
14344  XEvent
14345    event;
14346
14347  XFontStruct
14348    *font_info;
14349
14350  XGCValues
14351    context_values;
14352
14353  XPixelInfo
14354    *icon_pixel,
14355    *pixel;
14356
14357  XResourceInfo
14358    *icon_resources;
14359
14360  XStandardColormap
14361    *icon_map,
14362    *map_info;
14363
14364  XVisualInfo
14365    *icon_visual,
14366    *visual_info;
14367
14368  XWindowChanges
14369    window_changes;
14370
14371  XWindows
14372    *windows;
14373
14374  XWMHints
14375    *manager_hints;
14376
14377  assert(image != (Image **) NULL);
14378  assert((*image)->signature == MagickSignature);
14379  if ((*image)->debug != MagickFalse)
14380    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*image)->filename);
14381  display_image=(*image);
14382  warning_handler=(WarningHandler) NULL;
14383  windows=XSetWindows((XWindows *) ~0);
14384  if (windows != (XWindows *) NULL)
14385    {
14386      int
14387        status;
14388
14389      status=chdir(working_directory);
14390      if (status == -1)
14391        (void) ThrowMagickException(&(*image)->exception,GetMagickModule(),
14392          FileOpenError,"UnableToOpenFile","%s",working_directory);
14393      warning_handler=resource_info->display_warnings ?
14394        SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
14395      warning_handler=resource_info->display_warnings ?
14396        SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
14397    }
14398  else
14399    {
14400      /*
14401        Allocate windows structure.
14402      */
14403      resource_info->colors=display_image->colors;
14404      windows=XSetWindows(XInitializeWindows(display,resource_info));
14405      if (windows == (XWindows *) NULL)
14406        ThrowXWindowFatalException(XServerFatalError,"UnableToCreateWindow",
14407          (*image)->filename);
14408      /*
14409        Initialize window id's.
14410      */
14411      number_windows=0;
14412      magick_windows[number_windows++]=(&windows->icon);
14413      magick_windows[number_windows++]=(&windows->backdrop);
14414      magick_windows[number_windows++]=(&windows->image);
14415      magick_windows[number_windows++]=(&windows->info);
14416      magick_windows[number_windows++]=(&windows->command);
14417      magick_windows[number_windows++]=(&windows->widget);
14418      magick_windows[number_windows++]=(&windows->popup);
14419      magick_windows[number_windows++]=(&windows->magnify);
14420      magick_windows[number_windows++]=(&windows->pan);
14421      for (i=0; i < (int) number_windows; i++)
14422        magick_windows[i]->id=(Window) NULL;
14423      vid_info.x=0;
14424      vid_info.y=0;
14425    }
14426  /*
14427    Initialize font info.
14428  */
14429  if (windows->font_info != (XFontStruct *) NULL)
14430    (void) XFreeFont(display,windows->font_info);
14431  windows->font_info=XBestFont(display,resource_info,MagickFalse);
14432  if (windows->font_info == (XFontStruct *) NULL)
14433    ThrowXWindowFatalException(XServerFatalError,"UnableToLoadFont",
14434      resource_info->font);
14435  /*
14436    Initialize Standard Colormap.
14437  */
14438  map_info=windows->map_info;
14439  icon_map=windows->icon_map;
14440  visual_info=windows->visual_info;
14441  icon_visual=windows->icon_visual;
14442  pixel=windows->pixel_info;
14443  icon_pixel=windows->icon_pixel;
14444  font_info=windows->font_info;
14445  icon_resources=windows->icon_resources;
14446  class_hints=windows->class_hints;
14447  manager_hints=windows->manager_hints;
14448  root_window=XRootWindow(display,visual_info->screen);
14449  nexus=NewImageList();
14450  if (display_image->debug != MagickFalse)
14451    {
14452      (void) LogMagickEvent(X11Event,GetMagickModule(),
14453        "Image: %s[%.20g] %.20gx%.20g ",display_image->filename,
14454        (double) display_image->scene,(double) display_image->columns,
14455        (double) display_image->rows);
14456      if (display_image->colors != 0)
14457        (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
14458          display_image->colors);
14459      (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",
14460        display_image->magick);
14461    }
14462  XMakeStandardColormap(display,visual_info,resource_info,display_image,
14463    map_info,pixel);
14464  display_image->taint=MagickFalse;
14465  /*
14466    Initialize graphic context.
14467  */
14468  windows->context.id=(Window) NULL;
14469  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14470    resource_info,&windows->context);
14471  (void) CloneString(&class_hints->res_name,resource_info->client_name);
14472  (void) CloneString(&class_hints->res_class,resource_info->client_name);
14473  class_hints->res_class[0]=(char) toupper((int) class_hints->res_class[0]);
14474  manager_hints->flags=InputHint | StateHint;
14475  manager_hints->input=MagickFalse;
14476  manager_hints->initial_state=WithdrawnState;
14477  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14478    &windows->context);
14479  if (display_image->debug != MagickFalse)
14480    (void) LogMagickEvent(X11Event,GetMagickModule(),
14481      "Window id: 0x%lx (context)",windows->context.id);
14482  context_values.background=pixel->background_color.pixel;
14483  context_values.font=font_info->fid;
14484  context_values.foreground=pixel->foreground_color.pixel;
14485  context_values.graphics_exposures=MagickFalse;
14486  context_mask=(MagickStatusType)
14487    (GCBackground | GCFont | GCForeground | GCGraphicsExposures);
14488  if (pixel->annotate_context != (GC) NULL)
14489    (void) XFreeGC(display,pixel->annotate_context);
14490  pixel->annotate_context=XCreateGC(display,windows->context.id,
14491    context_mask,&context_values);
14492  if (pixel->annotate_context == (GC) NULL)
14493    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14494      display_image->filename);
14495  context_values.background=pixel->depth_color.pixel;
14496  if (pixel->widget_context != (GC) NULL)
14497    (void) XFreeGC(display,pixel->widget_context);
14498  pixel->widget_context=XCreateGC(display,windows->context.id,context_mask,
14499    &context_values);
14500  if (pixel->widget_context == (GC) NULL)
14501    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14502      display_image->filename);
14503  context_values.background=pixel->foreground_color.pixel;
14504  context_values.foreground=pixel->background_color.pixel;
14505  context_values.plane_mask=context_values.background ^
14506    context_values.foreground;
14507  if (pixel->highlight_context != (GC) NULL)
14508    (void) XFreeGC(display,pixel->highlight_context);
14509  pixel->highlight_context=XCreateGC(display,windows->context.id,
14510    (size_t) (context_mask | GCPlaneMask),&context_values);
14511  if (pixel->highlight_context == (GC) NULL)
14512    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14513      display_image->filename);
14514  (void) XDestroyWindow(display,windows->context.id);
14515  /*
14516    Initialize icon window.
14517  */
14518  XGetWindowInfo(display,icon_visual,icon_map,icon_pixel,(XFontStruct *) NULL,
14519    icon_resources,&windows->icon);
14520  windows->icon.geometry=resource_info->icon_geometry;
14521  XBestIconSize(display,&windows->icon,display_image);
14522  windows->icon.attributes.colormap=XDefaultColormap(display,
14523    icon_visual->screen);
14524  windows->icon.attributes.event_mask=ExposureMask | StructureNotifyMask;
14525  manager_hints->flags=InputHint | StateHint;
14526  manager_hints->input=MagickFalse;
14527  manager_hints->initial_state=IconicState;
14528  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14529    &windows->icon);
14530  if (display_image->debug != MagickFalse)
14531    (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (icon)",
14532      windows->icon.id);
14533  /*
14534    Initialize graphic context for icon window.
14535  */
14536  if (icon_pixel->annotate_context != (GC) NULL)
14537    (void) XFreeGC(display,icon_pixel->annotate_context);
14538  context_values.background=icon_pixel->background_color.pixel;
14539  context_values.foreground=icon_pixel->foreground_color.pixel;
14540  icon_pixel->annotate_context=XCreateGC(display,windows->icon.id,
14541    (size_t) (GCBackground | GCForeground),&context_values);
14542  if (icon_pixel->annotate_context == (GC) NULL)
14543    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14544      display_image->filename);
14545  windows->icon.annotate_context=icon_pixel->annotate_context;
14546  /*
14547    Initialize Image window.
14548  */
14549  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14550    &windows->image);
14551  windows->image.shape=MagickTrue;  /* non-rectangular shape hint */
14552  if (resource_info->use_shared_memory == MagickFalse)
14553    windows->image.shared_memory=MagickFalse;
14554  if ((resource_info->title != (char *) NULL) && !(*state & MontageImageState))
14555    {
14556      char
14557        *title;
14558
14559      title=InterpretImageProperties(resource_info->image_info,display_image,
14560        resource_info->title);
14561      (void) CopyMagickString(windows->image.name,title,MaxTextExtent);
14562      (void) CopyMagickString(windows->image.icon_name,title,MaxTextExtent);
14563      title=DestroyString(title);
14564    }
14565  else
14566    {
14567      char
14568        filename[MaxTextExtent];
14569
14570      /*
14571        Window name is the base of the filename.
14572      */
14573      GetPathComponent(display_image->magick_filename,TailPath,filename);
14574      if (GetImageListLength(display_image) == 1)
14575        (void) FormatLocaleString(windows->image.name,MaxTextExtent,
14576          "%s: %s",MagickPackageName,filename);
14577      else
14578        (void) FormatLocaleString(windows->image.name,MaxTextExtent,
14579          "%s: %s[scene: %.20g frames: %.20g]",MagickPackageName,filename,
14580          (double) display_image->scene,(double) GetImageListLength(
14581          display_image));
14582      (void) CopyMagickString(windows->image.icon_name,filename,MaxTextExtent);
14583    }
14584  if (resource_info->immutable)
14585    windows->image.immutable=MagickTrue;
14586  windows->image.use_pixmap=resource_info->use_pixmap;
14587  windows->image.geometry=resource_info->image_geometry;
14588  (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>!",
14589    XDisplayWidth(display,visual_info->screen),
14590    XDisplayHeight(display,visual_info->screen));
14591  geometry_info.width=display_image->columns;
14592  geometry_info.height=display_image->rows;
14593  geometry_info.x=0;
14594  geometry_info.y=0;
14595  (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
14596    &geometry_info.width,&geometry_info.height);
14597  windows->image.width=(unsigned int) geometry_info.width;
14598  windows->image.height=(unsigned int) geometry_info.height;
14599  windows->image.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14600    ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14601    KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14602    PropertyChangeMask | StructureNotifyMask | SubstructureNotifyMask;
14603  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14604    resource_info,&windows->backdrop);
14605  if ((resource_info->backdrop) || (windows->backdrop.id != (Window) NULL))
14606    {
14607      /*
14608        Initialize backdrop window.
14609      */
14610      windows->backdrop.x=0;
14611      windows->backdrop.y=0;
14612      (void) CloneString(&windows->backdrop.name,"Backdrop");
14613      windows->backdrop.flags=(size_t) (USSize | USPosition);
14614      windows->backdrop.width=(unsigned int)
14615        XDisplayWidth(display,visual_info->screen);
14616      windows->backdrop.height=(unsigned int)
14617        XDisplayHeight(display,visual_info->screen);
14618      windows->backdrop.border_width=0;
14619      windows->backdrop.immutable=MagickTrue;
14620      windows->backdrop.attributes.do_not_propagate_mask=ButtonPressMask |
14621        ButtonReleaseMask;
14622      windows->backdrop.attributes.event_mask=ButtonPressMask | KeyPressMask |
14623        StructureNotifyMask;
14624      manager_hints->flags=IconWindowHint | InputHint | StateHint;
14625      manager_hints->icon_window=windows->icon.id;
14626      manager_hints->input=MagickTrue;
14627      manager_hints->initial_state=resource_info->iconic ? IconicState :
14628        NormalState;
14629      XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14630        &windows->backdrop);
14631      if (display_image->debug != MagickFalse)
14632        (void) LogMagickEvent(X11Event,GetMagickModule(),
14633          "Window id: 0x%lx (backdrop)",windows->backdrop.id);
14634      (void) XMapWindow(display,windows->backdrop.id);
14635      (void) XClearWindow(display,windows->backdrop.id);
14636      if (windows->image.id != (Window) NULL)
14637        {
14638          (void) XDestroyWindow(display,windows->image.id);
14639          windows->image.id=(Window) NULL;
14640        }
14641      /*
14642        Position image in the center the backdrop.
14643      */
14644      windows->image.flags|=USPosition;
14645      windows->image.x=(XDisplayWidth(display,visual_info->screen)/2)-
14646        (windows->image.width/2);
14647      windows->image.y=(XDisplayHeight(display,visual_info->screen)/2)-
14648        (windows->image.height/2);
14649    }
14650  manager_hints->flags=IconWindowHint | InputHint | StateHint;
14651  manager_hints->icon_window=windows->icon.id;
14652  manager_hints->input=MagickTrue;
14653  manager_hints->initial_state=resource_info->iconic ? IconicState :
14654    NormalState;
14655  if (windows->group_leader.id != (Window) NULL)
14656    {
14657      /*
14658        Follow the leader.
14659      */
14660      manager_hints->flags|=WindowGroupHint;
14661      manager_hints->window_group=windows->group_leader.id;
14662      (void) XSelectInput(display,windows->group_leader.id,StructureNotifyMask);
14663      if (display_image->debug != MagickFalse)
14664        (void) LogMagickEvent(X11Event,GetMagickModule(),
14665          "Window id: 0x%lx (group leader)",windows->group_leader.id);
14666    }
14667  XMakeWindow(display,
14668    (Window) (resource_info->backdrop ? windows->backdrop.id : root_window),
14669    argv,argc,class_hints,manager_hints,&windows->image);
14670  (void) XChangeProperty(display,windows->image.id,windows->im_protocols,
14671    XA_STRING,8,PropModeReplace,(unsigned char *) NULL,0);
14672  if (windows->group_leader.id != (Window) NULL)
14673    (void) XSetTransientForHint(display,windows->image.id,
14674      windows->group_leader.id);
14675  if (display_image->debug != MagickFalse)
14676    (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (image)",
14677      windows->image.id);
14678  /*
14679    Initialize Info widget.
14680  */
14681  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14682    &windows->info);
14683  (void) CloneString(&windows->info.name,"Info");
14684  (void) CloneString(&windows->info.icon_name,"Info");
14685  windows->info.border_width=1;
14686  windows->info.x=2;
14687  windows->info.y=2;
14688  windows->info.flags|=PPosition;
14689  windows->info.attributes.win_gravity=UnmapGravity;
14690  windows->info.attributes.event_mask=ButtonPressMask | ExposureMask |
14691    StructureNotifyMask;
14692  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14693  manager_hints->input=MagickFalse;
14694  manager_hints->initial_state=NormalState;
14695  manager_hints->window_group=windows->image.id;
14696  XMakeWindow(display,windows->image.id,argv,argc,class_hints,manager_hints,
14697    &windows->info);
14698  windows->info.highlight_stipple=XCreateBitmapFromData(display,
14699    windows->info.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14700  windows->info.shadow_stipple=XCreateBitmapFromData(display,
14701    windows->info.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14702  (void) XSetTransientForHint(display,windows->info.id,windows->image.id);
14703  if (windows->image.mapped != MagickFalse)
14704    (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14705  if (display_image->debug != MagickFalse)
14706    (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (info)",
14707      windows->info.id);
14708  /*
14709    Initialize Command widget.
14710  */
14711  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14712    resource_info,&windows->command);
14713  windows->command.data=MagickMenus;
14714  (void) XCommandWidget(display,windows,CommandMenu,(XEvent *) NULL);
14715  (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.command",
14716    resource_info->client_name);
14717  windows->command.geometry=XGetResourceClass(resource_info->resource_database,
14718    resource_name,"geometry",(char *) NULL);
14719  (void) CloneString(&windows->command.name,MagickTitle);
14720  windows->command.border_width=0;
14721  windows->command.flags|=PPosition;
14722  windows->command.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14723    ButtonReleaseMask | EnterWindowMask | ExposureMask | LeaveWindowMask |
14724    OwnerGrabButtonMask | StructureNotifyMask;
14725  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14726  manager_hints->input=MagickTrue;
14727  manager_hints->initial_state=NormalState;
14728  manager_hints->window_group=windows->image.id;
14729  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14730    &windows->command);
14731  windows->command.highlight_stipple=XCreateBitmapFromData(display,
14732    windows->command.id,(char *) HighlightBitmap,HighlightWidth,
14733    HighlightHeight);
14734  windows->command.shadow_stipple=XCreateBitmapFromData(display,
14735    windows->command.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14736  (void) XSetTransientForHint(display,windows->command.id,windows->image.id);
14737  if (windows->command.mapped != MagickFalse)
14738    (void) XMapRaised(display,windows->command.id);
14739  if (display_image->debug != MagickFalse)
14740    (void) LogMagickEvent(X11Event,GetMagickModule(),
14741      "Window id: 0x%lx (command)",windows->command.id);
14742  /*
14743    Initialize Widget window.
14744  */
14745  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14746    resource_info,&windows->widget);
14747  (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.widget",
14748    resource_info->client_name);
14749  windows->widget.geometry=XGetResourceClass(resource_info->resource_database,
14750    resource_name,"geometry",(char *) NULL);
14751  windows->widget.border_width=0;
14752  windows->widget.flags|=PPosition;
14753  windows->widget.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14754    ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14755    KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14756    StructureNotifyMask;
14757  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14758  manager_hints->input=MagickTrue;
14759  manager_hints->initial_state=NormalState;
14760  manager_hints->window_group=windows->image.id;
14761  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14762    &windows->widget);
14763  windows->widget.highlight_stipple=XCreateBitmapFromData(display,
14764    windows->widget.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14765  windows->widget.shadow_stipple=XCreateBitmapFromData(display,
14766    windows->widget.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14767  (void) XSetTransientForHint(display,windows->widget.id,windows->image.id);
14768  if (display_image->debug != MagickFalse)
14769    (void) LogMagickEvent(X11Event,GetMagickModule(),
14770      "Window id: 0x%lx (widget)",windows->widget.id);
14771  /*
14772    Initialize popup window.
14773  */
14774  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14775    resource_info,&windows->popup);
14776  windows->popup.border_width=0;
14777  windows->popup.flags|=PPosition;
14778  windows->popup.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14779    ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14780    KeyReleaseMask | LeaveWindowMask | StructureNotifyMask;
14781  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14782  manager_hints->input=MagickTrue;
14783  manager_hints->initial_state=NormalState;
14784  manager_hints->window_group=windows->image.id;
14785  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14786    &windows->popup);
14787  windows->popup.highlight_stipple=XCreateBitmapFromData(display,
14788    windows->popup.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14789  windows->popup.shadow_stipple=XCreateBitmapFromData(display,
14790    windows->popup.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14791  (void) XSetTransientForHint(display,windows->popup.id,windows->image.id);
14792  if (display_image->debug != MagickFalse)
14793    (void) LogMagickEvent(X11Event,GetMagickModule(),
14794      "Window id: 0x%lx (pop up)",windows->popup.id);
14795  /*
14796    Initialize Magnify window and cursor.
14797  */
14798  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14799    resource_info,&windows->magnify);
14800  if (resource_info->use_shared_memory == MagickFalse)
14801    windows->magnify.shared_memory=MagickFalse;
14802  (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.magnify",
14803    resource_info->client_name);
14804  windows->magnify.geometry=XGetResourceClass(resource_info->resource_database,
14805    resource_name,"geometry",(char *) NULL);
14806  (void) FormatLocaleString(windows->magnify.name,MaxTextExtent,"Magnify %uX",
14807    resource_info->magnify);
14808  if (windows->magnify.cursor != (Cursor) NULL)
14809    (void) XFreeCursor(display,windows->magnify.cursor);
14810  windows->magnify.cursor=XMakeCursor(display,windows->image.id,
14811    map_info->colormap,resource_info->background_color,
14812    resource_info->foreground_color);
14813  if (windows->magnify.cursor == (Cursor) NULL)
14814    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateCursor",
14815      display_image->filename);
14816  windows->magnify.width=MagnifySize;
14817  windows->magnify.height=MagnifySize;
14818  windows->magnify.flags|=PPosition;
14819  windows->magnify.min_width=MagnifySize;
14820  windows->magnify.min_height=MagnifySize;
14821  windows->magnify.width_inc=MagnifySize;
14822  windows->magnify.height_inc=MagnifySize;
14823  windows->magnify.data=resource_info->magnify;
14824  windows->magnify.attributes.cursor=windows->magnify.cursor;
14825  windows->magnify.attributes.event_mask=ButtonPressMask | ButtonReleaseMask |
14826    ExposureMask | KeyPressMask | KeyReleaseMask | OwnerGrabButtonMask |
14827    StructureNotifyMask;
14828  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14829  manager_hints->input=MagickTrue;
14830  manager_hints->initial_state=NormalState;
14831  manager_hints->window_group=windows->image.id;
14832  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14833    &windows->magnify);
14834  if (display_image->debug != MagickFalse)
14835    (void) LogMagickEvent(X11Event,GetMagickModule(),
14836      "Window id: 0x%lx (magnify)",windows->magnify.id);
14837  (void) XSetTransientForHint(display,windows->magnify.id,windows->image.id);
14838  /*
14839    Initialize panning window.
14840  */
14841  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14842    resource_info,&windows->pan);
14843  (void) CloneString(&windows->pan.name,"Pan Icon");
14844  windows->pan.width=windows->icon.width;
14845  windows->pan.height=windows->icon.height;
14846  (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.pan",
14847    resource_info->client_name);
14848  windows->pan.geometry=XGetResourceClass(resource_info->resource_database,
14849    resource_name,"geometry",(char *) NULL);
14850  (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
14851    &windows->pan.width,&windows->pan.height);
14852  windows->pan.flags|=PPosition;
14853  windows->pan.immutable=MagickTrue;
14854  windows->pan.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14855    ButtonReleaseMask | ExposureMask | KeyPressMask | KeyReleaseMask |
14856    StructureNotifyMask;
14857  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14858  manager_hints->input=MagickFalse;
14859  manager_hints->initial_state=NormalState;
14860  manager_hints->window_group=windows->image.id;
14861  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14862    &windows->pan);
14863  if (display_image->debug != MagickFalse)
14864    (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (pan)",
14865      windows->pan.id);
14866  (void) XSetTransientForHint(display,windows->pan.id,windows->image.id);
14867  if (windows->info.mapped != MagickFalse)
14868    (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14869  if ((windows->image.mapped == MagickFalse) ||
14870      (windows->backdrop.id != (Window) NULL))
14871    (void) XMapWindow(display,windows->image.id);
14872  /*
14873    Set our progress monitor and warning handlers.
14874  */
14875  if (warning_handler == (WarningHandler) NULL)
14876    {
14877      warning_handler=resource_info->display_warnings ?
14878        SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
14879      warning_handler=resource_info->display_warnings ?
14880        SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
14881    }
14882  /*
14883    Initialize Image and Magnify X images.
14884  */
14885  windows->image.x=0;
14886  windows->image.y=0;
14887  windows->magnify.shape=MagickFalse;
14888  width=(unsigned int) display_image->columns;
14889  height=(unsigned int) display_image->rows;
14890  if ((display_image->columns != width) || (display_image->rows != height))
14891    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
14892      display_image->filename);
14893  status=XMakeImage(display,resource_info,&windows->image,display_image,
14894    width,height);
14895  if (status == MagickFalse)
14896    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
14897      display_image->filename);
14898  status=XMakeImage(display,resource_info,&windows->magnify,(Image *) NULL,
14899    windows->magnify.width,windows->magnify.height);
14900  if (status == MagickFalse)
14901    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
14902      display_image->filename);
14903  if (windows->magnify.mapped != MagickFalse)
14904    (void) XMapRaised(display,windows->magnify.id);
14905  if (windows->pan.mapped != MagickFalse)
14906    (void) XMapRaised(display,windows->pan.id);
14907  windows->image.window_changes.width=(int) display_image->columns;
14908  windows->image.window_changes.height=(int) display_image->rows;
14909  (void) XConfigureImage(display,resource_info,windows,display_image);
14910  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14911  (void) XSync(display,MagickFalse);
14912  /*
14913    Respond to events.
14914  */
14915  delay=display_image->delay/MagickMax(display_image->ticks_per_second,1L);
14916  timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
14917  update_time=0;
14918  if (resource_info->update != MagickFalse)
14919    {
14920      MagickBooleanType
14921        status;
14922
14923      /*
14924        Determine when file data was last modified.
14925      */
14926      status=GetPathAttributes(display_image->filename,&attributes);
14927      if (status != MagickFalse)
14928        update_time=attributes.st_mtime;
14929    }
14930  *state&=(~FormerImageState);
14931  *state&=(~MontageImageState);
14932  *state&=(~NextImageState);
14933  do
14934  {
14935    /*
14936      Handle a window event.
14937    */
14938    if (windows->image.mapped != MagickFalse)
14939      if ((display_image->delay != 0) || (resource_info->update != 0))
14940        {
14941          if (timer < time((time_t *) NULL))
14942            {
14943              if (resource_info->update == MagickFalse)
14944                *state|=NextImageState | ExitState;
14945              else
14946                {
14947                  MagickBooleanType
14948                    status;
14949
14950                  /*
14951                    Determine if image file was modified.
14952                  */
14953                  status=GetPathAttributes(display_image->filename,&attributes);
14954                  if (status != MagickFalse)
14955                    if (update_time != attributes.st_mtime)
14956                      {
14957                        /*
14958                          Redisplay image.
14959                        */
14960                        (void) FormatLocaleString(
14961                          resource_info->image_info->filename,MaxTextExtent,
14962                          "%s:%s",display_image->magick,
14963                          display_image->filename);
14964                        nexus=ReadImage(resource_info->image_info,
14965                          &display_image->exception);
14966                        if (nexus != (Image *) NULL)
14967                          {
14968                            nexus=DestroyImage(nexus);
14969                            *state|=NextImageState | ExitState;
14970                          }
14971                      }
14972                  delay=display_image->delay/MagickMax(
14973                    display_image->ticks_per_second,1L);
14974                  timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
14975                }
14976            }
14977          if (XEventsQueued(display,QueuedAfterFlush) == 0)
14978            {
14979              /*
14980                Do not block if delay > 0.
14981              */
14982              XDelay(display,SuspendTime << 2);
14983              continue;
14984            }
14985        }
14986    timestamp=time((time_t *) NULL);
14987    (void) XNextEvent(display,&event);
14988    if (windows->image.stasis == MagickFalse)
14989      windows->image.stasis=(time((time_t *) NULL)-timestamp) > 0 ?
14990        MagickTrue : MagickFalse;
14991    if (windows->magnify.stasis == MagickFalse)
14992      windows->magnify.stasis=(time((time_t *) NULL)-timestamp) > 0 ?
14993        MagickTrue : MagickFalse;
14994    if (event.xany.window == windows->command.id)
14995      {
14996        /*
14997          Select a command from the Command widget.
14998        */
14999        id=XCommandWidget(display,windows,CommandMenu,&event);
15000        if (id < 0)
15001          continue;
15002        (void) CopyMagickString(command,CommandMenu[id],MaxTextExtent);
15003        command_type=CommandMenus[id];
15004        if (id < MagickMenus)
15005          {
15006            /*
15007              Select a command from a pop-up menu.
15008            */
15009            entry=XMenuWidget(display,windows,CommandMenu[id],Menus[id],
15010              command);
15011            if (entry < 0)
15012              continue;
15013            (void) CopyMagickString(command,Menus[id][entry],MaxTextExtent);
15014            command_type=Commands[id][entry];
15015          }
15016        if (command_type != NullCommand)
15017          nexus=XMagickCommand(display,resource_info,windows,command_type,
15018            &display_image);
15019        continue;
15020      }
15021    switch (event.type)
15022    {
15023      case ButtonPress:
15024      {
15025        if (display_image->debug != MagickFalse)
15026          (void) LogMagickEvent(X11Event,GetMagickModule(),
15027            "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
15028            event.xbutton.button,event.xbutton.x,event.xbutton.y);
15029        if ((event.xbutton.button == Button3) &&
15030            (event.xbutton.state & Mod1Mask))
15031          {
15032            /*
15033              Convert Alt-Button3 to Button2.
15034            */
15035            event.xbutton.button=Button2;
15036            event.xbutton.state&=(~Mod1Mask);
15037          }
15038        if (event.xbutton.window == windows->backdrop.id)
15039          {
15040            (void) XSetInputFocus(display,event.xbutton.window,RevertToParent,
15041              event.xbutton.time);
15042            break;
15043          }
15044        if (event.xbutton.window == windows->image.id)
15045          {
15046            switch (event.xbutton.button)
15047            {
15048              case Button1:
15049              {
15050                if (resource_info->immutable)
15051                  {
15052                    /*
15053                      Select a command from the Virtual menu.
15054                    */
15055                    entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15056                      command);
15057                    if (entry >= 0)
15058                      nexus=XMagickCommand(display,resource_info,windows,
15059                        VirtualCommands[entry],&display_image);
15060                    break;
15061                  }
15062                /*
15063                  Map/unmap Command widget.
15064                */
15065                if (windows->command.mapped != MagickFalse)
15066                  (void) XWithdrawWindow(display,windows->command.id,
15067                    windows->command.screen);
15068                else
15069                  {
15070                    (void) XCommandWidget(display,windows,CommandMenu,
15071                      (XEvent *) NULL);
15072                    (void) XMapRaised(display,windows->command.id);
15073                  }
15074                break;
15075              }
15076              case Button2:
15077              {
15078                /*
15079                  User pressed the image magnify button.
15080                */
15081                (void) XMagickCommand(display,resource_info,windows,ZoomCommand,
15082                  &display_image);
15083                XMagnifyImage(display,windows,&event);
15084                break;
15085              }
15086              case Button3:
15087              {
15088                if (resource_info->immutable)
15089                  {
15090                    /*
15091                      Select a command from the Virtual menu.
15092                    */
15093                    entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15094                      command);
15095                    if (entry >= 0)
15096                      nexus=XMagickCommand(display,resource_info,windows,
15097                        VirtualCommands[entry],&display_image);
15098                    break;
15099                  }
15100                if (display_image->montage != (char *) NULL)
15101                  {
15102                    /*
15103                      Open or delete a tile from a visual image directory.
15104                    */
15105                    nexus=XTileImage(display,resource_info,windows,
15106                      display_image,&event);
15107                    if (nexus != (Image *) NULL)
15108                      *state|=MontageImageState | NextImageState | ExitState;
15109                    vid_info.x=(short int) windows->image.x;
15110                    vid_info.y=(short int) windows->image.y;
15111                    break;
15112                  }
15113                /*
15114                  Select a command from the Short Cuts menu.
15115                */
15116                entry=XMenuWidget(display,windows,"Short Cuts",ShortCutsMenu,
15117                  command);
15118                if (entry >= 0)
15119                  nexus=XMagickCommand(display,resource_info,windows,
15120                    ShortCutsCommands[entry],&display_image);
15121                break;
15122              }
15123              case Button4:
15124              {
15125                /*
15126                  Wheel up.
15127                */
15128                XTranslateImage(display,windows,*image,XK_Up);
15129                break;
15130              }
15131              case Button5:
15132              {
15133                /*
15134                  Wheel down.
15135                */
15136                XTranslateImage(display,windows,*image,XK_Down);
15137                break;
15138              }
15139              default:
15140                break;
15141            }
15142            break;
15143          }
15144        if (event.xbutton.window == windows->magnify.id)
15145          {
15146            int
15147              factor;
15148
15149            static const char
15150              *MagnifyMenu[] =
15151              {
15152                "2",
15153                "4",
15154                "5",
15155                "6",
15156                "7",
15157                "8",
15158                "9",
15159                "3",
15160                (char *) NULL,
15161              };
15162
15163            static KeySym
15164              MagnifyCommands[] =
15165              {
15166                XK_2,
15167                XK_4,
15168                XK_5,
15169                XK_6,
15170                XK_7,
15171                XK_8,
15172                XK_9,
15173                XK_3
15174              };
15175
15176            /*
15177              Select a magnify factor from the pop-up menu.
15178            */
15179            factor=XMenuWidget(display,windows,"Magnify",MagnifyMenu,command);
15180            if (factor >= 0)
15181              XMagnifyWindowCommand(display,windows,0,MagnifyCommands[factor]);
15182            break;
15183          }
15184        if (event.xbutton.window == windows->pan.id)
15185          {
15186            switch (event.xbutton.button)
15187            {
15188              case Button4:
15189              {
15190                /*
15191                  Wheel up.
15192                */
15193                XTranslateImage(display,windows,*image,XK_Up);
15194                break;
15195              }
15196              case Button5:
15197              {
15198                /*
15199                  Wheel down.
15200                */
15201                XTranslateImage(display,windows,*image,XK_Down);
15202                break;
15203              }
15204              default:
15205              {
15206                XPanImage(display,windows,&event);
15207                break;
15208              }
15209            }
15210            break;
15211          }
15212        delay=display_image->delay/MagickMax(display_image->ticks_per_second,
15213          1L);
15214        timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15215        break;
15216      }
15217      case ButtonRelease:
15218      {
15219        if (display_image->debug != MagickFalse)
15220          (void) LogMagickEvent(X11Event,GetMagickModule(),
15221            "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
15222            event.xbutton.button,event.xbutton.x,event.xbutton.y);
15223        break;
15224      }
15225      case ClientMessage:
15226      {
15227        if (display_image->debug != MagickFalse)
15228          (void) LogMagickEvent(X11Event,GetMagickModule(),
15229            "Client Message: 0x%lx 0x%lx %d 0x%lx",event.xclient.window,
15230            event.xclient.message_type,event.xclient.format,(unsigned long)
15231            event.xclient.data.l[0]);
15232        if (event.xclient.message_type == windows->im_protocols)
15233          {
15234            if (*event.xclient.data.l == (long) windows->im_update_widget)
15235              {
15236                (void) CloneString(&windows->command.name,MagickTitle);
15237                windows->command.data=MagickMenus;
15238                (void) XCommandWidget(display,windows,CommandMenu,
15239                  (XEvent *) NULL);
15240                break;
15241              }
15242            if (*event.xclient.data.l == (long) windows->im_update_colormap)
15243              {
15244                /*
15245                  Update graphic context and window colormap.
15246                */
15247                for (i=0; i < (int) number_windows; i++)
15248                {
15249                  if (magick_windows[i]->id == windows->icon.id)
15250                    continue;
15251                  context_values.background=pixel->background_color.pixel;
15252                  context_values.foreground=pixel->foreground_color.pixel;
15253                  (void) XChangeGC(display,magick_windows[i]->annotate_context,
15254                    context_mask,&context_values);
15255                  (void) XChangeGC(display,magick_windows[i]->widget_context,
15256                    context_mask,&context_values);
15257                  context_values.background=pixel->foreground_color.pixel;
15258                  context_values.foreground=pixel->background_color.pixel;
15259                  context_values.plane_mask=context_values.background ^
15260                    context_values.foreground;
15261                  (void) XChangeGC(display,magick_windows[i]->highlight_context,
15262                    (size_t) (context_mask | GCPlaneMask),
15263                    &context_values);
15264                  magick_windows[i]->attributes.background_pixel=
15265                    pixel->background_color.pixel;
15266                  magick_windows[i]->attributes.border_pixel=
15267                    pixel->border_color.pixel;
15268                  magick_windows[i]->attributes.colormap=map_info->colormap;
15269                  (void) XChangeWindowAttributes(display,magick_windows[i]->id,
15270                    (unsigned long) magick_windows[i]->mask,
15271                    &magick_windows[i]->attributes);
15272                }
15273                if (windows->pan.mapped != MagickFalse)
15274                  {
15275                    (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
15276                      windows->pan.pixmap);
15277                    (void) XClearWindow(display,windows->pan.id);
15278                    XDrawPanRectangle(display,windows);
15279                  }
15280                if (windows->backdrop.id != (Window) NULL)
15281                  (void) XInstallColormap(display,map_info->colormap);
15282                break;
15283              }
15284            if (*event.xclient.data.l == (long) windows->im_former_image)
15285              {
15286                *state|=FormerImageState | ExitState;
15287                break;
15288              }
15289            if (*event.xclient.data.l == (long) windows->im_next_image)
15290              {
15291                *state|=NextImageState | ExitState;
15292                break;
15293              }
15294            if (*event.xclient.data.l == (long) windows->im_retain_colors)
15295              {
15296                *state|=RetainColorsState;
15297                break;
15298              }
15299            if (*event.xclient.data.l == (long) windows->im_exit)
15300              {
15301                *state|=ExitState;
15302                break;
15303              }
15304            break;
15305          }
15306        if (event.xclient.message_type == windows->dnd_protocols)
15307          {
15308            Atom
15309              selection,
15310              type;
15311
15312            int
15313              format,
15314              status;
15315
15316            unsigned char
15317              *data;
15318
15319            unsigned long
15320              after,
15321              length;
15322
15323            /*
15324              Display image named by the Drag-and-Drop selection.
15325            */
15326            if ((*event.xclient.data.l != 2) && (*event.xclient.data.l != 128))
15327              break;
15328            selection=XInternAtom(display,"DndSelection",MagickFalse);
15329            status=XGetWindowProperty(display,root_window,selection,0L,(long)
15330              MaxTextExtent,MagickFalse,(Atom) AnyPropertyType,&type,&format,
15331              &length,&after,&data);
15332            if ((status != Success) || (length == 0))
15333              break;
15334            if (*event.xclient.data.l == 2)
15335              {
15336                /*
15337                  Offix DND.
15338                */
15339                (void) CopyMagickString(resource_info->image_info->filename,
15340                  (char *) data,MaxTextExtent);
15341              }
15342            else
15343              {
15344                /*
15345                  XDND.
15346                */
15347                if (strncmp((char *) data, "file:", 5) != 0)
15348                  {
15349                    (void) XFree((void *) data);
15350                    break;
15351                  }
15352                (void) CopyMagickString(resource_info->image_info->filename,
15353                  ((char *) data)+5,MaxTextExtent);
15354              }
15355            nexus=ReadImage(resource_info->image_info,
15356              &display_image->exception);
15357            CatchException(&display_image->exception);
15358            if (nexus != (Image *) NULL)
15359              *state|=NextImageState | ExitState;
15360            (void) XFree((void *) data);
15361            break;
15362          }
15363        /*
15364          If client window delete message, exit.
15365        */
15366        if (event.xclient.message_type != windows->wm_protocols)
15367          break;
15368        if (*event.xclient.data.l != (long) windows->wm_delete_window)
15369          break;
15370        (void) XWithdrawWindow(display,event.xclient.window,
15371          visual_info->screen);
15372        if (event.xclient.window == windows->image.id)
15373          {
15374            *state|=ExitState;
15375            break;
15376          }
15377        if (event.xclient.window == windows->pan.id)
15378          {
15379            /*
15380              Restore original image size when pan window is deleted.
15381            */
15382            windows->image.window_changes.width=windows->image.ximage->width;
15383            windows->image.window_changes.height=windows->image.ximage->height;
15384            (void) XConfigureImage(display,resource_info,windows,
15385              display_image);
15386          }
15387        break;
15388      }
15389      case ConfigureNotify:
15390      {
15391        if (display_image->debug != MagickFalse)
15392          (void) LogMagickEvent(X11Event,GetMagickModule(),
15393            "Configure Notify: 0x%lx %dx%d+%d+%d %d",event.xconfigure.window,
15394            event.xconfigure.width,event.xconfigure.height,event.xconfigure.x,
15395            event.xconfigure.y,event.xconfigure.send_event);
15396        if (event.xconfigure.window == windows->image.id)
15397          {
15398            /*
15399              Image window has a new configuration.
15400            */
15401            if (event.xconfigure.send_event != 0)
15402              {
15403                XWindowChanges
15404                  window_changes;
15405
15406                /*
15407                  Position the transient windows relative of the Image window.
15408                */
15409                if (windows->command.geometry == (char *) NULL)
15410                  if (windows->command.mapped == MagickFalse)
15411                    {
15412                      windows->command.x=event.xconfigure.x-
15413                        windows->command.width-25;
15414                      windows->command.y=event.xconfigure.y;
15415                      XConstrainWindowPosition(display,&windows->command);
15416                      window_changes.x=windows->command.x;
15417                      window_changes.y=windows->command.y;
15418                      (void) XReconfigureWMWindow(display,windows->command.id,
15419                        windows->command.screen,(unsigned int) (CWX | CWY),
15420                        &window_changes);
15421                    }
15422                if (windows->widget.geometry == (char *) NULL)
15423                  if (windows->widget.mapped == MagickFalse)
15424                    {
15425                      windows->widget.x=event.xconfigure.x+
15426                        event.xconfigure.width/10;
15427                      windows->widget.y=event.xconfigure.y+
15428                        event.xconfigure.height/10;
15429                      XConstrainWindowPosition(display,&windows->widget);
15430                      window_changes.x=windows->widget.x;
15431                      window_changes.y=windows->widget.y;
15432                      (void) XReconfigureWMWindow(display,windows->widget.id,
15433                        windows->widget.screen,(unsigned int) (CWX | CWY),
15434                        &window_changes);
15435                    }
15436                if (windows->magnify.geometry == (char *) NULL)
15437                  if (windows->magnify.mapped == MagickFalse)
15438                    {
15439                      windows->magnify.x=event.xconfigure.x+
15440                        event.xconfigure.width+25;
15441                      windows->magnify.y=event.xconfigure.y;
15442                      XConstrainWindowPosition(display,&windows->magnify);
15443                      window_changes.x=windows->magnify.x;
15444                      window_changes.y=windows->magnify.y;
15445                      (void) XReconfigureWMWindow(display,windows->magnify.id,
15446                        windows->magnify.screen,(unsigned int) (CWX | CWY),
15447                        &window_changes);
15448                    }
15449                if (windows->pan.geometry == (char *) NULL)
15450                  if (windows->pan.mapped == MagickFalse)
15451                    {
15452                      windows->pan.x=event.xconfigure.x+
15453                        event.xconfigure.width+25;
15454                      windows->pan.y=event.xconfigure.y+
15455                        windows->magnify.height+50;
15456                      XConstrainWindowPosition(display,&windows->pan);
15457                      window_changes.x=windows->pan.x;
15458                      window_changes.y=windows->pan.y;
15459                      (void) XReconfigureWMWindow(display,windows->pan.id,
15460                        windows->pan.screen,(unsigned int) (CWX | CWY),
15461                        &window_changes);
15462                    }
15463              }
15464            if ((event.xconfigure.width == (int) windows->image.width) &&
15465                (event.xconfigure.height == (int) windows->image.height))
15466              break;
15467            windows->image.width=(unsigned int) event.xconfigure.width;
15468            windows->image.height=(unsigned int) event.xconfigure.height;
15469            windows->image.x=0;
15470            windows->image.y=0;
15471            if (display_image->montage != (char *) NULL)
15472              {
15473                windows->image.x=vid_info.x;
15474                windows->image.y=vid_info.y;
15475              }
15476            if ((windows->image.mapped != MagickFalse) &&
15477                (windows->image.stasis != MagickFalse))
15478              {
15479                /*
15480                  Update image window configuration.
15481                */
15482                windows->image.window_changes.width=event.xconfigure.width;
15483                windows->image.window_changes.height=event.xconfigure.height;
15484                (void) XConfigureImage(display,resource_info,windows,
15485                  display_image);
15486              }
15487            /*
15488              Update pan window configuration.
15489            */
15490            if ((event.xconfigure.width < windows->image.ximage->width) ||
15491                (event.xconfigure.height < windows->image.ximage->height))
15492              {
15493                (void) XMapRaised(display,windows->pan.id);
15494                XDrawPanRectangle(display,windows);
15495              }
15496            else
15497              if (windows->pan.mapped != MagickFalse)
15498                (void) XWithdrawWindow(display,windows->pan.id,
15499                  windows->pan.screen);
15500            break;
15501          }
15502        if (event.xconfigure.window == windows->magnify.id)
15503          {
15504            unsigned int
15505              magnify;
15506
15507            /*
15508              Magnify window has a new configuration.
15509            */
15510            windows->magnify.width=(unsigned int) event.xconfigure.width;
15511            windows->magnify.height=(unsigned int) event.xconfigure.height;
15512            if (windows->magnify.mapped == MagickFalse)
15513              break;
15514            magnify=1;
15515            while ((int) magnify <= event.xconfigure.width)
15516              magnify<<=1;
15517            while ((int) magnify <= event.xconfigure.height)
15518              magnify<<=1;
15519            magnify>>=1;
15520            if (((int) magnify != event.xconfigure.width) ||
15521                ((int) magnify != event.xconfigure.height))
15522              {
15523                window_changes.width=(int) magnify;
15524                window_changes.height=(int) magnify;
15525                (void) XReconfigureWMWindow(display,windows->magnify.id,
15526                  windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
15527                  &window_changes);
15528                break;
15529              }
15530            if ((windows->magnify.mapped != MagickFalse) &&
15531                (windows->magnify.stasis != MagickFalse))
15532              {
15533                status=XMakeImage(display,resource_info,&windows->magnify,
15534                  display_image,windows->magnify.width,windows->magnify.height);
15535                XMakeMagnifyImage(display,windows);
15536              }
15537            break;
15538          }
15539        if ((windows->magnify.mapped != MagickFalse) &&
15540            (event.xconfigure.window == windows->pan.id))
15541          {
15542            /*
15543              Pan icon window has a new configuration.
15544            */
15545            if (event.xconfigure.send_event != 0)
15546              {
15547                windows->pan.x=event.xconfigure.x;
15548                windows->pan.y=event.xconfigure.y;
15549              }
15550            windows->pan.width=(unsigned int) event.xconfigure.width;
15551            windows->pan.height=(unsigned int) event.xconfigure.height;
15552            break;
15553          }
15554        if (event.xconfigure.window == windows->icon.id)
15555          {
15556            /*
15557              Icon window has a new configuration.
15558            */
15559            windows->icon.width=(unsigned int) event.xconfigure.width;
15560            windows->icon.height=(unsigned int) event.xconfigure.height;
15561            break;
15562          }
15563        break;
15564      }
15565      case DestroyNotify:
15566      {
15567        /*
15568          Group leader has exited.
15569        */
15570        if (display_image->debug != MagickFalse)
15571          (void) LogMagickEvent(X11Event,GetMagickModule(),
15572            "Destroy Notify: 0x%lx",event.xdestroywindow.window);
15573        if (event.xdestroywindow.window == windows->group_leader.id)
15574          {
15575            *state|=ExitState;
15576            break;
15577          }
15578        break;
15579      }
15580      case EnterNotify:
15581      {
15582        /*
15583          Selectively install colormap.
15584        */
15585        if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15586          if (event.xcrossing.mode != NotifyUngrab)
15587            XInstallColormap(display,map_info->colormap);
15588        break;
15589      }
15590      case Expose:
15591      {
15592        if (display_image->debug != MagickFalse)
15593          (void) LogMagickEvent(X11Event,GetMagickModule(),
15594            "Expose: 0x%lx %dx%d+%d+%d",event.xexpose.window,
15595            event.xexpose.width,event.xexpose.height,event.xexpose.x,
15596            event.xexpose.y);
15597        /*
15598          Refresh windows that are now exposed.
15599        */
15600        if ((event.xexpose.window == windows->image.id) &&
15601            (windows->image.mapped != MagickFalse))
15602          {
15603            XRefreshWindow(display,&windows->image,&event);
15604            delay=display_image->delay/MagickMax(
15605              display_image->ticks_per_second,1L);
15606            timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15607            break;
15608          }
15609        if ((event.xexpose.window == windows->magnify.id) &&
15610            (windows->magnify.mapped != MagickFalse))
15611          {
15612            XMakeMagnifyImage(display,windows);
15613            break;
15614          }
15615        if (event.xexpose.window == windows->pan.id)
15616          {
15617            XDrawPanRectangle(display,windows);
15618            break;
15619          }
15620        if (event.xexpose.window == windows->icon.id)
15621          {
15622            XRefreshWindow(display,&windows->icon,&event);
15623            break;
15624          }
15625        break;
15626      }
15627      case KeyPress:
15628      {
15629        int
15630          length;
15631
15632        /*
15633          Respond to a user key press.
15634        */
15635        length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
15636          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15637        *(command+length)='\0';
15638        if (display_image->debug != MagickFalse)
15639          (void) LogMagickEvent(X11Event,GetMagickModule(),
15640            "Key press: %d 0x%lx (%s)",event.xkey.state,(unsigned long)
15641            key_symbol,command);
15642        if (event.xkey.window == windows->image.id)
15643          {
15644            command_type=XImageWindowCommand(display,resource_info,windows,
15645              event.xkey.state,key_symbol,&display_image);
15646            if (command_type != NullCommand)
15647              nexus=XMagickCommand(display,resource_info,windows,command_type,
15648                &display_image);
15649          }
15650        if (event.xkey.window == windows->magnify.id)
15651          XMagnifyWindowCommand(display,windows,event.xkey.state,key_symbol);
15652        if (event.xkey.window == windows->pan.id)
15653          {
15654            if ((key_symbol == XK_q) || (key_symbol == XK_Escape))
15655              (void) XWithdrawWindow(display,windows->pan.id,
15656                windows->pan.screen);
15657            else
15658              if ((key_symbol == XK_F1) || (key_symbol == XK_Help))
15659                XTextViewWidget(display,resource_info,windows,MagickFalse,
15660                  "Help Viewer - Image Pan",ImagePanHelp);
15661              else
15662                XTranslateImage(display,windows,*image,key_symbol);
15663          }
15664        delay=display_image->delay/MagickMax(
15665          display_image->ticks_per_second,1L);
15666        timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15667        break;
15668      }
15669      case KeyRelease:
15670      {
15671        /*
15672          Respond to a user key release.
15673        */
15674        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
15675          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15676        if (display_image->debug != MagickFalse)
15677          (void) LogMagickEvent(X11Event,GetMagickModule(),
15678            "Key release: 0x%lx (%c)",(unsigned long) key_symbol,*command);
15679        break;
15680      }
15681      case LeaveNotify:
15682      {
15683        /*
15684          Selectively uninstall colormap.
15685        */
15686        if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15687          if (event.xcrossing.mode != NotifyUngrab)
15688            XUninstallColormap(display,map_info->colormap);
15689        break;
15690      }
15691      case MapNotify:
15692      {
15693        if (display_image->debug != MagickFalse)
15694          (void) LogMagickEvent(X11Event,GetMagickModule(),"Map Notify: 0x%lx",
15695            event.xmap.window);
15696        if (event.xmap.window == windows->backdrop.id)
15697          {
15698            (void) XSetInputFocus(display,event.xmap.window,RevertToParent,
15699              CurrentTime);
15700            windows->backdrop.mapped=MagickTrue;
15701            break;
15702          }
15703        if (event.xmap.window == windows->image.id)
15704          {
15705            if (windows->backdrop.id != (Window) NULL)
15706              (void) XInstallColormap(display,map_info->colormap);
15707            if (LocaleCompare(display_image->magick,"LOGO") == 0)
15708              {
15709                if (LocaleCompare(display_image->filename,"LOGO") == 0)
15710                  nexus=XOpenImage(display,resource_info,windows,MagickFalse);
15711              }
15712            if (((int) windows->image.width < windows->image.ximage->width) ||
15713                ((int) windows->image.height < windows->image.ximage->height))
15714              (void) XMapRaised(display,windows->pan.id);
15715            windows->image.mapped=MagickTrue;
15716            break;
15717          }
15718        if (event.xmap.window == windows->magnify.id)
15719          {
15720            XMakeMagnifyImage(display,windows);
15721            windows->magnify.mapped=MagickTrue;
15722            (void) XWithdrawWindow(display,windows->info.id,
15723              windows->info.screen);
15724            break;
15725          }
15726        if (event.xmap.window == windows->pan.id)
15727          {
15728            XMakePanImage(display,resource_info,windows,display_image);
15729            windows->pan.mapped=MagickTrue;
15730            break;
15731          }
15732        if (event.xmap.window == windows->info.id)
15733          {
15734            windows->info.mapped=MagickTrue;
15735            break;
15736          }
15737        if (event.xmap.window == windows->icon.id)
15738          {
15739            MagickBooleanType
15740              taint;
15741
15742            /*
15743              Create an icon image.
15744            */
15745            taint=display_image->taint;
15746            XMakeStandardColormap(display,icon_visual,icon_resources,
15747              display_image,icon_map,icon_pixel);
15748            (void) XMakeImage(display,icon_resources,&windows->icon,
15749              display_image,windows->icon.width,windows->icon.height);
15750            display_image->taint=taint;
15751            (void) XSetWindowBackgroundPixmap(display,windows->icon.id,
15752              windows->icon.pixmap);
15753            (void) XClearWindow(display,windows->icon.id);
15754            (void) XWithdrawWindow(display,windows->info.id,
15755              windows->info.screen);
15756            windows->icon.mapped=MagickTrue;
15757            break;
15758          }
15759        if (event.xmap.window == windows->command.id)
15760          {
15761            windows->command.mapped=MagickTrue;
15762            break;
15763          }
15764        if (event.xmap.window == windows->popup.id)
15765          {
15766            windows->popup.mapped=MagickTrue;
15767            break;
15768          }
15769        if (event.xmap.window == windows->widget.id)
15770          {
15771            windows->widget.mapped=MagickTrue;
15772            break;
15773          }
15774        break;
15775      }
15776      case MappingNotify:
15777      {
15778        (void) XRefreshKeyboardMapping(&event.xmapping);
15779        break;
15780      }
15781      case NoExpose:
15782        break;
15783      case PropertyNotify:
15784      {
15785        Atom
15786          type;
15787
15788        int
15789          format,
15790          status;
15791
15792        unsigned char
15793          *data;
15794
15795        unsigned long
15796          after,
15797          length;
15798
15799        if (display_image->debug != MagickFalse)
15800          (void) LogMagickEvent(X11Event,GetMagickModule(),
15801            "Property Notify: 0x%lx 0x%lx %d",event.xproperty.window,
15802            event.xproperty.atom,event.xproperty.state);
15803        if (event.xproperty.atom != windows->im_remote_command)
15804          break;
15805        /*
15806          Display image named by the remote command protocol.
15807        */
15808        status=XGetWindowProperty(display,event.xproperty.window,
15809          event.xproperty.atom,0L,(long) MaxTextExtent,MagickFalse,(Atom)
15810          AnyPropertyType,&type,&format,&length,&after,&data);
15811        if ((status != Success) || (length == 0))
15812          break;
15813        if (LocaleCompare((char *) data,"-quit") == 0)
15814          {
15815            XClientMessage(display,windows->image.id,windows->im_protocols,
15816              windows->im_exit,CurrentTime);
15817            (void) XFree((void *) data);
15818            break;
15819          }
15820        (void) CopyMagickString(resource_info->image_info->filename,
15821          (char *) data,MaxTextExtent);
15822        (void) XFree((void *) data);
15823        nexus=ReadImage(resource_info->image_info,&display_image->exception);
15824        CatchException(&display_image->exception);
15825        if (nexus != (Image *) NULL)
15826          *state|=NextImageState | ExitState;
15827        break;
15828      }
15829      case ReparentNotify:
15830      {
15831        if (display_image->debug != MagickFalse)
15832          (void) LogMagickEvent(X11Event,GetMagickModule(),
15833            "Reparent Notify: 0x%lx=>0x%lx",event.xreparent.parent,
15834            event.xreparent.window);
15835        break;
15836      }
15837      case UnmapNotify:
15838      {
15839        if (display_image->debug != MagickFalse)
15840          (void) LogMagickEvent(X11Event,GetMagickModule(),
15841            "Unmap Notify: 0x%lx",event.xunmap.window);
15842        if (event.xunmap.window == windows->backdrop.id)
15843          {
15844            windows->backdrop.mapped=MagickFalse;
15845            break;
15846          }
15847        if (event.xunmap.window == windows->image.id)
15848          {
15849            windows->image.mapped=MagickFalse;
15850            break;
15851          }
15852        if (event.xunmap.window == windows->magnify.id)
15853          {
15854            windows->magnify.mapped=MagickFalse;
15855            break;
15856          }
15857        if (event.xunmap.window == windows->pan.id)
15858          {
15859            windows->pan.mapped=MagickFalse;
15860            break;
15861          }
15862        if (event.xunmap.window == windows->info.id)
15863          {
15864            windows->info.mapped=MagickFalse;
15865            break;
15866          }
15867        if (event.xunmap.window == windows->icon.id)
15868          {
15869            if (map_info->colormap == icon_map->colormap)
15870              XConfigureImageColormap(display,resource_info,windows,
15871                display_image);
15872            (void) XFreeStandardColormap(display,icon_visual,icon_map,
15873              icon_pixel);
15874            windows->icon.mapped=MagickFalse;
15875            break;
15876          }
15877        if (event.xunmap.window == windows->command.id)
15878          {
15879            windows->command.mapped=MagickFalse;
15880            break;
15881          }
15882        if (event.xunmap.window == windows->popup.id)
15883          {
15884            if (windows->backdrop.id != (Window) NULL)
15885              (void) XSetInputFocus(display,windows->image.id,RevertToParent,
15886                CurrentTime);
15887            windows->popup.mapped=MagickFalse;
15888            break;
15889          }
15890        if (event.xunmap.window == windows->widget.id)
15891          {
15892            if (windows->backdrop.id != (Window) NULL)
15893              (void) XSetInputFocus(display,windows->image.id,RevertToParent,
15894                CurrentTime);
15895            windows->widget.mapped=MagickFalse;
15896            break;
15897          }
15898        break;
15899      }
15900      default:
15901      {
15902        if (display_image->debug != MagickFalse)
15903          (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
15904            event.type);
15905        break;
15906      }
15907    }
15908  } while (!(*state & ExitState));
15909  if ((*state & ExitState) == 0)
15910    (void) XMagickCommand(display,resource_info,windows,FreeBuffersCommand,
15911      &display_image);
15912  else
15913    if (resource_info->confirm_edit != MagickFalse)
15914      {
15915        /*
15916          Query user if image has changed.
15917        */
15918        if ((resource_info->immutable == MagickFalse) &&
15919            (display_image->taint != MagickFalse))
15920          {
15921            int
15922              status;
15923
15924            status=XConfirmWidget(display,windows,"Your image changed.",
15925              "Do you want to save it");
15926            if (status == 0)
15927              *state&=(~ExitState);
15928            else
15929              if (status > 0)
15930                (void) XMagickCommand(display,resource_info,windows,SaveCommand,
15931                  &display_image);
15932          }
15933      }
15934  if ((windows->visual_info->klass == GrayScale) ||
15935      (windows->visual_info->klass == PseudoColor) ||
15936      (windows->visual_info->klass == DirectColor))
15937    {
15938      /*
15939        Withdraw pan and Magnify window.
15940      */
15941      if (windows->info.mapped != MagickFalse)
15942        (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15943      if (windows->magnify.mapped != MagickFalse)
15944        (void) XWithdrawWindow(display,windows->magnify.id,
15945          windows->magnify.screen);
15946      if (windows->command.mapped != MagickFalse)
15947        (void) XWithdrawWindow(display,windows->command.id,
15948          windows->command.screen);
15949    }
15950  if (windows->pan.mapped != MagickFalse)
15951    (void) XWithdrawWindow(display,windows->pan.id,windows->pan.screen);
15952  if (resource_info->backdrop == MagickFalse)
15953    if (windows->backdrop.mapped)
15954      {
15955        (void) XWithdrawWindow(display,windows->backdrop.id,
15956          windows->backdrop.screen);
15957        (void) XDestroyWindow(display,windows->backdrop.id);
15958        windows->backdrop.id=(Window) NULL;
15959        (void) XWithdrawWindow(display,windows->image.id,
15960          windows->image.screen);
15961        (void) XDestroyWindow(display,windows->image.id);
15962        windows->image.id=(Window) NULL;
15963      }
15964  XSetCursorState(display,windows,MagickTrue);
15965  XCheckRefreshWindows(display,windows);
15966  if (((*state & FormerImageState) != 0) || ((*state & NextImageState) != 0))
15967    *state&=(~ExitState);
15968  if (*state & ExitState)
15969    {
15970      /*
15971        Free Standard Colormap.
15972      */
15973      (void) XFreeStandardColormap(display,icon_visual,icon_map,icon_pixel);
15974      if (resource_info->map_type == (char *) NULL)
15975        (void) XFreeStandardColormap(display,visual_info,map_info,pixel);
15976      /*
15977        Free X resources.
15978      */
15979      if (resource_info->copy_image != (Image *) NULL)
15980        {
15981          resource_info->copy_image=DestroyImage(resource_info->copy_image);
15982          resource_info->copy_image=NewImageList();
15983        }
15984      DestroyXResources();
15985    }
15986  (void) XSync(display,MagickFalse);
15987  /*
15988    Restore our progress monitor and warning handlers.
15989  */
15990  (void) SetErrorHandler(warning_handler);
15991  (void) SetWarningHandler(warning_handler);
15992  /*
15993    Change to home directory.
15994  */
15995  directory=getcwd(working_directory,MaxTextExtent);
15996  (void) directory;
15997  {
15998    int
15999      status;
16000
16001    status=chdir(resource_info->home_directory);
16002    if (status == -1)
16003      (void) ThrowMagickException(&display_image->exception,GetMagickModule(),
16004        FileOpenError,"UnableToOpenFile","%s",resource_info->home_directory);
16005  }
16006  *image=display_image;
16007  return(nexus);
16008}
16009#else
16010
16011/*
16012%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16013%                                                                             %
16014%                                                                             %
16015%                                                                             %
16016+   D i s p l a y I m a g e s                                                 %
16017%                                                                             %
16018%                                                                             %
16019%                                                                             %
16020%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16021%
16022%  DisplayImages() displays an image sequence to any X window screen.  It
16023%  returns a value other than 0 if successful.  Check the exception member
16024%  of image to determine the reason for any failure.
16025%
16026%  The format of the DisplayImages method is:
16027%
16028%      MagickBooleanType DisplayImages(const ImageInfo *image_info,
16029%        Image *images)
16030%
16031%  A description of each parameter follows:
16032%
16033%    o image_info: the image info.
16034%
16035%    o image: the image.
16036%
16037*/
16038MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
16039  Image *image)
16040{
16041  assert(image_info != (const ImageInfo *) NULL);
16042  assert(image_info->signature == MagickSignature);
16043  assert(image != (Image *) NULL);
16044  assert(image->signature == MagickSignature);
16045  if (image->debug != MagickFalse)
16046    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
16047  (void) ThrowMagickException(&image->exception,GetMagickModule(),
16048    MissingDelegateError,"DelegateLibrarySupportNotBuiltIn","`%s' (X11)",
16049    image->filename);
16050  return(MagickFalse);
16051}
16052
16053/*
16054%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16055%                                                                             %
16056%                                                                             %
16057%                                                                             %
16058+   R e m o t e D i s p l a y C o m m a n d                                   %
16059%                                                                             %
16060%                                                                             %
16061%                                                                             %
16062%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16063%
16064%  RemoteDisplayCommand() encourages a remote display program to display the
16065%  specified image filename.
16066%
16067%  The format of the RemoteDisplayCommand method is:
16068%
16069%      MagickBooleanType RemoteDisplayCommand(const ImageInfo *image,
16070%        const char *window,const char *filename,ExceptionInfo *exception)
16071%
16072%  A description of each parameter follows:
16073%
16074%    o image_info: the image info.
16075%
16076%    o window: Specifies the name or id of an X window.
16077%
16078%    o filename: the name of the image filename to display.
16079%
16080%    o exception: return any errors or warnings in this structure.
16081%
16082*/
16083MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
16084  const char *window,const char *filename,ExceptionInfo *exception)
16085{
16086  assert(image_info != (const ImageInfo *) NULL);
16087  assert(image_info->signature == MagickSignature);
16088  assert(filename != (char *) NULL);
16089  (void) window;
16090  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
16091  (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16092    "DelegateLibrarySupportNotBuiltIn","`%s' (X11)",image_info->filename);
16093  return(MagickFalse);
16094}
16095#endif
16096